summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/Changelog3
-rw-r--r--plugins/resume_loop.xml22
-rw-r--r--plugins/shutdown.xml10
-rw-r--r--portato/gui/gtk/windows.py2
-rw-r--r--portato/gui/gui_helper.py4
-rw-r--r--portato/gui/qt/terminal.py35
-rw-r--r--portato/gui/qt/tree.py2
-rw-r--r--portato/gui/qt/windows.py2
-rw-r--r--portato/plugin.py65
-rw-r--r--portato/plugins/resume_loop.py39
10 files changed, 162 insertions, 22 deletions
diff --git a/doc/Changelog b/doc/Changelog
index efa4a1e..0dc6dd4 100644
--- a/doc/Changelog
+++ b/doc/Changelog
@@ -1,5 +1,8 @@
next:
- consolefont changeble for Qt
+- added "shutdown" plugin
+- added "resume-loop" plugin
+- changed plugin-structures
0.7.5:
- new icon by p4r4d0x
diff --git a/plugins/resume_loop.xml b/plugins/resume_loop.xml
new file mode 100644
index 0000000..572ccfa
--- /dev/null
+++ b/plugins/resume_loop.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<plugin
+ author="René 'Necoro' Neumann"
+ name="Emerge Resume Loop">
+ <import>portato.plugins.resume_loop</import>
+
+ <hook
+ hook = "emerge"
+ call = "set_console">
+ <connect/>
+ </hook>
+
+ <hook
+ hook = "after_emerge"
+ call = "resume_loop">
+ <connect type="before">*</connect>
+ </hook>
+
+ <options>
+ <option>disabled</option>
+ </options>
+</plugin>
diff --git a/plugins/shutdown.xml b/plugins/shutdown.xml
index 2897bd8..586b57d 100644
--- a/plugins/shutdown.xml
+++ b/plugins/shutdown.xml
@@ -7,11 +7,11 @@
<hook
hook = "after_emerge"
call = "shutdown">
- <connect type="after">
- <!-- after everything else -->
- *
- </connect>
+ <connect type="after">*</connect>
</hook>
- <disabled />
+ <options>
+ <option>disabled</option>
+ </options>
+
</plugin>
diff --git a/portato/gui/gtk/windows.py b/portato/gui/gtk/windows.py
index 3ce8ae2..8b433ca 100644
--- a/portato/gui/gtk/windows.py
+++ b/portato/gui/gtk/windows.py
@@ -154,7 +154,7 @@ class PluginWindow (AbstractDialog):
def cb_ok_clicked (self, btn):
for plugin, val in self.changedPlugins.iteritems():
- plugin.set_enabled(val)
+ plugin.set_option("disabled", not val)
self.close()
return True
diff --git a/portato/gui/gui_helper.py b/portato/gui/gui_helper.py
index e827a22..358f56e 100644
--- a/portato/gui/gui_helper.py
+++ b/portato/gui/gui_helper.py
@@ -459,9 +459,11 @@ class EmergeQueue:
self.title_update(title)
time.sleep(0.5)
-
if self.title_update: self.title_update(None)
+ if self.process is None: # someone resetted this
+ return
+
@plugin.hook("after_emerge", packages = packages, retcode = self.process.returncode)
def update_packages():
for p in packages:
diff --git a/portato/gui/qt/terminal.py b/portato/gui/qt/terminal.py
index 91d2e94..23e090a 100644
--- a/portato/gui/qt/terminal.py
+++ b/portato/gui/qt/terminal.py
@@ -13,8 +13,9 @@
from PyQt4 import Qt
from Queue import Queue
-from threading import Thread
-from os import read
+from threading import Thread, currentThread
+from os import read, close
+import errno
try:
from curses.ascii import ctrl
@@ -45,6 +46,13 @@ class DeleteEvent (Qt.QEvent):
Qt.QEvent.__init__(self, self.TYPE)
self.del_type = type
+class SetPtyEvent (Qt.QEvent):
+ TYPE = Qt.QEvent.Type(1003)
+
+ def __init__ (self, pty):
+ Qt.QEvent.__init__(self, self.TYPE)
+ self.pty = pty
+
class BoldFormat (Qt.QTextCharFormat):
def __init__(self):
@@ -112,6 +120,11 @@ class QtConsole (Console, Qt.QTextEdit):
# set black bg
self.palette().setColor(Qt.QPalette.Base, Qt.QColor("black"))
+
+ # set highlighting colors ... XXX: for some reasons this does not work ... Qt sucks
+ self.palette().setColor(Qt.QPalette.Highlight, Qt.QColor("white"))
+ self.palette().setColor(Qt.QPalette.HighlightedText, Qt.QColor("black"))
+
self.setBackgroundRole(Qt.QPalette.Base)
self.setAutoFillBackground(True)
@@ -151,6 +164,11 @@ class QtConsole (Console, Qt.QTextEdit):
self._deletePrev(event.del_type)
event.accept()
return True
+
+ elif event.type() == SetPtyEvent.TYPE:
+ self.set_pty(event.pty)
+ event.accept()
+ return True
event.ignore()
return False
@@ -207,6 +225,10 @@ class QtConsole (Console, Qt.QTextEdit):
self.current.start()
def set_pty (self, pty):
+ if currentThread().getName() != "MainThread":
+ Qt.QCoreApplication.postEvent(self, SetPtyEvent(pty))
+ return
+
if not self.running:
self.pty = pty
self.start_new_thread()
@@ -215,6 +237,7 @@ class QtConsole (Console, Qt.QTextEdit):
else: # quit current thread
self.run = False
self.clear()
+ close(self.pty)
self.pty = pty # set this after clearing to lose no chars :)
self.start_new_thread()
@@ -226,7 +249,13 @@ class QtConsole (Console, Qt.QTextEdit):
got_cr = False
while self.run:
- s = read(self.pty, 1)
+ try:
+ s = read(self.pty, 1)
+ except OSError, e: # bug in Python with the subprocess module
+ if e.errno == errno.EINTR:
+ continue
+ raise
+
if s == "": break # nothing read -> finish
if self.isNotWrapping and s == "\n":
diff --git a/portato/gui/qt/tree.py b/portato/gui/qt/tree.py
index 6e9950f..7c0fa4c 100644
--- a/portato/gui/qt/tree.py
+++ b/portato/gui/qt/tree.py
@@ -72,7 +72,7 @@ class QtTree (Tree):
iter += 1 # next iter ...
newIt = iter.value()
- if newIt.parent() != it.parent(): # stop if we left the current parent
+ if not newIt or newIt.parent() != it.parent(): # stop if we left the current parent
return None
else:
return newIt
diff --git a/portato/gui/qt/windows.py b/portato/gui/qt/windows.py
index 3bd9296..5f1100f 100644
--- a/portato/gui/qt/windows.py
+++ b/portato/gui/qt/windows.py
@@ -116,7 +116,7 @@ class PluginDialog (Window):
@Qt.pyqtSignature("")
def on_buttonBox_accepted(self):
for pluginKey, value in self.changedPlugins.iteritems():
- self.plugins[pluginKey].set_enabled(value)
+ self.plugins[pluginKey].set_option("disabled", not value)
self.accept()
diff --git a/portato/plugin.py b/portato/plugin.py
index abdf86c..ebc1e2f 100644
--- a/portato/plugin.py
+++ b/portato/plugin.py
@@ -25,6 +25,43 @@ def error (reason, p):
reason = "("+reason+")"
debug("Malformed plugin:", p, reason, minus=1, error = 1)
+class Options (object):
+ """The <options>-element."""
+
+ __options = ("disabled", "blocking")
+
+ def __init__ (self, options = None):
+
+ self.disabled = False
+ self.blocking = False
+
+ if options:
+ self.parse(options)
+
+ def parse (self, options):
+ for opt in options:
+ if opt.hasChildNodes():
+ nodes = opt.childNodes
+
+ if len(nodes) > 1:
+ raise ParseException, "Malformed option"
+
+ if nodes[0].nodeType != nodes[0].TEXT_NODE:
+ raise ParseException, "Malformed option"
+
+ type = str(nodes[0].nodeValue.strip())
+
+ if type in self.__options:
+ self.set(type, True)
+ else:
+ raise ParseException, "Malformed option"
+
+ def get (self, name):
+ return self.__getattribute__(name)
+
+ def set (self, name, value):
+ return self.__setattr__(name, value)
+
class Menu:
"""A single <menu>-element."""
def __init__ (self, plugin, label, call):
@@ -172,7 +209,7 @@ class Plugin:
self._import = None
self.hooks = []
self.menus = []
- self.enabled = True
+ self.options = Options()
def parse_hooks (self, hooks):
"""Gets a list of <hook>-elements and parses them.
@@ -198,6 +235,11 @@ class Plugin:
for m in menus:
menu = Menu(self, str(m.getAttribute("label")), str(m.getAttribute("call")))
self.menus.append(menu)
+
+ def parse_options (self, options):
+ if options:
+ for o in options:
+ self.options.parse(o.getElementsByTagName("option"))
def set_import (self, imports):
"""This gets a list of imports and parses them - setting the import needed to call the plugin.
@@ -239,15 +281,14 @@ class Plugin:
@rtype: string"""
return self._import
- def set_disabled (self, nodes):
- if nodes:
- self.set_enabled(False)
+ def get_option(self, name):
+ return self.options.get(name)
- def is_enabled (self):
- return self.enabled
+ def set_option (self, name, value):
+ return self.options.set(name, value)
- def set_enabled (self, e):
- self.enabled = e
+ def is_enabled (self):
+ return not self.get_option("disabled")
class PluginQueue:
"""Class managing and loading the plugins."""
@@ -398,7 +439,7 @@ class PluginQueue:
plugin.parse_hooks(elem.getElementsByTagName("hook"))
plugin.set_import(elem.getElementsByTagName("import"))
plugin.parse_menus(elem.getElementsByTagName("menu"))
- plugin.set_disabled(elem.getElementsByTagName("disabled"))
+ plugin.parse_options(elem.getElementsByTagName("options"))
self.list.append(plugin)
@@ -428,6 +469,8 @@ class PluginQueue:
if connect.depend_plugin is None: # no dependency -> straight add
self.hooks[hook.hook][0].append(connect)
elif connect.depend_plugin == "*":
+ self.hooks[hook.hook][0][0:0] = [connect]
+ elif connect.depend_plugin == "-*":
if not hook.hook in star_before:
star_before[hook.hook] = []
@@ -451,6 +494,8 @@ class PluginQueue:
star_after[hook.hook] = []
star_after[hook.hook].append(connect)
+ elif connect.depend_plugin == "-*":
+ self.hooks[hook.hook][2][0:0] = [connect]
else:
named = [x.plugin.name for x in self.hooks[hook.hook][2]]
if connect.depend_plugin in named:
@@ -472,7 +517,7 @@ class PluginQueue:
self._resolve_unresolved(unresolved_before, unresolved_after)
for hook in star_before:
- self.hooks[hook][0][0:0] = star_before[hook] # prepend the list
+ self.hooks[hook][0].extend(star_before[hook]) # append the list
for hook in star_after:
self.hooks[hook][2].extend(star_after[hook]) # append the list
diff --git a/portato/plugins/resume_loop.py b/portato/plugins/resume_loop.py
new file mode 100644
index 0000000..9891fee
--- /dev/null
+++ b/portato/plugins/resume_loop.py
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+#
+# File: portato/plugins/resume_loop.py
+# This file is part of the Portato-Project, a graphical portage-frontend.
+#
+# Copyright (C) 2007 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 <necoro@necoro.net>
+
+import pty
+from subprocess import call, STDOUT
+from portato.backend import system
+from portato.helper import debug
+
+console = None
+command = "until emerge --resume --skipfirst; do : ; done"
+
+def set_console (*args, **kwargs):
+ global console
+ console = kwargs["console"]
+
+def resume_loop (retcode, *args, **kwargs):
+ if retcode is None:
+ debug("Resume-loop called while process is still running!", warn = True)
+ elif retcode == 0:
+ # everything ok - ignore
+ #pass
+ debug("Everything is ok")
+ else:
+ if console is None:
+ debug("No console for the resume loop...")
+ else:
+ # open tty
+ (master, slave) = pty.openpty()
+ console.set_pty(master)
+ call(command, stdout = slave, stderr = STDOUT, shell = True, env = system.get_environment())