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