summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/bb/ui/hob.py
diff options
context:
space:
mode:
Diffstat (limited to 'bitbake/lib/bb/ui/hob.py')
-rw-r--r--bitbake/lib/bb/ui/hob.py924
1 files changed, 625 insertions, 299 deletions
diff --git a/bitbake/lib/bb/ui/hob.py b/bitbake/lib/bb/ui/hob.py
index 175e5bdf6c..fca41e44dc 100644
--- a/bitbake/lib/bb/ui/hob.py
+++ b/bitbake/lib/bb/ui/hob.py
@@ -18,12 +18,16 @@
18# with this program; if not, write to the Free Software Foundation, Inc., 18# with this program; if not, write to the Free Software Foundation, Inc.,
19# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 20
21import glib
21import gobject 22import gobject
22import gtk 23import gtk
23from bb.ui.crumbs.progress import ProgressBar 24from bb.ui.crumbs.tasklistmodel import TaskListModel, BuildRep
24from bb.ui.crumbs.tasklistmodel import TaskListModel
25from bb.ui.crumbs.hobeventhandler import HobHandler 25from bb.ui.crumbs.hobeventhandler import HobHandler
26from bb.ui.crumbs.configurator import Configurator
27from bb.ui.crumbs.hobprefs import HobPrefs
28from bb.ui.crumbs.layereditor import LayerEditor
26from bb.ui.crumbs.runningbuild import RunningBuildTreeView, RunningBuild 29from bb.ui.crumbs.runningbuild import RunningBuildTreeView, RunningBuild
30from bb.ui.crumbs.hig import CrumbsDialog
27import xmlrpclib 31import xmlrpclib
28import logging 32import logging
29import Queue 33import Queue
@@ -32,226 +36,459 @@ extraCaches = ['bb.cache_extra:HobRecipeInfo']
32 36
33class MainWindow (gtk.Window): 37class MainWindow (gtk.Window):
34 38
35 def __init__(self, taskmodel, handler, curr_mach=None, curr_distro=None): 39 def __init__(self, taskmodel, handler, configurator, prefs, layers, mach):
36 gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL) 40 gtk.Window.__init__(self)
41 # global state
42 self.curr_mach = mach
43 self.machine_handler_id = None
44 self.image_combo_id = None
45 self.generating = False
46 self.files_to_clean = []
47 self.selected_image = None
48 self.selected_packages = None
49
37 self.model = taskmodel 50 self.model = taskmodel
38 self.model.connect("tasklist-populated", self.update_model) 51 self.model.connect("tasklist-populated", self.update_model)
39 self.curr_mach = curr_mach 52 self.model.connect("image-changed", self.image_changed_string_cb)
40 self.curr_distro = curr_distro 53 self.curr_image_path = None
41 self.handler = handler 54 self.handler = handler
42 self.set_border_width(10) 55 self.configurator = configurator
43 self.connect("delete-event", gtk.main_quit) 56 self.prefs = prefs
44 self.set_title("BitBake Image Creator") 57 self.layers = layers
45 self.set_default_size(700, 600) 58 self.save_path = None
59 self.dirty = False
60
61 self.connect("delete-event", self.destroy_window)
62 self.set_title("Image Creator")
63 self.set_icon_name("applications-development")
64 self.set_default_size(1000, 650)
46 65
47 self.build = RunningBuild() 66 self.build = RunningBuild()
48 self.build.connect("build-succeeded", self.running_build_succeeded_cb)
49 self.build.connect("build-failed", self.running_build_failed_cb) 67 self.build.connect("build-failed", self.running_build_failed_cb)
68 self.build.connect("build-complete", self.handler.build_complete_cb)
69 self.build.connect("build-started", self.build_started_cb)
50 70
51 createview = self.create_build_gui() 71 self.handler.connect("build-complete", self.build_complete_cb)
72
73 vbox = gtk.VBox(False, 0)
74 vbox.set_border_width(0)
75 vbox.show()
76 self.add(vbox)
77 self.menu = self.create_menu()
78 vbox.pack_start(self.menu, False)
79 createview = self.create_build_gui()
80 self.back = None
81 self.cancel = None
52 buildview = self.view_build_gui() 82 buildview = self.view_build_gui()
53 self.nb = gtk.Notebook() 83 self.nb = gtk.Notebook()
54 self.nb.append_page(createview) 84 self.nb.append_page(createview)
55 self.nb.append_page(buildview) 85 self.nb.append_page(buildview)
56 self.nb.set_current_page(0) 86 self.nb.set_current_page(0)
57 self.nb.set_show_tabs(False) 87 self.nb.set_show_tabs(False)
58 self.add(self.nb) 88 vbox.pack_start(self.nb, expand=True, fill=True)
59 self.generating = False 89
90 def destroy_window(self, widget, event):
91 self.quit()
92
93 def menu_quit(self, action):
94 self.quit()
95
96 def quit(self):
97 if self.dirty and len(self.model.contents):
98 question = "Would you like to save your customisations?"
99 dialog = CrumbsDialog(self, question, gtk.STOCK_DIALOG_WARNING)
100 dialog.add_buttons(gtk.STOCK_NO, gtk.RESPONSE_NO,
101 gtk.STOCK_YES, gtk.RESPONSE_YES)
102 resp = dialog.run()
103 if resp == gtk.RESPONSE_YES:
104 if not self.save_path:
105 self.get_save_path()
106 self.save_recipe_file()
107 rep = self.model.get_build_rep()
108 rep.writeRecipe(self.save_path, self.model)
109
110 gtk.main_quit()
60 111
61 def scroll_tv_cb(self, model, path, it, view): 112 def scroll_tv_cb(self, model, path, it, view):
62 view.scroll_to_cell(path) 113 view.scroll_to_cell(path)
63 114
64 def running_build_failed_cb(self, running_build): 115 def running_build_failed_cb(self, running_build):
65 # FIXME: handle this 116 # FIXME: handle this
66 return 117 print("Build failed")
118
119 def image_changed_string_cb(self, model, new_image):
120 cnt = 0
121 it = self.model.images.get_iter_first()
122 while it:
123 path = self.model.images.get_path(it)
124 if self.model.images[path][self.model.COL_NAME] == new_image:
125 self.image_combo.set_active(cnt)
126 break
127 it = self.model.images.iter_next(it)
128 cnt = cnt + 1
129
130 def image_changed_cb(self, combo):
131 model = self.image_combo.get_model()
132 it = self.image_combo.get_active_iter()
133 if it:
134 path = model.get_path(it)
135 # Firstly, deselect the previous image
136 if self.curr_image_path:
137 self.toggle_package(self.curr_image_path, model)
138 # Now select the new image and save its path in case we
139 # change the image later
140 self.curr_image_path = path
141 self.toggle_package(path, model, image=True)
142
143 def reload_triggered_cb(self, handler, image, packages):
144 if image:
145 self.selected_image = image
146 if len(packages):
147 self.selected_packages = packages.split()
67 148
68 def running_build_succeeded_cb(self, running_build): 149 def data_generated(self, handler):
69 label = gtk.Label("Build completed, start another build?") 150 self.generating = False
70 dialog = gtk.Dialog("Build complete", 151 self.image_combo.set_model(self.model.images_model())
71 self, 152 if not self.image_combo_id:
72 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, 153 self.image_combo_id = self.image_combo.connect("changed", self.image_changed_cb)
73 (gtk.STOCK_NO, gtk.RESPONSE_NO, 154 self.enable_widgets()
74 gtk.STOCK_YES, gtk.RESPONSE_YES))
75 dialog.vbox.pack_start(label)
76 label.show()
77 response = dialog.run()
78 dialog.destroy()
79 if response == gtk.RESPONSE_YES:
80 self.model.reset() # NOTE: really?
81 self.nb.set_current_page(0)
82 return
83 155
84 def machine_combo_changed_cb(self, combo, handler): 156 def machine_combo_changed_cb(self, combo, handler):
85 mach = combo.get_active_text() 157 mach = combo.get_active_text()
86 if mach != self.curr_mach: 158 if mach != self.curr_mach:
87 self.curr_mach = mach 159 self.curr_mach = mach
160 # Flush this straight to the file as MACHINE is changed
161 # independently of other 'Preferences'
162 self.configurator.setLocalConfVar('MACHINE', mach)
163 self.configurator.writeLocalConf()
88 handler.set_machine(mach) 164 handler.set_machine(mach)
165 handler.reload_data()
89 166
90 def update_machines(self, handler, machines): 167 def update_machines(self, handler, machines):
91 active = 0 168 active = 0
92 for machine in machines: 169 # disconnect the signal handler before updating the combo model
93 self.machine_combo.append_text(machine) 170 if self.machine_handler_id:
94 if machine == self.curr_mach: 171 self.machine_combo.disconnect(self.machine_handler_id)
172 self.machine_handler_id = None
173
174 model = self.machine_combo.get_model()
175 if model:
176 model.clear()
177
178 for machine in machines:
179 self.machine_combo.append_text(machine)
180 if machine == self.curr_mach:
95 self.machine_combo.set_active(active) 181 self.machine_combo.set_active(active)
96 active = active + 1 182 active = active + 1
97 self.machine_combo.connect("changed", self.machine_combo_changed_cb, handler) 183
98 184 self.machine_handler_id = self.machine_combo.connect("changed", self.machine_combo_changed_cb, handler)
99 def update_distros(self, handler, distros): 185
100 # FIXME: when we add UI for changing distro this will be used 186 def set_busy_cursor(self, busy=True):
101 return 187 """
102 188 Convenience method to set the cursor to a spinner when executing
103 def data_generated(self, handler): 189 a potentially lengthy process.
104 self.generating = False 190 A busy value of False will set the cursor back to the default
105 191 left pointer.
106 def spin_idle_func(self, pbar): 192 """
193 if busy:
194 cursor = gtk.gdk.Cursor(gtk.gdk.WATCH)
195 else:
196 # TODO: presumably the default cursor is different on RTL
197 # systems. Can we determine the default cursor? Or at least
198 # the cursor which is set before we change it?
199 cursor = gtk.gdk.Cursor(gtk.gdk.LEFT_PTR)
200 window = self.get_root_window()
201 window.set_cursor(cursor)
202
203 def busy_idle_func(self):
107 if self.generating: 204 if self.generating:
108 pbar.pulse() 205 self.progress.set_text("Loading...")
206 self.progress.pulse()
109 return True 207 return True
110 else: 208 else:
111 pbar.hide() 209 if not self.image_combo_id:
210 self.image_combo_id = self.image_combo.connect("changed", self.image_changed_cb)
211 self.progress.set_text("Loaded")
212 self.progress.set_fraction(0.0)
213 self.set_busy_cursor(False)
112 return False 214 return False
113 215
114 def busy(self, handler): 216 def busy(self, handler):
115 self.generating = True 217 self.generating = True
116 pbar = ProgressBar(self) 218 self.set_busy_cursor()
117 pbar.connect("delete-event", gtk.main_quit) # NOTE: questionable... 219 if self.image_combo_id:
118 pbar.pulse() 220 self.image_combo.disconnect(self.image_combo_id)
119 gobject.timeout_add (200, 221 self.image_combo_id = None
120 self.spin_idle_func, 222 self.progress.pulse()
121 pbar) 223 gobject.timeout_add (200, self.busy_idle_func)
224 self.disable_widgets()
225
226 def enable_widgets(self):
227 self.menu.set_sensitive(True)
228 self.machine_combo.set_sensitive(True)
229 self.image_combo.set_sensitive(True)
230 self.nb.set_sensitive(True)
231 self.contents_tree.set_sensitive(True)
232
233 def disable_widgets(self):
234 self.menu.set_sensitive(False)
235 self.machine_combo.set_sensitive(False)
236 self.image_combo.set_sensitive(False)
237 self.nb.set_sensitive(False)
238 self.contents_tree.set_sensitive(False)
122 239
123 def update_model(self, model): 240 def update_model(self, model):
124 pkgsaz_model = gtk.TreeModelSort(self.model.packages_model()) 241 # We want the packages model to be alphabetised and sortable so create
242 # a TreeModelSort to use in the view
243 pkgsaz_model = gtk.TreeModelSort(self.model.packages_model())
125 pkgsaz_model.set_sort_column_id(self.model.COL_NAME, gtk.SORT_ASCENDING) 244 pkgsaz_model.set_sort_column_id(self.model.COL_NAME, gtk.SORT_ASCENDING)
245 # Unset default sort func so that we only toggle between A-Z and
246 # Z-A sorting
247 pkgsaz_model.set_default_sort_func(None)
126 self.pkgsaz_tree.set_model(pkgsaz_model) 248 self.pkgsaz_tree.set_model(pkgsaz_model)
127 249
128 # FIXME: need to implement a custom sort function, as otherwise the column 250 # We want the contents to be alphabetised so create a TreeModelSort to
129 # is re-ordered when toggling the inclusion state (COL_INC) 251 # use in the view
130 pkgsgrp_model = gtk.TreeModelSort(self.model.packages_model()) 252 contents_model = gtk.TreeModelSort(self.model.contents_model())
131 pkgsgrp_model.set_sort_column_id(self.model.COL_GROUP, gtk.SORT_ASCENDING) 253 contents_model.set_sort_column_id(self.model.COL_NAME, gtk.SORT_ASCENDING)
132 self.pkgsgrp_tree.set_model(pkgsgrp_model) 254 # Unset default sort func so that we only toggle between A-Z and
133 255 # Z-A sorting
134 self.contents_tree.set_model(self.model.contents_model()) 256 contents_model.set_default_sort_func(None)
135 self.images_tree.set_model(self.model.images_model()) 257 self.contents_tree.set_model(contents_model)
136 self.tasks_tree.set_model(self.model.tasks_model()) 258 self.tasks_tree.set_model(self.model.tasks_model())
259
260 if self.selected_image:
261 if self.image_combo_id:
262 self.image_combo.disconnect(self.image_combo_id)
263 self.image_combo_id = None
264 self.model.set_selected_image(self.selected_image)
265 self.selected_image = None
266 if not self.image_combo_id:
267 self.image_combo_id = self.image_combo.connect("changed", self.image_changed_cb)
268
269 if self.selected_packages:
270 self.model.set_selected_packages(self.selected_packages)
271 self.selected_packages = None
137 272
138 def reset_clicked_cb(self, button): 273 def reset_clicked_cb(self, button):
139 label = gtk.Label("Are you sure you want to reset the image contents?") 274 lbl = "<b>Reset your selections?</b>\n\nAny new changes you have made will be lost"
140 dialog = gtk.Dialog("Confirm reset", self, 275 dialog = CrumbsDialog(self, lbl, gtk.STOCK_DIALOG_WARNING)
141 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, 276 dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
142 (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, 277 dialog.add_button("Reset", gtk.RESPONSE_OK)
143 gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
144 dialog.vbox.pack_start(label)
145 label.show()
146 response = dialog.run() 278 response = dialog.run()
147 dialog.destroy() 279 dialog.destroy()
148 if (response == gtk.RESPONSE_ACCEPT): 280 if response == gtk.RESPONSE_OK:
149 self.model.reset() 281 self.reset_build()
150 return 282 return
151 283
284 def reset_build(self):
285 self.image_combo.disconnect(self.image_combo_id)
286 self.image_combo_id = None
287 self.image_combo.set_active(-1)
288 self.image_combo_id = self.image_combo.connect("changed", self.image_changed_cb)
289 self.model.reset()
290
291 def layers_cb(self, action):
292 resp = self.layers.run()
293 self.layers.save_current_layers()
294 self.layers.hide()
295
296 def add_layer_cb(self, action):
297 self.layers.find_layer(self)
298
299 def preferences_cb(self, action):
300 resp = self.prefs.run()
301 self.prefs.write_changes()
302 self.prefs.hide()
303
304 def about_cb(self, action):
305 about = gtk.AboutDialog()
306 about.set_name("Image Creator")
307 about.set_copyright("Copyright (C) 2011 Intel Corporation")
308 about.set_authors(["Joshua Lock <josh@linux.intel.com>"])
309 about.set_logo_icon_name("applications-development")
310 about.run()
311 about.destroy()
312
313 def save_recipe_file(self):
314 rep = self.model.get_build_rep()
315 rep.writeRecipe(self.save_path, self.model)
316 self.dirty = False
317
318 def get_save_path(self):
319 chooser = gtk.FileChooserDialog(title=None, parent=self,
320 action=gtk.FILE_CHOOSER_ACTION_SAVE,
321 buttons=(gtk.STOCK_CANCEL,
322 gtk.RESPONSE_CANCEL,
323 gtk.STOCK_SAVE,
324 gtk.RESPONSE_OK,))
325 chooser.set_current_name("myimage.bb")
326 response = chooser.run()
327 if response == gtk.RESPONSE_OK:
328 self.save_path = chooser.get_filename()
329 chooser.destroy()
330
331 def save_cb(self, action):
332 if not self.save_path:
333 self.get_save_path()
334 self.save_recipe_file()
335
336 def save_as_cb(self, action):
337 self.get_save_path()
338 self.save_recipe_file()
339
340 def open_cb(self, action):
341 chooser = gtk.FileChooserDialog(title=None, parent=self,
342 action=gtk.FILE_CHOOSER_ACTION_OPEN,
343 buttons=(gtk.STOCK_CANCEL,
344 gtk.RESPONSE_CANCEL,
345 gtk.STOCK_OPEN,
346 gtk.RESPONSE_OK))
347 response = chooser.run()
348 rep = BuildRep(None, None, None)
349 if response == gtk.RESPONSE_OK:
350 rep.loadRecipe(chooser.get_filename())
351 chooser.destroy()
352 self.model.load_image_rep(rep)
353 self.dirty = False
354
152 def bake_clicked_cb(self, button): 355 def bake_clicked_cb(self, button):
153 if not self.model.targets_contains_image(): 356 rep = self.model.get_build_rep()
154 label = gtk.Label("No image was selected. Just build the selected packages?") 357 if not rep.base_image:
155 dialog = gtk.Dialog("Warning, no image selected", 358 lbl = "<b>Build only packages?</b>\n\nAn image has not been selected, so only the selected packages will be built."
156 self, 359 dialog = CrumbsDialog(self, lbl, gtk.STOCK_DIALOG_WARNING)
157 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, 360 dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
158 (gtk.STOCK_NO, gtk.RESPONSE_NO, 361 dialog.add_button("Build", gtk.RESPONSE_YES)
159 gtk.STOCK_YES, gtk.RESPONSE_YES))
160 dialog.vbox.pack_start(label)
161 label.show()
162 response = dialog.run() 362 response = dialog.run()
163 dialog.destroy() 363 dialog.destroy()
164 if not response == gtk.RESPONSE_YES: 364 if response == gtk.RESPONSE_CANCEL:
165 return 365 return
166
167 # Note: We could "squash" the targets list to only include things not brought in by an image
168 task_list = self.model.get_targets()
169 if len(task_list):
170 tasks = " ".join(task_list)
171 # TODO: show a confirmation dialog
172 print("Including these extra tasks in IMAGE_INSTALL: %s" % tasks)
173 else: 366 else:
174 return 367 # TODO: show a confirmation dialog ?
175 368 if not self.save_path:
369 import tempfile, datetime
370 image_name = "hob-%s-variant-%s.bb" % (rep.base_image, datetime.date.today().isoformat())
371 image_dir = os.path.join(tempfile.gettempdir(), 'hob-images')
372 bb.utils.mkdirhier(image_dir)
373 recipepath = os.path.join(image_dir, image_name)
374 else:
375 recipepath = self.save_path
376
377 rep.writeRecipe(recipepath, self.model)
378 # In the case where we saved the file for the purpose of building
379 # it we should then delete it so that the users workspace doesn't
380 # contain files they haven't explicitly saved there.
381 if not self.save_path:
382 self.files_to_clean.append(recipepath)
383
384 self.handler.queue_image_recipe_path(recipepath)
385
386 self.handler.build_packages(rep.allpkgs.split(" "))
176 self.nb.set_current_page(1) 387 self.nb.set_current_page(1)
177 self.handler.run_build(task_list)
178 388
179 return 389 def back_button_clicked_cb(self, button):
390 self.toggle_createview()
180 391
181 def advanced_expander_cb(self, expander, param): 392 def toggle_createview(self):
182 return 393 self.build.model.clear()
183 394 self.nb.set_current_page(0)
184 def images(self):
185 self.images_tree = gtk.TreeView()
186 self.images_tree.set_headers_visible(True)
187 self.images_tree.set_headers_clickable(False)
188 self.images_tree.set_enable_search(True)
189 self.images_tree.set_search_column(0)
190 self.images_tree.get_selection().set_mode(gtk.SELECTION_NONE)
191 395
192 col = gtk.TreeViewColumn('Package') 396 def build_complete_cb(self, running_build):
193 col1 = gtk.TreeViewColumn('Description') 397 self.back.connect("clicked", self.back_button_clicked_cb)
194 col2 = gtk.TreeViewColumn('License') 398 self.back.set_sensitive(True)
195 col3 = gtk.TreeViewColumn('Include') 399 self.cancel.set_sensitive(False)
196 col3.set_resizable(False) 400 for f in self.files_to_clean:
401 os.remove(f)
197 402
198 self.images_tree.append_column(col) 403 lbl = "<b>Build completed</b>\n\nClick 'Edit Image' to start another build or 'View Log' to view the build log."
199 self.images_tree.append_column(col1) 404 if self.handler.building == "image":
200 self.images_tree.append_column(col2) 405 deploy = self.handler.get_image_deploy_dir()
201 self.images_tree.append_column(col3) 406 lbl = lbl + "\n<a href=\"file://%s\" title=\"%s\">Browse folder of built images</a>." % (deploy, deploy)
202 407
203 cell = gtk.CellRendererText() 408 dialog = CrumbsDialog(self, lbl)
204 cell1 = gtk.CellRendererText() 409 dialog.add_button("View Log", gtk.RESPONSE_CANCEL)
205 cell2 = gtk.CellRendererText() 410 dialog.add_button("Edit Image", gtk.RESPONSE_OK)
206 cell3 = gtk.CellRendererToggle() 411 response = dialog.run()
207 cell3.set_property('activatable', True) 412 dialog.destroy()
208 cell3.connect("toggled", self.toggle_include_cb, self.images_tree) 413 if response == gtk.RESPONSE_OK:
209 414 self.toggle_createview()
210 col.pack_start(cell, True) 415
211 col1.pack_start(cell1, True) 416 def build_started_cb(self, running_build):
212 col2.pack_start(cell2, True) 417 self.back.set_sensitive(False)
213 col3.pack_start(cell3, True) 418 self.cancel.set_sensitive(True)
214 419
215 col.set_attributes(cell, text=self.model.COL_NAME) 420 def include_gplv3_cb(self, toggle):
216 col1.set_attributes(cell1, text=self.model.COL_DESC) 421 excluded = toggle.get_active()
217 col2.set_attributes(cell2, text=self.model.COL_LIC) 422 self.handler.toggle_gplv3(excluded)
218 col3.set_attributes(cell3, active=self.model.COL_INC) 423
219 424 def change_bb_threads(self, spinner):
220 self.images_tree.show() 425 val = spinner.get_value_as_int()
221 426 self.handler.set_bbthreads(val)
222 scroll = gtk.ScrolledWindow() 427
223 scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) 428 def change_make_threads(self, spinner):
224 scroll.set_shadow_type(gtk.SHADOW_IN) 429 val = spinner.get_value_as_int()
225 scroll.add(self.images_tree) 430 self.handler.set_pmake(val)
226 431
227 return scroll 432 def toggle_toolchain(self, check):
433 enabled = check.get_active()
434 self.handler.toggle_toolchain(enabled)
435
436 def toggle_headers(self, check):
437 enabled = check.get_active()
438 self.handler.toggle_toolchain_headers(enabled)
439
440 def toggle_package_idle_cb(self, opath, image):
441 """
442 As the operations which we're calling on the model can take
443 a significant amount of time (in the order of seconds) during which
444 the GUI is unresponsive as the main loop is blocked perform them in
445 an idle function which at least enables us to set the busy cursor
446 before the UI is blocked giving the appearance of being responsive.
447 """
448 # Whether the item is currently included
449 inc = self.model[opath][self.model.COL_INC]
450 # If the item is already included, mark it for removal then
451 # the sweep_up() method finds affected items and marks them
452 # appropriately
453 if inc:
454 self.model.mark(opath)
455 self.model.sweep_up()
456 # If the item isn't included, mark it for inclusion
457 else:
458 self.model.include_item(item_path=opath,
459 binb="User Selected",
460 image_contents=image)
461
462 self.set_busy_cursor(False)
463 return False
464
465 def toggle_package(self, path, model, image=False):
466 # Warn user before removing packages
467 inc = model[path][self.model.COL_INC]
468 if inc:
469 pn = model[path][self.model.COL_NAME]
470 revdeps = self.model.find_reverse_depends(pn)
471 if len(revdeps):
472 lbl = "<b>Remove %s?</b>\n\nThis action cannot be undone and all packages which depend on this will be removed\nPackages which depend on %s include %s." % (pn, pn, ", ".join(revdeps).rstrip(","))
473 else:
474 lbl = "<b>Remove %s?</b>\n\nThis action cannot be undone." % pn
475 dialog = CrumbsDialog(self, lbl, gtk.STOCK_DIALOG_WARNING)
476 dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
477 dialog.add_button("Remove", gtk.RESPONSE_OK)
478 response = dialog.run()
479 dialog.destroy()
480 if response == gtk.RESPONSE_CANCEL:
481 return
228 482
229 def toggle_package(self, path, model): 483 self.set_busy_cursor()
230 # Convert path to path in original model 484 # Convert path to path in original model
231 opath = model.convert_path_to_child_path(path) 485 opath = model.convert_path_to_child_path(path)
232 # current include status 486 # This is a potentially length call which can block the
233 inc = self.model[opath][self.model.COL_INC] 487 # main loop, therefore do the work in an idle func to keep
234 if inc: 488 # the UI responsive
235 self.model.mark(opath) 489 glib.idle_add(self.toggle_package_idle_cb, opath, image)
236 self.model.sweep_up()
237 #self.model.remove_package_full(cpath)
238 else:
239 self.model.include_item(opath)
240 return
241 490
242 def remove_package_cb(self, cell, path): 491 self.dirty = True
243 model = self.model.contents_model()
244 label = gtk.Label("Are you sure you want to remove this item?")
245 dialog = gtk.Dialog("Confirm removal", self,
246 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
247 (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
248 gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
249 dialog.vbox.pack_start(label)
250 label.show()
251 response = dialog.run()
252 dialog.destroy()
253 if (response == gtk.RESPONSE_ACCEPT):
254 self.toggle_package(path, model)
255 492
256 def toggle_include_cb(self, cell, path, tv): 493 def toggle_include_cb(self, cell, path, tv):
257 model = tv.get_model() 494 model = tv.get_model()
@@ -262,23 +499,36 @@ class MainWindow (gtk.Window):
262 sort_model = tv.get_model() 499 sort_model = tv.get_model()
263 cpath = sort_model.convert_path_to_child_path(path) 500 cpath = sort_model.convert_path_to_child_path(path)
264 self.toggle_package(cpath, sort_model.get_model()) 501 self.toggle_package(cpath, sort_model.get_model())
265 502
266 def pkgsaz(self): 503 def pkgsaz(self):
504 vbox = gtk.VBox(False, 6)
505 vbox.show()
267 self.pkgsaz_tree = gtk.TreeView() 506 self.pkgsaz_tree = gtk.TreeView()
268 self.pkgsaz_tree.set_headers_visible(True) 507 self.pkgsaz_tree.set_headers_visible(True)
269 self.pkgsaz_tree.set_headers_clickable(True) 508 self.pkgsaz_tree.set_headers_clickable(True)
270 self.pkgsaz_tree.set_enable_search(True) 509 self.pkgsaz_tree.set_enable_search(True)
271 self.pkgsaz_tree.set_search_column(0) 510 self.pkgsaz_tree.set_search_column(0)
272 self.pkgsaz_tree.get_selection().set_mode(gtk.SELECTION_NONE) 511 self.pkgsaz_tree.get_selection().set_mode(gtk.SELECTION_SINGLE)
273 512
274 col = gtk.TreeViewColumn('Package') 513 col = gtk.TreeViewColumn('Package')
514 col.set_clickable(True)
515 col.set_sort_column_id(self.model.COL_NAME)
516 col.set_min_width(220)
275 col1 = gtk.TreeViewColumn('Description') 517 col1 = gtk.TreeViewColumn('Description')
276 col1.set_resizable(True) 518 col1.set_resizable(True)
519 col1.set_min_width(360)
277 col2 = gtk.TreeViewColumn('License') 520 col2 = gtk.TreeViewColumn('License')
278 col2.set_resizable(True) 521 col2.set_resizable(True)
522 col2.set_clickable(True)
523 col2.set_sort_column_id(self.model.COL_LIC)
524 col2.set_min_width(170)
279 col3 = gtk.TreeViewColumn('Group') 525 col3 = gtk.TreeViewColumn('Group')
280 col4 = gtk.TreeViewColumn('Include') 526 col3.set_clickable(True)
281 col4.set_resizable(False) 527 col3.set_sort_column_id(self.model.COL_GROUP)
528 col4 = gtk.TreeViewColumn('Included')
529 col4.set_min_width(80)
530 col4.set_max_width(90)
531 col4.set_sort_column_id(self.model.COL_INC)
282 532
283 self.pkgsaz_tree.append_column(col) 533 self.pkgsaz_tree.append_column(col)
284 self.pkgsaz_tree.append_column(col1) 534 self.pkgsaz_tree.append_column(col1)
@@ -288,9 +538,9 @@ class MainWindow (gtk.Window):
288 538
289 cell = gtk.CellRendererText() 539 cell = gtk.CellRendererText()
290 cell1 = gtk.CellRendererText() 540 cell1 = gtk.CellRendererText()
291 cell1.set_property('width-chars', 20) 541 cell1.set_property('width-chars', 20)
292 cell2 = gtk.CellRendererText() 542 cell2 = gtk.CellRendererText()
293 cell2.set_property('width-chars', 20) 543 cell2.set_property('width-chars', 20)
294 cell3 = gtk.CellRendererText() 544 cell3 = gtk.CellRendererText()
295 cell4 = gtk.CellRendererToggle() 545 cell4 = gtk.CellRendererToggle()
296 cell4.set_property('activatable', True) 546 cell4.set_property('activatable', True)
@@ -300,7 +550,7 @@ class MainWindow (gtk.Window):
300 col1.pack_start(cell1, True) 550 col1.pack_start(cell1, True)
301 col2.pack_start(cell2, True) 551 col2.pack_start(cell2, True)
302 col3.pack_start(cell3, True) 552 col3.pack_start(cell3, True)
303 col4.pack_start(cell4, True) 553 col4.pack_end(cell4, True)
304 554
305 col.set_attributes(cell, text=self.model.COL_NAME) 555 col.set_attributes(cell, text=self.model.COL_NAME)
306 col1.set_attributes(cell1, text=self.model.COL_DESC) 556 col1.set_attributes(cell1, text=self.model.COL_DESC)
@@ -314,75 +564,43 @@ class MainWindow (gtk.Window):
314 scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) 564 scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
315 scroll.set_shadow_type(gtk.SHADOW_IN) 565 scroll.set_shadow_type(gtk.SHADOW_IN)
316 scroll.add(self.pkgsaz_tree) 566 scroll.add(self.pkgsaz_tree)
567 vbox.pack_start(scroll, True, True, 0)
568
569 hb = gtk.HBox(False, 0)
570 hb.show()
571 search = gtk.Entry()
572 search.set_icon_from_stock(gtk.ENTRY_ICON_SECONDARY, "gtk-clear")
573 search.connect("icon-release", self.search_entry_clear_cb)
574 search.show()
575 self.pkgsaz_tree.set_search_entry(search)
576 hb.pack_end(search, False, False, 0)
577 label = gtk.Label("Search packages:")
578 label.show()
579 hb.pack_end(label, False, False, 6)
580 vbox.pack_start(hb, False, False, 0)
317 581
318 return scroll 582 return vbox
319
320 def pkgsgrp(self):
321 self.pkgsgrp_tree = gtk.TreeView()
322 self.pkgsgrp_tree.set_headers_visible(True)
323 self.pkgsgrp_tree.set_headers_clickable(False)
324 self.pkgsgrp_tree.set_enable_search(True)
325 self.pkgsgrp_tree.set_search_column(0)
326 self.pkgsgrp_tree.get_selection().set_mode(gtk.SELECTION_NONE)
327
328 col = gtk.TreeViewColumn('Package')
329 col1 = gtk.TreeViewColumn('Description')
330 col1.set_resizable(True)
331 col2 = gtk.TreeViewColumn('License')
332 col2.set_resizable(True)
333 col3 = gtk.TreeViewColumn('Group')
334 col4 = gtk.TreeViewColumn('Include')
335 col4.set_resizable(False)
336
337 self.pkgsgrp_tree.append_column(col)
338 self.pkgsgrp_tree.append_column(col1)
339 self.pkgsgrp_tree.append_column(col2)
340 self.pkgsgrp_tree.append_column(col3)
341 self.pkgsgrp_tree.append_column(col4)
342
343 cell = gtk.CellRendererText()
344 cell1 = gtk.CellRendererText()
345 cell1.set_property('width-chars', 20)
346 cell2 = gtk.CellRendererText()
347 cell2.set_property('width-chars', 20)
348 cell3 = gtk.CellRendererText()
349 cell4 = gtk.CellRendererToggle()
350 cell4.set_property("activatable", True)
351 cell4.connect("toggled", self.toggle_pkg_include_cb, self.pkgsgrp_tree)
352
353 col.pack_start(cell, True)
354 col1.pack_start(cell1, True)
355 col2.pack_start(cell2, True)
356 col3.pack_start(cell3, True)
357 col4.pack_start(cell4, True)
358
359 col.set_attributes(cell, text=self.model.COL_NAME)
360 col1.set_attributes(cell1, text=self.model.COL_DESC)
361 col2.set_attributes(cell2, text=self.model.COL_LIC)
362 col3.set_attributes(cell3, text=self.model.COL_GROUP)
363 col4.set_attributes(cell4, active=self.model.COL_INC)
364
365 self.pkgsgrp_tree.show()
366
367 scroll = gtk.ScrolledWindow()
368 scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
369 scroll.set_shadow_type(gtk.SHADOW_IN)
370 scroll.add(self.pkgsgrp_tree)
371 583
372 return scroll 584 def search_entry_clear_cb(self, entry, icon_pos, event):
585 entry.set_text("")
373 586
374 def tasks(self): 587 def tasks(self):
588 vbox = gtk.VBox(False, 6)
589 vbox.show()
375 self.tasks_tree = gtk.TreeView() 590 self.tasks_tree = gtk.TreeView()
376 self.tasks_tree.set_headers_visible(True) 591 self.tasks_tree.set_headers_visible(True)
377 self.tasks_tree.set_headers_clickable(False) 592 self.tasks_tree.set_headers_clickable(False)
378 self.tasks_tree.set_enable_search(True) 593 self.tasks_tree.set_enable_search(True)
379 self.tasks_tree.set_search_column(0) 594 self.tasks_tree.set_search_column(0)
380 self.tasks_tree.get_selection().set_mode(gtk.SELECTION_NONE) 595 self.tasks_tree.get_selection().set_mode(gtk.SELECTION_SINGLE)
381 596
382 col = gtk.TreeViewColumn('Package') 597 col = gtk.TreeViewColumn('Package')
598 col.set_min_width(430)
383 col1 = gtk.TreeViewColumn('Description') 599 col1 = gtk.TreeViewColumn('Description')
600 col1.set_min_width(430)
384 col2 = gtk.TreeViewColumn('Include') 601 col2 = gtk.TreeViewColumn('Include')
385 col2.set_resizable(False) 602 col2.set_min_width(70)
603 col2.set_max_width(80)
386 604
387 self.tasks_tree.append_column(col) 605 self.tasks_tree.append_column(col)
388 self.tasks_tree.append_column(col1) 606 self.tasks_tree.append_column(col1)
@@ -396,7 +614,7 @@ class MainWindow (gtk.Window):
396 614
397 col.pack_start(cell, True) 615 col.pack_start(cell, True)
398 col1.pack_start(cell1, True) 616 col1.pack_start(cell1, True)
399 col2.pack_start(cell2, True) 617 col2.pack_end(cell2, True)
400 618
401 col.set_attributes(cell, text=self.model.COL_NAME) 619 col.set_attributes(cell, text=self.model.COL_NAME)
402 col1.set_attributes(cell1, text=self.model.COL_DESC) 620 col1.set_attributes(cell1, text=self.model.COL_DESC)
@@ -408,26 +626,37 @@ class MainWindow (gtk.Window):
408 scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) 626 scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
409 scroll.set_shadow_type(gtk.SHADOW_IN) 627 scroll.set_shadow_type(gtk.SHADOW_IN)
410 scroll.add(self.tasks_tree) 628 scroll.add(self.tasks_tree)
629 vbox.pack_start(scroll, True, True, 0)
630
631 hb = gtk.HBox(False, 0)
632 hb.show()
633 search = gtk.Entry()
634 search.show()
635 self.tasks_tree.set_search_entry(search)
636 hb.pack_end(search, False, False, 0)
637 label = gtk.Label("Search collections:")
638 label.show()
639 hb.pack_end(label, False, False, 6)
640 vbox.pack_start(hb, False, False, 0)
411 641
412 return scroll 642 return vbox
413 643
414 def cancel_build(self, button): 644 def cancel_build(self, button):
415 label = gtk.Label("Do you really want to stop this build?") 645 lbl = "<b>Stop build?</b>\n\nAre you sure you want to stop this build?"
416 dialog = gtk.Dialog("Cancel build", 646 dialog = CrumbsDialog(self, lbl, gtk.STOCK_DIALOG_WARNING)
417 self, 647 dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
418 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, 648 dialog.add_button("Stop", gtk.RESPONSE_OK)
419 (gtk.STOCK_NO, gtk.RESPONSE_NO, 649 dialog.add_button("Force Stop", gtk.RESPONSE_YES)
420 gtk.STOCK_YES, gtk.RESPONSE_YES))
421 dialog.vbox.pack_start(label)
422 label.show()
423 response = dialog.run() 650 response = dialog.run()
424 dialog.destroy() 651 dialog.destroy()
425 if response == gtk.RESPONSE_YES: 652 if response == gtk.RESPONSE_OK:
426 self.handler.cancel_build() 653 self.handler.cancel_build()
427 return 654 elif response == gtk.RESPONSE_YES:
655 self.handler.cancel_build(True)
428 656
429 def view_build_gui(self): 657 def view_build_gui(self):
430 vbox = gtk.VBox(False, 6) 658 vbox = gtk.VBox(False, 12)
659 vbox.set_border_width(6)
431 vbox.show() 660 vbox.show()
432 build_tv = RunningBuildTreeView() 661 build_tv = RunningBuildTreeView()
433 build_tv.show() 662 build_tv.show()
@@ -438,20 +667,74 @@ class MainWindow (gtk.Window):
438 scrolled_view.add(build_tv) 667 scrolled_view.add(build_tv)
439 scrolled_view.show() 668 scrolled_view.show()
440 vbox.pack_start(scrolled_view, expand=True, fill=True) 669 vbox.pack_start(scrolled_view, expand=True, fill=True)
441 hbox = gtk.HBox(False, 6) 670 hbox = gtk.HBox(False, 12)
442 hbox.show() 671 hbox.show()
443 vbox.pack_start(hbox, expand=False, fill=False) 672 vbox.pack_start(hbox, expand=False, fill=False)
444 cancel = gtk.Button(stock=gtk.STOCK_CANCEL) 673 self.back = gtk.Button("Back")
445 cancel.connect("clicked", self.cancel_build) 674 self.back.show()
446 cancel.show() 675 self.back.set_sensitive(False)
447 hbox.pack_end(cancel, expand=False, fill=False) 676 hbox.pack_start(self.back, expand=False, fill=False)
677 self.cancel = gtk.Button("Stop Build")
678 self.cancel.connect("clicked", self.cancel_build)
679 self.cancel.show()
680 hbox.pack_end(self.cancel, expand=False, fill=False)
448 681
449 return vbox 682 return vbox
683
684 def create_menu(self):
685 menu_items = '''<ui>
686 <menubar name="MenuBar">
687 <menu action="File">
688 <menuitem action="Save"/>
689 <menuitem action="Save As"/>
690 <menuitem action="Open"/>
691 <separator/>
692 <menuitem action="AddLayer" label="Add Layer"/>
693 <separator/>
694 <menuitem action="Quit"/>
695 </menu>
696 <menu action="Edit">
697 <menuitem action="Layers" label="Layers"/>
698 <menuitem action="Preferences"/>
699 </menu>
700 <menu action="Help">
701 <menuitem action="About"/>
702 </menu>
703 </menubar>
704 </ui>'''
705
706 uimanager = gtk.UIManager()
707 accel = uimanager.get_accel_group()
708 self.add_accel_group(accel)
709
710 actions = gtk.ActionGroup('ImageCreator')
711 self.actions = actions
712 actions.add_actions([('Quit', gtk.STOCK_QUIT, None, None,
713 None, self.menu_quit,),
714 ('File', None, '_File'),
715 ('Save', gtk.STOCK_SAVE, None, None, None, self.save_cb),
716 ('Save As', gtk.STOCK_SAVE_AS, None, None, None, self.save_as_cb),
717 ('Open', gtk.STOCK_OPEN, None, None, None, self.open_cb),
718 ('AddLayer', None, 'Add Layer', None, None, self.add_layer_cb),
719 ('Edit', None, '_Edit'),
720 ('Help', None, '_Help'),
721 ('Layers', None, 'Layers', None, None, self.layers_cb),
722 ('Preferences', gtk.STOCK_PREFERENCES, None, None, None, self.preferences_cb),
723 ('About', gtk.STOCK_ABOUT, None, None, None, self.about_cb)])
724 uimanager.insert_action_group(actions, 0)
725 uimanager.add_ui_from_string(menu_items)
726
727 menubar = uimanager.get_widget('/MenuBar')
728 menubar.show_all()
729
730 return menubar
450 731
451 def create_build_gui(self): 732 def create_build_gui(self):
452 vbox = gtk.VBox(False, 6) 733 vbox = gtk.VBox(False, 12)
734 vbox.set_border_width(6)
453 vbox.show() 735 vbox.show()
454 hbox = gtk.HBox(False, 6) 736
737 hbox = gtk.HBox(False, 12)
455 hbox.show() 738 hbox.show()
456 vbox.pack_start(hbox, expand=False, fill=False) 739 vbox.pack_start(hbox, expand=False, fill=False)
457 740
@@ -459,90 +742,92 @@ class MainWindow (gtk.Window):
459 label.show() 742 label.show()
460 hbox.pack_start(label, expand=False, fill=False, padding=6) 743 hbox.pack_start(label, expand=False, fill=False, padding=6)
461 self.machine_combo = gtk.combo_box_new_text() 744 self.machine_combo = gtk.combo_box_new_text()
462 self.machine_combo.set_active(0)
463 self.machine_combo.show() 745 self.machine_combo.show()
464 self.machine_combo.set_tooltip_text("Selects the architecture of the target board for which you would like to build an image.") 746 self.machine_combo.set_tooltip_text("Selects the architecture of the target board for which you would like to build an image.")
465 hbox.pack_start(self.machine_combo, expand=False, fill=False, padding=6) 747 hbox.pack_start(self.machine_combo, expand=False, fill=False, padding=6)
748 label = gtk.Label("Base image:")
749 label.show()
750 hbox.pack_start(label, expand=False, fill=False, padding=6)
751 self.image_combo = gtk.ComboBox()
752 self.image_combo.show()
753 self.image_combo.set_tooltip_text("Selects the image on which to base the created image")
754 image_combo_cell = gtk.CellRendererText()
755 self.image_combo.pack_start(image_combo_cell, True)
756 self.image_combo.add_attribute(image_combo_cell, 'text', self.model.COL_NAME)
757 hbox.pack_start(self.image_combo, expand=False, fill=False, padding=6)
758 self.progress = gtk.ProgressBar()
759 self.progress.set_size_request(250, -1)
760 hbox.pack_end(self.progress, expand=False, fill=False, padding=6)
466 761
467 ins = gtk.Notebook() 762 ins = gtk.Notebook()
468 vbox.pack_start(ins, expand=True, fill=True) 763 vbox.pack_start(ins, expand=True, fill=True)
469 ins.set_show_tabs(True) 764 ins.set_show_tabs(True)
470 label = gtk.Label("Images") 765 label = gtk.Label("Packages")
471 label.show() 766 label.show()
472 ins.append_page(self.images(), tab_label=label) 767 ins.append_page(self.pkgsaz(), tab_label=label)
473 label = gtk.Label("Tasks") 768 label = gtk.Label("Package Collections")
474 label.show() 769 label.show()
475 ins.append_page(self.tasks(), tab_label=label) 770 ins.append_page(self.tasks(), tab_label=label)
476 label = gtk.Label("Packages (by Group)")
477 label.show()
478 ins.append_page(self.pkgsgrp(), tab_label=label)
479 label = gtk.Label("Packages (by Name)")
480 label.show()
481 ins.append_page(self.pkgsaz(), tab_label=label)
482 ins.set_current_page(0) 771 ins.set_current_page(0)
483 ins.show_all() 772 ins.show_all()
484 773
485 hbox = gtk.HBox()
486 hbox.show()
487 vbox.pack_start(hbox, expand=False, fill=False)
488 label = gtk.Label("Image contents:") 774 label = gtk.Label("Image contents:")
775 self.model.connect("contents-changed", self.update_package_count_cb, label)
776 label.set_property("xalign", 0.00)
489 label.show() 777 label.show()
490 hbox.pack_start(label, expand=False, fill=False, padding=6) 778 vbox.pack_start(label, expand=False, fill=False, padding=6)
491 con = self.contents() 779 con = self.contents()
492 con.show() 780 con.show()
493 vbox.pack_start(con, expand=True, fill=True) 781 vbox.pack_start(con, expand=True, fill=True)
494 782
495 #advanced = gtk.Expander(label="Advanced") 783 bbox = gtk.HButtonBox()
496 #advanced.connect("notify::expanded", self.advanced_expander_cb) 784 bbox.set_spacing(12)
497 #advanced.show() 785 bbox.set_layout(gtk.BUTTONBOX_END)
498 #vbox.pack_start(advanced, expand=False, fill=False) 786 bbox.show()
499 787 vbox.pack_start(bbox, expand=False, fill=False)
500 hbox = gtk.HBox()
501 hbox.show()
502 vbox.pack_start(hbox, expand=False, fill=False)
503 bake = gtk.Button("Bake")
504 bake.connect("clicked", self.bake_clicked_cb)
505 bake.show()
506 hbox.pack_end(bake, expand=False, fill=False, padding=6)
507 reset = gtk.Button("Reset") 788 reset = gtk.Button("Reset")
508 reset.connect("clicked", self.reset_clicked_cb) 789 reset.connect("clicked", self.reset_clicked_cb)
509 reset.show() 790 reset.show()
510 hbox.pack_end(reset, expand=False, fill=False, padding=6) 791 bbox.add(reset)
792 bake = gtk.Button("Bake")
793 bake.connect("clicked", self.bake_clicked_cb)
794 bake.show()
795 bbox.add(bake)
511 796
512 return vbox 797 return vbox
513 798
799 def update_package_count_cb(self, model, count, label):
800 lbl = "Image contents (%s packages):" % count
801 label.set_text(lbl)
802
514 def contents(self): 803 def contents(self):
515 self.contents_tree = gtk.TreeView() 804 self.contents_tree = gtk.TreeView()
516 self.contents_tree.set_headers_visible(True) 805 self.contents_tree.set_headers_visible(True)
517 self.contents_tree.get_selection().set_mode(gtk.SELECTION_NONE) 806 self.contents_tree.get_selection().set_mode(gtk.SELECTION_SINGLE)
518 807
519 # allow searching in the package column 808 # allow searching in the package column
520 self.contents_tree.set_search_column(0) 809 self.contents_tree.set_search_column(0)
810 self.contents_tree.set_enable_search(True)
521 811
522 col = gtk.TreeViewColumn('Package') 812 col = gtk.TreeViewColumn('Package')
523 col.set_sort_column_id(0) 813 col.set_sort_column_id(0)
814 col.set_min_width(430)
524 col1 = gtk.TreeViewColumn('Brought in by') 815 col1 = gtk.TreeViewColumn('Brought in by')
525 col1.set_resizable(True) 816 col1.set_resizable(True)
526 col2 = gtk.TreeViewColumn('Remove') 817 col1.set_min_width(430)
527 col2.set_expand(False)
528 818
529 self.contents_tree.append_column(col) 819 self.contents_tree.append_column(col)
530 self.contents_tree.append_column(col1) 820 self.contents_tree.append_column(col1)
531 self.contents_tree.append_column(col2)
532 821
533 cell = gtk.CellRendererText() 822 cell = gtk.CellRendererText()
534 cell1 = gtk.CellRendererText() 823 cell1 = gtk.CellRendererText()
535 cell1.set_property('width-chars', 20) 824 cell1.set_property('width-chars', 20)
536 cell2 = gtk.CellRendererToggle()
537 cell2.connect("toggled", self.remove_package_cb)
538 825
539 col.pack_start(cell, True) 826 col.pack_start(cell, True)
540 col1.pack_start(cell1, True) 827 col1.pack_start(cell1, True)
541 col2.pack_start(cell2, True)
542 828
543 col.set_attributes(cell, text=self.model.COL_NAME) 829 col.set_attributes(cell, text=self.model.COL_NAME)
544 col1.set_attributes(cell1, text=self.model.COL_BINB) 830 col1.set_attributes(cell1, text=self.model.COL_BINB)
545 col2.set_attributes(cell2, active=self.model.COL_INC)
546 831
547 self.contents_tree.show() 832 self.contents_tree.show()
548 833
@@ -554,26 +839,67 @@ class MainWindow (gtk.Window):
554 return scroll 839 return scroll
555 840
556def main (server, eventHandler): 841def main (server, eventHandler):
842 import multiprocessing
843 cpu_cnt = multiprocessing.cpu_count()
844
557 gobject.threads_init() 845 gobject.threads_init()
558 846
559 taskmodel = TaskListModel() 847 taskmodel = TaskListModel()
848 configurator = Configurator()
560 handler = HobHandler(taskmodel, server) 849 handler = HobHandler(taskmodel, server)
561 mach = server.runCommand(["getVariable", "MACHINE"]) 850 mach = server.runCommand(["getVariable", "MACHINE"])
851 sdk_mach = server.runCommand(["getVariable", "SDKMACHINE"])
852 # If SDKMACHINE not set the default SDK_ARCH is used so we
853 # should represent that in the GUI
854 if not sdk_mach:
855 sdk_mach = server.runCommand(["getVariable", "SDK_ARCH"])
562 distro = server.runCommand(["getVariable", "DISTRO"]) 856 distro = server.runCommand(["getVariable", "DISTRO"])
563 857 bbthread = server.runCommand(["getVariable", "BB_NUMBER_THREADS"])
564 window = MainWindow(taskmodel, handler, mach, distro) 858 if not bbthread:
859 bbthread = cpu_cnt
860 handler.set_bbthreads(cpu_cnt)
861 else:
862 bbthread = int(bbthread)
863 pmake = server.runCommand(["getVariable", "PARALLEL_MAKE"])
864 if not pmake:
865 pmake = cpu_cnt
866 handler.set_pmake(cpu_cnt)
867 else:
868 # The PARALLEL_MAKE variable will be of the format: "-j 3" and we only
869 # want a number for the spinner, so strip everything from the variable
870 # up to and including the space
871 pmake = int(pmake[pmake.find(" ")+1:])
872
873 image_types = server.runCommand(["getVariable", "IMAGE_TYPES"])
874
875 pclasses = server.runCommand(["getVariable", "PACKAGE_CLASSES"]).split(" ")
876 # NOTE: we're only supporting one value for PACKAGE_CLASSES being set
877 # this seems OK because we're using the first package format set in
878 # PACKAGE_CLASSES and that's the package manager used for the rootfs
879 pkg, sep, pclass = pclasses[0].rpartition("_")
880
881 prefs = HobPrefs(configurator, handler, sdk_mach, distro, pclass, cpu_cnt,
882 pmake, bbthread, image_types)
883 layers = LayerEditor(configurator, None)
884 window = MainWindow(taskmodel, handler, configurator, prefs, layers, mach)
885 prefs.set_parent_window(window)
886 layers.set_parent_window(window)
565 window.show_all () 887 window.show_all ()
566 handler.connect("machines-updated", window.update_machines) 888 handler.connect("machines-updated", window.update_machines)
567 handler.connect("distros-updated", window.update_distros) 889 handler.connect("sdk-machines-updated", prefs.update_sdk_machines)
890 handler.connect("distros-updated", prefs.update_distros)
891 handler.connect("package-formats-found", prefs.update_package_formats)
568 handler.connect("generating-data", window.busy) 892 handler.connect("generating-data", window.busy)
569 handler.connect("data-generated", window.data_generated) 893 handler.connect("data-generated", window.data_generated)
570 pbar = ProgressBar(window) 894 handler.connect("reload-triggered", window.reload_triggered_cb)
571 pbar.connect("delete-event", gtk.main_quit) 895 configurator.connect("layers-loaded", layers.load_current_layers)
896 configurator.connect("layers-changed", handler.reload_data)
897 handler.connect("config-found", configurator.configFound)
572 898
573 try: 899 try:
574 # kick the while thing off 900 # kick the while thing off
575 handler.current_command = "findConfigFilesDistro" 901 handler.current_command = "findConfigFilePathLocal"
576 server.runCommand(["findConfigFiles", "DISTRO"]) 902 server.runCommand(["findConfigFilePath", "local.conf"])
577 except xmlrpclib.Fault: 903 except xmlrpclib.Fault:
578 print("XMLRPC Fault getting commandline:\n %s" % x) 904 print("XMLRPC Fault getting commandline:\n %s" % x)
579 return 1 905 return 1
@@ -584,7 +910,7 @@ def main (server, eventHandler):
584 handler.event_handle_idle_func, 910 handler.event_handle_idle_func,
585 eventHandler, 911 eventHandler,
586 window.build, 912 window.build,
587 pbar) 913 window.progress)
588 914
589 try: 915 try:
590 gtk.main() 916 gtk.main()