# # File: geneticone/gui/windows.py # This file is part of the Genetic/One-Project, a graphical portage-frontend. # # Copyright (C) 2006 René 'Necoro' Neumann # This is free software. You may redistribute copies of it under the terms of # the GNU General Public License version 2. # There is NO WARRANTY, to the extent permitted by law. # # Written by René 'Necoro' Neumann VERSION = "0.4.5" CONFIG_LOCATION = "/etc/geneticone/geneticone.cfg" # gtk stuff import pygtk pygtk.require("2.0") import gtk import gobject #our backend stuff from geneticone.helper import * from geneticone import backend from geneticone.backend import flags # more GUI stuff from gui_helper import Database, Config, EmergeQueue from dialogs import * # for the terminal import vte # other from portage_util import unique_array class AbstractDialog: """A class all our dialogs get derived from. It sets useful default vars and automatically handles the ESC-Button.""" def __init__ (self, parent, title): """Constructor. @param parent: the parent window @type parent: gtk.Window @param title: the title of the window @type title: string""" # create new self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) # set title self.window.set_title(title) # set modal and transient for the parent --> you have to close this one to get back to the parent self.window.set_modal(True) self.window.set_transient_for(parent) self.window.set_destroy_with_parent(True) # not resizable self.window.set_resizable(False) # default size = (1,1) ==> as small as possible self.window.set_default_size(1,1) # catch the ESC-key self.window.connect("key-press-event", self.cb_key_pressed) def cb_key_pressed (self, widget, event): """Closes the window if ESC is pressed.""" keyname = gtk.gdk.keyval_name(event.keyval) if keyname == "Escape": self.window.destroy() return True else: return False class AboutWindow (AbstractDialog): """A window showing the "about"-informations.""" def __init__ (self, parent): """Constructor. @param parent: the parent window @type parent: gtk.Window""" AbstractDialog.__init__(self, parent, "About Genetic/One") box = gtk.VBox(False) self.window.add(box) # about label label = gtk.Label() label.set_justify(gtk.JUSTIFY_CENTER) label.set_markup(""" Genetic/One v.%s A Portage-GUI This software is licensed under the terms of the GPLv2. Copyright (C) 2006 René 'Necoro' Neumann <necoro@necoro.net> Thanks to Fred for support and ideas :P """ % VERSION) box.pack_start(label) # button okBtn = gtk.Button("OK") okBtn.connect("clicked", lambda x: self.window.destroy()) box.pack_start(okBtn) # finished -> show self.window.show_all() class SearchWindow (AbstractDialog): """A window showing the results of a search process.""" def __init__ (self, parent, list, jump_to): """Constructor. @param parent: parent-window @type parent: gtk.Window @param list: list of results to show @type list: string[] @param jump_to: function to call if "OK"-Button is hit @type jump_to: function(string)""" AbstractDialog.__init__(self, parent, "Search results") self.list = list # list to show self.jump_to = jump_to # function to call for jumping box = gtk.HBox(False) self.window.add(box) # combo box self.combo = gtk.combo_box_new_text() for x in list: self.combo.append_text(x) self.combo.set_active(0) # first item self.combo.connect("key-press-event", self.cb_key_pressed_combo) box.pack_start(self.combo) # ok-button okBtn = gtk.Button("OK") okBtn.connect("clicked", self.cb_ok_btn_clicked) box.pack_start(okBtn) # finished --> show self.window.show_all() def cb_ok_btn_clicked (self, button): """Called if the OK-Button is clicked. Calls self.jump_to(selected_entry) and closes the window.""" self.window.destroy() self.jump_to(self.list[self.combo.get_active()]) return True def cb_key_pressed_combo (self, widget, event): """Emulates a ok-button-click.""" keyname = gtk.gdk.keyval_name(event.keyval) if keyname == "Return": # take it as an "OK" if Enter is pressed self.cb_ok_btn_clicked(widget) return True else: return False class PreferenceWindow (AbstractDialog): """Window displaying some preferences.""" def __init__ (self, parent, cfg): """Constructor. @param parent: parent window @type parent: gtk.Window @param cfg: configuration object @type cfg: gui_helper.Config""" AbstractDialog.__init__(self, parent, "Preferences") self.window.set_resizable(True) # override the default of the AbstractDialog # our config self.cfg = cfg box = gtk.VBox() box.set_spacing(5) self.window.add(box) # En-/Disable Debugging self.debugCb = self.draw_cb(box, "Debugging modus", "debug_opt") # --deep self.deepCb = self.draw_cb(box, "--deep", "deep_opt") # --newuse #self.newuseCb = self.draw_cb(box, "--newuse", "newuse_opt") pHolderLabel = gtk.Label("""For the following options, you might use these placeholders: $(cat) = category $(pkg) = package-name $(cat-1)/$(cat-2) = first/second part of the category""") pHolderLabel.set_use_markup(True) pHolderLabel.set_alignment(0,0) box.pack_start(pHolderLabel) # The use/mask/keywording checkboxes and edits self.usePerVersionCb, self.useFileEdit = self.draw_cb_and_edit(box, "package.use", "usePerVersion_opt", "useFile_opt") self.maskPerVersionCb, self.maskFileEdit = self.draw_cb_and_edit(box, "package.mask/package.unmask", "maskPerVersion_opt", "maskFile_opt") self.testPerVersionCb, self.testFileEdit = self.draw_cb_and_edit(box, "package.keywords", "testingPerVersion_opt", "testingFile_opt") # buttons buttonHB = gtk.HButtonBox() buttonHB.set_layout(gtk.BUTTONBOX_SPREAD) okBtn = gtk.Button("_OK") cancelBtn = gtk.Button("_Cancel") okBtn.connect("clicked", self.cb_ok_clicked) cancelBtn.connect("clicked", lambda x: self.window.destroy()) buttonHB.pack_start(okBtn) buttonHB.pack_start(cancelBtn) box.pack_start(buttonHB, True, True, 5) # finished --> show all self.window.show_all() def draw_cb_and_edit (self, box, string, cb_opt, edit_opt): """Draws a checkbox and an edit-field. @param box: box to place the both things into @type box: gtk.Box @param string: string to show @type string: string @param cb_opt: the option string for the Config.const-dict @type cb_opt: string @param edit_opt: the option string for the Config.const-dic @type edit_opt: string @return: the checkbox and the edit-field @rtype: (gtk.CheckButton, gtk.Edit)""" # check-button cb = self.draw_cb(box, label=("Add to %s on a per-version-base" % string), opt = cb_opt) # edit with label hBox = gtk.HBox() label = gtk.Label("File name to use if %s is a directory:" % string) edit = gtk.Entry() edit.set_text(self.cfg.get(self.cfg.const[edit_opt])) hBox.pack_start(label, False) hBox.pack_start(edit, True, True, 5) box.pack_start(hBox, True, True) return (cb, edit) def draw_cb (self, box, label, opt): """Draws a checkbox. @param box: box to place the cb into @type box: gtk.Box @param label: Label to show @type label: string @param opt: the option string for the Config.const-dict @type opt: string @returns: the checkbox @rtype: gtk.CheckButton""" cb = gtk.CheckButton(label=label) cb.set_active(self.cfg.get_boolean(self.cfg.const[opt])) box.pack_start(cb, True, True) return cb def _save(self): """Sets all options in the Config-instance.""" self.cfg.set(self.cfg.const["usePerVersion_opt"], str(self.usePerVersionCb.get_active())) self.cfg.set(self.cfg.const["useFile_opt"], self.useFileEdit.get_text()) self.cfg.set(self.cfg.const["maskPerVersion_opt"], str(self.maskPerVersionCb.get_active())) self.cfg.set(self.cfg.const["maskFile_opt"], self.maskFileEdit.get_text()) self.cfg.set(self.cfg.const["testingPerVersion_opt"], str(self.testPerVersionCb.get_active())) self.cfg.set(self.cfg.const["testingFile_opt"], self.testFileEdit.get_text()) self.cfg.set(self.cfg.const["debug_opt"], str(self.debugCb.get_active())) self.cfg.set(self.cfg.const["deep_opt"], str(self.deepCb.get_active())) #self.cfg.set(self.cfg.const["newuse_opt"], str(self.newuseCb.get_active())) def cb_ok_clicked(self, button): """Saves, writes to config-file and closes the window.""" self._save() self.cfg.write() self.window.destroy() class PackageWindow (AbstractDialog): """A window with data about a specfic package.""" def __init__ (self, parent, cp, queue = None, version = None, delOnClose = True, doEmerge = True): """Build up window contents.""" AbstractDialog.__init__(self, parent, cp) self.cp = cp # category/package self.version = version # version - if not None this is used self.queue = queue self.delOnClose = delOnClose self.doEmerge = doEmerge self.flagChanged = False # packages and installed packages self.packages = backend.sort_package_list(backend.get_all_versions(cp)) self.instPackages = backend.sort_package_list(backend.get_all_installed_versions(cp)) # main structure - the table self.table = gtk.Table(rows=4,columns=2) self.window.add(self.table) # version-combo-box self.vCombo = self.build_vers_combo() self.table.attach(self.vCombo, 0, 1, 1, 2, yoptions = gtk.FILL) if not self.doEmerge: self.vCombo.set_sensitive(False) # the label (must be here, because it depends on the combo box) desc = self.actual_package().get_env_var("DESCRIPTION") if not desc: desc = "" use_markup = False else: desc = ""+desc+"" use_markup = True self.descLabel = gtk.Label(desc) self.descLabel.set_line_wrap(True) self.descLabel.set_justify(gtk.JUSTIFY_CENTER) self.descLabel.set_use_markup(use_markup) self.table.attach(self.descLabel, 0, 2, 0, 1, xoptions = gtk.FILL, ypadding = 10) # the check boxes checkHB = gtk.HBox (True, 1) self.table.attach(checkHB, 1, 2, 1, 2, yoptions = gtk.FILL) self.installedCheck = gtk.CheckButton() self.installedCheck.connect("button-press-event", self.cb_button_pressed) self.installedCheck.set_label("Installed") self.installedCheck.set_no_show_all(True) checkHB.pack_start(self.installedCheck, True, False) self.maskedCheck = gtk.CheckButton() self.maskedCheck.connect("toggled", self.cb_masked_toggled) self.maskedCheck.set_label("Masked") self.maskedCheck.set_no_show_all(True) checkHB.pack_start(self.maskedCheck, True, False) self.testingCheck = gtk.CheckButton() self.testingCheck.connect("toggled", self.cb_testing_toggled) self.testingCheck.set_label("Testing") self.testingCheck.set_no_show_all(True) checkHB.pack_start(self.testingCheck, True, False) self.missing_label = gtk.Label("MISSING KEYWORD") self.missing_label.set_use_markup(True) self.missing_label.set_no_show_all(True) self.table.attach(self.missing_label, 1, 2, 1, 2, yoptions = gtk.FILL) self.not_in_sys_label = gtk.Label("Installed, but not in portage anymore") self.not_in_sys_label.set_use_markup(True) self.not_in_sys_label.set_no_show_all(True) self.table.attach(self.not_in_sys_label, 1, 2, 1, 2, yoptions = gtk.FILL) # use list self.useList = self.build_use_list() self.useListScroll = gtk.ScrolledWindow() self.useListScroll.add(self.useList) self.useListScroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_NEVER) # XXX: make this work correctly self.table.attach(self.useListScroll, 0, 2, 2, 3, ypadding = 10) # buttons buttonHB = gtk.HButtonBox() buttonHB.set_layout(gtk.BUTTONBOX_SPREAD) self.table.attach(buttonHB, 0, 2, 3, 4) self.emergeBtn = gtk.Button("_Emerge") self.unmergeBtn = gtk.Button("_Unmerge") if not self.queue or not self.doEmerge: self.emergeBtn.set_sensitive(False) self.unmergeBtn.set_sensitive(False) self.cancelBtn = gtk.Button("_Cancel") if not self.delOnClose: self.cancelBtn.set_label("_Close") self.cancelBtn.connect("clicked", self.cb_cancel_clicked) self.emergeBtn.connect("clicked", self.cb_emerge_clicked) self.unmergeBtn.connect("clicked", self.cb_unmerge_clicked) buttonHB.pack_start(self.emergeBtn) buttonHB.pack_start(self.unmergeBtn) buttonHB.pack_start(self.cancelBtn) # current status self.cb_combo_changed(self.vCombo) # show self.window.show_all() def fill_use_list(self, store): pkg = self.actual_package() pkg_flags = pkg.get_all_use_flags() pkg_flags.sort() for use in pkg_flags: if pkg.is_installed() and use in pkg.get_actual_use_flags(): # flags set during install enabled = True elif (not pkg.is_installed()) and use in pkg.get_settings("USE").split() and not flags.invert_use_flag(use) in pkg.get_new_use_flags(): # flags that would be set enabled = True elif use in pkg.get_new_use_flags(): enabled = True else: enabled = False store.append([enabled, use, backend.get_use_desc(use, self.cp)]) return store def build_use_list (self): """Builds the useList.""" store = gtk.ListStore(bool, str, str) self.fill_use_list(store) # build view view = gtk.TreeView(store) cell = gtk.CellRendererText() tCell = gtk.CellRendererToggle() tCell.set_property("activatable", True) tCell.connect("toggled", self.cb_use_flag_toggled, store) view.append_column(gtk.TreeViewColumn("Enabled", tCell, active = 0)) view.append_column(gtk.TreeViewColumn("Flags", cell, text = 1)) view.append_column(gtk.TreeViewColumn("Description", cell, text = 2)) if store.iter_n_children(None) == 0: # if there are no nodes in the list ... view.set_child_visible(False) # ... do not show the list else: view.set_child_visible(True) return view def build_vers_combo (self): """Creates the combo box with the different versions.""" combo = gtk.combo_box_new_text() # append versions for s in [x.get_version() for x in self.packages]: combo.append_text(s) # activate the first one try: best_version = "" if self.version: best_version = self.version else: best_version = backend.find_best_match(self.packages[0].get_cp(), (self.instPackages != [])).get_version() for i in range(len(self.packages)): if self.packages[i].get_version() == best_version: combo.set_active(i) break except AttributeError: # no package found debug('catched AttributeError => no "best package" found. Selected first one.') combo.set_active(0) combo.connect("changed", self.cb_combo_changed) return combo def actual_package (self): """Returns the actual package (a backend.Package-object).""" return self.packages[self.vCombo.get_active()] def cb_combo_changed (self, combo, data = None): """Callback for the changed ComboBox. It then rebuilds the useList and the checkboxes.""" # remove old useList self.useListScroll.remove(self.useList) # build new self.useList = self.build_use_list() self.useListScroll.add(self.useList) pkg = self.actual_package() if (not pkg.is_in_system()) or pkg.is_missing_keyword(): if not pkg.is_in_system(): self.missing_label.hide() self.not_in_sys_label.show() else: # missing keyword self.missing_label.show() self.not_in_sys_label.hide() self.installedCheck.hide() self.maskedCheck.hide() self.testingCheck.hide() self.emergeBtn.set_sensitive(False) else: self.missing_label.hide() self.not_in_sys_label.hide() self.installedCheck.show() self.maskedCheck.show() self.testingCheck.show() if self.doEmerge: self.emergeBtn.set_sensitive(True) self.installedCheck.set_active(pkg.is_installed()) self.maskedCheck.set_active(pkg.is_masked()) if pkg.is_testing(allowed = False) and not pkg.is_testing(allowed=True): self.testingCheck.set_label("(Testing)") self.testingCheck.get_child().set_use_markup(True) else: self.testingCheck.set_label("Testing") self.testingCheck.set_active(pkg.is_testing(allowed = False)) if self.doEmerge: # set emerge-button-label if not self.actual_package().is_installed(): self.emergeBtn.set_label("_Emerge") self.unmergeBtn.set_sensitive(False) else: self.emergeBtn.set_label("R_emerge") self.unmergeBtn.set_sensitive(True) # refresh - make window as small as possible self.table.show_all() self.window.resize(1,1) return True def cb_button_pressed (self, b, event, data = None): """Callback for pressed checkboxes. Just quits the event-loop - no redrawing.""" if not isinstance(b, gtk.CellRendererToggle): b.emit_stop_by_name("button-press-event") return True def cb_cancel_clicked (self, button, data = None): if self.delOnClose: self.actual_package().remove_new_use_flags() self.actual_package().remove_new_masked() self.actual_package().remove_new_testing() elif self.flagChanged: if self.queue: try: self.queue.append(self.actual_package().get_cpv(), update = True) except backend.PackageNotFoundException, e: if unmask_dialog(e[0]) == gtk.RESPONSE_YES: self.queue.append(self.actual_package().get_cpv(), update = True, unmask = True) self.window.destroy() return True def cb_emerge_clicked (self, button, data = None): """Adds the package to the EmergeQueue.""" if not am_i_root(): not_root_dialog() else: try: self.queue.append(self.actual_package().get_cpv(), unmerge = False) self.window.destroy() except backend.PackageNotFoundException, e: if unmask_dialog(e[0]) == gtk.RESPONSE_YES: self.queue.append(self.actual_package().get_cpv(), unmerge = False, unmask = True) self.window.destroy() return True def cb_unmerge_clicked (self, button, data = None): """Adds the package to the UnmergeQueue.""" if not am_i_root(): not_root_dialog() else: try: self.queue.append(self.actual_package().get_cpv(), unmerge = True) except backend.PackageNotFoundException, e: masked_dialog(e[0]) self.window.destroy() return True def cb_testing_toggled (self, button): status = button.get_active() if self.actual_package().is_testing(allowed = False) == status: return False if not self.actual_package().is_testing(allowed = True): self.actual_package().set_testing(False) button.set_label("Testing") button.set_active(True) else: self.actual_package().set_testing(True) if self.actual_package().is_testing(allowed=False): button.set_label("(Testing)") button.get_child().set_use_markup(True) button.set_active(True) self.flagChanged = True return True def cb_masked_toggled (self, button): status = button.get_active() self.actual_package().set_masked(status) self.flagChanged = True return True def cb_use_flag_toggled (self, cell, path, store, data = None): store[path][0] = not store[path][0] prefix = "" if not store[path][0]: prefix = "-" self.actual_package().set_use_flag(prefix+store[path][1]) self.flagChanged = True return True class MainWindow: """Application main window.""" def __init__ (self): """Build up window""" # window self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) self.window.set_title(("Genetic/One (%s)" % VERSION)) self.window.connect("destroy", self.cb_destroy) self.window.set_border_width(2) self.window.set_resizable(True) mHeight = 800 if gtk.gdk.screen_height() <= 800: mHeight = 600 self.window.set_geometry_hints (self.window, min_width = 600, min_height = mHeight, max_height = gtk.gdk.screen_height(), max_width = gtk.gdk.screen_width()) # booleans self.doUpdate = False # package db self.db = Database() self.db.populate() # config self.cfg = Config(CONFIG_LOCATION) self.cfg.modify_external_configs() # actions needed self.emergeAction = gtk.Action("Emerge", "_Emerge", None, None) self.emergeAction.connect("activate", self.cb_emerge_clicked) self.unmergeAction = gtk.Action("Unmerge", "_Unmerge", None, None) self.unmergeAction.connect("activate", self.cb_emerge_clicked) self.updateAction = gtk.Action("UpdateWorld", "Update _World", None, None) self.updateAction.connect("activate", self.cb_update_clicked) # main vb vb = gtk.VBox(False, 1) self.window.add(vb) # menus self.uimanager = self.create_uimanager() self.queuePopup = self.uimanager.get_widget("/popupQueue") menubar = self.uimanager.get_widget("/bar") vb.pack_start(menubar, False) # search self.searchEntry = gtk.Entry() self.searchBtn = gtk.Button("_Search") self.searchBtn.connect("clicked", self.cb_search_clicked) self.searchEntry.connect("activate", self.cb_search_clicked) hbSearch = gtk.HBox(False, 5) hbSearch.pack_start(self.searchEntry, True, True) hbSearch.pack_start(self.searchBtn, False, False) vb.pack_start(hbSearch, False, False, 5) # VPaned holding the lists and the Terminal vpaned = gtk.VPaned() vpaned.set_position(mHeight/2) vb.pack_start(vpaned, True, True) # a HB holding the lists hb = gtk.HBox(True, 5) hbFrame = gtk.Frame() hbFrame.add(hb) hbFrame.set_shadow_type(gtk.SHADOW_IN) vpaned.pack1(hbFrame, shrink = True, resize = True) self.scroll_1 = gtk.ScrolledWindow() self.scroll_2 = gtk.ScrolledWindow() self.scroll_1.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) self.scroll_2.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) hb.pack_start(self.scroll_1, True, True) hb.pack_start(self.scroll_2, True, True) # create cat List self.catList = self.create_cat_list() self.scroll_1.add(self.catList) # create pkg list self.pkgList = self.create_pkg_list() self.scroll_2.add(self.pkgList) # queue list queueHB = gtk.HBox(False, 0) queueScroll = gtk.ScrolledWindow() queueScroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) emergeStore = gtk.TreeStore(str,str) self.emergeView = gtk.TreeView(emergeStore) cell = gtk.CellRendererText() col = gtk.TreeViewColumn("Queue", cell, text = 0) self.emergeView.append_column(col) col = gtk.TreeViewColumn("Options", cell, markup = 1) self.emergeView.append_column(col) self.emergeView.connect("row-activated", self.cb_row_activated, emergeStore) self.emergeView.connect("button-press-event", self.cb_queue_right_click) self.emergeView.set_headers_visible(False) queueScroll.add(self.emergeView) queueHB.pack_start(queueScroll, True, True) # buttons right to the queue list buttonBox = gtk.VButtonBox() buttonBox.set_layout(gtk.BUTTONBOX_SPREAD) queueHB.pack_start(buttonBox, False) emergeBtn = gtk.Button() self.emergeAction.connect_proxy(emergeBtn) updateBtn = gtk.Button() self.updateAction.connect_proxy(updateBtn) unmergeBtn = gtk.Button() self.unmergeAction.connect_proxy(unmergeBtn) removeBtn = gtk.Button("_Remove") removeBtn.connect("clicked", self.cb_remove_clicked) buttonBox.pack_start(emergeBtn) buttonBox.pack_start(unmergeBtn) buttonBox.pack_start(updateBtn) buttonBox.pack_start(removeBtn) # the terminal term = vte.Terminal() term.set_scrollback_lines(1024) term.set_scroll_on_output(True) term.set_font_from_string("Monospace 11") # XXX why is this not working with the colors term.set_color_background(gtk.gdk.color_parse("white")) term.set_color_foreground(gtk.gdk.color_parse("black")) termBox = gtk.HBox(False, 0) termScroll = gtk.VScrollbar(term.get_adjustment()) termBox.pack_start(term, True, True) termBox.pack_start(termScroll, False) # notebook self.notebook = gtk.Notebook() self.notebook.append_page(queueHB, gtk.Label("Queue")) self.notebook.append_page(termBox, gtk.Label("Console")) vpaned.pack2(self.notebook, shrink = True, resize = True) # the status line self.statusLabel = gtk.Label("Genetic/One - A Portage GUI") self.statusLabel.set_alignment(0.0,0.7) self.statusLabel.set_single_line_mode(True) vb.pack_start(self.statusLabel, False, False) # show self.window.show_all() # set emerge queue self.queue = EmergeQueue(console=term, tree = emergeStore, db = self.db) def create_uimanager(self): ui =""" """ um = gtk.UIManager() group = gtk.ActionGroup("MenuActions") group.add_actions([ ("File", None, "_File"), ("EmergeMenu", None, "_Emerge"), ("Help", None, "_?"), ("Sync", None, "_Sync", None, None, self.cb_sync_clicked), ("Prefs", None, "_Preferences", None, None, lambda x: PreferenceWindow(self.window, self.cfg)), ("Reload", None, "_Reload Portage", None, None, self.cb_reload_clicked), ("Close", None, "_Close", None, None, self.cb_destroy), ("About", None, "_About", None, None, lambda x: AboutWindow(self.window))]) group.add_action(self.emergeAction) group.add_action(self.unmergeAction) group.add_action(self.updateAction) um.insert_action_group(group,0) group = gtk.ActionGroup("PopupActions") group.add_actions([ ("Oneshot", None, "Oneshot", None, None, self.cb_oneshot_clicked)]) um.insert_action_group(group, 1) um.add_ui_from_string(ui) return um def fill_pkg_store (self, store, name = None): if name: for p in self.db.get_cat(name): store.append([p]) return store def create_pkg_list (self, name = None, force = False): """Creates the package list.""" store = gtk.ListStore(str) self.fill_pkg_store(store,name) # build view pkgList = gtk.TreeView(store) cell = gtk.CellRendererText() col = gtk.TreeViewColumn("Packages", cell, text = 0) pkgList.append_column(col) pkgList.connect("row-activated", self.cb_row_activated, store) return pkgList def create_cat_list (self): """Creates the category list.""" store = gtk.ListStore(str) # build categories for p in backend.list_categories(): store.append([p]) # sort them alphabetically store.set_sort_column_id(0, gtk.SORT_ASCENDING) view = gtk.TreeView(store) cell = gtk.CellRendererText() col = gtk.TreeViewColumn("Categories", cell, text = 0) view.append_column(col) view.connect("cursor-changed", self.cb_cat_list_selection) view.connect("row-activated", lambda v,p,c : self.cb_cat_list_selection(v)) view.set_search_column(0) return view def jump_to (self, cp): """Is called when we want to jump to a specific package.""" PackageWindow(self.window, cp, self.queue) def cb_destroy (self, widget, data = None): """Calls main_quit().""" gtk.main_quit() def cb_cat_list_selection (self, view, data = None, force = False): """Callback for a category-list selection. Updates the package list with these packages in the category.""" if view == self.catList: # be sure it is the catList # get the selected category sel = view.get_selection() store, it = sel.get_selected() if it: self.selCatName = store.get_value(it, 0) self.pkgList.get_model().clear() self.fill_pkg_store(self.pkgList.get_model(), self.selCatName) return False def cb_row_activated (self, view, path, col, store = None): """Callback for an activated row in the pkgList. Opens a package window.""" if view == self.pkgList: package = store.get_value(store.get_iter(path), 0) if package[-1] == '*': package = package[:-1] PackageWindow(self.window, self.selCatName+"/"+package, self.queue) elif view == self.emergeView: if len(path) > 1: package = store.get_value(store.get_iter(path), 0) cat, name, vers, rev = backend.split_package_name(package) if rev != "r0": vers = vers+"-"+rev PackageWindow(self.window, cat+"/"+name, queue = self.queue, version = vers, delOnClose = False, doEmerge = False) return True def cb_remove_clicked (self, button, data = None): """Removes a selected item in the (un)emerge-queue if possible.""" selected = self.emergeView.get_selection() if selected: model, iter = selected.get_selected() if not model.iter_parent(iter): # top-level if model.iter_n_children(iter) > 0: # and has children which can be removed :) if remove_queue_dialog() == gtk.RESPONSE_YES : self.queue.remove_children(iter) self.doUpdate = False elif model.iter_parent(model.iter_parent(iter)): # this is in the 3rd level => dependency remove_deps_dialog() else: self.queue.remove_children(iter) # remove children first self.queue.remove(iter) self.doUpdate = False return True def cb_emerge_clicked (self, action): """Do emerge or unemerge.""" self.notebook.set_current_page(1) if action == self.emergeAction: if len(flags.newUseFlags) > 0: changed_flags_dialog("use flags") flags.write_use_flags() if len(flags.new_masked)>0 or len(flags.new_unmasked)>0 or len(flags.newTesting)>0: changed_flags_dialog("masking keywords") flags.write_masked() flags.write_testing() backend.reload_settings() if not self.doUpdate: self.queue.emerge(force=True) else: self.queue.update_world(force=True, newuse = self.cfg.get_boolean(self.cfg.const["newuse_opt"]), deep = self.cfg.get_boolean(self.cfg.const["deep_opt"])) self.doUpdate = False elif action == self.unmergeAction: self.queue.unmerge(force=True) return True def watch_cursor (func): """This is a decorator for functions being so time consuming, that it is appropriate to show the watch-cursor. @attention: this function relies on the gtk.Window-Object being stored as self.window""" def wrapper (self, *args, **kwargs): ret = None def cb_idle(): try: ret = func(self, *args, **kwargs) finally: self.window.window.set_cursor(None) return False watch = gtk.gdk.Cursor(gtk.gdk.WATCH) self.window.window.set_cursor(watch) gobject.idle_add(cb_idle) return ret return wrapper @watch_cursor def cb_update_clicked (self, action): if not backend.am_i_root(): not_root_dialog() else: updating = backend.update_world(newuse = self.cfg.get_boolean(self.cfg.const["newuse_opt"]), deep = self.cfg.get_boolean(self.cfg.const["deep_opt"])) debug("updating list:", [(x.get_cpv(), y.get_cpv()) for x,y in updating]) for pkg, old_pkg in updating: self.queue.append(pkg.get_cpv()) if len(updating): self.doUpdate = True return True def cb_sync_clicked (self, action): self.notebook.set_current_page(1) self.queue.sync() @watch_cursor def cb_reload_clicked (self, action): """Reloads the portage settings and the database.""" backend.reload_settings() del self.db self.db = Database() self.db.populate() @watch_cursor def cb_search_clicked (self, button, data = None): """Do a search.""" if self.searchEntry.get_text() != "": packages = backend.find_all_packages(self.searchEntry.get_text(), withVersion = False) if packages == []: nothing_found_dialog() else: if len(packages) == 1: self.jump_to(packages[0]) else: SearchWindow(self.window, packages, self.jump_to) def cb_queue_right_click (self, queue, event): if event.button == 3: x = int(event.x) y = int(event.y) time = event.time pthinfo = queue.get_path_at_pos(x, y) if pthinfo is not None: path, col, cellx, celly = pthinfo queue.grab_focus() queue.set_cursor( path, col, 0) self.queuePopup.popup( None, None, None, event.button, time) return True else: return False def cb_oneshot_clicked (self, action): sel = self.emergeView.get_selection() store, it = sel.get_selected() if it: package = store.get_value(it, 0) if not self.cfg.get_local(package, self.cfg.const["oneshot_opt"]): set = True else: set = False self.cfg.set_local(package, self.cfg.const["oneshot_opt"], set) self.queue.append(package, update = True, oneshot = set, forceUpdate = True) def main (self): """Main.""" gobject.threads_init() # now subthreads can run normally, but are not allowed to touch the GUI. If threads should change sth there - use gobject.idle_add(). # for more informations on threading and gtk: http://www.async.com.br/faq/pygtk/index.py?req=show&file=faq20.006.htp gtk.main()