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