Fix bad merge of #2162
[bitbake.git] / lib / bb / ui / crumbs / hig.py
1 #
2 # BitBake Graphical GTK User Interface
3 #
4 # Copyright (C) 2011-2012   Intel Corporation
5 #
6 # Authored by Joshua Lock <josh@linux.intel.com>
7 # Authored by Dongxiao Xu <dongxiao.xu@intel.com>
8 # Authored by Shane Wang <shane.wang@intel.com>
9 #
10 # This program is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License version 2 as
12 # published by the Free Software Foundation.
13 #
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 # GNU General Public License for more details.
18 #
19 # You should have received a copy of the GNU General Public License along
20 # with this program; if not, write to the Free Software Foundation, Inc.,
21 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22
23 import glob
24 import glib
25 import gtk
26 import gobject
27 import hashlib
28 import os
29 import re
30 import shlex
31 import subprocess
32 import tempfile
33 from bb.ui.crumbs.hobcolor import HobColors
34 from bb.ui.crumbs.hobwidget import hic, HobViewTable, HobInfoButton, HobButton, HobAltButton, HobIconChecker
35 from bb.ui.crumbs.progressbar import HobProgressBar
36 import bb.ui.crumbs.utils
37 import bb.process
38
39 """
40 The following are convenience classes for implementing GNOME HIG compliant
41 BitBake GUI's
42 In summary: spacing = 12px, border-width = 6px
43 """
44
45
46 class SettingsUIHelper():
47
48     def gen_label_widget(self, content):
49         label = gtk.Label()
50         label.set_alignment(0, 0)
51         label.set_markup(content)
52         label.show()
53         return label
54
55     def gen_label_info_widget(self, content, tooltip):
56         table = gtk.Table(1, 10, False)
57         label = self.gen_label_widget(content)
58         info = HobInfoButton(tooltip, self)
59         table.attach(label, 0, 1, 0, 1, xoptions=gtk.FILL)
60         table.attach(info, 1, 2, 0, 1, xoptions=gtk.FILL, xpadding=10)
61         return table
62
63     def gen_spinner_widget(self, content, lower, upper, tooltip=""):
64         hbox = gtk.HBox(False, 12)
65         adjust = gtk.Adjustment(value=content, lower=lower, upper=upper, step_incr=1)
66         spinner = gtk.SpinButton(adjustment=adjust, climb_rate=1, digits=0)
67
68         spinner.set_value(content)
69         hbox.pack_start(spinner, expand=False, fill=False)
70
71         info = HobInfoButton(tooltip, self)
72         hbox.pack_start(info, expand=False, fill=False)
73
74         hbox.show_all()
75         return hbox, spinner
76
77     def gen_combo_widget(self, curr_item, all_item, tooltip=""):
78         hbox = gtk.HBox(False, 12)
79         combo = gtk.combo_box_new_text()
80         hbox.pack_start(combo, expand=False, fill=False)
81
82         index = 0
83         for item in all_item or []:
84             combo.append_text(item)
85             if item == curr_item:
86                 combo.set_active(index)
87             index += 1
88
89         info = HobInfoButton(tooltip, self)
90         hbox.pack_start(info, expand=False, fill=False)
91
92         hbox.show_all()
93         return hbox, combo
94
95     def entry_widget_select_path_cb(self, action, parent, entry):
96         dialog = gtk.FileChooserDialog("", parent,
97                                        gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
98         text = entry.get_text()
99         dialog.set_current_folder(text if len(text) > 0 else os.getcwd())
100         button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
101         HobAltButton.style_button(button)
102         button = dialog.add_button("Open", gtk.RESPONSE_YES)
103         HobButton.style_button(button)
104         response = dialog.run()
105         if response == gtk.RESPONSE_YES:
106             path = dialog.get_filename()
107             entry.set_text(path)
108
109         dialog.destroy()
110
111     def gen_entry_widget(self, content, parent, tooltip="", need_button=True):
112         hbox = gtk.HBox(False, 12)
113         entry = gtk.Entry()
114         entry.set_text(content)
115
116         if need_button:
117             table = gtk.Table(1, 10, True)
118             hbox.pack_start(table, expand=True, fill=True)
119             table.attach(entry, 0, 9, 0, 1)
120             image = gtk.Image()
121             image.set_from_stock(gtk.STOCK_OPEN,gtk.ICON_SIZE_BUTTON)
122             open_button = gtk.Button()
123             open_button.set_image(image)
124             open_button.connect("clicked", self.entry_widget_select_path_cb, parent, entry)
125             table.attach(open_button, 9, 10, 0, 1)
126         else:
127             hbox.pack_start(entry, expand=True, fill=True)
128
129         if tooltip != "":
130             info = HobInfoButton(tooltip, self)
131             hbox.pack_start(info, expand=False, fill=False)
132
133         hbox.show_all()
134         return hbox, entry
135
136     def gen_mirror_entry_widget(self, content, index, match_content=""):
137         hbox = gtk.HBox(False, 12)
138         entry = gtk.Entry()
139         content = content[:-2]
140         entry.set_text(content)
141         entry_match = gtk.Entry()
142         entry_match.set_text(match_content)
143
144         table = gtk.Table(2, 6, True)
145         hbox.pack_start(table, expand=True, fill=True)
146         label_configuration = gtk.Label("Configuration")
147         label_mirror_url = gtk.Label("Mirror URL")
148         label_match = gtk.Label("Match")
149         label_replace_with = gtk.Label("Replace with")
150
151         combo = gtk.combo_box_new_text()
152         combo.append_text("Standard")
153         combo.append_text("Custom")
154         if match_content == "":
155             combo.set_active(0)
156         else:
157             combo.set_active(1)
158         combo.connect("changed", self.on_combo_changed, index)
159
160         delete_button = HobAltButton("Delete")
161         delete_button.connect("clicked", self.delete_cb, index, entry)
162         if content == "":
163             delete_button.set_sensitive(False)
164
165         entry_match.connect("changed", self.insert_entry_match_cb, index)
166         entry.connect("changed", self.insert_entry_cb, index, delete_button)
167
168         if match_content == "":
169             table.attach(label_configuration, 0, 1, 0, 1)
170             table.attach(label_mirror_url, 1, 2, 0, 1)
171             table.attach(combo, 0, 1, 1, 2)
172             table.attach(entry, 1, 5, 1, 2)
173             table.attach(delete_button, 5, 6, 1, 2)
174         else:
175             table.attach(label_configuration, 0, 1, 0, 1)
176             table.attach(label_match, 1, 2, 0, 1)
177             table.attach(label_replace_with, 2, 3, 0, 1)
178             table.attach(combo, 0, 1, 1, 2)
179             table.attach(entry_match, 1, 2, 1, 2)
180             table.attach(entry, 2, 5, 1, 2)
181             table.attach(delete_button, 5, 6, 1, 2)
182
183         hbox.show_all()
184         return hbox
185
186     def insert_entry_match_cb(self, entry_match, index):
187         self.sstatemirrors_list[index][2] = entry_match.get_text()
188
189     def insert_entry_cb(self, entry, index, button):
190         self.sstatemirrors_list[index][1] = entry.get_text()
191         if entry.get_text() == "":
192             button.set_sensitive(False)
193         else:
194             button.set_sensitive(True)
195
196     def on_combo_changed(self, combo, index):
197         if combo.get_active_text() == "Standard":
198             self.sstatemirrors_list[index][0] = 0
199         else:
200             self.sstatemirrors_list[index][0] = 1
201         self.refresh_shared_state_page()
202
203     def delete_cb(self, button, index, entry):
204         if index == 0 and len(self.sstatemirrors_list)==1:
205             entry.set_text("")
206         else:
207             self.sstatemirrors_list.pop(index)
208             self.refresh_shared_state_page()
209
210     def add_mirror(self, button):
211         tooltip = "Select the pre-built mirror that will speed your build"
212         index = len(self.sstatemirrors_list)
213         sm_list = [0, "", "file://(.*)"]
214         self.sstatemirrors_list.append(sm_list)
215         self.refresh_shared_state_page()
216
217 #
218 # CrumbsDialog
219 #
220 class CrumbsDialog(gtk.Dialog):
221     """
222     A GNOME HIG compliant dialog widget.
223     Add buttons with gtk.Dialog.add_button or gtk.Dialog.add_buttons
224     """
225     def __init__(self, title="", parent=None, flags=0, buttons=None):
226         super(CrumbsDialog, self).__init__(title, parent, flags, buttons)
227
228         self.set_property("has-separator", False) # note: deprecated in 2.22
229
230         self.set_border_width(6)
231         self.vbox.set_property("spacing", 12)
232         self.action_area.set_property("spacing", 12)
233         self.action_area.set_property("border-width", 6)
234
235 class CrumbsMessageDialog(CrumbsDialog):
236     """
237     A GNOME HIG compliant dialog widget.
238     Add buttons with gtk.Dialog.add_button or gtk.Dialog.add_buttons
239     """
240     def __init__(self, parent=None, label="", icon=gtk.STOCK_INFO, msg=""):
241         super(CrumbsMessageDialog, self).__init__("", parent, gtk.DIALOG_MODAL)
242
243         self.set_border_width(6)
244         self.vbox.set_property("spacing", 12)
245         self.action_area.set_property("spacing", 12)
246         self.action_area.set_property("border-width", 6)
247
248         first_column = gtk.HBox(spacing=12)
249         first_column.set_property("border-width", 6)
250         first_column.show()
251         self.vbox.add(first_column)
252
253         self.icon = gtk.Image()
254         # We have our own Info icon which should be used in preference of the stock icon
255         self.icon_chk = HobIconChecker()
256         self.icon.set_from_stock(self.icon_chk.check_stock_icon(icon), gtk.ICON_SIZE_DIALOG)
257         self.icon.set_property("yalign", 0.00)
258         self.icon.show()
259         first_column.pack_start(self.icon, expand=False, fill=True, padding=0)
260         
261         if 0 <= len(msg) < 200:
262             lbl = label + "%s" % glib.markup_escape_text(msg)
263             self.label_short = gtk.Label()
264             self.label_short.set_use_markup(True)
265             self.label_short.set_line_wrap(True)
266             self.label_short.set_markup(lbl)
267             self.label_short.set_property("yalign", 0.00)
268             self.label_short.show()
269             first_column.add(self.label_short)
270         else:
271             second_row = gtk.VBox(spacing=12)
272             second_row.set_property("border-width", 6)
273             self.label_long = gtk.Label()
274             self.label_long.set_use_markup(True)
275             self.label_long.set_line_wrap(True)
276             self.label_long.set_markup(label)
277             self.label_long.set_alignment(0.0, 0.0)
278             second_row.pack_start(self.label_long, expand=False, fill=False, padding=0)
279             self.label_long.show()
280             self.textWindow = gtk.ScrolledWindow()
281             self.textWindow.set_shadow_type(gtk.SHADOW_IN)
282             self.msgView = gtk.TextView()
283             self.msgView.set_editable(False)
284             self.msgView.set_wrap_mode(gtk.WRAP_WORD)
285             self.msgView.set_cursor_visible(False)
286             self.msgView.set_size_request(300, 300)
287             self.buf = gtk.TextBuffer()
288             self.buf.set_text(msg)
289             self.msgView.set_buffer(self.buf)
290             self.textWindow.add(self.msgView)
291             self.msgView.show()
292             second_row.add(self.textWindow)
293             self.textWindow.show()
294             first_column.add(second_row)
295             second_row.show()
296
297 #
298 # SimpleSettings Dialog
299 #
300 class SimpleSettingsDialog (CrumbsDialog, SettingsUIHelper):
301
302     (BUILD_ENV_PAGE_ID,
303      SHARED_STATE_PAGE_ID,
304      PROXIES_PAGE_ID,
305      OTHERS_PAGE_ID) = range(4)
306
307     def __init__(self, title, configuration, all_image_types,
308             all_package_formats, all_distros, all_sdk_machines,
309             max_threads, parent, flags, buttons=None):
310         super(SimpleSettingsDialog, self).__init__(title, parent, flags, buttons)
311
312         # class members from other objects
313         # bitbake settings from Builder.Configuration
314         self.configuration = configuration
315         self.image_types = all_image_types
316         self.all_package_formats = all_package_formats
317         self.all_distros = all_distros
318         self.all_sdk_machines = all_sdk_machines
319         self.max_threads = max_threads
320
321         # class members for internal use
322         self.dldir_text = None
323         self.sstatedir_text = None
324         self.sstatemirrors_list = []
325         self.sstatemirrors_changed = 0
326         self.bb_spinner = None
327         self.pmake_spinner = None
328         self.rootfs_size_spinner = None
329         self.extra_size_spinner = None
330         self.gplv3_checkbox = None
331         self.toolchain_checkbox = None
332         self.setting_store = None
333         self.image_types_checkbuttons = {}
334
335         self.md5 = self.config_md5()
336         self.settings_changed = False
337
338         # create visual elements on the dialog
339         self.create_visual_elements()
340         self.connect("response", self.response_cb)
341
342     def _get_sorted_value(self, var):
343         return " ".join(sorted(str(var).split())) + "\n"
344
345     def config_md5(self):
346         data = ""
347         data += ("ENABLE_PROXY: "         + self._get_sorted_value(self.configuration.enable_proxy))
348         if self.configuration.enable_proxy:
349             for protocol in self.configuration.proxies.keys():
350                 data += (protocol + ": " + self._get_sorted_value(self.configuration.combine_proxy(protocol)))
351         for key in self.configuration.extra_setting.keys():
352             data += (key + ": " + self._get_sorted_value(self.configuration.extra_setting[key]))
353         return hashlib.md5(data).hexdigest()
354
355     def details_cb(self, button, parent, protocol):
356         dialog = ProxyDetailsDialog(title = protocol.upper() + " Proxy Details",
357             user = self.configuration.proxies[protocol][1],
358             passwd = self.configuration.proxies[protocol][2],
359             parent = parent,
360             flags = gtk.DIALOG_MODAL
361                     | gtk.DIALOG_DESTROY_WITH_PARENT
362                     | gtk.DIALOG_NO_SEPARATOR)
363         dialog.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_OK)
364         response = dialog.run()
365         if response == gtk.RESPONSE_OK:
366             self.configuration.proxies[protocol][1] = dialog.user
367             self.configuration.proxies[protocol][2] = dialog.passwd
368             self.refresh_proxy_components()
369         dialog.destroy()
370
371     def gen_proxy_entry_widget(self, protocol, parent, need_button=True):
372         hbox = gtk.HBox(False, 12)
373
374         label = gtk.Label(protocol.upper() + " proxy")
375         hbox.pack_start(label, expand=True, fill=False, padding=24)
376
377         proxy_entry = gtk.Entry()
378         proxy_entry.set_size_request(300, -1)
379         hbox.pack_start(proxy_entry, expand=False, fill=False)
380
381         hbox.pack_start(gtk.Label(":"), expand=False, fill=False)
382
383         port_entry = gtk.Entry()
384         port_entry.set_size_request(60, -1)
385         hbox.pack_start(port_entry, expand=False, fill=False)
386
387         details_button = HobAltButton("Details")
388         details_button.connect("clicked", self.details_cb, parent, protocol)
389         hbox.pack_start(details_button, expand=False, fill=False)
390
391         hbox.show_all()
392         return hbox, proxy_entry, port_entry, details_button
393
394     def refresh_proxy_components(self):
395         self.same_checkbox.set_sensitive(self.configuration.enable_proxy)
396
397         self.http_proxy.set_text(self.configuration.combine_host_only("http"))
398         self.http_proxy.set_editable(self.configuration.enable_proxy)
399         self.http_proxy.set_sensitive(self.configuration.enable_proxy)
400         self.http_proxy_port.set_text(self.configuration.combine_port_only("http"))
401         self.http_proxy_port.set_editable(self.configuration.enable_proxy)
402         self.http_proxy_port.set_sensitive(self.configuration.enable_proxy)
403         self.http_proxy_details.set_sensitive(self.configuration.enable_proxy)
404
405         self.https_proxy.set_text(self.configuration.combine_host_only("https"))
406         self.https_proxy.set_editable(self.configuration.enable_proxy and (not self.configuration.same_proxy))
407         self.https_proxy.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
408         self.https_proxy_port.set_text(self.configuration.combine_port_only("https"))
409         self.https_proxy_port.set_editable(self.configuration.enable_proxy and (not self.configuration.same_proxy))
410         self.https_proxy_port.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
411         self.https_proxy_details.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
412
413         self.ftp_proxy.set_text(self.configuration.combine_host_only("ftp"))
414         self.ftp_proxy.set_editable(self.configuration.enable_proxy and (not self.configuration.same_proxy))
415         self.ftp_proxy.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
416         self.ftp_proxy_port.set_text(self.configuration.combine_port_only("ftp"))
417         self.ftp_proxy_port.set_editable(self.configuration.enable_proxy and (not self.configuration.same_proxy))
418         self.ftp_proxy_port.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
419         self.ftp_proxy_details.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
420
421         self.git_proxy.set_text(self.configuration.combine_host_only("git"))
422         self.git_proxy.set_editable(self.configuration.enable_proxy and (not self.configuration.same_proxy))
423         self.git_proxy.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
424         self.git_proxy_port.set_text(self.configuration.combine_port_only("git"))
425         self.git_proxy_port.set_editable(self.configuration.enable_proxy and (not self.configuration.same_proxy))
426         self.git_proxy_port.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
427         self.git_proxy_details.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
428
429         self.cvs_proxy.set_text(self.configuration.combine_host_only("cvs"))
430         self.cvs_proxy.set_editable(self.configuration.enable_proxy and (not self.configuration.same_proxy))
431         self.cvs_proxy.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
432         self.cvs_proxy_port.set_text(self.configuration.combine_port_only("cvs"))
433         self.cvs_proxy_port.set_editable(self.configuration.enable_proxy and (not self.configuration.same_proxy))
434         self.cvs_proxy_port.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
435         self.cvs_proxy_details.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
436
437     def proxy_checkbox_toggled_cb(self, button):
438         self.configuration.enable_proxy = self.proxy_checkbox.get_active()
439         if not self.configuration.enable_proxy:
440             self.configuration.same_proxy = False
441             self.same_checkbox.set_active(self.configuration.same_proxy)
442         self.refresh_proxy_components()
443
444     def same_checkbox_toggled_cb(self, button):
445         self.configuration.same_proxy = self.same_checkbox.get_active()
446         self.refresh_proxy_components()
447
448     def response_cb(self, dialog, response_id):        
449         self.configuration.dldir = self.dldir_text.get_text()
450         self.configuration.sstatedir = self.sstatedir_text.get_text()
451         self.configuration.sstatemirror = ""
452         for mirror in self.sstatemirrors_list:
453             if mirror[1] != "" or len(self.sstatemirrors_list)==1:
454                 if mirror[1].endswith("\\1"):
455                     smirror = mirror[2] + " " + mirror[1] + " \\n "
456                 else:
457                     smirror = mirror[2] + " " + mirror[1] + "\\1 \\n "
458                 self.configuration.sstatemirror += smirror
459         self.configuration.bbthread = self.bb_spinner.get_value_as_int()
460         self.configuration.pmake = self.pmake_spinner.get_value_as_int()
461                 
462         self.configuration.split_proxy("http", self.http_proxy.get_text() + ":" + self.http_proxy_port.get_text())
463         if self.configuration.same_proxy:
464             self.configuration.split_proxy("https", self.http_proxy.get_text() + ":" + self.http_proxy_port.get_text())
465             self.configuration.split_proxy("ftp", self.http_proxy.get_text() + ":" + self.http_proxy_port.get_text())
466             self.configuration.split_proxy("git", self.http_proxy.get_text() + ":" + self.http_proxy_port.get_text())
467             self.configuration.split_proxy("cvs", self.http_proxy.get_text() + ":" + self.http_proxy_port.get_text())
468         else:
469             self.configuration.split_proxy("https", self.https_proxy.get_text() + ":" + self.https_proxy_port.get_text())
470             self.configuration.split_proxy("ftp", self.ftp_proxy.get_text() + ":" + self.ftp_proxy_port.get_text())
471             self.configuration.split_proxy("git", self.git_proxy.get_text() + ":" + self.git_proxy_port.get_text())
472             self.configuration.split_proxy("cvs", self.cvs_proxy.get_text() + ":" + self.cvs_proxy_port.get_text())
473
474         self.configuration.extra_setting = {}
475         it = self.setting_store.get_iter_first()
476         while it:
477             key = self.setting_store.get_value(it, 0)
478             value = self.setting_store.get_value(it, 1)
479             self.configuration.extra_setting[key] = value
480             it = self.setting_store.iter_next(it)
481
482         md5 = self.config_md5()
483         self.settings_changed = (self.md5 != md5)
484
485     def create_build_environment_page(self):
486         advanced_vbox = gtk.VBox(False, 6)
487         advanced_vbox.set_border_width(6)
488
489         advanced_vbox.pack_start(self.gen_label_widget('<span weight="bold">Parallel threads</span>'), expand=False, fill=False)
490         sub_vbox = gtk.VBox(False, 6)
491         advanced_vbox.pack_start(sub_vbox, expand=False, fill=False)
492         label = self.gen_label_widget("BitBake parallel threads")
493         tooltip = "Sets the number of threads that BitBake tasks can simultaneously run. See the <a href=\""
494         tooltip += "http://www.yoctoproject.org/docs/current/poky-ref-manual/"
495         tooltip += "poky-ref-manual.html#var-BB_NUMBER_THREADS\">Poky reference manual</a> for information"
496         bbthread_widget, self.bb_spinner = self.gen_spinner_widget(self.configuration.bbthread, 1, self.max_threads, tooltip)
497         sub_vbox.pack_start(label, expand=False, fill=False)
498         sub_vbox.pack_start(bbthread_widget, expand=False, fill=False)
499
500         sub_vbox = gtk.VBox(False, 6)
501         advanced_vbox.pack_start(sub_vbox, expand=False, fill=False)
502         label = self.gen_label_widget("Make parallel threads")
503         tooltip = "Sets the maximum number of threads the host can use during the build. See the <a href=\""
504         tooltip += "http://www.yoctoproject.org/docs/current/poky-ref-manual/"
505         tooltip += "poky-ref-manual.html#var-PARALLEL_MAKE\">Poky reference manual</a> for information"
506         pmake_widget, self.pmake_spinner = self.gen_spinner_widget(self.configuration.pmake, 1, self.max_threads, tooltip)
507         sub_vbox.pack_start(label, expand=False, fill=False)
508         sub_vbox.pack_start(pmake_widget, expand=False, fill=False)
509
510         advanced_vbox.pack_start(self.gen_label_widget('<span weight="bold">Cache directories and mirror</span>'), expand=False, fill=False)
511         sub_vbox = gtk.VBox(False, 6)
512         advanced_vbox.pack_start(sub_vbox, expand=False, fill=False)
513         label = self.gen_label_widget("Download directory")
514         tooltip = "Select a folder that caches the upstream project source code"
515         dldir_widget, self.dldir_text = self.gen_entry_widget(self.configuration.dldir, self, tooltip)
516         sub_vbox.pack_start(label, expand=False, fill=False)
517         sub_vbox.pack_start(dldir_widget, expand=False, fill=False)
518
519         return advanced_vbox
520
521     def create_shared_state_page(self):
522         advanced_vbox = gtk.VBox(False, 6)
523         advanced_vbox.set_border_width(6)
524
525         sub_vbox = gtk.VBox(False, 6)
526         advanced_vbox.pack_start(sub_vbox, expand=False, fill=False)
527         content = "<span weight=\"bold\">Shared state directory</span>"
528         tooltip = "Select a folder that caches your prebuilt results"
529         label = self.gen_label_info_widget(content, tooltip)
530         sstatedir_widget, self.sstatedir_text = self.gen_entry_widget(self.configuration.sstatedir, self)
531         sub_vbox.pack_start(label, expand=False, fill=False)
532         sub_vbox.pack_start(sstatedir_widget, expand=False, fill=False)
533
534         sub_vbox = gtk.VBox(False, 6)
535         advanced_vbox.pack_start(sub_vbox, expand=False, fill=False)
536         content = "<span weight=\"bold\">Shared state mirrors</span>"
537         tooltip = "URLs pointing to pre-built mirrors that will speed your build. "
538         tooltip += "Select the \'Standard\' configuration if the structure of your "
539         tooltip += "mirror replicates the structure of your local shared state directory. "
540         tooltip += "For more information on shared state mirrors, check the <a href=\""
541         tooltip += "http://www.yoctoproject.org/docs/current/poky-ref-manual/"
542         tooltip += "poky-ref-manual.html#shared-state\">Yocto Project Reference Manual</a>."
543         table = self.gen_label_info_widget(content, tooltip)
544         sub_vbox.pack_start(table, expand=False, fill=False)
545
546         searched_string = "file://"
547
548         if self.sstatemirrors_changed == 0:
549             self.sstatemirrors_changed = 1
550             sstatemirrors = self.configuration.sstatemirror
551             while sstatemirrors.find(searched_string) != -1:
552                 if sstatemirrors.find(searched_string,1) != -1:
553                     sstatemirror = sstatemirrors[:sstatemirrors.find(searched_string,1)]
554                     sstatemirrors = sstatemirrors[sstatemirrors.find(searched_string,1):]
555                 else:
556                     sstatemirror = sstatemirrors
557                     sstatemirrors = sstatemirrors[1:]
558
559                 sstatemirror_fields = [x for x in sstatemirror.split(' ') if x.strip()]
560                 if sstatemirror_fields[0] == "file://(.*)":
561                     sm_list = [ 0, sstatemirror_fields[1], "file://(.*)"]
562                 else:
563                     sm_list = [ 1, sstatemirror_fields[1], sstatemirror_fields[0]]
564                 self.sstatemirrors_list.append(sm_list)
565
566         index = 0
567         for mirror in self.sstatemirrors_list:
568             if mirror[0] == 0:
569                 sstatemirror_widget = self.gen_mirror_entry_widget(mirror[1], index)
570             else:
571                 sstatemirror_widget = self.gen_mirror_entry_widget(mirror[1], index, mirror[2])
572             sub_vbox.pack_start(sstatemirror_widget, expand=False, fill=False)
573             index += 1
574
575         sub_vbox = gtk.VBox(False, 6)
576         advanced_vbox.pack_start(sub_vbox, expand=False, fill=False)
577         add_mirror_button = HobAltButton("Add another mirror")
578         add_mirror_button.set_size_request(100, -1)
579         add_mirror_button.connect("clicked", self.add_mirror)
580         sub_vbox.pack_start(add_mirror_button, expand=False, fill=False)
581
582         return advanced_vbox
583
584     def refresh_shared_state_page(self):
585         page_num = self.nb.get_current_page()
586         self.nb.remove_page(page_num);
587         self.nb.insert_page(self.create_shared_state_page(), gtk.Label("Shared state"),page_num)
588         self.show_all()
589         self.nb.set_current_page(page_num)
590
591
592     def create_proxy_page(self):
593         advanced_vbox = gtk.VBox(False, 6)
594         advanced_vbox.set_border_width(6)
595
596         sub_vbox = gtk.VBox(False, 6)
597         advanced_vbox.pack_start(sub_vbox, expand=False, fill=False)
598         label = self.gen_label_widget("<span weight=\"bold\">Set the proxies used when fetching source code</span>")
599         tooltip = "Set the proxies used when fetching source code.  A blank field uses a direct internet connection."
600         info = HobInfoButton(tooltip, self)
601         hbox = gtk.HBox(False, 12)
602         hbox.pack_start(label, expand=True, fill=True)
603         hbox.pack_start(info, expand=False, fill=False)
604         sub_vbox.pack_start(hbox, expand=False, fill=False)
605
606         self.direct_checkbox = gtk.RadioButton(None, "Direct internet connection")
607         self.direct_checkbox.set_tooltip_text("Check this box to use a direct internet connection with no proxy")
608         self.direct_checkbox.set_active(not self.configuration.enable_proxy)
609         sub_vbox.pack_start(self.direct_checkbox, expand=False, fill=False)
610
611         self.proxy_checkbox = gtk.RadioButton(self.direct_checkbox, "Manual proxy configuration")
612         self.proxy_checkbox.set_tooltip_text("Check this box to manually set up a specific proxy")
613         self.proxy_checkbox.set_active(self.configuration.enable_proxy)
614         sub_vbox.pack_start(self.proxy_checkbox, expand=False, fill=False)
615
616         self.same_checkbox = gtk.CheckButton("Use the same proxy for all protocols")
617         self.same_checkbox.set_tooltip_text("Check this box to use the HTTP proxy for all five proxies")
618         self.same_checkbox.set_active(self.configuration.same_proxy)
619         hbox = gtk.HBox(False, 12)
620         hbox.pack_start(self.same_checkbox, expand=False, fill=False, padding=24)
621         sub_vbox.pack_start(hbox, expand=False, fill=False)
622
623         proxy_widget, self.http_proxy, self.http_proxy_port, self.http_proxy_details = self.gen_proxy_entry_widget(
624             "http", self, True)
625         sub_vbox.pack_start(proxy_widget, expand=False, fill=False)
626
627         proxy_widget, self.https_proxy, self.https_proxy_port, self.https_proxy_details = self.gen_proxy_entry_widget(
628             "https", self, True)
629         sub_vbox.pack_start(proxy_widget, expand=False, fill=False)
630
631         proxy_widget, self.ftp_proxy, self.ftp_proxy_port, self.ftp_proxy_details = self.gen_proxy_entry_widget(
632             "ftp", self, True)
633         sub_vbox.pack_start(proxy_widget, expand=False, fill=False)
634
635         proxy_widget, self.git_proxy, self.git_proxy_port, self.git_proxy_details = self.gen_proxy_entry_widget(
636             "git", self, True)
637         sub_vbox.pack_start(proxy_widget, expand=False, fill=False)
638
639         proxy_widget, self.cvs_proxy, self.cvs_proxy_port, self.cvs_proxy_details = self.gen_proxy_entry_widget(
640             "cvs", self, True)
641         sub_vbox.pack_start(proxy_widget, expand=False, fill=False)
642
643         self.direct_checkbox.connect("toggled", self.proxy_checkbox_toggled_cb)
644         self.proxy_checkbox.connect("toggled", self.proxy_checkbox_toggled_cb)
645         self.same_checkbox.connect("toggled", self.same_checkbox_toggled_cb)
646
647         self.refresh_proxy_components()
648         return advanced_vbox
649
650     def switch_to_page(self, page_id):
651         self.nb.set_current_page(page_id)
652
653     def details_cb(self, button, parent, protocol):
654         dialog = ProxyDetailsDialog(title = protocol.upper() + " Proxy Details",
655             user = self.configuration.proxies[protocol][1],
656             passwd = self.configuration.proxies[protocol][2],
657             parent = parent,
658             flags = gtk.DIALOG_MODAL
659                     | gtk.DIALOG_DESTROY_WITH_PARENT
660                     | gtk.DIALOG_NO_SEPARATOR)
661         dialog.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_OK)
662         response = dialog.run()
663         if response == gtk.RESPONSE_OK:
664             self.configuration.proxies[protocol][1] = dialog.user
665             self.configuration.proxies[protocol][2] = dialog.passwd
666             self.refresh_proxy_components()
667         dialog.destroy()    
668
669     def rootfs_combo_changed_cb(self, rootfs_combo, all_package_format, check_hbox):
670         combo_item = self.rootfs_combo.get_active_text()
671         for child in check_hbox.get_children():
672             if isinstance(child, gtk.CheckButton):
673                 check_hbox.remove(child)
674         for format in all_package_format:
675             if format != combo_item:
676                 check_button = gtk.CheckButton(format)
677                 check_hbox.pack_start(check_button, expand=False, fill=False)
678         check_hbox.show_all()
679
680     def gen_pkgfmt_widget(self, curr_package_format, all_package_format, tooltip_combo="", tooltip_extra=""):
681         pkgfmt_hbox = gtk.HBox(False, 24)
682
683         rootfs_vbox = gtk.VBox(False, 6)
684         pkgfmt_hbox.pack_start(rootfs_vbox, expand=False, fill=False)
685
686         label = self.gen_label_widget("Root file system package format")
687         rootfs_vbox.pack_start(label, expand=False, fill=False)
688
689         rootfs_format = ""
690         if curr_package_format:
691             rootfs_format = curr_package_format.split()[0]
692
693         rootfs_format_widget, rootfs_combo = self.gen_combo_widget(rootfs_format, all_package_format, tooltip_combo)
694         rootfs_vbox.pack_start(rootfs_format_widget, expand=False, fill=False)
695
696         extra_vbox = gtk.VBox(False, 6)
697         pkgfmt_hbox.pack_start(extra_vbox, expand=False, fill=False)
698
699         label = self.gen_label_widget("Additional package formats")
700         extra_vbox.pack_start(label, expand=False, fill=False)
701
702         check_hbox = gtk.HBox(False, 12)
703         extra_vbox.pack_start(check_hbox, expand=False, fill=False)
704         for format in all_package_format:
705             if format != rootfs_format:
706                 check_button = gtk.CheckButton(format)
707                 is_active = (format in curr_package_format.split())
708                 check_button.set_active(is_active)
709                 check_hbox.pack_start(check_button, expand=False, fill=False)
710
711         info = HobInfoButton(tooltip_extra, self)
712         check_hbox.pack_end(info, expand=False, fill=False)
713
714         rootfs_combo.connect("changed", self.rootfs_combo_changed_cb, all_package_format, check_hbox)
715
716         pkgfmt_hbox.show_all()
717
718         return pkgfmt_hbox, rootfs_combo, check_hbox
719
720     def editable_settings_cell_edited(self, cell, path_string, new_text, model):
721         it = model.get_iter_from_string(path_string)
722         column = cell.get_data("column")
723         model.set(it, column, new_text)
724
725     def editable_settings_add_item_clicked(self, button, model):
726         new_item = ["##KEY##", "##VALUE##"]
727
728         iter = model.append()
729         model.set (iter,
730             0, new_item[0],
731             1, new_item[1],
732        )
733
734     def editable_settings_remove_item_clicked(self, button, treeview):
735         selection = treeview.get_selection()
736         model, iter = selection.get_selected()
737
738         if iter:
739             path = model.get_path(iter)[0]
740             model.remove(iter)
741  
742     def gen_editable_settings(self, setting, tooltip=""):
743         setting_hbox = gtk.HBox(False, 12)
744
745         vbox = gtk.VBox(False, 12)
746         setting_hbox.pack_start(vbox, expand=True, fill=True)
747
748         setting_store = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
749         for key in setting.keys():
750             setting_store.set(setting_store.append(), 0, key, 1, setting[key])
751
752         setting_tree = gtk.TreeView(setting_store)
753         setting_tree.set_headers_visible(True)
754         setting_tree.set_size_request(300, 100)
755
756         col = gtk.TreeViewColumn('Key')
757         col.set_min_width(100)
758         col.set_max_width(150)
759         col.set_resizable(True)
760         col1 = gtk.TreeViewColumn('Value')
761         col1.set_min_width(100)
762         col1.set_max_width(150)
763         col1.set_resizable(True)
764         setting_tree.append_column(col)
765         setting_tree.append_column(col1)
766         cell = gtk.CellRendererText()
767         cell.set_property('width-chars', 10)
768         cell.set_property('editable', True)
769         cell.set_data("column", 0)
770         cell.connect("edited", self.editable_settings_cell_edited, setting_store)
771         cell1 = gtk.CellRendererText()
772         cell1.set_property('width-chars', 10)
773         cell1.set_property('editable', True)
774         cell1.set_data("column", 1)
775         cell1.connect("edited", self.editable_settings_cell_edited, setting_store)
776         col.pack_start(cell, True)
777         col1.pack_end(cell1, True)
778         col.set_attributes(cell, text=0)
779         col1.set_attributes(cell1, text=1)
780
781         scroll = gtk.ScrolledWindow()
782         scroll.set_shadow_type(gtk.SHADOW_IN)
783         scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
784         scroll.add(setting_tree)
785         vbox.pack_start(scroll, expand=True, fill=True)
786
787         # some buttons
788         hbox = gtk.HBox(True, 6)
789         vbox.pack_start(hbox, False, False)
790
791         button = gtk.Button(stock=gtk.STOCK_ADD)
792         button.connect("clicked", self.editable_settings_add_item_clicked, setting_store)
793         hbox.pack_start(button)
794
795         button = gtk.Button(stock=gtk.STOCK_REMOVE)
796         button.connect("clicked", self.editable_settings_remove_item_clicked, setting_tree)
797         hbox.pack_start(button)
798
799         info = HobInfoButton(tooltip, self)
800         setting_hbox.pack_start(info, expand=False, fill=False)
801
802         return setting_hbox, setting_store
803
804     def create_others_page(self):
805         advanced_vbox = gtk.VBox(False, 6)
806         advanced_vbox.set_border_width(6)
807
808         sub_vbox = gtk.VBox(False, 6)
809         advanced_vbox.pack_start(sub_vbox, expand=True, fill=True)
810         label = self.gen_label_widget("<span weight=\"bold\">Add your own variables:</span>")
811         tooltip = "These are key/value pairs for your extra settings. Click \'Add\' and then directly edit the key and the value"
812         setting_widget, self.setting_store = self.gen_editable_settings(self.configuration.extra_setting, tooltip)
813         sub_vbox.pack_start(label, expand=False, fill=False)
814         sub_vbox.pack_start(setting_widget, expand=True, fill=True)
815
816         return advanced_vbox
817
818     def create_visual_elements(self):
819         self.nb = gtk.Notebook()
820         self.nb.set_show_tabs(True)        
821         self.nb.append_page(self.create_build_environment_page(), gtk.Label("Build environment"))
822         self.nb.append_page(self.create_shared_state_page(), gtk.Label("Shared state"))
823         self.nb.append_page(self.create_proxy_page(), gtk.Label("Proxies"))        
824         self.nb.append_page(self.create_others_page(), gtk.Label("Others"))
825         self.nb.set_current_page(0)
826         self.vbox.pack_start(self.nb, expand=True, fill=True)
827         self.vbox.pack_end(gtk.HSeparator(), expand=True, fill=True)
828
829         self.show_all()
830
831
832 #
833 # AdvancedSettings Dialog
834 #
835 class AdvancedSettingDialog (CrumbsDialog, SettingsUIHelper):
836     
837     def details_cb(self, button, parent, protocol):
838         dialog = ProxyDetailsDialog(title = protocol.upper() + " Proxy Details",
839             user = self.configuration.proxies[protocol][1],
840             passwd = self.configuration.proxies[protocol][2],
841             parent = parent,
842             flags = gtk.DIALOG_MODAL
843                     | gtk.DIALOG_DESTROY_WITH_PARENT
844                     | gtk.DIALOG_NO_SEPARATOR)
845         dialog.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_OK)
846         response = dialog.run()
847         if response == gtk.RESPONSE_OK:
848             self.configuration.proxies[protocol][1] = dialog.user
849             self.configuration.proxies[protocol][2] = dialog.passwd
850             self.refresh_proxy_components()
851         dialog.destroy()
852
853     def set_save_button(self, button):
854         self.save_button = button
855
856     def rootfs_combo_changed_cb(self, rootfs_combo, all_package_format, check_hbox):
857         combo_item = self.rootfs_combo.get_active_text()
858         for child in check_hbox.get_children():
859             if isinstance(child, gtk.CheckButton):
860                 check_hbox.remove(child)
861         for format in all_package_format:
862             if format != combo_item:
863                 check_button = gtk.CheckButton(format)
864                 check_hbox.pack_start(check_button, expand=False, fill=False)
865         check_hbox.show_all()
866
867     def gen_pkgfmt_widget(self, curr_package_format, all_package_format, tooltip_combo="", tooltip_extra=""):
868         pkgfmt_vbox = gtk.VBox(False, 6)
869
870         label = self.gen_label_widget("Root file system package format")
871         pkgfmt_vbox.pack_start(label, expand=False, fill=False)
872
873         rootfs_format = ""
874         if curr_package_format:
875             rootfs_format = curr_package_format.split()[0]
876
877         rootfs_format_widget, rootfs_combo = self.gen_combo_widget(rootfs_format, all_package_format, tooltip_combo)
878         pkgfmt_vbox.pack_start(rootfs_format_widget, expand=False, fill=False)
879
880         label = self.gen_label_widget("Additional package formats")
881         pkgfmt_vbox.pack_start(label, expand=False, fill=False)
882
883         check_hbox = gtk.HBox(False, 12)
884         pkgfmt_vbox.pack_start(check_hbox, expand=False, fill=False)
885         for format in all_package_format:
886             if format != rootfs_format:
887                 check_button = gtk.CheckButton(format)
888                 is_active = (format in curr_package_format.split())
889                 check_button.set_active(is_active)
890                 check_hbox.pack_start(check_button, expand=False, fill=False)
891
892         info = HobInfoButton(tooltip_extra, self)
893         check_hbox.pack_start(info, expand=False, fill=False)
894
895         rootfs_combo.connect("changed", self.rootfs_combo_changed_cb, all_package_format, check_hbox)
896
897         pkgfmt_vbox.show_all()
898
899         return pkgfmt_vbox, rootfs_combo, check_hbox
900
901     def __init__(self, title, configuration, all_image_types,
902             all_package_formats, all_distros, all_sdk_machines,
903             max_threads, parent, flags, buttons=None):
904         super(AdvancedSettingDialog, self).__init__(title, parent, flags, buttons)
905
906         # class members from other objects
907         # bitbake settings from Builder.Configuration
908         self.configuration = configuration
909         self.image_types = all_image_types
910         self.all_package_formats = all_package_formats
911         self.all_distros = all_distros
912         self.all_sdk_machines = all_sdk_machines
913         self.max_threads = max_threads
914
915         # class members for internal use
916         self.distro_combo = None
917         self.dldir_text = None
918         self.sstatedir_text = None
919         self.sstatemirror_text = None
920         self.bb_spinner = None
921         self.pmake_spinner = None
922         self.rootfs_size_spinner = None
923         self.extra_size_spinner = None
924         self.gplv3_checkbox = None
925         self.toolchain_checkbox = None
926         self.image_types_checkbuttons = {}
927
928         self.md5 = self.config_md5()
929         self.settings_changed = False
930
931         # create visual elements on the dialog
932         self.save_button = None
933         self.create_visual_elements()
934         self.connect("response", self.response_cb)
935
936     def _get_sorted_value(self, var):
937         return " ".join(sorted(str(var).split())) + "\n"
938
939     def config_md5(self):
940         data = ""
941         data += ("PACKAGE_CLASSES: "      + self.configuration.curr_package_format + '\n')
942         data += ("DISTRO: "               + self._get_sorted_value(self.configuration.curr_distro))
943         data += ("IMAGE_ROOTFS_SIZE: "    + self._get_sorted_value(self.configuration.image_rootfs_size))
944         data += ("IMAGE_EXTRA_SIZE: "     + self._get_sorted_value(self.configuration.image_extra_size))
945         data += ("INCOMPATIBLE_LICENSE: " + self._get_sorted_value(self.configuration.incompat_license))
946         data += ("SDK_MACHINE: "          + self._get_sorted_value(self.configuration.curr_sdk_machine))
947         data += ("TOOLCHAIN_BUILD: "      + self._get_sorted_value(self.configuration.toolchain_build))
948         data += ("IMAGE_FSTYPES: "        + self._get_sorted_value(self.configuration.image_fstypes))
949         return hashlib.md5(data).hexdigest()
950
951     def create_visual_elements(self):
952         self.nb = gtk.Notebook()
953         self.nb.set_show_tabs(True)
954         self.nb.append_page(self.create_image_types_page(), gtk.Label("Image types"))
955         self.nb.append_page(self.create_output_page(), gtk.Label("Output"))
956         self.nb.set_current_page(0)
957         self.vbox.pack_start(self.nb, expand=True, fill=True)
958         self.vbox.pack_end(gtk.HSeparator(), expand=True, fill=True)
959
960         self.show_all()
961
962     def get_num_checked_image_types(self):
963         total = 0
964         for b in self.image_types_checkbuttons.values():
965             if b.get_active():
966               total = total + 1
967         return total
968
969     def set_save_button_state(self):
970         if self.save_button:
971             self.save_button.set_sensitive(self.get_num_checked_image_types() > 0)
972
973     def image_type_checkbutton_clicked_cb(self, button):
974         self.set_save_button_state()
975         if self.get_num_checked_image_types() == 0:
976             # Show an error dialog
977             lbl = "<b>Select an image type</b>\n\nYou need to select at least one image type."
978             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_WARNING)
979             button = dialog.add_button("OK", gtk.RESPONSE_OK)
980             HobButton.style_button(button)
981             response = dialog.run()
982             dialog.destroy()
983
984     def create_image_types_page(self):
985         main_vbox = gtk.VBox(False, 16)
986         main_vbox.set_border_width(6)
987
988         advanced_vbox = gtk.VBox(False, 6)
989         advanced_vbox.set_border_width(6)
990
991         distro_vbox = gtk.VBox(False, 6)        
992         label = self.gen_label_widget("Distro:")
993         tooltip = "Selects the Yocto Project distribution you want"
994         distro_widget, self.distro_combo = self.gen_combo_widget(self.configuration.curr_distro, self.all_distros, tooltip)
995         distro_vbox.pack_start(label, expand=False, fill=False)
996         distro_vbox.pack_start(distro_widget, expand=False, fill=False)
997         main_vbox.pack_start(distro_vbox, expand=False, fill=False)
998
999
1000         rows = (len(self.image_types)+1)/3
1001         table = gtk.Table(rows + 1, 10, True)
1002         advanced_vbox.pack_start(table, expand=False, fill=False)
1003
1004         tooltip = "Image file system types you want."
1005         info = HobInfoButton(tooltip, self)
1006         label = self.gen_label_widget("Image types:")
1007         align = gtk.Alignment(0, 0.5, 0, 0)
1008         table.attach(align, 0, 4, 0, 1)
1009         align.add(label)
1010         table.attach(info, 4, 5, 0, 1)
1011
1012         i = 1
1013         j = 1
1014         for image_type in sorted(self.image_types):
1015             self.image_types_checkbuttons[image_type] = gtk.CheckButton(image_type)
1016             self.image_types_checkbuttons[image_type].connect("toggled", self.image_type_checkbutton_clicked_cb)
1017             article = ""
1018             if image_type.startswith(("a", "e", "i", "o", "u")):
1019                 article = "n"
1020             self.image_types_checkbuttons[image_type].set_tooltip_text("Build a%s %s image" % (article, image_type))
1021             table.attach(self.image_types_checkbuttons[image_type], j - 1, j + 3, i, i + 1)
1022             if image_type in self.configuration.image_fstypes.split():
1023                 self.image_types_checkbuttons[image_type].set_active(True)
1024             i += 1
1025             if i > rows:
1026                 i = 1
1027                 j = j + 4
1028
1029         main_vbox.pack_start(advanced_vbox, expand=False, fill=False)
1030         self.set_save_button_state()
1031         
1032         return main_vbox
1033
1034     def create_output_page(self):
1035         advanced_vbox = gtk.VBox(False, 6)
1036         advanced_vbox.set_border_width(6)
1037
1038         advanced_vbox.pack_start(self.gen_label_widget('<span weight="bold">Package format</span>'), expand=False, fill=False)
1039         sub_vbox = gtk.VBox(False, 6)
1040         advanced_vbox.pack_start(sub_vbox, expand=False, fill=False)
1041         tooltip_combo = "Selects the package format used to generate rootfs."
1042         tooltip_extra = "Selects extra package formats to build"
1043         pkgfmt_widget, self.rootfs_combo, self.check_hbox = self.gen_pkgfmt_widget(self.configuration.curr_package_format, self.all_package_formats, tooltip_combo, tooltip_extra)
1044         sub_vbox.pack_start(pkgfmt_widget, expand=False, fill=False)
1045
1046         advanced_vbox.pack_start(self.gen_label_widget('<span weight="bold">Image size</span>'), expand=False, fill=False)
1047         sub_vbox = gtk.VBox(False, 6)
1048         advanced_vbox.pack_start(sub_vbox, expand=False, fill=False)
1049         label = self.gen_label_widget("Image basic size (in MB)")
1050         tooltip = "Sets the basic size of your target image.\nThis is the basic size of your target image unless your selected package size exceeds this value or you select \'Image Extra Size\'."
1051         rootfs_size_widget, self.rootfs_size_spinner = self.gen_spinner_widget(int(self.configuration.image_rootfs_size*1.0/1024), 0, 65536, tooltip)
1052         sub_vbox.pack_start(label, expand=False, fill=False)
1053         sub_vbox.pack_start(rootfs_size_widget, expand=False, fill=False)
1054
1055         sub_vbox = gtk.VBox(False, 6)
1056         advanced_vbox.pack_start(sub_vbox, expand=False, fill=False)
1057         label = self.gen_label_widget("Additional free space (in MB)")
1058         tooltip = "Sets the extra free space of your target image.\nBy default, the system reserves 30% of your image size as free space. If your image contains zypper, it brings in 50MB more space. The maximum free space is 64GB."
1059         extra_size_widget, self.extra_size_spinner = self.gen_spinner_widget(int(self.configuration.image_extra_size*1.0/1024), 0, 65536, tooltip)
1060         sub_vbox.pack_start(label, expand=False, fill=False)
1061         sub_vbox.pack_start(extra_size_widget, expand=False, fill=False)
1062
1063         advanced_vbox.pack_start(self.gen_label_widget('<span weight="bold">Licensing</span>'), expand=False, fill=False)
1064         self.gplv3_checkbox = gtk.CheckButton("Exclude GPLv3 packages")
1065         self.gplv3_checkbox.set_tooltip_text("Check this box to prevent GPLv3 packages from being included in your image")
1066         if "GPLv3" in self.configuration.incompat_license.split():
1067             self.gplv3_checkbox.set_active(True)
1068         else:
1069             self.gplv3_checkbox.set_active(False)
1070         advanced_vbox.pack_start(self.gplv3_checkbox, expand=False, fill=False)
1071
1072         advanced_vbox.pack_start(self.gen_label_widget('<span weight="bold">Toolchain</span>'), expand=False, fill=False)
1073         sub_hbox = gtk.HBox(False, 6)
1074         advanced_vbox.pack_start(sub_hbox, expand=False, fill=False)
1075         self.toolchain_checkbox = gtk.CheckButton("Build toolchain")
1076         self.toolchain_checkbox.set_tooltip_text("Check this box to build the related toolchain with your image")
1077         self.toolchain_checkbox.set_active(self.configuration.toolchain_build)
1078         sub_hbox.pack_start(self.toolchain_checkbox, expand=False, fill=False)
1079
1080         tooltip = "Selects the host platform for which you want to run the toolchain"
1081         sdk_machine_widget, self.sdk_machine_combo = self.gen_combo_widget(self.configuration.curr_sdk_machine, self.all_sdk_machines, tooltip)
1082         sub_hbox.pack_start(sdk_machine_widget, expand=False, fill=False)
1083
1084         return advanced_vbox
1085
1086     def response_cb(self, dialog, response_id):
1087         package_format = []
1088         package_format.append(self.rootfs_combo.get_active_text())
1089         for child in self.check_hbox:
1090             if isinstance(child, gtk.CheckButton) and child.get_active():
1091                 package_format.append(child.get_label())
1092         self.configuration.curr_package_format = " ".join(package_format)
1093
1094         self.configuration.curr_distro = self.distro_combo.get_active_text()        
1095         self.configuration.image_rootfs_size = self.rootfs_size_spinner.get_value_as_int() * 1024
1096         self.configuration.image_extra_size = self.extra_size_spinner.get_value_as_int() * 1024
1097
1098         self.configuration.image_fstypes = ""
1099         for image_type in self.image_types:
1100             if self.image_types_checkbuttons[image_type].get_active():
1101                 self.configuration.image_fstypes += (" " + image_type)
1102         self.configuration.image_fstypes.strip()
1103
1104         if self.gplv3_checkbox.get_active():
1105             if "GPLv3" not in self.configuration.incompat_license.split():
1106                 self.configuration.incompat_license += " GPLv3"
1107         else:
1108             if "GPLv3" in self.configuration.incompat_license.split():
1109                 self.configuration.incompat_license = self.configuration.incompat_license.split().remove("GPLv3")
1110                 self.configuration.incompat_license = " ".join(self.configuration.incompat_license or [])
1111         self.configuration.incompat_license = self.configuration.incompat_license.strip()
1112
1113         self.configuration.toolchain_build = self.toolchain_checkbox.get_active()
1114         self.configuration.curr_sdk_machine = self.sdk_machine_combo.get_active_text()
1115         md5 = self.config_md5()
1116         self.settings_changed = (self.md5 != md5)
1117
1118 #
1119 # DeployImageDialog
1120 #
1121 class DeployImageDialog (CrumbsDialog):
1122
1123     __dummy_usb__ = "--select a usb drive--"
1124
1125     def __init__(self, title, image_path, parent, flags, buttons=None, standalone=False):
1126         super(DeployImageDialog, self).__init__(title, parent, flags, buttons)
1127
1128         self.image_path = image_path
1129         self.standalone = standalone
1130
1131         self.create_visual_elements()
1132         self.connect("response", self.response_cb)
1133
1134     def create_visual_elements(self):
1135         self.set_size_request(600, 400)
1136         label = gtk.Label()
1137         label.set_alignment(0.0, 0.5)
1138         markup = "<span font_desc='12'>The image to be written into usb drive:</span>"
1139         label.set_markup(markup)
1140         self.vbox.pack_start(label, expand=False, fill=False, padding=2)
1141
1142         table = gtk.Table(2, 10, False)
1143         table.set_col_spacings(5)
1144         table.set_row_spacings(5)
1145         self.vbox.pack_start(table, expand=True, fill=True)
1146
1147         scroll = gtk.ScrolledWindow()
1148         scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
1149         scroll.set_shadow_type(gtk.SHADOW_IN)
1150         tv = gtk.TextView()
1151         tv.set_editable(False)
1152         tv.set_wrap_mode(gtk.WRAP_WORD)
1153         tv.set_cursor_visible(False)
1154         self.buf = gtk.TextBuffer()
1155         self.buf.set_text(self.image_path)
1156         tv.set_buffer(self.buf)
1157         scroll.add(tv)
1158         table.attach(scroll, 0, 10, 0, 1)
1159
1160         # There are 2 ways to use DeployImageDialog
1161         # One way is that called by HOB when the 'Deploy Image' button is clicked
1162         # The other way is that called by a standalone script.
1163         # Following block of codes handles the latter way. It adds a 'Select Image' button and
1164         # emit a signal when the button is clicked.
1165         if self.standalone:
1166                 gobject.signal_new("select_image_clicked", self, gobject.SIGNAL_RUN_FIRST,
1167                                    gobject.TYPE_NONE, ())
1168                 icon = gtk.Image()
1169                 pix_buffer = gtk.gdk.pixbuf_new_from_file(hic.ICON_IMAGES_DISPLAY_FILE)
1170                 icon.set_from_pixbuf(pix_buffer)
1171                 button = gtk.Button("Select Image")
1172                 button.set_image(icon)
1173                 #button.set_size_request(140, 50)
1174                 table.attach(button, 9, 10, 1, 2, gtk.FILL, 0, 0, 0)
1175                 button.connect("clicked", self.select_image_button_clicked_cb)
1176
1177         separator = gtk.HSeparator()
1178         self.vbox.pack_start(separator, expand=False, fill=False, padding=10)
1179
1180         self.usb_desc = gtk.Label()
1181         self.usb_desc.set_alignment(0.0, 0.5)
1182         markup = "<span font_desc='12'>You haven't chosen any USB drive.</span>"
1183         self.usb_desc.set_markup(markup)
1184
1185         self.usb_combo = gtk.combo_box_new_text()
1186         self.usb_combo.connect("changed", self.usb_combo_changed_cb)
1187         model = self.usb_combo.get_model()
1188         model.clear()
1189         self.usb_combo.append_text(self.__dummy_usb__)
1190         for usb in self.find_all_usb_devices():
1191             self.usb_combo.append_text("/dev/" + usb)
1192         self.usb_combo.set_active(0)
1193         self.vbox.pack_start(self.usb_combo, expand=False, fill=False)
1194         self.vbox.pack_start(self.usb_desc, expand=False, fill=False, padding=2)
1195
1196         self.progress_bar = HobProgressBar()
1197         self.vbox.pack_start(self.progress_bar, expand=False, fill=False)
1198         separator = gtk.HSeparator()
1199         self.vbox.pack_start(separator, expand=False, fill=True, padding=10)
1200
1201         self.vbox.show_all()
1202         self.progress_bar.hide()
1203
1204     def set_image_text_buffer(self, image_path):
1205         self.buf.set_text(image_path)
1206
1207     def set_image_path(self, image_path):
1208         self.image_path = image_path
1209
1210     def popen_read(self, cmd):
1211         tmpout, errors = bb.process.run("%s" % cmd)
1212         return tmpout.strip()
1213
1214     def find_all_usb_devices(self):
1215         usb_devs = [ os.readlink(u)
1216             for u in glob.glob('/dev/disk/by-id/usb*')
1217             if not re.search(r'part\d+', u) ]
1218         return [ '%s' % u[u.rfind('/')+1:] for u in usb_devs ]
1219
1220     def get_usb_info(self, dev):
1221         return "%s %s" % \
1222             (self.popen_read('cat /sys/class/block/%s/device/vendor' % dev),
1223             self.popen_read('cat /sys/class/block/%s/device/model' % dev))
1224
1225     def select_image_button_clicked_cb(self, button):
1226             self.emit('select_image_clicked')
1227
1228     def usb_combo_changed_cb(self, usb_combo):
1229         combo_item = self.usb_combo.get_active_text()
1230         if not combo_item or combo_item == self.__dummy_usb__:
1231             markup = "<span font_desc='12'>You haven't chosen any USB drive.</span>"
1232             self.usb_desc.set_markup(markup)
1233         else:
1234             markup = "<span font_desc='12'>" + self.get_usb_info(combo_item.lstrip("/dev/")) + "</span>"
1235             self.usb_desc.set_markup(markup)
1236
1237     def response_cb(self, dialog, response_id):
1238         if response_id == gtk.RESPONSE_YES:
1239             lbl = ''
1240             combo_item = self.usb_combo.get_active_text()
1241             if combo_item and combo_item != self.__dummy_usb__ and self.image_path:
1242                 cmdline = bb.ui.crumbs.utils.which_terminal()
1243                 if cmdline:
1244                     tmpfile = tempfile.NamedTemporaryFile()
1245                     cmdline += "\"sudo dd if=" + self.image_path + \
1246                                 " of=" + combo_item + "; echo $? > " + tmpfile.name + "\""
1247                     subprocess.call(shlex.split(cmdline))
1248
1249                     if int(tmpfile.readline().strip()) == 0:
1250                         lbl = "<b>Deploy image successfully.</b>"
1251                     else:
1252                         lbl = "<b>Failed to deploy image.</b>\nPlease check image <b>%s</b> exists and USB device <b>%s</b> is writable." % (self.image_path, combo_item)
1253                     tmpfile.close()
1254             else:
1255                 if not self.image_path:
1256                     lbl = "<b>No selection made.</b>\nYou have not selected an image to deploy."
1257                 else:
1258                     lbl = "<b>No selection made.</b>\nYou have not selected a USB device."
1259             if len(lbl):
1260                 crumbs_dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
1261                 button = crumbs_dialog.add_button("Close", gtk.RESPONSE_OK)
1262                 HobButton.style_button(button)
1263                 crumbs_dialog.run()
1264                 crumbs_dialog.destroy()
1265
1266     def update_progress_bar(self, title, fraction, status=None):
1267         self.progress_bar.update(fraction)
1268         self.progress_bar.set_title(title)
1269         self.progress_bar.set_rcstyle(status)
1270
1271     def write_file(self, ifile, ofile):
1272         self.progress_bar.reset()
1273         self.progress_bar.show()
1274
1275         f_from = os.open(ifile, os.O_RDONLY)
1276         f_to = os.open(ofile, os.O_WRONLY)
1277
1278         total_size = os.stat(ifile).st_size
1279         written_size = 0
1280
1281         while True:
1282             buf = os.read(f_from, 1024*1024)
1283             if not buf:
1284                 break
1285             os.write(f_to, buf)
1286             written_size += 1024*1024
1287             self.update_progress_bar("Writing to usb:", written_size * 1.0/total_size)
1288
1289         self.update_progress_bar("Writing completed:", 1.0)
1290         os.close(f_from)
1291         os.close(f_to)
1292         self.progress_bar.hide()
1293
1294 class CellRendererPixbufActivatable(gtk.CellRendererPixbuf):
1295     """
1296     A custom CellRenderer implementation which is activatable
1297     so that we can handle user clicks
1298     """
1299     __gsignals__    = { 'clicked' : (gobject.SIGNAL_RUN_LAST,
1300                                      gobject.TYPE_NONE,
1301                                      (gobject.TYPE_STRING,)), }
1302
1303     def __init__(self):
1304         gtk.CellRendererPixbuf.__init__(self)
1305         self.set_property('mode', gtk.CELL_RENDERER_MODE_ACTIVATABLE)
1306         self.set_property('follow-state', True)
1307
1308     """
1309     Respond to a user click on a cell
1310     """
1311     def do_activate(self, even, widget, path, background_area, cell_area, flags):
1312         self.emit('clicked', path)
1313
1314 #
1315 # LayerSelectionDialog
1316 #
1317 class LayerSelectionDialog (CrumbsDialog):
1318
1319     def gen_label_widget(self, content):
1320         label = gtk.Label()
1321         label.set_alignment(0, 0)
1322         label.set_markup(content)
1323         label.show()
1324         return label
1325
1326     def layer_widget_toggled_cb(self, cell, path, layer_store):
1327         name = layer_store[path][0]
1328         toggle = not layer_store[path][1]
1329         layer_store[path][1] = toggle
1330
1331     def layer_widget_add_clicked_cb(self, action, layer_store, parent):
1332         dialog = gtk.FileChooserDialog("Add new layer", parent,
1333                                        gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
1334         button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1335         HobAltButton.style_button(button)
1336         button = dialog.add_button("Open", gtk.RESPONSE_YES)
1337         HobButton.style_button(button)
1338         label = gtk.Label("Select the layer you wish to add")
1339         label.show()
1340         dialog.set_extra_widget(label)
1341         response = dialog.run()
1342         path = dialog.get_filename()
1343         dialog.destroy()
1344
1345         lbl = "<b>Error</b>\nUnable to load layer <i>%s</i> because " % path
1346         if response == gtk.RESPONSE_YES:
1347             import os
1348             import os.path
1349             layers = []
1350             it = layer_store.get_iter_first()
1351             while it:
1352                 layers.append(layer_store.get_value(it, 0))
1353                 it = layer_store.iter_next(it)
1354
1355             if not path:
1356                 lbl += "it is an invalid path."
1357             elif not os.path.exists(path+"/conf/layer.conf"):
1358                 lbl += "there is no layer.conf inside the directory."
1359             elif path in layers:
1360                 lbl += "it is already in loaded layers."
1361             else:
1362                 layer_store.append([path])
1363                 return
1364             dialog = CrumbsMessageDialog(parent, lbl)
1365             dialog.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_OK)
1366             response = dialog.run()
1367             dialog.destroy()
1368
1369     def layer_widget_del_clicked_cb(self, action, tree_selection, layer_store):
1370         model, iter = tree_selection.get_selected()
1371         if iter:
1372             layer_store.remove(iter)
1373
1374
1375     def gen_layer_widget(self, layers, layers_avail, window, tooltip=""):
1376         hbox = gtk.HBox(False, 6)
1377
1378         layer_tv = gtk.TreeView()
1379         layer_tv.set_rules_hint(True)
1380         layer_tv.set_headers_visible(False)
1381         tree_selection = layer_tv.get_selection()
1382         tree_selection.set_mode(gtk.SELECTION_NONE)
1383
1384         col0= gtk.TreeViewColumn('Path')
1385         cell0 = gtk.CellRendererText()
1386         cell0.set_padding(5,2)
1387         col0.pack_start(cell0, True)
1388         col0.set_cell_data_func(cell0, self.draw_layer_path_cb)
1389         layer_tv.append_column(col0)
1390
1391         scroll = gtk.ScrolledWindow()
1392         scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
1393         scroll.set_shadow_type(gtk.SHADOW_IN)
1394         scroll.add(layer_tv)
1395
1396         table_layer = gtk.Table(2, 10, False)
1397         hbox.pack_start(table_layer, expand=True, fill=True)
1398
1399         table_layer.attach(scroll, 0, 10, 0, 1)
1400
1401         layer_store = gtk.ListStore(gobject.TYPE_STRING)
1402         for layer in layers:
1403             layer_store.append([layer])
1404
1405         col1 = gtk.TreeViewColumn('Enabled')
1406         layer_tv.append_column(col1)
1407
1408         cell1 = CellRendererPixbufActivatable()
1409         cell1.set_fixed_size(-1,35)
1410         cell1.connect("clicked", self.del_cell_clicked_cb, layer_store)
1411         col1.pack_start(cell1, True)
1412         col1.set_cell_data_func(cell1, self.draw_delete_button_cb, layer_tv)
1413
1414         add_button = gtk.Button()
1415         add_button.set_relief(gtk.RELIEF_NONE)
1416         box = gtk.HBox(False, 6)
1417         box.show()
1418         add_button.add(box)
1419         add_button.connect("enter-notify-event", self.add_hover_cb)
1420         add_button.connect("leave-notify-event", self.add_leave_cb)
1421         self.im = gtk.Image()
1422         self.im.set_from_file(hic.ICON_INDI_ADD_FILE)
1423         self.im.show()
1424         box.pack_start(self.im, expand=False, fill=False, padding=6)
1425         lbl = gtk.Label("Add layer")
1426         lbl.set_alignment(0.0, 0.5)
1427         lbl.show()
1428         box.pack_start(lbl, expand=True, fill=True, padding=6)
1429         add_button.connect("clicked", self.layer_widget_add_clicked_cb, layer_store, window)
1430         table_layer.attach(add_button, 0, 10, 1, 2, gtk.EXPAND | gtk.FILL, 0, 0, 6)
1431         layer_tv.set_model(layer_store)
1432
1433         hbox.show_all()
1434
1435         return hbox, layer_store
1436
1437     def add_hover_cb(self, button, event):
1438         self.im.set_from_file(hic.ICON_INDI_ADD_HOVER_FILE)
1439
1440     def add_leave_cb(self, button, event):
1441         self.im.set_from_file(hic.ICON_INDI_ADD_FILE)
1442
1443     def __init__(self, title, layers, all_layers, parent, flags, buttons=None):
1444         super(LayerSelectionDialog, self).__init__(title, parent, flags, buttons)
1445
1446         # class members from other objects
1447         self.layers = layers
1448         self.all_layers = all_layers
1449         self.layers_changed = False
1450
1451         # icon for remove button in TreeView
1452         im = gtk.Image()
1453         im.set_from_file(hic.ICON_INDI_REMOVE_FILE)
1454         self.rem_icon = im.get_pixbuf()
1455
1456         # class members for internal use
1457         self.layer_store = None
1458
1459         # create visual elements on the dialog
1460         self.create_visual_elements()
1461         self.connect("response", self.response_cb)
1462
1463     def create_visual_elements(self):
1464         layer_widget, self.layer_store = self.gen_layer_widget(self.layers, self.all_layers, self, None)
1465         layer_widget.set_size_request(450, 250)
1466         self.vbox.pack_start(layer_widget, expand=True, fill=True)
1467         self.show_all()
1468
1469     def response_cb(self, dialog, response_id):
1470         model = self.layer_store
1471         it = model.get_iter_first()
1472         layers = []
1473         while it:
1474             layers.append(model.get_value(it, 0))
1475             it = model.iter_next(it)
1476
1477         self.layers_changed = (self.layers != layers)
1478         self.layers = layers
1479
1480     """
1481     A custom cell_data_func to draw a delete 'button' in the TreeView for layers
1482     other than the meta layer. The deletion of which is prevented so that the
1483     user can't shoot themselves in the foot too badly.
1484     """
1485     def draw_delete_button_cb(self, col, cell, model, it, tv):
1486         path =  model.get_value(it, 0)
1487         # Trailing slashes are uncommon in bblayers.conf but confuse os.path.basename
1488         path.rstrip('/')
1489         name = os.path.basename(path)
1490         if name == "meta" or name == "meta-hob":
1491             cell.set_sensitive(False)
1492             cell.set_property('pixbuf', None)
1493             cell.set_property('mode', gtk.CELL_RENDERER_MODE_INERT)
1494         else:
1495             cell.set_property('pixbuf', self.rem_icon)
1496             cell.set_sensitive(True)
1497             cell.set_property('mode', gtk.CELL_RENDERER_MODE_ACTIVATABLE)
1498
1499         return True
1500
1501     """
1502     A custom cell_data_func to write an extra message into the layer path cell
1503     for the meta layer. We should inform the user that they can't remove it for
1504     their own safety.
1505     """
1506     def draw_layer_path_cb(self, col, cell, model, it):
1507         path = model.get_value(it, 0)
1508         name = os.path.basename(path)
1509         if name == "meta":
1510             cell.set_property('markup', "<b>Core layer for images: it cannot be removed</b>\n%s" % path)
1511         elif name == "meta-hob":
1512             cell.set_property('markup', "<b>Core layer for Hob: it cannot be removed</b>\n%s" % path)
1513         else:
1514             cell.set_property('text', path)
1515
1516     def del_cell_clicked_cb(self, cell, path, model):
1517         it = model.get_iter_from_string(path)
1518         model.remove(it)
1519
1520 class ImageSelectionDialog (CrumbsDialog):
1521
1522     __columns__ = [{
1523             'col_name' : 'Image name',
1524             'col_id'   : 0,
1525             'col_style': 'text',
1526             'col_min'  : 400,
1527             'col_max'  : 400
1528         }, {
1529             'col_name' : 'Select',
1530             'col_id'   : 1,
1531             'col_style': 'radio toggle',
1532             'col_min'  : 160,
1533             'col_max'  : 160
1534     }]
1535
1536
1537     def __init__(self, image_folder, image_types, title, parent, flags, buttons=None, image_extension = {}):
1538         super(ImageSelectionDialog, self).__init__(title, parent, flags, buttons)
1539         self.connect("response", self.response_cb)
1540
1541         self.image_folder = image_folder
1542         self.image_types  = image_types
1543         self.image_list = []
1544         self.image_names = []
1545         self.image_extension = image_extension
1546
1547         # create visual elements on the dialog
1548         self.create_visual_elements()
1549
1550         self.image_store = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_BOOLEAN)
1551         self.fill_image_store()
1552
1553     def create_visual_elements(self):
1554         hbox = gtk.HBox(False, 6)
1555
1556         self.vbox.pack_start(hbox, expand=False, fill=False)
1557
1558         entry = gtk.Entry()
1559         entry.set_text(self.image_folder)
1560         table = gtk.Table(1, 10, True)
1561         table.set_size_request(560, -1)
1562         hbox.pack_start(table, expand=False, fill=False)
1563         table.attach(entry, 0, 9, 0, 1)
1564         image = gtk.Image()
1565         image.set_from_stock(gtk.STOCK_OPEN, gtk.ICON_SIZE_BUTTON)
1566         open_button = gtk.Button()
1567         open_button.set_image(image)
1568         open_button.connect("clicked", self.select_path_cb, self, entry)
1569         table.attach(open_button, 9, 10, 0, 1)
1570
1571         self.image_table = HobViewTable(self.__columns__)
1572         self.image_table.set_size_request(-1, 300)
1573         self.image_table.connect("toggled", self.toggled_cb)
1574         self.image_table.connect_group_selection(self.table_selected_cb)
1575         self.image_table.connect("row-activated", self.row_actived_cb)
1576         self.vbox.pack_start(self.image_table, expand=True, fill=True)
1577
1578         self.show_all()
1579
1580     def change_image_cb(self, model, path, columnid):
1581         if not model:
1582             return
1583         iter = model.get_iter_first()
1584         while iter:
1585             rowpath = model.get_path(iter)
1586             model[rowpath][columnid] = False
1587             iter = model.iter_next(iter)
1588
1589         model[path][columnid] = True
1590
1591     def toggled_cb(self, table, cell, path, columnid, tree):
1592         model = tree.get_model()
1593         self.change_image_cb(model, path, columnid)
1594
1595     def table_selected_cb(self, selection):
1596         model, paths = selection.get_selected_rows()
1597         if paths:
1598             self.change_image_cb(model, paths[0], 1)
1599
1600     def row_actived_cb(self, tab, model, path):
1601         self.change_image_cb(model, path, 1)
1602         self.emit('response', gtk.RESPONSE_YES)
1603
1604     def select_path_cb(self, action, parent, entry):
1605         dialog = gtk.FileChooserDialog("", parent,
1606                                        gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
1607         text = entry.get_text()
1608         dialog.set_current_folder(text if len(text) > 0 else os.getcwd())
1609         button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1610         HobAltButton.style_button(button)
1611         button = dialog.add_button("Open", gtk.RESPONSE_YES)
1612         HobButton.style_button(button)
1613         response = dialog.run()
1614         if response == gtk.RESPONSE_YES:
1615             path = dialog.get_filename()
1616             entry.set_text(path)
1617             self.image_folder = path
1618             self.fill_image_store()
1619
1620         dialog.destroy()
1621
1622     def fill_image_store(self):
1623         self.image_list = []
1624         self.image_store.clear()
1625         imageset = set()
1626         for root, dirs, files in os.walk(self.image_folder):
1627             # ignore the sub directories
1628             dirs[:] = []
1629             for f in files:
1630                 for image_type in self.image_types:
1631                     if image_type in self.image_extension:
1632                         real_types = self.image_extension[image_type]
1633                     else:
1634                         real_types = [image_type]
1635                     for real_image_type in real_types:
1636                         if f.endswith('.' + real_image_type):
1637                             imageset.add(f.rsplit('.' + real_image_type)[0].rsplit('.rootfs')[0])
1638                             self.image_list.append(f)
1639
1640         for image in imageset:
1641             self.image_store.set(self.image_store.append(), 0, image, 1, False)
1642
1643         self.image_table.set_model(self.image_store)
1644
1645     def response_cb(self, dialog, response_id):
1646         self.image_names = []
1647         if response_id == gtk.RESPONSE_YES:
1648             iter = self.image_store.get_iter_first()
1649             while iter:
1650                 path = self.image_store.get_path(iter)
1651                 if self.image_store[path][1]:
1652                     for f in self.image_list:
1653                         if f.startswith(self.image_store[path][0] + '.'):
1654                             self.image_names.append(f)
1655                     break
1656                 iter = self.image_store.iter_next(iter)
1657
1658 class ProxyDetailsDialog (CrumbsDialog):
1659
1660     def __init__(self, title, user, passwd, parent, flags, buttons=None):
1661         super(ProxyDetailsDialog, self).__init__(title, parent, flags, buttons)
1662         self.connect("response", self.response_cb)
1663
1664         self.auth = not (user == None or passwd == None or user == "")
1665         self.user = user or ""
1666         self.passwd = passwd or ""
1667
1668         # create visual elements on the dialog
1669         self.create_visual_elements()
1670
1671     def create_visual_elements(self):
1672         self.auth_checkbox = gtk.CheckButton("Use authentication")
1673         self.auth_checkbox.set_tooltip_text("Check this box to set the username and the password")
1674         self.auth_checkbox.set_active(self.auth)
1675         self.auth_checkbox.connect("toggled", self.auth_checkbox_toggled_cb)
1676         self.vbox.pack_start(self.auth_checkbox, expand=False, fill=False)
1677
1678         hbox = gtk.HBox(False, 6)
1679         self.user_label = gtk.Label("Username:")
1680         self.user_text = gtk.Entry()
1681         self.user_text.set_text(self.user)
1682         hbox.pack_start(self.user_label, expand=False, fill=False)
1683         hbox.pack_end(self.user_text, expand=False, fill=False)
1684         self.vbox.pack_start(hbox, expand=False, fill=False)
1685
1686         hbox = gtk.HBox(False, 6)
1687         self.passwd_label = gtk.Label("Password:")
1688         self.passwd_text = gtk.Entry()
1689         self.passwd_text.set_text(self.passwd)
1690         hbox.pack_start(self.passwd_label, expand=False, fill=False)
1691         hbox.pack_end(self.passwd_text, expand=False, fill=False)
1692         self.vbox.pack_start(hbox, expand=False, fill=False)
1693
1694         self.refresh_auth_components()
1695         self.show_all()
1696
1697     def refresh_auth_components(self):
1698         self.user_label.set_sensitive(self.auth)
1699         self.user_text.set_editable(self.auth)
1700         self.user_text.set_sensitive(self.auth)
1701         self.passwd_label.set_sensitive(self.auth)
1702         self.passwd_text.set_editable(self.auth)
1703         self.passwd_text.set_sensitive(self.auth)
1704
1705     def auth_checkbox_toggled_cb(self, button):
1706         self.auth = self.auth_checkbox.get_active()
1707         self.refresh_auth_components()
1708
1709     def response_cb(self, dialog, response_id):
1710         if response_id == gtk.RESPONSE_OK:
1711             if self.auth:
1712                 self.user = self.user_text.get_text()
1713                 self.passwd = self.passwd_text.get_text()
1714             else:
1715                 self.user = None
1716                 self.passwd = None