summaryrefslogtreecommitdiff
path: root/portato
diff options
context:
space:
mode:
Diffstat (limited to 'portato')
-rw-r--r--portato/backend/flags.py58
-rw-r--r--portato/backend/package.py8
-rw-r--r--portato/backend/portage/package.py27
-rw-r--r--portato/gui/gtk/windows.py34
-rw-r--r--portato/gui/qt/tree.py2
-rw-r--r--portato/gui/qt/ui/MainWindow.ui98
-rw-r--r--portato/gui/qt/windows.py142
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>&amp;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>&amp;About</string>
</property>
</action>
+ <action name="emergeAction" >
+ <property name="text" >
+ <string>&amp;Emerge</string>
+ </property>
+ </action>
+ <action name="unmergeAction" >
+ <property name="text" >
+ <string>&amp;Unmerge</string>
+ </property>
+ </action>
+ <action name="updateAction" >
+ <property name="text" >
+ <string>Update &amp;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)