diff options
-rw-r--r-- | portato/backend/flags.py | 58 | ||||
-rw-r--r-- | portato/backend/package.py | 8 | ||||
-rw-r--r-- | portato/backend/portage/package.py | 27 | ||||
-rw-r--r-- | portato/gui/gtk/windows.py | 34 | ||||
-rw-r--r-- | portato/gui/qt/tree.py | 2 | ||||
-rw-r--r-- | portato/gui/qt/ui/MainWindow.ui | 98 | ||||
-rw-r--r-- | portato/gui/qt/windows.py | 142 |
7 files changed, 334 insertions, 35 deletions
diff --git a/portato/backend/flags.py b/portato/backend/flags.py index 7f37766..d43f567 100644 --- a/portato/backend/flags.py +++ b/portato/backend/flags.py @@ -420,13 +420,13 @@ def set_masked (pkg, masked = True): copy = link_neq[cpv][:] for file, line in copy: if line != "-1": - link_neq[cpv].remove(file, line) + link_neq[cpv].remove((file, line)) if masked == pkg.is_masked(): return data = get_data(pkg, path) - debug("data: "+str(link_eq)) + debug("data:", str(data)) done = False for file, line, crit, flags in data: if pkg.matches(crit): @@ -469,12 +469,56 @@ def new_masking_status (cpv): if isinstance(cpv, package.Package): cpv = cpv.get_cpv() - if cpv in new_masked and new_masked[cpv] != []: - return "masked" - elif cpv in new_unmasked and new_unmasked[cpv] != []: - return "unmasked" - else: return None + def get(list): + ret = None + if cpv in list and list[cpv] != []: + for file, line in list[cpv]: + _ret = (int(line) == -1) + if ret is not None and _ret != ret: + debug("Conflicting values for masking status!", list, error = True) + else: + ret = _ret + return ret + + masked = get(new_masked) + if masked is None: + masked = get(new_unmasked) + if masked is not None: + masked = not masked # revert for new_unmasked + + if masked is not None: + if masked: return "masked" + else: return "unmasked" + else: + return None + +def is_locally_masked (pkg, changes = True): + + if not isinstance(pkg, package.Package): + pkg = system.new_package(pkg) # assume it is a cpv or a gentoolkit.Package + if changes: + if new_masking_status(pkg) == "masked": # we masked it ourselves, but did not save it yet + # but sometimes, new_masking_status() returns "masked" if a package's unmask is removed + # then it is masked by the system but not locally (except rarely exotic cases) + if pkg.get_cpv() in new_unmasked: + if new_unmasked[pkg.get_cpv()]: return False # assume that there only exists one entry for this package + # else new_masking_status should have printed an error + return True + + if new_masking_status(pkg) == "unmasked": # we unmasked it + return False + + list = get_data(pkg, CONST.mask_path()) + + if not list: return False + + for file, line, crit, fl in list: + if pkg.matches(crit): + return True + + return False + def write_masked (): global new_unmasked, new_masked file_cache = {} diff --git a/portato/backend/package.py b/portato/backend/package.py index 70773cc..f9b675d 100644 --- a/portato/backend/package.py +++ b/portato/backend/package.py @@ -58,6 +58,14 @@ class Package: flags.remove_new_masked(self.get_cpv()) + def is_locally_masked (self): + """Checks whether the package is masked by the user. + + @returns: True if masked by the user; False if not + @rtype: bool""" + + return flags.is_locally_masked(self) + def get_installed_use_flags (self): """Returns a list of the useflags enabled at installation time. If package is not installed, it returns an empty list. diff --git a/portato/backend/portage/package.py b/portato/backend/portage/package.py index b455530..38e7369 100644 --- a/portato/backend/portage/package.py +++ b/portato/backend/portage/package.py @@ -82,20 +82,29 @@ class PortagePackage (Package): if status == "masked": return True elif status == "unmasked": return False else: - debug("BUG in flags.new_masking_status. It returns",status) + debug("BUG in flags.new_masking_status. It returns", status, error = True) else: # we have not touched the status if self._status and ("profile" in self._status or "package.mask" in self._status): return True return False - else: - try: - masked = self._settings.settings.pmaskdict[self.get_cp()] - except KeyError: # key error: not masked - return False - - for cpv in masked: - if self.matches(cpv): + else: # we want the original portage value XXX: bug if masked by user AND by system + + # get the normal masked ones + if self._status and ("profile" in self._status or "package.mask" in self._status): + if not flags.is_locally_masked(self, changes = False): # assume that if it is locally masked, it is not masked by the system return True + else: # more difficult: get the ones we unmasked, but are masked by the system + try: + masked = self._settings.settings.pmaskdict[self.get_cp()] + except KeyError: # key error: not masked + return False + + for cpv in masked: + if self.matches(cpv): + if not flags.is_locally_masked(self, changes = False): # assume that if it is locally masked, it is not masked by the system + return True + else: + return False return False diff --git a/portato/gui/gtk/windows.py b/portato/gui/gtk/windows.py index e62ed52..f70d2bb 100644 --- a/portato/gui/gtk/windows.py +++ b/portato/gui/gtk/windows.py @@ -575,7 +575,12 @@ class PackageTable: else: self.maskedCheck.set_label("Masked") - self.maskedCheck.set_active(pkg.is_masked(use_changed = False)) + if pkg.is_locally_masked(): + self.maskedCheck.set_label("<b>Masked</b>") + self.maskedCheck.get_child().set_use_markup(True) + self.maskedCheck.set_active(True) + else: + self.maskedCheck.set_active(pkg.is_masked(use_changed = False)) if pkg.is_testing(use_keywords = False) and not pkg.is_testing(use_keywords = True): self.testingCheck.set_label("<i>(Testing)</i>") @@ -663,7 +668,32 @@ class PackageTable: def cb_masked_toggled (self, button): """Callback for toggled masking-checkbox.""" status = button.get_active() - self.actual_package().set_masked(status) + pkg = self.actual_package() + + if pkg.is_masked(use_changed = False) == status and not pkg.is_locally_masked(): + return False + + if pkg.is_locally_masked() and status: + return False + + if not pkg.is_masked(use_changed = True): + pkg.set_masked(True) + if pkg.is_locally_masked(): + button.set_label("<b>Masked</b>") + button.get_child().set_use_markup(True) + else: + button.set_label("Masked") + + button.set_active(True) + else: + locally = pkg.is_locally_masked() + pkg.set_masked(False) + if pkg.is_masked(use_changed=False) and not locally: + button.set_label("<i>(Masked)</i>") + button.get_child().set_use_markup(True) + button.set_active(True) + else: + button.set_label("Masked") if self.instantChange: self._update_keywords(True, update = True) diff --git a/portato/gui/qt/tree.py b/portato/gui/qt/tree.py index 3e64f09..fd616b6 100644 --- a/portato/gui/qt/tree.py +++ b/portato/gui/qt/tree.py @@ -33,7 +33,7 @@ class QtTree (Tree): if update: string += "(updating" if version is not None: - string += "from version %s" % version + string += " from version %s" % version string += ")" return [cpv, string] diff --git a/portato/gui/qt/ui/MainWindow.ui b/portato/gui/qt/ui/MainWindow.ui index f942ea7..2830291 100644 --- a/portato/gui/qt/ui/MainWindow.ui +++ b/portato/gui/qt/ui/MainWindow.ui @@ -80,7 +80,7 @@ </widget> <widget class="QTabWidget" name="tabWidget" > <property name="currentIndex" > - <number>0</number> + <number>1</number> </property> <widget class="QWidget" name="pkgTab" > <attribute name="title" > @@ -170,8 +170,8 @@ p, li { white-space: pre-wrap; } <widget class="UncheckBox" name="installedCheck" > <property name="sizePolicy" > <sizepolicy> - <hsizetype>13</hsizetype> - <vsizetype>13</vsizetype> + <hsizetype>3</hsizetype> + <vsizetype>5</vsizetype> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> @@ -182,10 +182,7 @@ p, li { white-space: pre-wrap; } </widget> </item> <item> - <widget class="QCheckBox" name="testingCheck" > - <property name="enabled" > - <bool>true</bool> - </property> + <widget class="QCheckBox" name="maskedCheck" > <property name="sizePolicy" > <sizepolicy> <hsizetype>3</hsizetype> @@ -195,12 +192,15 @@ p, li { white-space: pre-wrap; } </sizepolicy> </property> <property name="text" > - <string>Testing</string> + <string>Masked</string> </property> </widget> </item> <item> - <widget class="QCheckBox" name="maskedCheck" > + <widget class="QCheckBox" name="testingCheck" > + <property name="enabled" > + <bool>true</bool> + </property> <property name="sizePolicy" > <sizepolicy> <hsizetype>3</hsizetype> @@ -210,7 +210,7 @@ p, li { white-space: pre-wrap; } </sizepolicy> </property> <property name="text" > - <string>Masked</string> + <string>Testing</string> </property> </widget> </item> @@ -224,6 +224,9 @@ p, li { white-space: pre-wrap; } <property name="editTriggers" > <set>QAbstractItemView::NoEditTriggers</set> </property> + <property name="textElideMode" > + <enum>Qt::ElideNone</enum> + </property> <property name="rootIsDecorated" > <bool>true</bool> </property> @@ -388,7 +391,7 @@ p, li { white-space: pre-wrap; } <x>0</x> <y>0</y> <width>466</width> - <height>28</height> + <height>31</height> </rect> </property> <widget class="QMenu" name="menuFile" > @@ -403,7 +406,17 @@ p, li { white-space: pre-wrap; } </property> <addaction name="aboutAction" /> </widget> + <widget class="QMenu" name="menu_Emerge" > + <property name="title" > + <string>&Emerge</string> + </property> + <addaction name="emergeAction" /> + <addaction name="unmergeAction" /> + <addaction name="updateAction" /> + <addaction name="separator" /> + </widget> <addaction name="menuFile" /> + <addaction name="menu_Emerge" /> <addaction name="menuHelp" /> </widget> <widget class="QStatusBar" name="statusbar" > @@ -427,6 +440,21 @@ p, li { white-space: pre-wrap; } <string>&About</string> </property> </action> + <action name="emergeAction" > + <property name="text" > + <string>&Emerge</string> + </property> + </action> + <action name="unmergeAction" > + <property name="text" > + <string>&Unmerge</string> + </property> + </action> + <action name="updateAction" > + <property name="text" > + <string>Update &World</string> + </property> + </action> </widget> <customwidgets> <customwidget> @@ -469,5 +497,53 @@ p, li { white-space: pre-wrap; } </hint> </hints> </connection> + <connection> + <sender>emergeAction</sender> + <signal>triggered()</signal> + <receiver>emergeBtn</receiver> + <slot>click()</slot> + <hints> + <hint type="sourcelabel" > + <x>-1</x> + <y>-1</y> + </hint> + <hint type="destinationlabel" > + <x>66</x> + <y>642</y> + </hint> + </hints> + </connection> + <connection> + <sender>unmergeAction</sender> + <signal>triggered()</signal> + <receiver>unmergeBtn</receiver> + <slot>click()</slot> + <hints> + <hint type="sourcelabel" > + <x>-1</x> + <y>-1</y> + </hint> + <hint type="destinationlabel" > + <x>177</x> + <y>642</y> + </hint> + </hints> + </connection> + <connection> + <sender>updateAction</sender> + <signal>triggered()</signal> + <receiver>updateBtn</receiver> + <slot>click()</slot> + <hints> + <hint type="sourcelabel" > + <x>-1</x> + <y>-1</y> + </hint> + <hint type="destinationlabel" > + <x>288</x> + <y>642</y> + </hint> + </hints> + </connection> </connections> </ui> diff --git a/portato/gui/qt/windows.py b/portato/gui/qt/windows.py index 46d9ec6..4c62d8f 100644 --- a/portato/gui/qt/windows.py +++ b/portato/gui/qt/windows.py @@ -38,6 +38,9 @@ def qCheck (check): else: return QtCore.Qt.Unchecked +def qIsChecked (check): + return check == QtCore.Qt.Checked + class WindowMeta (sip.wrappertype, type): def __new__ (cls, name, bases, dict): @@ -57,6 +60,22 @@ class Window: self._qt_base.__init__(self, parent) self.setupUi(self) + @staticmethod + def watch_cursor (func): + """This is a decorator for functions being so time consuming, that it is appropriate to show the watch-cursor.""" + def wrapper (*args, **kwargs): + ret = None + + QtGui.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) + try: + ret = func(*args, **kwargs) + finally: + QtGui.QApplication.restoreOverrideCursor() + + return ret + + return wrapper + class AboutDialog (Window): """A window showing the "about"-informations.""" __metaclass__ = WindowMeta @@ -111,14 +130,20 @@ class PackageDetails: self.window.installedCheck.blockSignals(True) + # combo QtCore.QObject.connect(self.window.versCombo, QtCore.SIGNAL("currentIndexChanged(int)"), self.cb_combo_changed) + # buttons QtCore.QObject.connect(self.window.pkgEmergeBtn, QtCore.SIGNAL("clicked()"), self.cb_emerge_clicked) QtCore.QObject.connect(self.window.pkgUnmergeBtn, QtCore.SIGNAL("clicked()"), self.cb_unmerge_clicked) QtCore.QObject.connect(self.window.pkgRevertBtn, QtCore.SIGNAL("clicked()"), self.cb_revert_clicked) - #QtCore.QObject.connect(self.window.maskedCheck, QtCore.SIGNAL("clicked(bool)"), self.cb_masked_clicked) - #QtCore.QObject.connect(self.window.testingCheck, QtCore.SIGNAL("clicked(bool)"), self.cb_testing_clicked) + # checkboxes + QtCore.QObject.connect(self.window.maskedCheck, QtCore.SIGNAL("clicked(bool)"), self.cb_masked_clicked) + QtCore.QObject.connect(self.window.testingCheck, QtCore.SIGNAL("clicked(bool)"), self.cb_testing_clicked) + + # useflags + QtCore.QObject.connect(self.window.useList, QtCore.SIGNAL("itemClicked(QTreeWidgetItem*, int)"), self.cb_use_flag_changed) def update (self, cp, queue = None, version = None, doEmerge = True, instantChange = False): """Updates the table to show the contents for the package. @@ -263,6 +288,79 @@ class PackageDetails: if self.instantChange: self._update_keywords(True, update = True) return True + + def cb_testing_clicked (self, status): + """Callback for toggled testing-checkbox.""" + button = self.window.testingCheck + + if self.actual_package().is_testing(use_keywords = False) == status: + return + + if not self.actual_package().is_testing(use_keywords = True): + self.actual_package().set_testing(False) + button.setText("Testing") + button.setCheckState(qCheck(True)) + else: + self.actual_package().set_testing(True) + if self.actual_package().is_testing(use_keywords=False): + button.setText("(Testing)") + button.setCheckState(qCheck(True)) + + if self.instantChange: + self._update_keywords(True, update = True) + + def cb_masked_clicked (self, status): + """Callback for toggled masking-checkbox.""" + pkg = self.actual_package() + button = self.window.maskedCheck + + if pkg.is_masked(use_changed = False) == status and not pkg.is_locally_masked(): + return + + if pkg.is_locally_masked() and status: + return False + + if not pkg.is_masked(use_changed = True): + pkg.set_masked(True) + if pkg.is_locally_masked(): + button.setText("Masked by User") + else: + button.setText("Masked") + + button.setCheckState(qCheck(True)) + + else: + locally = pkg.is_locally_masked() + pkg.set_masked(False) + + if pkg.is_masked(use_changed=False) and not locally: + button.setText("(Masked)") + button.setCheckState(qCheck(True)) + else: + button.setText("Masked") + + if self.instantChange: + self._update_keywords(True, update = True) + + return True + + def cb_use_flag_changed (self, item, col): + if col != 0: return + + flag = str(item.text(1)) + pkg = self.actual_package() + + if flag in pkg.get_global_settings("USE_EXPAND").split(" "): # ignore expanded flags + return + + checked = qIsChecked(item.checkState(0)) + + prefix = "" + if not checked: prefix = "-" + + pkg.set_use_flag(prefix+flag) + if self.instantChange: + self._update_keywords(True, update = True) def cb_combo_changed (self): """Callback for the changed ComboBox. @@ -305,7 +403,11 @@ class PackageDetails: else: self.window.maskedCheck.setText("Masked") - self.window.maskedCheck.setCheckState(qCheck(pkg.is_masked(use_changed = False))) + if pkg.is_locally_masked(): + self.window.maskedCheck.setText("Masked by User") + self.window.maskedCheck.setCheckState(qCheck(True)) + else: + self.window.maskedCheck.setCheckState(qCheck(pkg.is_masked(use_changed = False))) if pkg.is_testing(use_keywords = False) and not pkg.is_testing(use_keywords = True): self.window.testingCheck.setText("(Testing)") @@ -367,12 +469,13 @@ class MainWindow (Window): self.consoleLayout.setSpacing(0) self.consoleTab.setLayout(self.consoleLayout) self.consoleLayout.addWidget(self.console) + QtCore.QObject.connect(self, QtCore.SIGNAL("doTitleUpdate"), self._title_update) # build queueList self.queueList.setHeaderLabels(["Package", "Additional infos"]) self.queueTree = QtTree(self.queueList) - - QtCore.QObject.connect(self, QtCore.SIGNAL("doTitleUpdate"), self._title_update) + QtCore.QObject.connect(self.queueList.model(), QtCore.SIGNAL("rowsInserted (const QModelIndex&, int, int)"), self.cb_queue_list_items_added) + QtCore.QObject.connect(self.queueList, QtCore.SIGNAL("expanded (const QModelIndex&)"), self.cb_queue_list_items_added) # set emerge queue self.queue = EmergeQueue(console = self.console, tree = self.queueTree, db = self.db, title_update = self.title_update) @@ -414,6 +517,7 @@ class MainWindow (Window): AboutDialog(self).exec_() @QtCore.pyqtSignature("") + @Window.watch_cursor def on_searchBtn_clicked (self): """Do a search.""" text = str(self.searchEdit.text()) @@ -478,6 +582,34 @@ class MainWindow (Window): self.tabWidget.setCurrentIndex(self.CONSOLE_PAGE) self.queue.unmerge(force = True) + @QtCore.pyqtSignature("") + @Window.watch_cursor + def on_updateBtn_clicked (self): + if not am_i_root(): + not_root_dialog(self) + + else: + updating = system.update_world(newuse = self.cfg.get_boolean("newuse_opt"), deep = self.cfg.get_boolean("deep_opt")) + + debug("updating list:", [(x.get_cpv(), y.get_cpv()) for x,y in updating],"--> length:",len(updating)) + try: + try: + for pkg, old_pkg in updating: + self.queue.append(pkg.get_cpv(), unmask = False) + except PackageNotFoundException, e: + if unmask_dialog(self, e[0]) == QtGui.QMessageBox.Yes: + for pkg, old_pkg in updating: + self.queue.append(pkg.get_cpv(), unmask = True) + + except BlockedException, e: + blocked_dialog(self, e[0], e[1]) + self.queue.remove_children(self.queue.emergeIt) + + if len(updating): self.doUpdate = True + + def cb_queue_list_items_added (self, *args): + self.queueList.resizeColumnToContents(0) + def cb_cat_list_selected (self, index, prev): self.selCatName = str(index.data().toString()) self.fill_pkg_list(self.selCatName) |