diff options
Diffstat (limited to 'bitbake/lib/bb/ui/hob.py')
-rw-r--r-- | bitbake/lib/bb/ui/hob.py | 924 |
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 | ||
21 | import glib | ||
21 | import gobject | 22 | import gobject |
22 | import gtk | 23 | import gtk |
23 | from bb.ui.crumbs.progress import ProgressBar | 24 | from bb.ui.crumbs.tasklistmodel import TaskListModel, BuildRep |
24 | from bb.ui.crumbs.tasklistmodel import TaskListModel | ||
25 | from bb.ui.crumbs.hobeventhandler import HobHandler | 25 | from bb.ui.crumbs.hobeventhandler import HobHandler |
26 | from bb.ui.crumbs.configurator import Configurator | ||
27 | from bb.ui.crumbs.hobprefs import HobPrefs | ||
28 | from bb.ui.crumbs.layereditor import LayerEditor | ||
26 | from bb.ui.crumbs.runningbuild import RunningBuildTreeView, RunningBuild | 29 | from bb.ui.crumbs.runningbuild import RunningBuildTreeView, RunningBuild |
30 | from bb.ui.crumbs.hig import CrumbsDialog | ||
27 | import xmlrpclib | 31 | import xmlrpclib |
28 | import logging | 32 | import logging |
29 | import Queue | 33 | import Queue |
@@ -32,226 +36,459 @@ extraCaches = ['bb.cache_extra:HobRecipeInfo'] | |||
32 | 36 | ||
33 | class MainWindow (gtk.Window): | 37 | class 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 | ||
556 | def main (server, eventHandler): | 841 | def 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() |