diff options
37 files changed, 2052 insertions, 2306 deletions
diff --git a/doc/Howto_Write_Plugins b/doc/Howto_Write_Plugins index 6f58de5..bb63120 100644 --- a/doc/Howto_Write_Plugins +++ b/doc/Howto_Write_Plugins @@ -1,67 +1,26 @@ HowTo Write A Plugin For Portato ================================= -(NOTE: The XML schema is likely to change in the future.) +Writing plugins for Portato is quite easy: (Nearly) all you have to do is to write a plain Python module :). -Writing a plugin is (quite) simple. You have to provide a XML file which tells portato how to communicate with the plugin and in most cases a Python module doing the actual work. This howto is not going to cover the writing of the plugin, but only the XML. +A plugin has two more builtins than a normal Python module: -General -------- + ``Plugin`` + This is a class representing a plugin. -So - how is a plugin is working in general? Portato defines a set of hooks (see the Hooks file for a complete list) to which your plugin can connect. For each hook you have three choices: To connect before, after or instead of the function being hooked. (Of course you can connect several times to one hook ...) The latter one should be used only if you really know what you are doing as it is likely that Portato won't work any longer after this. Also have in mind, that only one plugin can override. Thus: if several plugins want to override one hook, a randomly chosen one will get the allowance. + ``register`` + A function which you have to call to get the your plugin added to Portato. -For each of the "before" and "after" mode a queue exists, holding the plugins to execute. A plugin is allowed to state another plugin which should be executed after (in "before" mode) or before (in "after" mode) your plugin. The star * exists to note that this should be applied to ALL other plugins. (And "-*" does exactly the opposite.) Portato TRIES to satisfy your request... +In this module you need to have at least one class, which inherits from ``Plugin``. This class does all the handling you want your plugin to do. If you want, you can implement more classes - from Portato's view they are handled as different plugins. Thus: It is not the module hierarchy, but the classes that count. +Add the end you only call ``register`` once for each class and are done :). -When you now have chosen the connect(s) to chose you write an appropriate function (or better: a Python callable) which will be given in the XML-definition to be called by Portato with the hook's arguments. (Hint: All arguments will be passed as keyword parameters. So you can easily pick the ones you need and add a "**kwargs" argument, which will take the rest. As a nice side effect you do not have to reflect any API changes which will come with additional parameters :)). +Of course there are some things you should bare in mind: + + 1. Do not use the ``__init__`` method, but ``init``. + 2. Do not use a member which shadows one from the original class: + ``description``, ``author``, ``status``, ``menus``, ``name``, ``calls``, ``deps``, ``enabled``, ``add_menu``, ``add_call`` + 3. Of course you can *use* the members mentioned under point 2. -Finally: Add an import tag stating the module to import to execute the function(s) given - and you are done. +For the details, please see the source code at the moment or write your question to portato@necoro.net -If you are finished with your plugin, you can check if it complies with the XML Schema by doing: "portato -x $PLUGIN_FILE". - -Sample XML ----------- - -<?xml version="1.0" encoding="UTF-8" ?> -<plugin xmlns="http://portato.sourceforge.net/plugin" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://portato.sourceforge.net/plugin http://portato.sourceforge.net/plugin.xsd"> - <author>Joe Smith</author> - <name>Some small sample plugin</name> - - <import>plugins.sample.small</import> - - <hooks> - <hook type = "a_hook" call = "the_calling_function"> - <connect type="after" /> - </hook - </hooks> - - <options> - <option>disabled</option> - </options -</plugin> - -Notes: - -- If you want to specify a dependency plugin the connect tag has to be like: <connect type = "after"> The other plugin we depend on </connect>. -- The "connect"-tag can be omitted. It then defaults to "<connect type='before' />". -- It is possible of course to have more than one "hook" tag. -- The options tag is optional. For a complete list of options, see below. - -Additional Tags ---------------- - -Menu: - It is possible, that your plugin gets an entry in the "Plugin"-menu. Therefore you have to provide a "menu" tag and one or more "item" tags: - - <menu> - <item call = "the_calling_menu_function"> - A small _Plugin - </item> - </menu> - - Note, that the underscore in front of a character will make it be underlined in the menu and thus accessible by a shortcut. - -Options --------- - -disabled: - Disable the plugin by default, i.e. the user has to enable it, if he wants to use it. +.. vim: ft=rst @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: Portato\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2008-06-23 23:48+0100\n" +"PO-Revision-Date: 2008-07-08 16:43+0100\n" "Last-Translator: René 'Necoro' Neumann <necoro@necoro.net>\n" "Language-Team: \n" "MIME-Version: 1.0\n" @@ -12,39 +12,6 @@ msgstr "" "X-Poedit-Country: GERMANY\n" "X-Poedit-SourceCharset: utf-8\n" -#: portato/gui/templates/popups.glade:12 -#: portato/gui/templates/MainWindow.glade:192 -#: portato/gui/templates/MainWindow.glade:284 -msgid "Emerge _Paused" -msgstr "Emerge _angehalten" - -#: portato/gui/templates/popups.glade:19 -#: portato/gui/templates/MainWindow.glade:200 -#: portato/gui/templates/MainWindow.glade:266 -msgid "_Kill Emerge" -msgstr "_Kill Emerge" - -#: portato/gui/templates/popups.glade:37 -#: portato/gui/templates/MainWindow.glade:70 -msgid "gtk-quit" -msgstr "" - -#: portato/gui/templates/UpdateWindow.glade:7 -msgid "Updatable Packages" -msgstr "Pakete mit Updates" - -#: portato/gui/templates/UpdateWindow.glade:49 -msgid "_Close" -msgstr "_Schließen" - -#: portato/gui/templates/UpdateWindow.glade:61 -msgid "Select _All" -msgstr "_Alles auswählen" - -#: portato/gui/templates/UpdateWindow.glade:76 -msgid "_Install Selected" -msgstr "_Installiere ausgewählte" - #: portato/gui/templates/AboutWindow.glade:8 msgid "About Portato" msgstr "Portato" @@ -57,236 +24,6 @@ msgstr "" "This software is licensed under the terms of the GPLv2.\n" "Copyright (C) 2006-2008 René 'Necoro' Neumann <necoro@necoro.net>" -#: portato/gui/templates/PreferenceWindow.glade:7 -msgid "Preferences" -msgstr "Einstellungen" - -#: portato/gui/templates/PreferenceWindow.glade:42 -msgid "Debug" -msgstr "Debug" - -#: portato/gui/templates/PreferenceWindow.glade:55 -msgid "Browser command: " -msgstr "Browser-Befehl: " - -#: portato/gui/templates/PreferenceWindow.glade:81 -#: portato/gui/templates/PreferenceWindow.glade:539 -msgid "<b>General Options</b>" -msgstr "<b>Allgemeine Optionen</b>" - -#: portato/gui/templates/PreferenceWindow.glade:131 -msgid "<b>Update World Options</b>" -msgstr "<b>Optionen für \"update world\"</b>" - -#: portato/gui/templates/PreferenceWindow.glade:159 -msgid "Sync command: " -msgstr "Sync-Befehl: " - -#: portato/gui/templates/PreferenceWindow.glade:180 -msgid "<b>Sync Options</b>" -msgstr "<b>Sync Optionen</b>" - -#: portato/gui/templates/PreferenceWindow.glade:231 -msgid "File name to use, if package.use is a directory: " -msgstr "Zu benutzender Dateiname, wenn package.use ein Verzeichnis ist:" - -#: portato/gui/templates/PreferenceWindow.glade:242 -msgid "Add only exact version to package.use" -msgstr "Füge nur die exakte Paketversion zu package.keywords hinzu" - -#: portato/gui/templates/PreferenceWindow.glade:255 -msgid "Add only exact version to package.keywords" -msgstr "Füge nur die exakte Paketversion zu package.keywords hinzu" - -#: portato/gui/templates/PreferenceWindow.glade:269 -msgid "File name to use, if package.keywords is a directory: " -msgstr "Zu benutzender Dateiname, wenn package.keywords ein Verzeichnis ist:" - -#: portato/gui/templates/PreferenceWindow.glade:303 -msgid "File name to use, if package.mask/package.unmask is a directory: " -msgstr "Zu benutzender Dateiname, wenn package.mask/package.unmask ein Verzeichnis ist:" - -#: portato/gui/templates/PreferenceWindow.glade:314 -msgid "Add only exact version to package.mask/package.unmask" -msgstr "Füge nur die exakte Paketversion zu package.mask/package.unmask hinzu" - -#: portato/gui/templates/PreferenceWindow.glade:336 -msgid "" -"<u>You may use the following placeholders:</u>\n" -"\n" -"<i>$(cat)</i>: category\n" -"<i>$(pkg)</i>: package name\n" -"<i>$(cat-1)/$(cat-2)</i>: first/second part of the category" -msgstr "" -"<u>Die folgenden Platzhalter stehen zur Verfügung:</u>\n" -"\n" -"<i>$(cat)</i>: Kategorie\n" -"<i>$(pkg)</i>: Paketname\n" -"<i>$(cat-1)/$(cat-2)</i>: erster/zweiter Teil der Kategorie" - -#: portato/gui/templates/PreferenceWindow.glade:362 -msgid "<u><i>Use-Flags</i></u>" -msgstr "<u><i>Use-Flags</i></u>" - -#: portato/gui/templates/PreferenceWindow.glade:377 -msgid "<u><i>Testing Keywords</i></u>" -msgstr "<u><i>Testing Keywords</i></u>" - -#: portato/gui/templates/PreferenceWindow.glade:392 -msgid "<u><i>Masking Keywords</i></u>" -msgstr "<u><i>Masking Keywords</i></u>" - -#: portato/gui/templates/PreferenceWindow.glade:409 -msgid "<b>Use Flag and Keyword Options</b>" -msgstr "<b>Use-Flag- und Keyword-Optionen</b>" - -#: portato/gui/templates/PreferenceWindow.glade:427 -#: portato/gui/templates/MainWindow.glade:895 -msgid "General" -msgstr "Allgemein" - -#: portato/gui/templates/PreferenceWindow.glade:464 -msgid "Enable systray" -msgstr "Aktiviere Systray" - -#: portato/gui/templates/PreferenceWindow.glade:477 -msgid "Show emerge progress in window title" -msgstr "Zeige den Emerge Prozess im Fenstertitel" - -#: portato/gui/templates/PreferenceWindow.glade:491 -msgid "Show emerge progress in console title" -msgstr "Zeige den Emerge Prozess im Konsolentitel" - -#: portato/gui/templates/PreferenceWindow.glade:505 -msgid "Hide on minimization (only if systray is enabled)" -msgstr "Minimiere zu Systray" - -#: portato/gui/templates/PreferenceWindow.glade:520 -msgid "" -"Update the package list with the current search results while you are typing.\n" -"<b>Note</b>: Will slow down the typing process." -msgstr "" -"Aktualisiert die Paketliste mit den Suchergebnissen während du tippst.\n" -"<b>Achtung</b>: Dies verlangsamt die Reaktion auf das Tippen." - -#: portato/gui/templates/PreferenceWindow.glade:522 -msgid "Search while typing" -msgstr "Suche währen des Tippens" - -#: portato/gui/templates/PreferenceWindow.glade:576 -msgid "Console Font" -msgstr "Schriftart in Konsole" - -#: portato/gui/templates/PreferenceWindow.glade:587 -msgid "Chose a console font" -msgstr "Wähle eine Schriftart" - -#: portato/gui/templates/PreferenceWindow.glade:605 -msgid "Maximum length of the console title" -msgstr "Maximale Länge des Konsolentitels" - -#: portato/gui/templates/PreferenceWindow.glade:635 -msgid "<b>Console Options</b>" -msgstr "<b>Konsolen Optionen</b>" - -#: portato/gui/templates/PreferenceWindow.glade:675 -msgid "Package Tabs" -msgstr "Pakettabs" - -#: portato/gui/templates/PreferenceWindow.glade:700 -msgid "System Tabs" -msgstr "Systemtabs" - -#: portato/gui/templates/PreferenceWindow.glade:726 -msgid "<b>Tab Options</b>" -msgstr "<b>Tab Optionen</b>" - -#: portato/gui/templates/PreferenceWindow.glade:759 -msgid "Show slots in the version list" -msgstr "Zeige die Slots in der Versionsliste" - -#: portato/gui/templates/PreferenceWindow.glade:769 -msgid "" -"Organize the categories in a tree. Thereby collapse categories with the same prefix:\n" -"As an example: <i>app-admin</i>, <i>app-emacs</i>, and <i>app-vim</i> would be collapsed into <i><b>app</b></i> as root and <i>admin</i>, <i>emacs</i>, and <i>vim</i> as children." -msgstr "" -"Verwalte die Kategorien in einem Baum. Dabei werden Kategorien mit einem gleich Präfix zusammengefasst:\n" -"Als Beispiel: <i>app-admin</i>, <i>app-emacs</i> und <i>app-vim</i> würden zusammengefasst in <i><b>app</b></i> als Wurzel and <i>admin</i>, <i>emacs</i> und <i>vim</i> als Kindelemente." - -#: portato/gui/templates/PreferenceWindow.glade:771 -msgid "Collapse categories with same prefix" -msgstr "Fasse Kategorien mit gleichem Präfix zusammen" - -#: portato/gui/templates/PreferenceWindow.glade:787 -msgid "<b>Package Options</b>" -msgstr "<b>Paket Optionen</b>" - -#: portato/gui/templates/PreferenceWindow.glade:811 -msgid "Visual" -msgstr "Oberfläche" - -#: portato/gui/templates/PreferenceWindow.glade:829 -#: portato/gui/templates/PluginWindow.glade:47 -#: portato/gui/templates/SearchWindow.glade:48 -#: portato/gui/templates/MailInfoWindow.glade:148 -msgid "gtk-cancel" -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:838 -#: portato/gui/templates/PluginWindow.glade:59 -msgid "gtk-apply" -msgstr "" - -#: portato/gui/templates/PluginWindow.glade:8 -msgid "Plugins" -msgstr "Plugins" - -#: portato/gui/templates/SearchWindow.glade:8 -msgid "Search Results" -msgstr "Ergebnisse" - -#: portato/gui/templates/SearchWindow.glade:60 -msgid "gtk-jump-to" -msgstr "" - -#: portato/gui/templates/SearchWindow.glade:75 -msgid "gtk-ok" -msgstr "" - -#: portato/gui/templates/MailInfoWindow.glade:6 -msgid "Send Bug Mail ..." -msgstr "Sende Bug-Email ..." - -#: portato/gui/templates/MailInfoWindow.glade:50 -msgid "" -"Comments /\n" -"what did you do to hit the bug?" -msgstr "" -"Kommentare /\n" -"Vorgehensweise um den Bug zu erzeugen?" - -#: portato/gui/templates/MailInfoWindow.glade:64 -msgid "Name:" -msgstr "Name:" - -#: portato/gui/templates/MailInfoWindow.glade:79 -msgid "Email address:" -msgstr "Email-Adresse:" - -#: portato/gui/templates/MailInfoWindow.glade:122 -msgid "" -"<b><u>Additional Information</u></b>\n" -"\n" -"(all optional)" -msgstr "" -"<b><u>Zusätzliche Informationen</u></b>\n" -"\n" -"(alle optional)" - -#: portato/gui/templates/MailInfoWindow.glade:176 -msgid "_Send" -msgstr "_Send" - #: portato/gui/templates/MainWindow.glade:20 msgid "_File" msgstr "_Datei" @@ -299,6 +36,11 @@ msgstr "_Einstellungen" msgid "Re_load Portage" msgstr "Aktua_lisiere Portage-Cache" +#: portato/gui/templates/MainWindow.glade:70 +#: portato/gui/templates/popups.glade:37 +msgid "gtk-quit" +msgstr "" + #: portato/gui/templates/MainWindow.glade:83 msgid "_Emerge" msgstr "_Emerge" @@ -332,6 +74,18 @@ msgstr "_Sync" msgid "Save _Flags" msgstr "Speichere _Flags" +#: portato/gui/templates/MainWindow.glade:192 +#: portato/gui/templates/MainWindow.glade:284 +#: portato/gui/templates/popups.glade:12 +msgid "Emerge _Paused" +msgstr "Emerge _angehalten" + +#: portato/gui/templates/MainWindow.glade:200 +#: portato/gui/templates/MainWindow.glade:266 +#: portato/gui/templates/popups.glade:19 +msgid "_Kill Emerge" +msgstr "_Kill Emerge" + #: portato/gui/templates/MainWindow.glade:223 #: portato/gui/templates/MainWindow.glade:1170 #: portato/gui/windows/main.py:763 @@ -344,8 +98,8 @@ msgstr "Oneshot" #: portato/gui/templates/MainWindow.glade:242 #: portato/gui/templates/MainWindow.glade:1195 +#: portato/gui/windows/main.py:1206 #: portato/gui/windows/main.py:1208 -#: portato/gui/windows/main.py:1210 msgid "Console" msgstr "Konsole" @@ -381,6 +135,8 @@ msgstr "" #: portato/gui/templates/MainWindow.glade:623 #: portato/gui/templates/MainWindow.glade:736 #: portato/gui/templates/MainWindow.glade:751 +#: portato/gui/templates/PluginWindow.glade:98 +#: portato/gui/templates/PluginWindow.glade:123 msgid "label" msgstr "" @@ -444,6 +200,11 @@ msgstr "Testing" msgid "<b>Use Flags:</b>" msgstr "<b>Use Flags:</b>" +#: portato/gui/templates/MainWindow.glade:895 +#: portato/gui/templates/PreferenceWindow.glade:427 +msgid "General" +msgstr "Allgemein" + #: portato/gui/templates/MainWindow.glade:926 msgid "Use List" msgstr "Use-Flag-Liste" @@ -488,262 +249,338 @@ msgstr "_Löschen" msgid "Log" msgstr "Log" -#: portato.py:40 -msgid "validates the given plugin xml instead of launching Portato" -msgstr "Validiert die gegebene Plugin-XML. Startet nicht Portato." - -#: portato.py:43 -msgid "do not fork off as root" -msgstr "erzeuge keinen Root-Prozess" - -#: portato.py:43 -msgid "-L is deprecated" -msgstr "-L ist veraltet" +#: portato/gui/templates/PluginWindow.glade:8 +msgid "Plugins" +msgstr "Plugins" -#: portato.py:53 -#, python-format -msgid "Validation failed. XML syntax error: %s." -msgstr "Validierung fehlgeschlagen. XML Syntax Fehler: %s." +#: portato/gui/templates/PluginWindow.glade:52 +msgid "_Install dependencies" +msgstr "_Installiere Abhängigkeiten" -#: portato.py:56 -msgid "Validation failed. Does not comply with schema." -msgstr "Validierung gegen das Schema fehlgeschlagen." +#: portato/gui/templates/PluginWindow.glade:81 +msgid "Needed dependencies" +msgstr "Abhängigkeiten" -#: portato.py:59 -msgid "Validation succeeded." -msgstr "Validierung erfolgreich." +#: portato/gui/templates/PluginWindow.glade:109 +msgid "<b>Author:</b>" +msgstr "<b>Autor:</b>" -#: portato.py:65 -msgid "Starting Portato" -msgstr "Starte Portato" +#: portato/gui/templates/PluginWindow.glade:144 +#: portato/gui/windows/main.py:325 +#: portato/gui/windows/plugin.py:27 +#: portato/gui/windows/update.py:47 +msgid "Enabled" +msgstr "Aktiviert" -#: portato/gui/wrapper.py:59 -msgid "oneshot" -msgstr "oneshot" +#: portato/gui/templates/PluginWindow.glade:154 +#: portato/gui/windows/plugin.py:27 +msgid "Temporarily enabled" +msgstr "Aktiviert (temporär)" -#: portato/gui/wrapper.py:64 -#, python-format -msgid "updating from version %s" -msgstr "Update von Version %s" +#: portato/gui/templates/PluginWindow.glade:166 +#: portato/gui/windows/plugin.py:27 +msgid "Temporarily disabled" +msgstr "Deaktiviert (temporär)" -#: portato/gui/wrapper.py:66 -msgid "updating" -msgstr "Update" +#: portato/gui/templates/PluginWindow.glade:179 +#: portato/gui/windows/plugin.py:27 +msgid "Disabled" +msgstr "Deaktiviert" -#: portato/gui/wrapper.py:71 -#, python-format -msgid "downgrading from version %s" -msgstr "Downgrade von Version %s" +#: portato/gui/templates/PluginWindow.glade:223 +#: portato/gui/templates/SearchWindow.glade:48 +#: portato/gui/templates/MailInfoWindow.glade:148 +#: portato/gui/templates/PreferenceWindow.glade:829 +msgid "gtk-cancel" +msgstr "" -#: portato/gui/wrapper.py:73 -msgid "downgrading" -msgstr "Downgrade" +#: portato/gui/templates/PluginWindow.glade:235 +#: portato/gui/templates/PreferenceWindow.glade:838 +msgid "gtk-apply" +msgstr "" -#: portato/gui/wrapper.py:77 -msgid "IUSE changes:" -msgstr "IUSE Änderungen:" +#: portato/gui/templates/UpdateWindow.glade:7 +msgid "Updatable Packages" +msgstr "Pakete mit Updates" -#: portato/gui/wrapper.py:95 -msgid "(In Progress)" -msgstr "(In Bearbeitung)" +#: portato/gui/templates/UpdateWindow.glade:49 +msgid "_Close" +msgstr "_Schließen" -#: portato/gui/wrapper.py:120 -msgid "Install" -msgstr "Installieren" +#: portato/gui/templates/UpdateWindow.glade:61 +msgid "Select _All" +msgstr "_Alles auswählen" -#: portato/gui/wrapper.py:131 -msgid "Uninstall" -msgstr "Deinstallieren" +#: portato/gui/templates/UpdateWindow.glade:76 +msgid "_Install Selected" +msgstr "_Installiere ausgewählte" -#: portato/gui/wrapper.py:143 -msgid "Update" -msgstr "Update" +#: portato/gui/templates/SearchWindow.glade:8 +msgid "Search Results" +msgstr "Ergebnisse" -#: portato/gui/dialogs.py:17 -msgid "" -"There are some packages in the emerge queue and/or an emerge process is running.\n" -"Do you really want to quit?" +#: portato/gui/templates/SearchWindow.glade:60 +msgid "gtk-jump-to" msgstr "" -"Es sind noch Pakete in der Emerge-Queue und/oder emerge läuft noch.\n" -"Wirklich beenden?" -#: portato/gui/dialogs.py:36 -#, python-format -msgid "" -"%(blocked)s is blocked by %(blocks)s.\n" -"Please unmerge the blocking package." +#: portato/gui/templates/SearchWindow.glade:75 +msgid "gtk-ok" msgstr "" -"%(blocks)s blockiert %(blocked)s.\n" -"Bitte deinstalliere das blockierende Paket." -#: portato/gui/dialogs.py:42 -msgid "You are not root." -msgstr "Du bist nicht root." +#: portato/gui/templates/MailInfoWindow.glade:6 +msgid "Send Bug Mail ..." +msgstr "Sende Bug-Email ..." -#: portato/gui/dialogs.py:48 -#, python-format +#: portato/gui/templates/MailInfoWindow.glade:50 msgid "" -"%s seems to be masked.\n" -"Do you want to unmask it and its dependencies?" +"Comments /\n" +"what did you do to hit the bug?" msgstr "" -"%s scheint maskiert zu sein.\n" -"Soll das Paket und seine Abhängigkeiten demaskiert werden?" +"Kommentare /\n" +"Vorgehensweise um den Bug zu erzeugen?" -#: portato/gui/dialogs.py:54 -msgid "Package not found!" -msgstr "Paket nicht gefunden!" +#: portato/gui/templates/MailInfoWindow.glade:64 +msgid "Name:" +msgstr "Name:" -#: portato/gui/dialogs.py:60 -msgid "Do not show this dialog again." -msgstr "Diesen Dialog nicht wieder anzeigen." +#: portato/gui/templates/MailInfoWindow.glade:79 +msgid "Email address:" +msgstr "Email-Adresse:" -#: portato/gui/dialogs.py:62 -#, python-format +#: portato/gui/templates/MailInfoWindow.glade:122 msgid "" -"You have changed %s.\n" -"Portato will write these changes into the appropriate files.\n" -"Please backup them if you think it is necessairy." +"<b><u>Additional Information</u></b>\n" +"\n" +"(all optional)" msgstr "" -"Du hast die %s geändert.\n" -"Portato wird diese Änderungen speichern.\n" -"Bitte sichere die entsprechenden Dateien, wenn du es als notwendig erachtest." +"<b><u>Zusätzliche Informationen</u></b>\n" +"\n" +"(alle optional)" -#: portato/gui/dialogs.py:71 -msgid "You cannot remove dependencies. :)" -msgstr "Du kannst keine Abhängigkeiten löschen ;)." +#: portato/gui/templates/MailInfoWindow.glade:176 +msgid "_Send" +msgstr "_Send" -#: portato/gui/dialogs.py:77 -msgid "" -"This is the updates queue. You cannot remove single elements.\n" -"Do you want to clear the whole queue instead?" -msgstr "" -"Das ist die Update-Queue. Aus dieser können keine einzelnen Pakete entfernt werden.\n" -"Soll stattdessen die komplette Queue entfernt werden?" +#: portato/gui/templates/PreferenceWindow.glade:7 +msgid "Preferences" +msgstr "Einstellungen" -#: portato/gui/dialogs.py:83 -msgid "Do you really want to clear the whole queue?" -msgstr "Wirklich die gesamte Queue löschen?" +#: portato/gui/templates/PreferenceWindow.glade:42 +msgid "Debug" +msgstr "Debug" -#: portato/gui/views.py:70 -msgid "Package is not installed" -msgstr "Paket ist nicht installiert" +#: portato/gui/templates/PreferenceWindow.glade:55 +msgid "Browser command: " +msgstr "Browser-Befehl: " -#: portato/gui/views.py:87 -#, python-format -msgid "No %(old)s language file installed. Falling back to %(new)s." -msgstr "Keine \"%(old)s\" Syntaxdatei gefunden. Benutze \"%(new)s\"-Syntax." +#: portato/gui/templates/PreferenceWindow.glade:81 +#: portato/gui/templates/PreferenceWindow.glade:539 +msgid "<b>General Options</b>" +msgstr "<b>Allgemeine Optionen</b>" -#: portato/gui/views.py:96 -#, python-format -msgid "No %(old)s language file installed. Disable highlighting." -msgstr "Keine \"%(old)s\" Syntaxdatei gefunden. Deaktiviere Hervorhebung." +#: portato/gui/templates/PreferenceWindow.glade:131 +msgid "<b>Update World Options</b>" +msgstr "<b>Optionen für \"update world\"</b>" -#: portato/gui/views.py:115 -#: portato/gui/windows/main.py:669 -#, python-format -msgid "Error: %s" -msgstr "Fehler: %s" +#: portato/gui/templates/PreferenceWindow.glade:159 +msgid "Sync command: " +msgstr "Sync-Befehl: " -#: portato/gui/updater.py:96 -#, python-format -msgid "No unmasked version of package '%s' found. Trying masked ones. This normally should not happen..." -msgstr "Keine demaskierte Version vom Paket '%s' gefunden. Versuche die maskierten. Eigentlich sollte das nicht passieren..." +#: portato/gui/templates/PreferenceWindow.glade:180 +msgid "<b>Sync Options</b>" +msgstr "<b>Sync Optionen</b>" -#: portato/gui/updater.py:100 -#, python-format -msgid "Trying to remove package '%s' from queue which does not exist in system." -msgstr "Das Paket '%s' sollte aus der Queue entfernt werden, aber es befindet sich nicht im System." +#: portato/gui/templates/PreferenceWindow.glade:231 +msgid "File name to use, if package.use is a directory: " +msgstr "Zu benutzender Dateiname, wenn package.use ein Verzeichnis ist:" -#: portato/gui/exception_handling.py:31 -msgid "A programming error has been detected during the execution of this program." -msgstr "Ein Fehler ist aufgetreten." +#: portato/gui/templates/PreferenceWindow.glade:242 +msgid "Add only exact version to package.use" +msgstr "Füge nur die exakte Paketversion zu package.keywords hinzu" -#: portato/gui/exception_handling.py:32 -msgid "Bug Detected" -msgstr "Fehler aufgetreten" +#: portato/gui/templates/PreferenceWindow.glade:255 +msgid "Add only exact version to package.keywords" +msgstr "Füge nur die exakte Paketversion zu package.keywords hinzu" -#: portato/gui/exception_handling.py:33 -msgid "It probably isn't fatal, but should be reported to the developers nonetheless." -msgstr "Dies ist möglicherweise nicht kritisch, sollte aber trotzdem an die Entwickler weitergeleitet werden." +#: portato/gui/templates/PreferenceWindow.glade:269 +msgid "File name to use, if package.keywords is a directory: " +msgstr "Zu benutzender Dateiname, wenn package.keywords ein Verzeichnis ist:" -#: portato/gui/exception_handling.py:35 -msgid "Show Details" -msgstr "Details" +#: portato/gui/templates/PreferenceWindow.glade:303 +msgid "File name to use, if package.mask/package.unmask is a directory: " +msgstr "Zu benutzender Dateiname, wenn package.mask/package.unmask ein Verzeichnis ist:" -#: portato/gui/exception_handling.py:36 -msgid "Send..." -msgstr "Sende..." +#: portato/gui/templates/PreferenceWindow.glade:314 +msgid "Add only exact version to package.mask/package.unmask" +msgstr "Füge nur die exakte Paketversion zu package.mask/package.unmask hinzu" -#: portato/gui/exception_handling.py:59 -#: portato/gui/exception_handling.py:128 -#, python-format +#: portato/gui/templates/PreferenceWindow.glade:336 msgid "" -"Exception in thread \"%(thread)s\":\n" -"%(trace)s" +"<u>You may use the following placeholders:</u>\n" +"\n" +"<i>$(cat)</i>: category\n" +"<i>$(pkg)</i>: package name\n" +"<i>$(cat-1)/$(cat-2)</i>: first/second part of the category" msgstr "" -"Exception im Thread \"%(thread)s\":\n" -"%(trace)s" +"<u>Die folgenden Platzhalter stehen zur Verfügung:</u>\n" +"\n" +"<i>$(cat)</i>: Kategorie\n" +"<i>$(pkg)</i>: Paketname\n" +"<i>$(cat-1)/$(cat-2)</i>: erster/zweiter Teil der Kategorie" -#: portato/gui/exception_handling.py:75 -msgid "Save traceback..." -msgstr "Speichere Traceback..." +#: portato/gui/templates/PreferenceWindow.glade:362 +msgid "<u><i>Use-Flags</i></u>" +msgstr "<u><i>Use-Flags</i></u>" -#: portato/gui/exception_handling.py:130 -#, python-format +#: portato/gui/templates/PreferenceWindow.glade:377 +msgid "<u><i>Testing Keywords</i></u>" +msgstr "<u><i>Testing Keywords</i></u>" + +#: portato/gui/templates/PreferenceWindow.glade:392 +msgid "<u><i>Masking Keywords</i></u>" +msgstr "<u><i>Masking Keywords</i></u>" + +#: portato/gui/templates/PreferenceWindow.glade:409 +msgid "<b>Use Flag and Keyword Options</b>" +msgstr "<b>Use-Flag- und Keyword-Optionen</b>" + +#: portato/gui/templates/PreferenceWindow.glade:464 +msgid "Enable systray" +msgstr "Aktiviere Systray" + +#: portato/gui/templates/PreferenceWindow.glade:477 +msgid "Show emerge progress in window title" +msgstr "Zeige den Emerge Prozess im Fenstertitel" + +#: portato/gui/templates/PreferenceWindow.glade:491 +msgid "Show emerge progress in console title" +msgstr "Zeige den Emerge Prozess im Konsolentitel" + +#: portato/gui/templates/PreferenceWindow.glade:505 +msgid "Hide on minimization (only if systray is enabled)" +msgstr "Minimiere zu Systray" + +#: portato/gui/templates/PreferenceWindow.glade:520 msgid "" -"Exception:\n" -"%s" +"Update the package list with the current search results while you are typing.\n" +"<b>Note</b>: Will slow down the typing process." msgstr "" -"Exception:\n" -"%s" +"Aktualisiert die Paketliste mit den Suchergebnissen während du tippst.\n" +"<b>Achtung</b>: Dies verlangsamt die Reaktion auf das Tippen." -#: portato/gui/__init__.py:21 -msgid "Loading Backend" -msgstr "Lade Backend" +#: portato/gui/templates/PreferenceWindow.glade:522 +msgid "Search while typing" +msgstr "Suche währen des Tippens" -#: portato/gui/windows/search.py:57 -msgid "Results" -msgstr "Ergebnisse" +#: portato/gui/templates/PreferenceWindow.glade:576 +msgid "Console Font" +msgstr "Schriftart in Konsole" -#: portato/gui/windows/mailinfo.py:69 -#, python-format -msgid "An error occurred while sending. I think we where greylisted. The error: %s" -msgstr "Während des Sendes trat ein Fehler auf. Wahrscheinlich wurden wir ge-greylistet. Der Fehler: %s" +#: portato/gui/templates/PreferenceWindow.glade:587 +msgid "Chose a console font" +msgstr "Wähle eine Schriftart" -#: portato/gui/windows/mailinfo.py:70 -msgid "Wait 60 seconds and try again." -msgstr "Warte 60 Sekunden und versuche es dann noch einmal." +#: portato/gui/templates/PreferenceWindow.glade:605 +msgid "Maximum length of the console title" +msgstr "Maximale Länge des Konsolentitels" -#: portato/gui/windows/update.py:47 -#: portato/gui/windows/main.py:325 -#: portato/gui/windows/plugin.py:24 -msgid "Enabled" -msgstr "Aktiviert" +#: portato/gui/templates/PreferenceWindow.glade:635 +msgid "<b>Console Options</b>" +msgstr "<b>Konsolen Optionen</b>" -#: portato/gui/windows/update.py:48 -msgid "Package" -msgstr "Paket" +#: portato/gui/templates/PreferenceWindow.glade:675 +msgid "Package Tabs" +msgstr "Pakettabs" -#: portato/gui/windows/splash.py:35 +#: portato/gui/templates/PreferenceWindow.glade:700 +msgid "System Tabs" +msgstr "Systemtabs" + +#: portato/gui/templates/PreferenceWindow.glade:726 +msgid "<b>Tab Options</b>" +msgstr "<b>Tab Optionen</b>" + +#: portato/gui/templates/PreferenceWindow.glade:759 +msgid "Show slots in the version list" +msgstr "Zeige die Slots in der Versionsliste" + +#: portato/gui/templates/PreferenceWindow.glade:769 +msgid "" +"Organize the categories in a tree. Thereby collapse categories with the same prefix:\n" +"As an example: <i>app-admin</i>, <i>app-emacs</i>, and <i>app-vim</i> would be collapsed into <i><b>app</b></i> as root and <i>admin</i>, <i>emacs</i>, and <i>vim</i> as children." +msgstr "" +"Verwalte die Kategorien in einem Baum. Dabei werden Kategorien mit einem gleich Präfix zusammengefasst:\n" +"Als Beispiel: <i>app-admin</i>, <i>app-emacs</i> und <i>app-vim</i> würden zusammengefasst in <i><b>app</b></i> als Wurzel and <i>admin</i>, <i>emacs</i> und <i>vim</i> als Kindelemente." + +#: portato/gui/templates/PreferenceWindow.glade:771 +msgid "Collapse categories with same prefix" +msgstr "Fasse Kategorien mit gleichem Präfix zusammen" + +#: portato/gui/templates/PreferenceWindow.glade:787 +msgid "<b>Package Options</b>" +msgstr "<b>Paket Optionen</b>" + +#: portato/gui/templates/PreferenceWindow.glade:811 +msgid "Visual" +msgstr "Oberfläche" + +#: portato.py:40 +msgid "do not fork off as root" +msgstr "erzeuge keinen Root-Prozess" + +#: portato.py:40 +msgid "-L is deprecated" +msgstr "-L ist veraltet" + +#: portato.py:48 +msgid "Starting Portato" +msgstr "Starte Portato" + +#: plugins/etc_proposals.py:32 +msgid "Cannot start etc-proposals. Not root!" +msgstr "Kann etc-proposals nicht starten. Nur root kann das!" + +#: plugins/notify.py:35 +msgid "Notify called while process is still running!" +msgstr "\"Notify\" aufgerufen, während Emerge noch lief." + +#: plugins/notify.py:39 +msgid "Emerge finished!" +msgstr "Emerge-Prozess erfolgreich beendet!" + +#: plugins/notify.py:43 +msgid "Emerge failed!" +msgstr "Emerge fehlgeschlagen!" + +#: plugins/notify.py:44 #, python-format -msgid "... is starting up: %s" -msgstr "... startet: %s" +msgid "Error Code: %d" +msgstr "Fehler-Code: %d" -#: portato/gui/windows/preference.py:124 -msgid "Top" -msgstr "Oben" +#: portato/backend/portage/package.py:130 +#, python-format +msgid "BUG in flags.new_masking_status. It returns '%s'" +msgstr "BUG in flags.new_masking_status. Es gibt '%s' zurück." -#: portato/gui/windows/preference.py:124 -msgid "Bottom" -msgstr "Unten" +#: portato/backend/portage/system.py:329 +#, python-format +msgid "No best match for %s. It seems not to be in the tree anymore." +msgstr "Es konnte kein bester Treffer für das Paket %s gefunden werden. Es scheint sich nicht mehr im Tree zu befinden." -#: portato/gui/windows/preference.py:124 -msgid "Left" -msgstr "Links" +#: portato/backend/portage/system.py:397 +#, python-format +msgid "Found a not installed dependency: %s." +msgstr "Nicht installierte Abhängigkeit gefunden: %s" -#: portato/gui/windows/preference.py:124 -msgid "Right" -msgstr "Rechts" +#: portato/backend/portage/system.py:439 +#, python-format +msgid "Bug? No best match could be found for '%(package)s'. Needed by: '%(cpv)s'." +msgstr "Es konnte kein bester Treffer für das Paket '%(package)s' ermittelt werden (gebraucht von '%(cpv)s'). Bug?" + +#: portato/backend/flags.py:527 +#, python-format +msgid "Conflicting values for masking status: %s" +msgstr "Konflikt beim Masking-Status: %s" #: portato/gui/windows/main.py:154 msgid "<no description>" @@ -817,6 +654,12 @@ msgstr "Lade Plugins" msgid "Building frontend" msgstr "Erstelle Oberfläche" +#: portato/gui/windows/main.py:669 +#: portato/gui/views.py:115 +#, python-format +msgid "Error: %s" +msgstr "Fehler: %s" + #: portato/gui/windows/main.py:705 msgid "Restoring Session" msgstr "Lade Session" @@ -855,37 +698,174 @@ msgstr "Upgrade Session von Version %d auf Version %d." msgid "Cannot translate session from version %d to %d." msgstr "Kann Session nicht von Version %d nach %d upgraden." -#: portato/gui/windows/main.py:1361 +#: portato/gui/windows/main.py:1359 msgid "use flags" msgstr "Use Flags" -#: portato/gui/windows/main.py:1373 +#: portato/gui/windows/main.py:1371 msgid "masking keywords" msgstr "Masking Keywords" -#: portato/gui/windows/plugin.py:24 -msgid "Disabled" -msgstr "Deaktiviert" +#: portato/gui/windows/preference.py:124 +msgid "Top" +msgstr "Oben" -#: portato/gui/windows/plugin.py:24 -msgid "Temporarily enabled" -msgstr "Aktiviert (temporär)" +#: portato/gui/windows/preference.py:124 +msgid "Bottom" +msgstr "Unten" -#: portato/gui/windows/plugin.py:24 -msgid "Temporarily disabled" -msgstr "Deaktiviert (temporär)" +#: portato/gui/windows/preference.py:124 +msgid "Left" +msgstr "Links" + +#: portato/gui/windows/preference.py:124 +msgid "Right" +msgstr "Rechts" + +#: portato/gui/windows/search.py:57 +msgid "Results" +msgstr "Ergebnisse" + +#: portato/gui/windows/splash.py:35 +#, python-format +msgid "... is starting up: %s" +msgstr "... startet: %s" + +#: portato/gui/windows/update.py:48 +msgid "Package" +msgstr "Paket" -#: portato/gui/windows/plugin.py:43 -msgid "Plugin" -msgstr "Plugin" +#: portato/gui/windows/mailinfo.py:71 +#, python-format +msgid "An error occurred while sending. I think we where greylisted. The error: %s" +msgstr "Während des Sendes trat ein Fehler auf. Wahrscheinlich wurden wir ge-greylistet. Der Fehler: %s" + +#: portato/gui/windows/mailinfo.py:72 +msgid "Wait 60 seconds and try again." +msgstr "Warte 60 Sekunden und versuche es dann noch einmal." + +#: portato/gui/__init__.py:21 +msgid "Loading Backend" +msgstr "Lade Backend" + +#: portato/gui/dialogs.py:17 +msgid "Mail could not be sent" +msgstr "Mail konnte nicht gesendet werden: %s" + +#: portato/gui/dialogs.py:18 +#, python-format +msgid "The error was: %s" +msgstr "Der Fehler war: %s" + +#: portato/gui/dialogs.py:24 +msgid "Do you really want to quit?" +msgstr "Wirklich beenden?" + +#: portato/gui/dialogs.py:25 +msgid "There are some packages in the emerge queue and/or an emerge process is running." +msgstr "Es sind noch Pakete in der Emerge-Queue und/oder emerge läuft noch." + +#: portato/gui/dialogs.py:43 +#, python-format +msgid "%(blocked)s is blocked by %(blocks)s." +msgstr "%(blocks)s blockiert %(blocked)s." + +#: portato/gui/dialogs.py:44 +msgid "Please unmerge the blocking package." +msgstr "Bitte deinstalliere das blockierende Paket." + +#: portato/gui/dialogs.py:50 +msgid "You are not root." +msgstr "Du bist nicht root." + +#: portato/gui/dialogs.py:56 +#, python-format +msgid "%s seems to be masked." +msgstr "" + +#: portato/gui/dialogs.py:57 +msgid "Do you want to unmask it and its dependencies?" +msgstr "Soll das Paket und seine Abhängigkeiten demaskiert werden?" + +#: portato/gui/dialogs.py:63 +msgid "Package not found!" +msgstr "Paket nicht gefunden!" -#: portato/gui/windows/plugin.py:46 -msgid "Authors" -msgstr "Entwickler" +#: portato/gui/dialogs.py:69 +msgid "Do not show this dialog again." +msgstr "Diesen Dialog nicht wieder anzeigen." + +#: portato/gui/dialogs.py:70 +#, python-format +msgid "Changed %s" +msgstr "%s wurde geändert" + +#: portato/gui/dialogs.py:71 +msgid "" +"Portato will write these changes into the appropriate files.\n" +"Please backup them if you think it is necessairy." +msgstr "" +"Portato wird diese Änderungen speichern.\n" +"Bitte sichere die entsprechenden Dateien, wenn du es als notwendig erachtest." + +#: portato/gui/dialogs.py:80 +msgid "You cannot remove dependencies. :)" +msgstr "Du kannst keine Abhängigkeiten löschen ;)." + +#: portato/gui/dialogs.py:86 +msgid "" +"This is the updates queue. You cannot remove single elements.\n" +"Do you want to clear the whole queue instead?" +msgstr "" +"Das ist die Update-Queue. Aus dieser können keine einzelnen Pakete entfernt werden.\n" +"Soll stattdessen die komplette Queue entfernt werden?" + +#: portato/gui/dialogs.py:92 +msgid "Do you really want to clear the whole queue?" +msgstr "Wirklich die gesamte Queue löschen?" + +#: portato/gui/exception_handling.py:31 +msgid "A programming error has been detected during the execution of this program." +msgstr "Ein Fehler ist aufgetreten." + +#: portato/gui/exception_handling.py:32 +msgid "Bug Detected" +msgstr "Fehler aufgetreten" + +#: portato/gui/exception_handling.py:33 +msgid "It probably isn't fatal, but should be reported to the developers nonetheless." +msgstr "Dies ist möglicherweise nicht kritisch, sollte aber trotzdem an die Entwickler weitergeleitet werden." + +#: portato/gui/exception_handling.py:35 +msgid "Show Details" +msgstr "Details" + +#: portato/gui/exception_handling.py:36 +msgid "Send..." +msgstr "Sende..." + +#: portato/gui/exception_handling.py:59 +#: portato/gui/exception_handling.py:125 +#, python-format +msgid "" +"Exception in thread \"%(thread)s\":\n" +"%(trace)s" +msgstr "" +"Exception im Thread \"%(thread)s\":\n" +"%(trace)s" + +#: portato/gui/exception_handling.py:75 +msgid "Save traceback..." +msgstr "Speichere Traceback..." -#: portato/gui/windows/plugin.py:55 -msgid "Status" -msgstr "Status" +#: portato/gui/exception_handling.py:127 +#, python-format +msgid "" +"Exception:\n" +"%s" +msgstr "" +"Exception:\n" +"%s" #: portato/gui/session.py:21 msgid "Version mismatch." @@ -904,6 +884,16 @@ msgstr "Das aktuelle Sessionformat ist zu alt." msgid "Current session format is newer than this version supports." msgstr "Das aktuelle Sessionformat ist neuer, als diese Portato-Version unterstützt." +#: portato/gui/updater.py:96 +#, python-format +msgid "No unmasked version of package '%s' found. Trying masked ones. This normally should not happen..." +msgstr "Keine demaskierte Version vom Paket '%s' gefunden. Versuche die maskierten. Eigentlich sollte das nicht passieren..." + +#: portato/gui/updater.py:100 +#, python-format +msgid "Trying to remove package '%s' from queue which does not exist in system." +msgstr "Das Paket '%s' sollte aus der Queue entfernt werden, aber es befindet sich nicht im System." + #: portato/gui/utils.py:154 msgid "ALL" msgstr "ALLE" @@ -918,115 +908,116 @@ msgstr "Catched KeyError => %s scheint keine valide Kategorie zu sein. Hast du m msgid "Error while compiling search expression: '%s'." msgstr "Fehler beim erstellen des Suchausdrucks: '%s'." -#: portato/backend/flags.py:527 -#, python-format -msgid "Conflicting values for masking status: %s" -msgstr "Konflikt beim Masking-Status: %s" +#: portato/gui/views.py:70 +msgid "Package is not installed" +msgstr "Paket ist nicht installiert" -#: portato/backend/portage/system.py:330 +#: portato/gui/views.py:87 #, python-format -msgid "No best match for %s. It seems not to be in the tree anymore." -msgstr "Es konnte kein bester Treffer für das Paket %s gefunden werden. Es scheint sich nicht mehr im Tree zu befinden." +msgid "No %(old)s language file installed. Falling back to %(new)s." +msgstr "Keine \"%(old)s\" Syntaxdatei gefunden. Benutze \"%(new)s\"-Syntax." -#: portato/backend/portage/system.py:398 +#: portato/gui/views.py:96 #, python-format -msgid "Found a not installed dependency: %s." -msgstr "Nicht installierte Abhängigkeit gefunden: %s" +msgid "No %(old)s language file installed. Disable highlighting." +msgstr "Keine \"%(old)s\" Syntaxdatei gefunden. Deaktiviere Hervorhebung." -#: portato/backend/portage/system.py:440 -#, python-format -msgid "Bug? No best match could be found for '%(package)s'. Needed by: '%(cpv)s'." -msgstr "Es konnte kein bester Treffer für das Paket '%(package)s' ermittelt werden (gebraucht von '%(cpv)s'). Bug?" +#: portato/gui/wrapper.py:59 +msgid "oneshot" +msgstr "oneshot" -#: portato/backend/portage/package.py:125 -#: portato/backend/catapult/package.py:98 +#: portato/gui/wrapper.py:64 #, python-format -msgid "BUG in flags.new_masking_status. It returns '%s'" -msgstr "BUG in flags.new_masking_status. Es gibt '%s' zurück." +msgid "updating from version %s" +msgstr "Update von Version %s" -#: portato/helper.py:76 -msgid "Invalid dependency string" -msgstr "Ungültiger Dependency-String." +#: portato/gui/wrapper.py:66 +msgid "updating" +msgstr "Update" -#: portato/session.py:47 +#: portato/gui/wrapper.py:71 #, python-format -msgid "Loading session from '%s'." -msgstr "Lade Session von '%s'." +msgid "downgrading from version %s" +msgstr "Downgrade von Version %s" -#: portato/plistener.py:88 -msgid "Listener has not been started." -msgstr "Listener wurde nicht gestartet." +#: portato/gui/wrapper.py:73 +msgid "downgrading" +msgstr "Downgrade" -#: portato/plugins/etc_proposals.py:24 -msgid "Cannot start etc-proposals. Not root!" -msgstr "Kann etc-proposals nicht starten. Nur root kann das!" +#: portato/gui/wrapper.py:77 +msgid "IUSE changes:" +msgstr "IUSE Änderungen:" -#: portato/plugins/notify.py:10 -msgid "Notify called while process is still running!" -msgstr "\"Notify\" aufgerufen, während Emerge noch lief." +#: portato/gui/wrapper.py:95 +msgid "(In Progress)" +msgstr "(In Bearbeitung)" -#: portato/plugins/notify.py:14 -msgid "Emerge finished!" -msgstr "Emerge-Prozess erfolgreich beendet!" +#: portato/gui/wrapper.py:120 +msgid "Install" +msgstr "Installieren" -#: portato/plugins/notify.py:18 -msgid "Emerge failed!" -msgstr "Emerge fehlgeschlagen!" +#: portato/gui/wrapper.py:131 +msgid "Uninstall" +msgstr "Deinstallieren" -#: portato/plugins/notify.py:19 -#, python-format -msgid "Error Code: %d" -msgstr "Fehler-Code: %d" +#: portato/gui/wrapper.py:143 +msgid "Update" +msgstr "Update" #: portato/config_parser.py:270 #, python-format msgid "Unrecognized line in configuration: %s" msgstr "Unbekannte Zeile in Konfiguration: %s" -#: portato/plugin.py:298 -#: portato/plugin.py:304 -#: portato/plugin.py:309 -#, python-format -msgid "%s cannot be imported." -msgstr "%s kann nicht importiert werden." - -#: portato/plugin.py:336 -#, python-format -msgid "Accessing hook '%(hook)s' of plugin '%(plugin)s' (before)." -msgstr "Aufruf des Hooks '%(hook)s' vom Plugin '%(plugin)s'. (before)" +#: portato/helper.py:76 +msgid "Invalid dependency string" +msgstr "Ungültiger Dependency-String." -#: portato/plugin.py:340 -#, python-format -msgid "Overriding hook '%(hook)s' with plugin '%(plugin)s'." -msgstr "Überschreibe den Hook '%(hook)s' mit Plugin '%(plugin)s'." +#: portato/plistener.py:88 +msgid "Listener has not been started." +msgstr "Listener wurde nicht gestartet." -#: portato/plugin.py:347 +#: portato/plugin.py:330 +#: portato/plugin.py:333 #, python-format -msgid "Accessing hook '%(hook)s' of plugin '%(plugin)s' (after)." -msgstr "Aufruf des Hooks '%(hook)s' vom Plugin '%(plugin)s'. (after)" +msgid "Loading plugin '%(plugin)s' failed: %(error)s" +msgstr "Laden des Plugins '%(plugin)s' fehlgeschlagen: %(error)s" -#: portato/plugin.py:367 -#, python-format -msgid "Loading plugin '%s' failed. Invalid XML syntax." -msgstr "Laden des Plugins '%s\" fehlgeschlagen: XML-Syntax-Fehler." +#: portato/plugin.py:366 +msgid "Plugin is disabled!" +msgstr "Plugin ist deaktiviert!" -#: portato/plugin.py:370 -#, python-format -msgid "Loading plugin '%s' failed. Plugin does not comply with schema." -msgstr "Laden des Plugins '%s\" fehlgeschlagen. Plugin erfüllt nicht das XML-Schema." +#: portato/plugin.py:368 +msgid "Plugin has unresolved dependencies - disabled!" +msgstr "Das Plugin hat nicht erfüllte Abhängigkeiten - deaktiviert!" -#: portato/plugin.py:398 +#: portato/plugin.py:372 #, python-format msgid "Plugin '%s' loaded." msgstr "Plugin '%s' geladen." -#: portato/plugin.py:401 +#: portato/plugin.py:412 #, python-format -msgid "Loading plugin '%(plugin)s' failed: Could not import %(import)s" -msgstr "Laden des Plugins '%(plugin)s\" fehlgeschlagen. Import von '%(import)s' nicht möglich." +msgid "Overriding hook '%(hook)s' with plugin '%(plugin)s'." +msgstr "Überschreibe den Hook '%(hook)s' mit Plugin '%(plugin)s'." -#: portato/plugin.py:466 +#: portato/plugin.py:470 #, python-format msgid "For hook '%(hook)s' an override is already defined by plugin '%(plugin)s'!" msgstr "Das Plugin '%(plugin)s' definiert bereits einen \"Override\" fürr den Hook '%(hook)s'!" +#: portato/plugin.py:471 +#, python-format +msgid "It is now replaced by the one from plugin '%s'!" +msgstr "Es wird ersetzt durch die Variante vom Plugin '%s'!" + +#: portato/plugin.py:504 +#, python-format +msgid "Command for hook '%(hook)s' in plugin '%(plugin)s' could not be added due to missing dependant: '%(dep)s'!" +msgstr "Der Aufruf für Hook '%(hook)s' im Plugin '%(plugin)s' kann nicht hinzugefügt werden, da eine Abhängigkeit fehlt: '%(dep)s'!" + +#: portato/session.py:47 +#, python-format +msgid "Loading session from '%s'." +msgstr "Lade Session von '%s'." + diff --git a/i18n/messages.pot b/i18n/messages.pot index e414438..d4de7fc 100644 --- a/i18n/messages.pot +++ b/i18n/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2008-06-23 23:43+0200\n" +"POT-Creation-Date: 2008-07-08 16:39+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -16,39 +16,6 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: portato/gui/templates/popups.glade:12 -#: portato/gui/templates/MainWindow.glade:192 -#: portato/gui/templates/MainWindow.glade:284 -msgid "Emerge _Paused" -msgstr "" - -#: portato/gui/templates/popups.glade:19 -#: portato/gui/templates/MainWindow.glade:200 -#: portato/gui/templates/MainWindow.glade:266 -msgid "_Kill Emerge" -msgstr "" - -#: portato/gui/templates/popups.glade:37 -#: portato/gui/templates/MainWindow.glade:70 -msgid "gtk-quit" -msgstr "" - -#: portato/gui/templates/UpdateWindow.glade:7 -msgid "Updatable Packages" -msgstr "" - -#: portato/gui/templates/UpdateWindow.glade:49 -msgid "_Close" -msgstr "" - -#: portato/gui/templates/UpdateWindow.glade:61 -msgid "Select _All" -msgstr "" - -#: portato/gui/templates/UpdateWindow.glade:76 -msgid "_Install Selected" -msgstr "" - #: portato/gui/templates/AboutWindow.glade:8 msgid "About Portato" msgstr "" @@ -59,226 +26,6 @@ msgid "" "Copyright (C) 2006-2008 René 'Necoro' Neumann <necoro@necoro.net>" msgstr "" -#: portato/gui/templates/PreferenceWindow.glade:7 -msgid "Preferences" -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:42 -msgid "Debug" -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:55 -msgid "Browser command: " -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:81 -#: portato/gui/templates/PreferenceWindow.glade:539 -msgid "<b>General Options</b>" -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:131 -msgid "<b>Update World Options</b>" -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:159 -msgid "Sync command: " -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:180 -msgid "<b>Sync Options</b>" -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:231 -msgid "File name to use, if package.use is a directory: " -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:242 -msgid "Add only exact version to package.use" -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:255 -msgid "Add only exact version to package.keywords" -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:269 -msgid "File name to use, if package.keywords is a directory: " -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:303 -msgid "File name to use, if package.mask/package.unmask is a directory: " -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:314 -msgid "Add only exact version to package.mask/package.unmask" -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:336 -msgid "" -"<u>You may use the following placeholders:</u>\n" -"\n" -"<i>$(cat)</i>: category\n" -"<i>$(pkg)</i>: package name\n" -"<i>$(cat-1)/$(cat-2)</i>: first/second part of the category" -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:362 -msgid "<u><i>Use-Flags</i></u>" -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:377 -msgid "<u><i>Testing Keywords</i></u>" -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:392 -msgid "<u><i>Masking Keywords</i></u>" -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:409 -msgid "<b>Use Flag and Keyword Options</b>" -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:427 -#: portato/gui/templates/MainWindow.glade:895 -msgid "General" -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:464 -msgid "Enable systray" -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:477 -msgid "Show emerge progress in window title" -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:491 -msgid "Show emerge progress in console title" -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:505 -msgid "Hide on minimization (only if systray is enabled)" -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:520 -msgid "" -"Update the package list with the current search results while you are " -"typing.\n" -"<b>Note</b>: Will slow down the typing process." -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:522 -msgid "Search while typing" -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:576 -msgid "Console Font" -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:587 -msgid "Chose a console font" -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:605 -msgid "Maximum length of the console title" -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:635 -msgid "<b>Console Options</b>" -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:675 -msgid "Package Tabs" -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:700 -msgid "System Tabs" -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:726 -msgid "<b>Tab Options</b>" -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:759 -msgid "Show slots in the version list" -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:769 -msgid "" -"Organize the categories in a tree. Thereby collapse categories with the same " -"prefix:\n" -"As an example: <i>app-admin</i>, <i>app-emacs</i>, and <i>app-vim</i> would " -"be collapsed into <i><b>app</b></i> as root and <i>admin</i>, <i>emacs</i>, " -"and <i>vim</i> as children." -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:771 -msgid "Collapse categories with same prefix" -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:787 -msgid "<b>Package Options</b>" -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:811 -msgid "Visual" -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:829 -#: portato/gui/templates/PluginWindow.glade:47 -#: portato/gui/templates/SearchWindow.glade:48 -#: portato/gui/templates/MailInfoWindow.glade:148 -msgid "gtk-cancel" -msgstr "" - -#: portato/gui/templates/PreferenceWindow.glade:838 -#: portato/gui/templates/PluginWindow.glade:59 -msgid "gtk-apply" -msgstr "" - -#: portato/gui/templates/PluginWindow.glade:8 -msgid "Plugins" -msgstr "" - -#: portato/gui/templates/SearchWindow.glade:8 -msgid "Search Results" -msgstr "" - -#: portato/gui/templates/SearchWindow.glade:60 -msgid "gtk-jump-to" -msgstr "" - -#: portato/gui/templates/SearchWindow.glade:75 -msgid "gtk-ok" -msgstr "" - -#: portato/gui/templates/MailInfoWindow.glade:6 -msgid "Send Bug Mail ..." -msgstr "" - -#: portato/gui/templates/MailInfoWindow.glade:50 -msgid "" -"Comments /\n" -"what did you do to hit the bug?" -msgstr "" - -#: portato/gui/templates/MailInfoWindow.glade:64 -msgid "Name:" -msgstr "" - -#: portato/gui/templates/MailInfoWindow.glade:79 -msgid "Email address:" -msgstr "" - -#: portato/gui/templates/MailInfoWindow.glade:122 -msgid "" -"<b><u>Additional Information</u></b>\n" -"\n" -"(all optional)" -msgstr "" - -#: portato/gui/templates/MailInfoWindow.glade:176 -msgid "_Send" -msgstr "" - #: portato/gui/templates/MainWindow.glade:20 msgid "_File" msgstr "" @@ -291,6 +38,11 @@ msgstr "" msgid "Re_load Portage" msgstr "" +#: portato/gui/templates/MainWindow.glade:70 +#: portato/gui/templates/popups.glade:37 +msgid "gtk-quit" +msgstr "" + #: portato/gui/templates/MainWindow.glade:83 msgid "_Emerge" msgstr "" @@ -324,6 +76,18 @@ msgstr "" msgid "Save _Flags" msgstr "" +#: portato/gui/templates/MainWindow.glade:192 +#: portato/gui/templates/MainWindow.glade:284 +#: portato/gui/templates/popups.glade:12 +msgid "Emerge _Paused" +msgstr "" + +#: portato/gui/templates/MainWindow.glade:200 +#: portato/gui/templates/MainWindow.glade:266 +#: portato/gui/templates/popups.glade:19 +msgid "_Kill Emerge" +msgstr "" + #: portato/gui/templates/MainWindow.glade:223 #: portato/gui/templates/MainWindow.glade:1170 portato/gui/windows/main.py:763 msgid "Queue" @@ -335,7 +99,7 @@ msgstr "" #: portato/gui/templates/MainWindow.glade:242 #: portato/gui/templates/MainWindow.glade:1195 -#: portato/gui/windows/main.py:1208 portato/gui/windows/main.py:1210 +#: portato/gui/windows/main.py:1206 portato/gui/windows/main.py:1208 msgid "Console" msgstr "" @@ -371,6 +135,8 @@ msgstr "" #: portato/gui/templates/MainWindow.glade:623 #: portato/gui/templates/MainWindow.glade:736 #: portato/gui/templates/MainWindow.glade:751 +#: portato/gui/templates/PluginWindow.glade:98 +#: portato/gui/templates/PluginWindow.glade:123 msgid "label" msgstr "" @@ -427,6 +193,11 @@ msgstr "" msgid "<b>Use Flags:</b>" msgstr "" +#: portato/gui/templates/MainWindow.glade:895 +#: portato/gui/templates/PreferenceWindow.glade:427 +msgid "General" +msgstr "" + #: portato/gui/templates/MainWindow.glade:926 msgid "Use List" msgstr "" @@ -472,251 +243,327 @@ msgstr "" msgid "Log" msgstr "" -#: portato.py:40 -msgid "validates the given plugin xml instead of launching Portato" +#: portato/gui/templates/PluginWindow.glade:8 +msgid "Plugins" msgstr "" -#: portato.py:43 -msgid "do not fork off as root" +#: portato/gui/templates/PluginWindow.glade:52 +msgid "_Install dependencies" msgstr "" -#: portato.py:43 -msgid "-L is deprecated" +#: portato/gui/templates/PluginWindow.glade:81 +msgid "Needed dependencies" msgstr "" -#: portato.py:53 -#, python-format -msgid "Validation failed. XML syntax error: %s." +#: portato/gui/templates/PluginWindow.glade:109 +msgid "<b>Author:</b>" msgstr "" -#: portato.py:56 -msgid "Validation failed. Does not comply with schema." +#: portato/gui/templates/PluginWindow.glade:144 +#: portato/gui/windows/main.py:325 portato/gui/windows/plugin.py:27 +#: portato/gui/windows/update.py:47 +msgid "Enabled" msgstr "" -#: portato.py:59 -msgid "Validation succeeded." +#: portato/gui/templates/PluginWindow.glade:154 +#: portato/gui/windows/plugin.py:27 +msgid "Temporarily enabled" msgstr "" -#: portato.py:65 -msgid "Starting Portato" +#: portato/gui/templates/PluginWindow.glade:166 +#: portato/gui/windows/plugin.py:27 +msgid "Temporarily disabled" msgstr "" -#: portato/gui/wrapper.py:59 -msgid "oneshot" +#: portato/gui/templates/PluginWindow.glade:179 +#: portato/gui/windows/plugin.py:27 +msgid "Disabled" msgstr "" -#: portato/gui/wrapper.py:64 -#, python-format -msgid "updating from version %s" +#: portato/gui/templates/PluginWindow.glade:223 +#: portato/gui/templates/SearchWindow.glade:48 +#: portato/gui/templates/MailInfoWindow.glade:148 +#: portato/gui/templates/PreferenceWindow.glade:829 +msgid "gtk-cancel" msgstr "" -#: portato/gui/wrapper.py:66 -msgid "updating" +#: portato/gui/templates/PluginWindow.glade:235 +#: portato/gui/templates/PreferenceWindow.glade:838 +msgid "gtk-apply" msgstr "" -#: portato/gui/wrapper.py:71 -#, python-format -msgid "downgrading from version %s" +#: portato/gui/templates/UpdateWindow.glade:7 +msgid "Updatable Packages" msgstr "" -#: portato/gui/wrapper.py:73 -msgid "downgrading" +#: portato/gui/templates/UpdateWindow.glade:49 +msgid "_Close" msgstr "" -#: portato/gui/wrapper.py:77 -msgid "IUSE changes:" +#: portato/gui/templates/UpdateWindow.glade:61 +msgid "Select _All" msgstr "" -#: portato/gui/wrapper.py:95 -msgid "(In Progress)" +#: portato/gui/templates/UpdateWindow.glade:76 +msgid "_Install Selected" msgstr "" -#: portato/gui/wrapper.py:120 -msgid "Install" +#: portato/gui/templates/SearchWindow.glade:8 +msgid "Search Results" msgstr "" -#: portato/gui/wrapper.py:131 -msgid "Uninstall" +#: portato/gui/templates/SearchWindow.glade:60 +msgid "gtk-jump-to" msgstr "" -#: portato/gui/wrapper.py:143 -msgid "Update" +#: portato/gui/templates/SearchWindow.glade:75 +msgid "gtk-ok" msgstr "" -#: portato/gui/dialogs.py:17 -msgid "" -"There are some packages in the emerge queue and/or an emerge process is " -"running.\n" -"Do you really want to quit?" +#: portato/gui/templates/MailInfoWindow.glade:6 +msgid "Send Bug Mail ..." msgstr "" -#: portato/gui/dialogs.py:36 -#, python-format +#: portato/gui/templates/MailInfoWindow.glade:50 msgid "" -"%(blocked)s is blocked by %(blocks)s.\n" -"Please unmerge the blocking package." +"Comments /\n" +"what did you do to hit the bug?" msgstr "" -#: portato/gui/dialogs.py:42 -msgid "You are not root." +#: portato/gui/templates/MailInfoWindow.glade:64 +msgid "Name:" msgstr "" -#: portato/gui/dialogs.py:48 -#, python-format +#: portato/gui/templates/MailInfoWindow.glade:79 +msgid "Email address:" +msgstr "" + +#: portato/gui/templates/MailInfoWindow.glade:122 msgid "" -"%s seems to be masked.\n" -"Do you want to unmask it and its dependencies?" +"<b><u>Additional Information</u></b>\n" +"\n" +"(all optional)" msgstr "" -#: portato/gui/dialogs.py:54 -msgid "Package not found!" +#: portato/gui/templates/MailInfoWindow.glade:176 +msgid "_Send" msgstr "" -#: portato/gui/dialogs.py:60 -msgid "Do not show this dialog again." +#: portato/gui/templates/PreferenceWindow.glade:7 +msgid "Preferences" msgstr "" -#: portato/gui/dialogs.py:62 -#, python-format -msgid "" -"You have changed %s.\n" -"Portato will write these changes into the appropriate files.\n" -"Please backup them if you think it is necessairy." +#: portato/gui/templates/PreferenceWindow.glade:42 +msgid "Debug" msgstr "" -#: portato/gui/dialogs.py:71 -msgid "You cannot remove dependencies. :)" +#: portato/gui/templates/PreferenceWindow.glade:55 +msgid "Browser command: " msgstr "" -#: portato/gui/dialogs.py:77 -msgid "" -"This is the updates queue. You cannot remove single elements.\n" -"Do you want to clear the whole queue instead?" +#: portato/gui/templates/PreferenceWindow.glade:81 +#: portato/gui/templates/PreferenceWindow.glade:539 +msgid "<b>General Options</b>" msgstr "" -#: portato/gui/dialogs.py:83 -msgid "Do you really want to clear the whole queue?" +#: portato/gui/templates/PreferenceWindow.glade:131 +msgid "<b>Update World Options</b>" msgstr "" -#: portato/gui/views.py:70 -msgid "Package is not installed" +#: portato/gui/templates/PreferenceWindow.glade:159 +msgid "Sync command: " msgstr "" -#: portato/gui/views.py:87 -#, python-format -msgid "No %(old)s language file installed. Falling back to %(new)s." +#: portato/gui/templates/PreferenceWindow.glade:180 +msgid "<b>Sync Options</b>" msgstr "" -#: portato/gui/views.py:96 -#, python-format -msgid "No %(old)s language file installed. Disable highlighting." +#: portato/gui/templates/PreferenceWindow.glade:231 +msgid "File name to use, if package.use is a directory: " msgstr "" -#: portato/gui/views.py:115 portato/gui/windows/main.py:669 -#, python-format -msgid "Error: %s" +#: portato/gui/templates/PreferenceWindow.glade:242 +msgid "Add only exact version to package.use" msgstr "" -#: portato/gui/updater.py:96 -#, python-format -msgid "" -"No unmasked version of package '%s' found. Trying masked ones. This normally " -"should not happen..." +#: portato/gui/templates/PreferenceWindow.glade:255 +msgid "Add only exact version to package.keywords" msgstr "" -#: portato/gui/updater.py:100 -#, python-format -msgid "" -"Trying to remove package '%s' from queue which does not exist in system." +#: portato/gui/templates/PreferenceWindow.glade:269 +msgid "File name to use, if package.keywords is a directory: " msgstr "" -#: portato/gui/exception_handling.py:31 -msgid "" -"A programming error has been detected during the execution of this program." +#: portato/gui/templates/PreferenceWindow.glade:303 +msgid "File name to use, if package.mask/package.unmask is a directory: " msgstr "" -#: portato/gui/exception_handling.py:32 -msgid "Bug Detected" +#: portato/gui/templates/PreferenceWindow.glade:314 +msgid "Add only exact version to package.mask/package.unmask" msgstr "" -#: portato/gui/exception_handling.py:33 +#: portato/gui/templates/PreferenceWindow.glade:336 msgid "" -"It probably isn't fatal, but should be reported to the developers " -"nonetheless." +"<u>You may use the following placeholders:</u>\n" +"\n" +"<i>$(cat)</i>: category\n" +"<i>$(pkg)</i>: package name\n" +"<i>$(cat-1)/$(cat-2)</i>: first/second part of the category" msgstr "" -#: portato/gui/exception_handling.py:35 -msgid "Show Details" +#: portato/gui/templates/PreferenceWindow.glade:362 +msgid "<u><i>Use-Flags</i></u>" msgstr "" -#: portato/gui/exception_handling.py:36 -msgid "Send..." +#: portato/gui/templates/PreferenceWindow.glade:377 +msgid "<u><i>Testing Keywords</i></u>" msgstr "" -#: portato/gui/exception_handling.py:59 portato/gui/exception_handling.py:128 -#, python-format -msgid "" -"Exception in thread \"%(thread)s\":\n" -"%(trace)s" +#: portato/gui/templates/PreferenceWindow.glade:392 +msgid "<u><i>Masking Keywords</i></u>" msgstr "" -#: portato/gui/exception_handling.py:75 -msgid "Save traceback..." +#: portato/gui/templates/PreferenceWindow.glade:409 +msgid "<b>Use Flag and Keyword Options</b>" msgstr "" -#: portato/gui/exception_handling.py:130 -#, python-format +#: portato/gui/templates/PreferenceWindow.glade:464 +msgid "Enable systray" +msgstr "" + +#: portato/gui/templates/PreferenceWindow.glade:477 +msgid "Show emerge progress in window title" +msgstr "" + +#: portato/gui/templates/PreferenceWindow.glade:491 +msgid "Show emerge progress in console title" +msgstr "" + +#: portato/gui/templates/PreferenceWindow.glade:505 +msgid "Hide on minimization (only if systray is enabled)" +msgstr "" + +#: portato/gui/templates/PreferenceWindow.glade:520 msgid "" -"Exception:\n" -"%s" +"Update the package list with the current search results while you are " +"typing.\n" +"<b>Note</b>: Will slow down the typing process." msgstr "" -#: portato/gui/__init__.py:21 -msgid "Loading Backend" +#: portato/gui/templates/PreferenceWindow.glade:522 +msgid "Search while typing" msgstr "" -#: portato/gui/windows/search.py:57 -msgid "Results" +#: portato/gui/templates/PreferenceWindow.glade:576 +msgid "Console Font" msgstr "" -#: portato/gui/windows/mailinfo.py:69 -#, python-format +#: portato/gui/templates/PreferenceWindow.glade:587 +msgid "Chose a console font" +msgstr "" + +#: portato/gui/templates/PreferenceWindow.glade:605 +msgid "Maximum length of the console title" +msgstr "" + +#: portato/gui/templates/PreferenceWindow.glade:635 +msgid "<b>Console Options</b>" +msgstr "" + +#: portato/gui/templates/PreferenceWindow.glade:675 +msgid "Package Tabs" +msgstr "" + +#: portato/gui/templates/PreferenceWindow.glade:700 +msgid "System Tabs" +msgstr "" + +#: portato/gui/templates/PreferenceWindow.glade:726 +msgid "<b>Tab Options</b>" +msgstr "" + +#: portato/gui/templates/PreferenceWindow.glade:759 +msgid "Show slots in the version list" +msgstr "" + +#: portato/gui/templates/PreferenceWindow.glade:769 msgid "" -"An error occurred while sending. I think we where greylisted. The error: %s" +"Organize the categories in a tree. Thereby collapse categories with the same " +"prefix:\n" +"As an example: <i>app-admin</i>, <i>app-emacs</i>, and <i>app-vim</i> would " +"be collapsed into <i><b>app</b></i> as root and <i>admin</i>, <i>emacs</i>, " +"and <i>vim</i> as children." msgstr "" -#: portato/gui/windows/mailinfo.py:70 -msgid "Wait 60 seconds and try again." +#: portato/gui/templates/PreferenceWindow.glade:771 +msgid "Collapse categories with same prefix" msgstr "" -#: portato/gui/windows/update.py:47 portato/gui/windows/main.py:325 -#: portato/gui/windows/plugin.py:24 -msgid "Enabled" +#: portato/gui/templates/PreferenceWindow.glade:787 +msgid "<b>Package Options</b>" msgstr "" -#: portato/gui/windows/update.py:48 -msgid "Package" +#: portato/gui/templates/PreferenceWindow.glade:811 +msgid "Visual" msgstr "" -#: portato/gui/windows/splash.py:35 +#: portato.py:40 +msgid "do not fork off as root" +msgstr "" + +#: portato.py:40 +msgid "-L is deprecated" +msgstr "" + +#: portato.py:48 +msgid "Starting Portato" +msgstr "" + +#: plugins/etc_proposals.py:32 +msgid "Cannot start etc-proposals. Not root!" +msgstr "" + +#: plugins/notify.py:35 +msgid "Notify called while process is still running!" +msgstr "" + +#: plugins/notify.py:39 +msgid "Emerge finished!" +msgstr "" + +#: plugins/notify.py:43 +msgid "Emerge failed!" +msgstr "" + +#: plugins/notify.py:44 #, python-format -msgid "... is starting up: %s" +msgid "Error Code: %d" msgstr "" -#: portato/gui/windows/preference.py:124 -msgid "Top" +#: portato/backend/portage/package.py:130 +#, python-format +msgid "BUG in flags.new_masking_status. It returns '%s'" msgstr "" -#: portato/gui/windows/preference.py:124 -msgid "Bottom" +#: portato/backend/portage/system.py:329 +#, python-format +msgid "No best match for %s. It seems not to be in the tree anymore." msgstr "" -#: portato/gui/windows/preference.py:124 -msgid "Left" +#: portato/backend/portage/system.py:397 +#, python-format +msgid "Found a not installed dependency: %s." msgstr "" -#: portato/gui/windows/preference.py:124 -msgid "Right" +#: portato/backend/portage/system.py:439 +#, python-format +msgid "" +"Bug? No best match could be found for '%(package)s'. Needed by: '%(cpv)s'." +msgstr "" + +#: portato/backend/flags.py:527 +#, python-format +msgid "Conflicting values for masking status: %s" msgstr "" #: portato/gui/windows/main.py:154 @@ -792,6 +639,11 @@ msgstr "" msgid "Building frontend" msgstr "" +#: portato/gui/windows/main.py:669 portato/gui/views.py:115 +#, python-format +msgid "Error: %s" +msgstr "" + #: portato/gui/windows/main.py:705 msgid "Restoring Session" msgstr "" @@ -830,36 +682,170 @@ msgstr "" msgid "Cannot translate session from version %d to %d." msgstr "" -#: portato/gui/windows/main.py:1361 +#: portato/gui/windows/main.py:1359 msgid "use flags" msgstr "" -#: portato/gui/windows/main.py:1373 +#: portato/gui/windows/main.py:1371 msgid "masking keywords" msgstr "" -#: portato/gui/windows/plugin.py:24 -msgid "Disabled" +#: portato/gui/windows/preference.py:124 +msgid "Top" msgstr "" -#: portato/gui/windows/plugin.py:24 -msgid "Temporarily enabled" +#: portato/gui/windows/preference.py:124 +msgid "Bottom" msgstr "" -#: portato/gui/windows/plugin.py:24 -msgid "Temporarily disabled" +#: portato/gui/windows/preference.py:124 +msgid "Left" msgstr "" -#: portato/gui/windows/plugin.py:43 -msgid "Plugin" +#: portato/gui/windows/preference.py:124 +msgid "Right" msgstr "" -#: portato/gui/windows/plugin.py:46 -msgid "Authors" +#: portato/gui/windows/search.py:57 +msgid "Results" +msgstr "" + +#: portato/gui/windows/splash.py:35 +#, python-format +msgid "... is starting up: %s" +msgstr "" + +#: portato/gui/windows/update.py:48 +msgid "Package" +msgstr "" + +#: portato/gui/windows/mailinfo.py:71 +#, python-format +msgid "" +"An error occurred while sending. I think we where greylisted. The error: %s" +msgstr "" + +#: portato/gui/windows/mailinfo.py:72 +msgid "Wait 60 seconds and try again." +msgstr "" + +#: portato/gui/__init__.py:21 +msgid "Loading Backend" +msgstr "" + +#: portato/gui/dialogs.py:17 +msgid "Mail could not be sent" +msgstr "" + +#: portato/gui/dialogs.py:18 +#, python-format +msgid "The error was: %s" +msgstr "" + +#: portato/gui/dialogs.py:24 +msgid "Do you really want to quit?" +msgstr "" + +#: portato/gui/dialogs.py:25 +msgid "" +"There are some packages in the emerge queue and/or an emerge process is " +"running." +msgstr "" + +#: portato/gui/dialogs.py:43 +#, python-format +msgid "%(blocked)s is blocked by %(blocks)s." +msgstr "" + +#: portato/gui/dialogs.py:44 +msgid "Please unmerge the blocking package." +msgstr "" + +#: portato/gui/dialogs.py:50 +msgid "You are not root." +msgstr "" + +#: portato/gui/dialogs.py:56 +#, python-format +msgid "%s seems to be masked." +msgstr "" + +#: portato/gui/dialogs.py:57 +msgid "Do you want to unmask it and its dependencies?" +msgstr "" + +#: portato/gui/dialogs.py:63 +msgid "Package not found!" +msgstr "" + +#: portato/gui/dialogs.py:69 +msgid "Do not show this dialog again." +msgstr "" + +#: portato/gui/dialogs.py:70 +#, python-format +msgid "Changed %s" +msgstr "" + +#: portato/gui/dialogs.py:71 +msgid "" +"Portato will write these changes into the appropriate files.\n" +"Please backup them if you think it is necessairy." +msgstr "" + +#: portato/gui/dialogs.py:80 +msgid "You cannot remove dependencies. :)" +msgstr "" + +#: portato/gui/dialogs.py:86 +msgid "" +"This is the updates queue. You cannot remove single elements.\n" +"Do you want to clear the whole queue instead?" +msgstr "" + +#: portato/gui/dialogs.py:92 +msgid "Do you really want to clear the whole queue?" +msgstr "" + +#: portato/gui/exception_handling.py:31 +msgid "" +"A programming error has been detected during the execution of this program." +msgstr "" + +#: portato/gui/exception_handling.py:32 +msgid "Bug Detected" +msgstr "" + +#: portato/gui/exception_handling.py:33 +msgid "" +"It probably isn't fatal, but should be reported to the developers " +"nonetheless." +msgstr "" + +#: portato/gui/exception_handling.py:35 +msgid "Show Details" +msgstr "" + +#: portato/gui/exception_handling.py:36 +msgid "Send..." +msgstr "" + +#: portato/gui/exception_handling.py:59 portato/gui/exception_handling.py:125 +#, python-format +msgid "" +"Exception in thread \"%(thread)s\":\n" +"%(trace)s" +msgstr "" + +#: portato/gui/exception_handling.py:75 +msgid "Save traceback..." msgstr "" -#: portato/gui/windows/plugin.py:55 -msgid "Status" +#: portato/gui/exception_handling.py:127 +#, python-format +msgid "" +"Exception:\n" +"%s" msgstr "" #: portato/gui/session.py:21 @@ -879,6 +865,19 @@ msgstr "" msgid "Current session format is newer than this version supports." msgstr "" +#: portato/gui/updater.py:96 +#, python-format +msgid "" +"No unmasked version of package '%s' found. Trying masked ones. This normally " +"should not happen..." +msgstr "" + +#: portato/gui/updater.py:100 +#, python-format +msgid "" +"Trying to remove package '%s' from queue which does not exist in system." +msgstr "" + #: portato/gui/utils.py:154 msgid "ALL" msgstr "" @@ -895,65 +894,60 @@ msgstr "" msgid "Error while compiling search expression: '%s'." msgstr "" -#: portato/backend/flags.py:527 -#, python-format -msgid "Conflicting values for masking status: %s" +#: portato/gui/views.py:70 +msgid "Package is not installed" msgstr "" -#: portato/backend/portage/system.py:330 +#: portato/gui/views.py:87 #, python-format -msgid "No best match for %s. It seems not to be in the tree anymore." +msgid "No %(old)s language file installed. Falling back to %(new)s." msgstr "" -#: portato/backend/portage/system.py:398 +#: portato/gui/views.py:96 #, python-format -msgid "Found a not installed dependency: %s." +msgid "No %(old)s language file installed. Disable highlighting." msgstr "" -#: portato/backend/portage/system.py:440 -#, python-format -msgid "" -"Bug? No best match could be found for '%(package)s'. Needed by: '%(cpv)s'." +#: portato/gui/wrapper.py:59 +msgid "oneshot" msgstr "" -#: portato/backend/portage/package.py:125 -#: portato/backend/catapult/package.py:98 +#: portato/gui/wrapper.py:64 #, python-format -msgid "BUG in flags.new_masking_status. It returns '%s'" +msgid "updating from version %s" msgstr "" -#: portato/helper.py:76 -msgid "Invalid dependency string" +#: portato/gui/wrapper.py:66 +msgid "updating" msgstr "" -#: portato/session.py:47 +#: portato/gui/wrapper.py:71 #, python-format -msgid "Loading session from '%s'." +msgid "downgrading from version %s" msgstr "" -#: portato/plistener.py:88 -msgid "Listener has not been started." +#: portato/gui/wrapper.py:73 +msgid "downgrading" msgstr "" -#: portato/plugins/etc_proposals.py:24 -msgid "Cannot start etc-proposals. Not root!" +#: portato/gui/wrapper.py:77 +msgid "IUSE changes:" msgstr "" -#: portato/plugins/notify.py:10 -msgid "Notify called while process is still running!" +#: portato/gui/wrapper.py:95 +msgid "(In Progress)" msgstr "" -#: portato/plugins/notify.py:14 -msgid "Emerge finished!" +#: portato/gui/wrapper.py:120 +msgid "Install" msgstr "" -#: portato/plugins/notify.py:18 -msgid "Emerge failed!" +#: portato/gui/wrapper.py:131 +msgid "Uninstall" msgstr "" -#: portato/plugins/notify.py:19 -#, python-format -msgid "Error Code: %d" +#: portato/gui/wrapper.py:143 +msgid "Update" msgstr "" #: portato/config_parser.py:270 @@ -961,48 +955,56 @@ msgstr "" msgid "Unrecognized line in configuration: %s" msgstr "" -#: portato/plugin.py:298 portato/plugin.py:304 portato/plugin.py:309 -#, python-format -msgid "%s cannot be imported." +#: portato/helper.py:76 +msgid "Invalid dependency string" msgstr "" -#: portato/plugin.py:336 -#, python-format -msgid "Accessing hook '%(hook)s' of plugin '%(plugin)s' (before)." +#: portato/plistener.py:88 +msgid "Listener has not been started." msgstr "" -#: portato/plugin.py:340 +#: portato/plugin.py:330 portato/plugin.py:333 #, python-format -msgid "Overriding hook '%(hook)s' with plugin '%(plugin)s'." +msgid "Loading plugin '%(plugin)s' failed: %(error)s" msgstr "" -#: portato/plugin.py:347 -#, python-format -msgid "Accessing hook '%(hook)s' of plugin '%(plugin)s' (after)." +#: portato/plugin.py:366 +msgid "Plugin is disabled!" +msgstr "" + +#: portato/plugin.py:368 +msgid "Plugin has unresolved dependencies - disabled!" msgstr "" -#: portato/plugin.py:367 +#: portato/plugin.py:372 #, python-format -msgid "Loading plugin '%s' failed. Invalid XML syntax." +msgid "Plugin '%s' loaded." msgstr "" -#: portato/plugin.py:370 +#: portato/plugin.py:412 #, python-format -msgid "Loading plugin '%s' failed. Plugin does not comply with schema." +msgid "Overriding hook '%(hook)s' with plugin '%(plugin)s'." msgstr "" -#: portato/plugin.py:398 +#: portato/plugin.py:470 #, python-format -msgid "Plugin '%s' loaded." +msgid "" +"For hook '%(hook)s' an override is already defined by plugin '%(plugin)s'!" msgstr "" -#: portato/plugin.py:401 +#: portato/plugin.py:471 #, python-format -msgid "Loading plugin '%(plugin)s' failed: Could not import %(import)s" +msgid "It is now replaced by the one from plugin '%s'!" msgstr "" -#: portato/plugin.py:466 +#: portato/plugin.py:504 #, python-format msgid "" -"For hook '%(hook)s' an override is already defined by plugin '%(plugin)s'!" +"Command for hook '%(hook)s' in plugin '%(plugin)s' could not be added due to " +"missing dependant: '%(dep)s'!" +msgstr "" + +#: portato/session.py:47 +#, python-format +msgid "Loading session from '%s'." msgstr "" diff --git a/plugin.xsd b/plugin.xsd deleted file mode 100644 index 38cb872..0000000 --- a/plugin.xsd +++ /dev/null @@ -1,89 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://portato.sourceforge.net/plugin" targetNamespace="http://portato.sourceforge.net/plugin" elementFormDefault="qualified"> - <xs:element name="plugin"> - <xs:complexType> - <xs:all> - <xs:element name="name" type="string" /> - <xs:element name="author" type="string" /> - <xs:element name="import" type="importString" minOccurs="0"/> - <xs:element name="frontends" type="stringList" minOccurs="0" /> - <xs:element name="hooks" minOccurs="0"> - <xs:complexType> - <xs:sequence> - <xs:element name="hook" minOccurs="1" maxOccurs="unbounded"> - <xs:complexType> - <xs:sequence> - <xs:element name="connect" minOccurs="0" maxOccurs="unbounded"> - <xs:complexType> - <xs:simpleContent> - <xs:extension base="xs:string"> - <xs:attribute name="type" default="before"> - <xs:simpleType> - <xs:restriction base="xs:string"> - <xs:enumeration value="before" /> - <xs:enumeration value="override" /> - <xs:enumeration value="after" /> - </xs:restriction> - </xs:simpleType> - </xs:attribute> - </xs:extension> - </xs:simpleContent> - </xs:complexType> - </xs:element> - </xs:sequence> - <xs:attribute name="type" type="string" use="required" /> - <xs:attribute name="call" type="functionCall" use="required" /> - </xs:complexType> - </xs:element> - </xs:sequence> - </xs:complexType> - </xs:element> - <xs:element name="options" minOccurs="0"> - <xs:complexType> - <xs:sequence> - <xs:element name="option" minOccurs="1" maxOccurs="unbounded" type="string" /> - </xs:sequence> - </xs:complexType> - </xs:element> - <xs:element name="menu" minOccurs="0"> - <xs:complexType> - <xs:sequence> - <xs:element name="item" minOccurs="1" maxOccurs="unbounded"> - <xs:complexType> - <xs:simpleContent> - <xs:extension base="string"> - <xs:attribute name="call" type="functionCall" use="required" /> - </xs:extension> - </xs:simpleContent> - </xs:complexType> - </xs:element> - </xs:sequence> - </xs:complexType> - </xs:element> - </xs:all> - </xs:complexType> - </xs:element> - <xs:simpleType name="importString"> - <xs:restriction base="xs:string"> - <xs:pattern value="([a-zA-Z_]+\.?)+" /> - </xs:restriction> - </xs:simpleType> - <xs:simpleType name="functionCall"> - <xs:restriction base="xs:string"> - <xs:pattern value="[a-zA-Z_][0-9a-zA-Z_]*" /> - </xs:restriction> - </xs:simpleType> - <xs:simpleType name="string"> - <xs:restriction base="xs:string"> - <xs:minLength value="1" /> - </xs:restriction> - </xs:simpleType> - <xs:simpleType name="_stringList"> - <xs:list itemType="string"/> - </xs:simpleType> - <xs:simpleType name="stringList"> - <xs:restriction base="_stringList"> - <xs:minLength value="1" /> - </xs:restriction> - </xs:simpleType> -</xs:schema> diff --git a/plugins/etc_proposals.py b/plugins/etc_proposals.py new file mode 100644 index 0000000..c32c8f3 --- /dev/null +++ b/plugins/etc_proposals.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +# +# File: plugins/etc_proposals.py +# This file is part of the Portato-Project, a graphical portage-frontend. +# +# Copyright (C) 2007-2008 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> + +from portato.helper import error + +import os +from subprocess import Popen + +class EtcProposals (Plugin): + __author__ = "René 'Necoro' Neumann" + __description__ = "Adds support for <b>etc-proposals</b>, a graphical etc-update replacement." + __dependency__ = ["app-portage/etc-proposals"] + + def init (self): + self.prog = ["/usr/sbin/etc-proposals"] + self.add_call("after_emerge", self.hook, type = "after") + self.add_menu("Et_c-Proposals", self.menu) + + def launch (self, options = []): + if os.getuid() == 0: + Popen(self.prog+options) + else: + error("ETC_PROPOSALS :: %s",_("Cannot start etc-proposals. Not root!")) + + def hook (self, *args, **kwargs): + """Entry point for this plugin.""" + self.launch(["--fastexit"]) + + def menu (self, *args): + self.launch() + +register(EtcProposals) diff --git a/plugins/etc_proposals.xml b/plugins/etc_proposals.xml deleted file mode 100644 index 2caf341..0000000 --- a/plugins/etc_proposals.xml +++ /dev/null @@ -1,19 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<plugin xmlns="http://portato.sourceforge.net/plugin" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://portato.sourceforge.net/plugin http://portato.sourceforge.net/plugin.xsd"> - - <author>René 'Necoro' Neumann</author> - <name>Etc-proposals</name> - - <import>portato.plugins.etc_proposals</import> - - <hooks> - <hook type = "after_emerge" call = "etc_prop"> - <connect type="after" /> - </hook> - </hooks> - - <menu> - <item call="etc_prop_menu">Et_c-Proposals</item> - </menu> - -</plugin> diff --git a/portato/plugins/gpytage.py b/plugins/exception.py index 22cc7ef..e653853 100644 --- a/portato/plugins/gpytage.py +++ b/plugins/exception.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# File: portato/plugins/gpytage.py +# File: plugins/exception.py # This file is part of the Portato-Project, a graphical portage-frontend. # # Copyright (C) 2008 René 'Necoro' Neumann @@ -10,7 +10,11 @@ # # Written by René 'Necoro' Neumann <necoro@necoro.net> -from subprocess import Popen +def throw (*args, **kwargs): + raise Exception, "As requested, Sir!" -def gpytage(*args, **kwargs): - Popen(["/usr/bin/gpytage"]) +p = Plugin() +p.__name__ = "ExceptionThrower" +p.__author__ = "René 'Necoro' Neumann" +p.add_menu("Throw exception", throw) +register(p) diff --git a/plugins/exception.xml b/plugins/exception.xml deleted file mode 100644 index 385e743..0000000 --- a/plugins/exception.xml +++ /dev/null @@ -1,13 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<plugin xmlns="http://portato.sourceforge.net/plugin" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://portato.sourceforge.net/plugin http://portato.sourceforge.net/plugin.xsd"> - - <author>René 'Necoro' Neumann</author> - <name>Exception Thrower</name> - - <import>portato.plugins.exception</import> - - <menu> - <item call="throw">Throw exception</item> - </menu> - -</plugin> diff --git a/plugins/gpytage.py b/plugins/gpytage.py new file mode 100644 index 0000000..d8c2831 --- /dev/null +++ b/plugins/gpytage.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# +# File: plugins/gpytage.py +# This file is part of the Portato-Project, a graphical portage-frontend. +# +# Copyright (C) 2008 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> + +from subprocess import Popen + +class GPytage (Plugin): + __author__ = "René 'Necoro' Neumann" + __description__ = "Adds a menu entry to directly start <b>gpytage</b>, a config editor." + __dependency__ = ["app-portage/gpytage"] + + def init (self): + self.add_menu("Config _Editor", self.menu) + + def menu (self, *args): + Popen(["/usr/bin/gpytage"]) + +register(GPytage) diff --git a/plugins/gpytage.xml b/plugins/gpytage.xml deleted file mode 100644 index b203ae0..0000000 --- a/plugins/gpytage.xml +++ /dev/null @@ -1,13 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<plugin xmlns="http://portato.sourceforge.net/plugin" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://portato.sourceforge.net/plugin http://portato.sourceforge.net/plugin.xsd"> - - <author>René 'Necoro' Neumann</author> - <name>GPytage</name> - - <import>portato.plugins.gpytage</import> - - <menu> - <item call="gpytage">Config _Editor</item> - </menu> - -</plugin> diff --git a/plugins/new_version.py b/plugins/new_version.py new file mode 100644 index 0000000..f3479b4 --- /dev/null +++ b/plugins/new_version.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- +# +# File: plugins/new_version.py +# This file is part of the Portato-Project, a graphical portage-frontend. +# +# Copyright (C) 2008 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> + +try: + from bzrlib import plugin, branch +except ImportError: + plugin = branch = None +import gobject + +from portato.helper import debug, warning +from portato import get_listener +from portato.constants import VERSION, APP_ICON, APP +from portato.gui.utils import GtkThread + +class NewVersionFinder(Plugin): + """ + Checks for a new version of portato every 30 minutes and on startup. + """ + __author__ = "René 'Necoro' Neumann" + __dependency__ = ["dev-util/bzr"] + + def init (self): + self.add_call("main", self.run) + self.add_menu("Check for new _versions", self.menu) + + def find_version (self, rev): + try: + b = branch.Branch.open("lp:portato") + except Exception, e: + warning("NEW_VERSION :: Exception occured while accessing the remote branch: %s", str(e)) + return + + debug("NEW_VERSION :: Installed rev: %s - Current rev: %s", rev, b.revno()) + if int(rev) < int(b.revno()): + def callback(): + get_listener().send_notify(base = "New Portato Live Version Found", descr = "You have rev. %s, but the most recent revision is %s." % (rev, b.revno()), icon = APP_ICON) + return False + + gobject.idle_add(callback) + + def start_thread(self, rev): + t = GtkThread(target = self.find_version, name = "Version Updater Thread", args = (rev,)) + t.setDaemon(True) + t.start() + return True + + def menu (self, *args, **kwargs): + """ + Run the thread once. + """ + v = VERSION.split() + if len(v) != 3 or v[0] != "9999": + return None + + rev = v[-1] + + plugin.load_plugins() # to have lp: addresses parsed + + self.start_thread(rev) + return rev + + def run (self, *args, **kwargs): + """ + Run the thread once and add a 30 minutes timer. + """ + rev = self.menu() + + if rev is not None: + gobject.timeout_add(30*60*1000, self.start_thread, rev) # call it every 30 minutes + +register(NewVersionFinder, (branch is None)) diff --git a/plugins/new_version.xml b/plugins/new_version.xml deleted file mode 100644 index 2711054..0000000 --- a/plugins/new_version.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<plugin xmlns="http://portato.sourceforge.net/plugin" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://portato.sourceforge.net/plugin http://portato.sourceforge.net/plugin.xsd"> - - <author>René 'Necoro' Neumann</author> - <name>New Version Reminder</name> - - <import>portato.plugins.new_version</import> - - <hooks> - <hook type="main" call="run" /> - </hooks> - - <menu> - <item call="run_menu">Check for new _versions</item> - </menu> -</plugin> diff --git a/plugins/notify.py b/plugins/notify.py new file mode 100644 index 0000000..6446812 --- /dev/null +++ b/plugins/notify.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +# +# File: plugins/notify.py +# This file is part of the Portato-Project, a graphical portage-frontend. +# +# Copyright (C) 2007-2008 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> + +disable = False + +try: + import pynotify +except ImportError: + disable = True + +from portato import get_listener + +from portato.helper import warning, error, debug +from portato.constants import APP_ICON, APP + +class Notify (Plugin): + __author__ = "René 'Necoro' Neumann" + __description__ = "Show notifications when an emerge process finishes." + __dependency__ = ["dev-python/notify-python"] + + def init (self): + self.add_call("after_emerge", self.notify) + + def notify (self, retcode, **kwargs): + if retcode is None: + warning("NOTIFY :: %s", _("Notify called while process is still running!")) + else: + icon = APP_ICON + if retcode == 0: + text = _("Emerge finished!") + descr = "" + urgency = pynotify.URGENCY_NORMAL + else: + text = _("Emerge failed!") + descr = _("Error Code: %d") % retcode + urgency = pynotify.URGENCY_CRITICAL + + get_listener().send_notify(base = text, descr = descr, icon = icon, urgency = urgency) + +register(Notify, disable) diff --git a/plugins/notify.xml b/plugins/notify.xml deleted file mode 100644 index 8de08c8..0000000 --- a/plugins/notify.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<plugin xmlns="http://portato.sourceforge.net/plugin" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://portato.sourceforge.net/plugin http://portato.sourceforge.net/plugin.xsd"> - - <author>René 'Necoro' Neumann</author> - <name>Notify</name> - <frontends>gtk</frontends> - - <import>portato.plugins.notify</import> - - <hooks> - <hook type = "after_emerge" call = "notify" /> - </hooks> - -</plugin> diff --git a/plugins/reload_portage.py b/plugins/reload_portage.py new file mode 100644 index 0000000..280bd92 --- /dev/null +++ b/plugins/reload_portage.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# +# File: plugins/reload_portage.py +# This file is part of the Portato-Project, a graphical portage-frontend. +# +# Copyright (C) 2007-2008 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> + +from portato.backend import system + +class ReloadPortage (Plugin): + __author__ = "René 'Necoro' Neumann" + __description__ = """Reloads portage when an emerge process has finished. +This can take some time, but sometimes it is necessairy.""" + + def init(self): + self.add_call("after_emerge", self.hook, type = "after", dep = "EtcProposals") + self.status = self.STAT_DISABLED # disable by default + + def hook (self, *args, **kwargs): + system.reload_settings() + +register(ReloadPortage) @@ -19,7 +19,7 @@ import gettext, locale from optparse import OptionParser, SUPPRESS_HELP from portato import get_listener -from portato.constants import VERSION, XSD_LOCATION, LOCALE_DIR, APP, SU_COMMAND +from portato.constants import VERSION, LOCALE_DIR, APP, SU_COMMAND def main (): # set gettext stuff @@ -36,30 +36,13 @@ def main (): parser.add_option("--shm", action = "store", nargs = 3, type="long", dest = "shm", help = SUPPRESS_HELP) - parser.add_option("-x", "--validate", action = "store", dest = "validate", metavar="PLUGIN", - help = _("validates the given plugin xml instead of launching Portato")) - parser.add_option("-F", "--no-fork", "-L", action = "store_true", dest = "nofork", default = False, help = _("do not fork off as root") + (" (%s)" % _("-L is deprecated"))) # run parser (options, args) = parser.parse_args() - if options.validate: # validate a plugin - from lxml import etree - try: - etree.XMLSchema(file = XSD_LOCATION).assertValid(etree.parse(options.validate)) - except etree.XMLSyntaxError, e: - print _("Validation failed. XML syntax error: %s.") % e[0] - sys.exit(3) - except etree.DocumentInvalid: - print _("Validation failed. Does not comply with schema.") - sys.exit(3) - else: - print _("Validation succeeded.") - return - - elif options.nofork or os.getuid() == 0: # start GUI + if options.nofork or os.getuid() == 0: # start GUI from portato.gui import run from portato.helper import info info("%s v. %s", _("Starting Portato"), VERSION) diff --git a/portato/backend/__init__.py b/portato/backend/__init__.py index 003feb7..b2a5a43 100644 --- a/portato/backend/__init__.py +++ b/portato/backend/__init__.py @@ -13,14 +13,10 @@ from __future__ import absolute_import from ..helper import debug -from ..constants import USE_CATAPULT from .system_interface import SystemInterface from .exceptions import BlockedException, PackageNotFoundException, DependencyCalcError, InvalidSystemError -if USE_CATAPULT: - SYSTEM = "catapult" -else: - SYSTEM = "portage" # the name of the current system +SYSTEM = "portage" # the name of the current system _sys = None # the SystemInterface-instance class _Package (object): @@ -45,8 +41,9 @@ def set_system (new_sys): @type new_sys: string""" global SYSTEM - SYSTEM = new_sys - load_system() + if new_sys != SYSTEM: + SYSTEM = new_sys + load_system() def load_system (): """Loads the current chosen system. diff --git a/portato/backend/catapult/__init__.py b/portato/backend/catapult/__init__.py deleted file mode 100644 index 348b941..0000000 --- a/portato/backend/catapult/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -# -# File: portato/backend/catapult/__init__.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> - -from __future__ import absolute_import - -from dbus.mainloop.glib import DBusGMainLoop -DBusGMainLoop(set_as_default=True) - -from .system import CatapultSystem -from .package import CatapultPackage diff --git a/portato/backend/catapult/package.py b/portato/backend/catapult/package.py deleted file mode 100644 index 2e4f471..0000000 --- a/portato/backend/catapult/package.py +++ /dev/null @@ -1,156 +0,0 @@ -# -*- coding: utf-8 -*- -# -# File: portato/backend/catapult/package.py -# This file is part of the Portato-Project, a graphical portage-frontend. -# -# Copyright (C) 2007-2008 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> - -from __future__ import absolute_import, with_statement - -from ..package import Package -from .. import flags -from .. import system -from ..exceptions import BlockedException, PackageNotFoundException -from ...helper import debug, unique_array - -import dbus -import catapult - -import os.path - -class CatapultPackage(Package): - - bus = dbus.SessionBus() - dbus_object = bus.get_object(catapult.get_dbus_address(catapult.DEFAULT), catapult.CATAPULT_PACKAGE_BUS, follow_name_owner_changes = True) - proxy = dbus.Interface(dbus_object, catapult.CATAPULT_PACKAGE_IFACE) - - _expand = {} - - def _new_flags (self): - flags = self.get_new_use_flags() - - nflags = [] - - for flag in flags: - if flag[0] == "~": - nflags.append((flag[1:], True)) - else: - nflags.append((flag, False)) - - return nflags - - def use_expanded (self, flag, suggest = None): - - exp = self._expand.get(flag, False) - - if exp is False: - if not suggest: - suggest = "" - s = str(self.proxy.use_expanded(self.get_cpv(), flag, suggest)) - if not s: - s = None - - self._expand[flag] = s - return s - else: - return exp - - def get_package_path(self): - return str(self.proxy.get_package_path(self.get_cpv())) - - def is_installed(self): - return self.proxy.is_installed(self.get_cpv()) - - def is_overlay(self): - return self.proxy.is_in_overlay(self.get_cpv()) - - def get_overlay_path(self): - return str(self.proxy.get_overlay_path(self.get_cpv())) - - def is_in_system (self): - return self.proxy.is_in_system(self.get_cpv()) - - def is_missing_keyword(self): - return self.proxy.is_missing_keyword(self.get_cpv()) - - def is_testing(self, use_keywords = False): - if not use_keywords: - return self.proxy.is_testing(self.get_cpv(), False) - else: - status = flags.new_testing_status(self.get_cpv()) - if status is None: - return self.proxy.is_testing(self.get_cpv(), True) - else: - return status - - def is_masked (self, use_changed = True): - if use_changed: - status = flags.new_masking_status(self.get_cpv()) - if status != None: # we have locally changed it - if status == "masked": return True - elif status == "unmasked": return False - else: - error(_("BUG in flags.new_masking_status. It returns \'%s\'"), status) - else: # we have not touched the status - return self.proxy.is_masked(self.get_cpv()) - else: # we want the original portage value XXX: bug if masked by user AND by system - if self.proxy.is_masked(self.get_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 - - return False - - def get_masking_reason (self): - return str(self.proxy.get_masking_reason(self.get_cpv())) - - def get_iuse_flags (self, installed = False, removeForced = True): - return [str(x) for x in self.proxy.get_iuse_flags(self.get_cpv(), installed, removeForced)] - - def get_matched_dep_packages (self, depvar): - return [str(x) for x in self.proxy.get_matched_dep_packages(self.get_cpv(), self._new_flags())] - - def get_dep_packages (self, depvar = ["RDEPEND", "PDEPEND", "DEPEND"], with_criterions = False): - pkgs = self.proxy.get_dep_packages(self.get_cpv(), depvar, self._new_flags()) - - if not with_criterions: - return [str(x) for x,y in pkgs] - else: - return [(str(x),str(y)) for x,y in pkgs] - - def get_global_settings(self, key, installed = True): - return str(self.proxy.get_global_settings(self.get_cpv(), key, installed)) - - def get_ebuild_path(self): - return str(self.proxy.get_ebuild_path(self.get_cpv())) - - def get_package_settings(self, var, tree = True): - return str(self.proxy.get_package_settings(self.get_cpv(), var, tree)) - - def get_installed_use_flags(self): - return self.proxy.get_installed_use_flags(self.get_cpv()) - - def get_actual_use_flags(self): - return self.proxy.get_actual_use_flags(self.get_cpv(), self._new_flags()) - - def compare_version(self, other): - return self.proxy.compare_version(self.get_cpv(), other.get_cpv()) - - def matches (self, criterion): - return self.proxy.matches(self.get_cpv(), criterion) - - def get_files (self): - return self.proxy.get_files(self.get_cpv()) - - def get_name(self): - return str(self.proxy.get_name(self.get_cpv())) - - def get_version(self): - return str(self.proxy.get_version(self.get_cpv())) - - def get_category(self): - return str(self.proxy.get_category(self.get_cpv())) diff --git a/portato/backend/catapult/system.py b/portato/backend/catapult/system.py deleted file mode 100644 index 3a3bac5..0000000 --- a/portato/backend/catapult/system.py +++ /dev/null @@ -1,252 +0,0 @@ -# -*- coding: utf-8 -*- -# -# File: portato/backend/catapult/system.py -# This file is part of the Portato-Project, a graphical portage-frontend. -# -# Copyright (C) 2007-2008 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> - -from __future__ import absolute_import - -import re, os -from threading import Event -import dbus -import catapult - -from .package import CatapultPackage -from ..system_interface import SystemInterface -from ...helper import debug, info, warning, unique_array - -class CatapultSystem (SystemInterface): - - def __init__ (self): - SystemInterface.__init__(self) - - self.bus = dbus.SessionBus() - # get the system - so = self.bus.get_object(catapult.get_dbus_address(catapult.DEFAULT), catapult.CATAPULT_SYSTEM_BUS, follow_name_owner_changes = True) - self.proxy = dbus.Interface(so, catapult.CATAPULT_SYSTEM_IFACE) - - def get_version (self): - admint = dbus.Interface(self.bus.get_object(catapult.get_dbus_address(catapult.DEFAULT), catapult.CATAPULT_BUS), catapult.CATAPULT_ADMIN_IFACE) - return "Catapult: %s v. %s" % (self.proxy.bus_name.split(".")[-1], str(admint.version())) - - def geneticize_list (self, list_of_packages, only_cpv = False): - """Convertes a list of cpv's into L{backend.Package}s. - - @param list_of_packages: the list of packages - @type list_of_packages: string[] - @param only_cpv: do nothing - return the passed list - @type only_cpv: boolean - @returns: converted list - @rtype: PortagePackage[] - """ - - if not only_cpv: - return [CatapultPackage(x) for x in list_of_packages] - else: - return [str(x) for x in list_of_packages] - - - def split_cpv (self, cpv): - split = self.proxy.split_cpv(cpv) - if all(split): - return map(str, split) - else: - return None - - def cpv_matches (self, cpv, criterion): - return CatapultPackage(cpv).matches(criterion) - - def find_best(self, list, only_cpv = False): - if only_cpv: - return str(self.proxy.find_best(list)) - else: - return CatapultPackage(self.proxy.find_best(list)) - - def find_best_match (self, search_key, masked = False, only_installed = False, only_cpv = False): - p = self.proxy.find_best_match(search_key, masked, only_installed) - - if p : - if not only_cpv: - return CatapultPackage(p) - else: - return str(p) - return None - - def _wrap_find(self, key, masked, set, withVersion, only_cpv): - - l = [] - try: - l = self.proxy.find_packages(key, set, masked, withVersion) - except dbus.DBusException, e: - name, data = str(e).split("\n")[-2].split(": ")[1:] - - if name == catapult.CATAPULT_ERR_AMBIGUOUS_PACKAGE: - debug("Ambigous packages: %s.", data) - l = [] - for cp in data.split(","): - l += self.proxy.find_packages(cp, set, masked, withVersion) - else: - raise - - return self.geneticize_list(l, not(withVersion) or only_cpv) - - def find_packages (self, search_key, masked = False, only_cpv = False): - return self._wrap_find(search_key, masked, "all", True, only_cpv) - - def find_installed_packages (self, search_key, masked = False, only_cpv = False): - return self._wrap_find(search_key, masked, "installed", True, only_cpv) - - def find_system_packages (self, only_cpv = False): -# result = self.proxy.find_system_packages() -# if only_cpv: -# return result -# else: -# return tuple(map(self.geneticize_list, result)) - return (self._wrap_find(search_key, False, "system", True, only_cpv), []) - - def find_world_packages (self, only_cpv = False): -# result = self.proxy.find_world_packages() -# if only_cpv: -# return result -# else: -# return tuple(map(self.geneticize_list, result)) - return (self._wrap_find(search_key, False, "world", True, only_cpv), []) - - def _wrap_find_all (self, key, masked, set, withVersion, only_cpv): - if not key: - key = "" - else: - key = "*%s*" % key - - l = self.proxy.find_packages("", set, masked, withVersion) - - if key: - l = catapult.filter_list(key, l) - - return self.geneticize_list(l, not(withVersion) or only_cpv) - - def find_all_installed_packages (self, name = None, withVersion = True, only_cpv = False): - return self._wrap_find_all(name, True, "installed", withVersion, only_cpv) - - def find_all_uninstalled_packages (self, name = None, only_cpv = False): - return self._wrap_find_all(name, True, "uninstalled", True, only_cpv) - - def find_all_packages (self, name = None, withVersion = True, only_cpv = False): - return self._wrap_find_all(name, True, "all", withVersion, only_cpv) - - def find_all_world_packages (self, name = None, only_cpv = False): - return self._wrap_find_all(name, True, "world", withVersion, only_cpv) - - def find_all_system_packages (self, name = None, only_cpv = False): - return self._wrap_find_all(name, True, "system", withVersion, only_cpv) - - def list_categories (self, name = None): - cats = self.proxy.list_categories() - if name: - cats = catapult.filter_list("*%s*" % name, cats) - - return map(str, cats) - - def sort_package_list(self, pkglist): - return self.geneticize_list(self.proxy.sort_package_list([x.get_cpv() for x in pkglist])) - - def reload_settings (self): - return self.proxy.reload_settings() - - def update_world (self, newuse = False, deep = False): - - ret = [] - e = Event() - - def wait (list): - ret.extend([(CatapultPackage(x), CatapultPackage(y)) for x,y in list]) - e.set() - - def error (ex): - e.set() - raise ex - - self.proxy.update_world(newuse, deep, {}, reply_handler = wait, error_handler = error, timeout = 300) - e.wait() - return ret - # return [(CatapultPackage(x), CatapultPackage(y)) for x,y in self.proxy.update_world(newuse, deep, {}, timeout = 300)] - - def get_updated_packages (self): - ret = [] - e = Event() - - def wait (list): - ret.extend([CatapultPackage(x) for x in list]) - e.set() - - def error (ex): - e.set() - raise ex - - self.proxy.get_updated_packages(reply_handler = wait, error_handler = error, timeout = 300) - e.wait() - return ret - - def get_use_desc (self, flag, package = None): - if not package: - package = "" - return str(self.proxy.get_use_desc(flag, package)) - - def get_global_settings(self, key): - return str(self.proxy.get_global_settings(key)) - - def new_package (self, cpv): - return CatapultPackage(cpv) - - def get_config_path (self): - return str(self.proxy.get_config_path()) - - def get_sync_command (self): - return [str(x) for x in self.proxy.get_sync_command()] - - def get_merge_command (self): - return [str(x) for x in self.proxy.get_merge_command()] - - def get_oneshot_option (self): - return [str(x) for x in self.proxy.get_oneshot_option()] - - def get_newuse_option (self): - return [str(x) for x in self.proxy.get_newuse_option()] - - def get_deep_option (self): - return [str(x) for x in self.proxy.get_deep_option()] - - def get_update_option (self): - return [str(x) for x in self.proxy.get_update_option()] - - def get_pretend_option (self): - return [str(x) for x in self.proxy.get_pretend_option()] - - def get_unmerge_option (self): - return [str(x) for x in self.proxy.get_unmerge_option()] - - def get_environment (self): - default_opts = self.get_global_settings("EMERGE_DEFAULT_OPTS") - opts = dict(os.environ) - - if default_opts: - opt_list = default_opts.split() - changed = False - - for option in ["--ask", "-a", "--pretend", "-p"]: - if option in opt_list: - opt_list.remove(option) - changed = True - - if changed: - opts.update(EMERGE_DEFAULT_OPTS = " ".join(opt_list)) - - opts.update(TERM = "xterm") - - return opts diff --git a/portato/backend/portage/system.py b/portato/backend/portage/system.py index c241a4b..4453df7 100644 --- a/portato/backend/portage/system.py +++ b/portato/backend/portage/system.py @@ -194,8 +194,8 @@ class PortageSystem (SystemInterface): if VERSION >= (2,1,5): t += [pkg.get_cpv() for pkg in self.find_packages(search_key, self.SET_INSTALLED) if not (pkg.is_testing(True) or pkg.is_masked())] - else: - t = self.find_packages(search_key, self.SET_INSTALLED, only_cpv=True) + elif not only_installed: # no need to run twice + t += self.find_packages(search_key, self.SET_INSTALLED, only_cpv=True) if t: t = unique_array(t) @@ -237,6 +237,19 @@ class PortageSystem (SystemInterface): """ new_packages = [] + + def append(crit, best, inst): + if not best: + return + + if not best.is_installed() and (best.is_masked() or best.is_testing(True)): # check to not update unnecessairily + for i in inst: + if i.matches(crit): + debug("The installed %s matches %s. Discarding upgrade to masked version.", i.get_cpv(), crit) + return + + new_packages.append(best) + for p in packages: inst = self.find_packages(p, self.SET_INSTALLED) @@ -256,10 +269,10 @@ class PortageSystem (SystemInterface): myslots.add(best_p.get_package_settings("SLOT")) # add the slot of the best package in portage for slot in myslots: - new_packages.append(\ - self.find_best(self.find_packages("%s:%s" % (i.get_cp(), slot), only_cpv = True))) + crit = "%s:%s" % (p, slot) + append(crit, self.find_best_match(crit), inst) else: - new_packages.append(best_p) + append(p, best_p, inst) return new_packages @@ -348,15 +361,6 @@ class PortageSystem (SystemInterface): else: for pkg in bm: if not pkg: continue - if not pkg.is_installed() and (pkg.is_masked() or pkg.is_testing(True)): # check to not update unnecessairily - cont = False - for inst in self.find_packages(pkg.get_cp(), self.SET_INSTALLED, only_cpv = True): - if self.cpv_matches(inst, i): - debug("The installed %s matches %s. Discarding upgrade to masked version.", inst, i) - cont = True - break - if cont: continue - check(pkg, state[1], appended) # XXX: should be 'or'ed with prev_appended? for p in self.get_new_packages(packages): diff --git a/portato/config_parser.py b/portato/config_parser.py index 1383d69..6515d1b 100644 --- a/portato/config_parser.py +++ b/portato/config_parser.py @@ -285,8 +285,8 @@ class ConfigParser: :Exceptions: - KeyNotFoundException : Raised if the specified key could not be found. - SectionNotFoundException : Raised if the specified section could not be found. + - `KeyNotFoundException` : Raised if the specified key could not be found. + - `SectionNotFoundException` : Raised if the specified section could not be found. """ try: @@ -315,8 +315,8 @@ class ConfigParser: :Exceptions: - KeyNotFoundException : Raised if the specified key could not be found. - SectionNotFoundException : Raised if the specified section could not be found. + - `KeyNotFoundException` : Raised if the specified key could not be found. + - `SectionNotFoundException` : Raised if the specified section could not be found. """ section = section.upper() @@ -339,9 +339,9 @@ class ConfigParser: :Exceptions: - KeyNotFoundException : Raised if the specified key could not be found. - SectionNotFoundException : Raised if the specified section could not be found. - ValueError : Raised if the key accessed is not a boolean. + - `KeyNotFoundException` : Raised if the specified key could not be found. + - `SectionNotFoundException` : Raised if the specified section could not be found. + - `ValueError` : Raised if the key accessed is not a boolean. """ section = section.upper() @@ -369,8 +369,8 @@ class ConfigParser: :Exceptions: - KeyNotFoundException : Raised if the specified key could not be found. - SectionNotFoundException : Raised if the specified section could not be found. + - `KeyNotFoundException` : Raised if the specified key could not be found. + - `SectionNotFoundException` : Raised if the specified section could not be found. """ section = section.upper() @@ -394,9 +394,9 @@ class ConfigParser: :Exceptions: - KeyNotFoundException : Raised if the specified key could not be found. - SectionNotFoundException : Raised if the specified section could not be found. - ValueError : if the old/new value is not a boolean + - `KeyNotFoundException` : Raised if the specified key could not be found. + - `SectionNotFoundException` : Raised if the specified section could not be found. + - `ValueError` : if the old/new value is not a boolean """ section = section.upper() diff --git a/portato/constants.py b/portato/constants.py index 32f0f9b..3d7217f 100644 --- a/portato/constants.py +++ b/portato/constants.py @@ -22,8 +22,6 @@ These should be set during the installation. @type HOME: string @var SU_COMMAND: command to execute to "su" @type SU_COMMAND: string -@var USE_CATAPULT: use the catapult backend or the normal ones -@type USE_CATAPULT: boolean @var CONFIG_DIR: The configuration directory. @type CONFIG_DIR: string @@ -45,8 +43,6 @@ These should be set during the installation. @type SETTINGS_DIR: string @var TEMPLATE_DIR: Directory containing the UI template files. @type TEMPLATE_DIR: string -@var XSD_LOCATION: Path of the plugin schema. -@type XSD_LOCATION: string """ import os from os.path import join as pjoin @@ -60,7 +56,6 @@ APP = "portato" VERSION = "9999" HOME = os.environ["HOME"] SU_COMMAND = "gksu -D 'Portato'" -USE_CATAPULT = False # config CONFIG_DIR = "/etc/portato/" @@ -73,5 +68,3 @@ LOCALE_DIR = "i18n/" PLUGIN_DIR = pjoin(DATA_DIR, "plugins/") SETTINGS_DIR = pjoin(HOME, "."+APP) TEMPLATE_DIR = "portato/gui/templates/" - -XSD_LOCATION = pjoin(DATA_DIR, "plugin.xsd") diff --git a/portato/gui/dialogs.py b/portato/gui/dialogs.py index 7f8a736..bf7acc7 100644 --- a/portato/gui/dialogs.py +++ b/portato/gui/dialogs.py @@ -21,8 +21,8 @@ def mail_failure_dialog(e): return ret def queue_not_empty_dialog(): - dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_NONE, _("There are some packages in the emerge queue and/or an emerge process is running.\nDo you really want to quit?")) - #dialog.add_buttons(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_SAVE, gtk.RESPONSE_YES, gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE) + dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_NONE, _("Do you really want to quit?")) + dialog.format_secondary_text(_("There are some packages in the emerge queue and/or an emerge process is running.")) dialog.add_buttons(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE) ret = dialog.run() dialog.destroy() @@ -40,7 +40,8 @@ def io_ex_dialog (io_ex): return ret def blocked_dialog (blocked, blocks): - dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _("%(blocked)s is blocked by %(blocks)s.\nPlease unmerge the blocking package.") % {"blocked":blocked, "blocks" : blocks}) + dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _("%(blocked)s is blocked by %(blocks)s.") % {"blocked":blocked, "blocks" : blocks}) + dialog.format_secondary_text(_("Please unmerge the blocking package.")) ret = dialog.run() dialog.destroy() return ret @@ -52,7 +53,8 @@ def not_root_dialog (): return ret def unmask_dialog (cpv): - dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, _("%s seems to be masked.\nDo you want to unmask it and its dependencies?") % cpv) + dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, _("%s seems to be masked.") % cpv ) + dialog.format_secondary_text(_("Do you want to unmask it and its dependencies?")) ret = dialog.run() dialog.destroy() return ret @@ -65,8 +67,8 @@ def nothing_found_dialog (): def changed_flags_dialog (what = "flags"): check = gtk.CheckButton(_("Do not show this dialog again.")) - hintMB = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, - _("You have changed %s.\nPortato will write these changes into the appropriate files.\nPlease backup them if you think it is necessairy.") % what) + hintMB = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, _("Changed %s") % what) + hintMB.format_secondary_text(_("Portato will write these changes into the appropriate files.\nPlease backup them if you think it is necessairy.")) hintMB.vbox.add(check) hintMB.vbox.show_all() ret = hintMB.run() diff --git a/portato/gui/exception_handling.py b/portato/gui/exception_handling.py index eadf124..dae95ed 100644 --- a/portato/gui/exception_handling.py +++ b/portato/gui/exception_handling.py @@ -100,16 +100,13 @@ def convert (version): def get_version_infos(): from ..constants import VERSION from ..backend import system - from lxml import etree return "\n".join(( "Portato version: %s" % VERSION, "Python version: %s" % sys.version, "Used backend: %s" % system.get_version(), "pygtk: %s (using GTK+: %s)" % (convert(gtk.pygtk_version), convert(gtk.gtk_version)), - "pygobject: %s (using GLib: %s)" % (convert(gobject.pygobject_version), convert(gobject.glib_version)), - "lxml: %s" % convert(etree.LXML_VERSION), - "")) + "pygobject: %s (using GLib: %s)" % (convert(gobject.pygobject_version), convert(gobject.glib_version)))) def get_trace(type, value, tb): trace = StringIO() diff --git a/portato/gui/queue.py b/portato/gui/queue.py index cb5b334..c04d449 100644 --- a/portato/gui/queue.py +++ b/portato/gui/queue.py @@ -248,9 +248,9 @@ class EmergeQueue: self.update_tree(parentIt, cpv, unmask, oneshot = oneshot, type = type) else: # not update if type == "install": - self._queue_append(cpv, oneshot) if self.tree: self.update_tree(self.tree.get_emerge_it(), cpv, unmask, type = type, oneshot = oneshot) + self._queue_append(cpv, oneshot) elif type == "update" and self.tree: self.update_tree(self.tree.get_update_it(), cpv, unmask, type = type, oneshot = oneshot) diff --git a/portato/gui/templates/PluginWindow.glade b/portato/gui/templates/PluginWindow.glade index 1de5fc0..f76193e 100644 --- a/portato/gui/templates/PluginWindow.glade +++ b/portato/gui/templates/PluginWindow.glade @@ -1,11 +1,12 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd"> -<!--Generated with glade3 3.4.1 on Fri Feb 29 00:03:30 2008 --> +<!--Generated with glade3 3.4.5 on Fri Jul 4 15:24:27 2008 --> <glade-interface> <widget class="GtkWindow" id="PluginWindow"> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="border_width">5</property> <property name="title" translatable="yes">Plugins</property> + <property name="resizable">False</property> <property name="modal">True</property> <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property> <property name="destroy_with_parent">True</property> @@ -26,6 +27,7 @@ <child> <widget class="GtkTreeView" id="pluginList"> <property name="visible">True</property> + <property name="headers_visible">False</property> <property name="rules_hint">True</property> <property name="enable_search">False</property> </widget> @@ -33,6 +35,180 @@ </widget> </child> <child> + <widget class="GtkFrame" id="frame1"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <child> + <widget class="GtkTable" id="table1"> + <property name="visible">True</property> + <property name="n_rows">4</property> + <property name="n_columns">2</property> + <property name="row_spacing">10</property> + <child> + <widget class="GtkButton" id="installBtn"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="label" translatable="yes">_Install dependencies</property> + <property name="use_underline">True</property> + <property name="response_id">0</property> + <signal name="clicked" handler="cb_install_clicked"/> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="y_options"></property> + <property name="x_padding">10</property> + </packing> + </child> + <child> + <widget class="GtkExpander" id="depExpander"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <child> + <widget class="GtkTreeView" id="depList"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="headers_visible">False</property> + <property name="headers_clickable">True</property> + </widget> + </child> + <child> + <widget class="GtkLabel" id="label4"> + <property name="visible">True</property> + <property name="label" translatable="yes">Needed dependencies</property> + <property name="single_line_mode">True</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_padding">10</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="descrLabel"> + <property name="visible">True</property> + <property name="label" translatable="yes">label</property> + </widget> + <packing> + <property name="right_attach">2</property> + <property name="y_options">GTK_FILL</property> + <property name="y_padding">10</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label2"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Author:</b></property> + <property name="use_markup">True</property> + <property name="single_line_mode">True</property> + </widget> + <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options">GTK_FILL</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="authorLabel"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">label</property> + <property name="single_line_mode">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options">GTK_FILL</property> + </packing> + </child> + <child> + <widget class="GtkHButtonBox" id="buttonBox"> + <property name="visible">True</property> + <property name="spacing">5</property> + <property name="homogeneous">True</property> + <property name="layout_style">GTK_BUTTONBOX_EDGE</property> + <child> + <widget class="GtkRadioButton" id="enabledRB"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Enabled</property> + <property name="response_id">0</property> + <property name="active">True</property> + <signal name="toggled" handler="cb_state_toggled"/> + </widget> + </child> + <child> + <widget class="GtkRadioButton" id="tempEnabledRB"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Temporarily enabled</property> + <property name="response_id">0</property> + <property name="group">enabledRB</property> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + <child> + <widget class="GtkRadioButton" id="tempDisabledRB"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Temporarily disabled</property> + <property name="response_id">0</property> + <property name="group">enabledRB</property> + <signal name="toggled" handler="cb_state_toggled"/> + </widget> + <packing> + <property name="position">2</property> + </packing> + </child> + <child> + <widget class="GtkRadioButton" id="disabledRB"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Disabled</property> + <property name="response_id">0</property> + <property name="group">enabledRB</property> + <signal name="toggled" handler="cb_state_toggled"/> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">3</property> + </packing> + </child> + </widget> + <packing> + <property name="right_attach">2</property> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + <property name="y_options">GTK_FILL</property> + </packing> + </child> + </widget> + </child> + <child> + <placeholder/> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + <child> <widget class="GtkHButtonBox" id="hbuttonbox2"> <property name="visible">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> @@ -68,7 +244,7 @@ </widget> <packing> <property name="expand">False</property> - <property name="position">1</property> + <property name="position">2</property> </packing> </child> </widget> diff --git a/portato/gui/utils.py b/portato/gui/utils.py index a4e1e6e..923f2fa 100644 --- a/portato/gui/utils.py +++ b/portato/gui/utils.py @@ -24,7 +24,7 @@ from functools import wraps # some backend things from ..backend import flags, system, set_system from ..helper import debug, info, set_log_level -from ..constants import USE_CATAPULT, APP, LOCALE_DIR +from ..constants import APP, LOCALE_DIR # parser from ..config_parser import ConfigParser @@ -86,8 +86,7 @@ class Config (ConfigParser): def modify_system_config (self): """Sets the system config. @see: L{backend.set_system()}""" - if not USE_CATAPULT: - set_system(self.get("system")) + set_system(self.get("system")) def modify_external_configs (self): """Convenience function setting all external configs.""" @@ -132,7 +131,8 @@ class Config (ConfigParser): ConfigParser.write(self) self.modify_external_configs() -class PkgData: +class PkgData (object): + __slots__ = ("cat", "pkg", "inst") def __init__ (self, cat, pkg, inst): self.cat = cat @@ -146,7 +146,7 @@ class PkgData: return cmp(self.pkg.lower(), other.pkg.lower()) def __repr__ (self): - return "<Package (%(cat)s, %(pkg)s, %(inst)s)>" % self.__dict__ + return "<Package (%(cat)s, %(pkg)s, %(inst)s)>" % {"cat" : self.cat, "pkg" : self.pkg, "inst" : self.inst} class Database (object): """An internal database which holds a simple dictionary cat -> [package_list].""" diff --git a/portato/gui/windows/main.py b/portato/gui/windows/main.py index 188a5e1..50dd366 100644 --- a/portato/gui/windows/main.py +++ b/portato/gui/windows/main.py @@ -607,13 +607,13 @@ class MainWindow (Window): # set plugins and plugin-menu splash(_("Loading Plugins")) - plugin.load_plugins("gtk") - menus = plugin.get_plugin_queue().get_plugin_menus() + plugin.load_plugins() + menus = [p.menus for p in plugin.get_plugin_queue().get_plugins()] if menus: self.tree.get_widget("pluginMenuItem").set_no_show_all(False) pluginMenu = self.tree.get_widget("pluginMenu") - for m in menus: + for m in itt.chain(*menus): item = gtk.MenuItem(m.label) item.connect("activate", m.call) pluginMenu.append(item) @@ -1082,13 +1082,11 @@ class MainWindow (Window): def save_plugin (p): def _save (): - stat_on = p.status >= p.STAT_ENABLED - hard_on = not p.get_option("disabled") - - if stat_on != hard_on: - return int(stat_on) - else: + if p.status == p.STAT_HARD_DISABLED: return "" + + return int(p.status >= p.STAT_ENABLED) + return _save # SESSION VERSION @@ -1559,9 +1557,9 @@ class MainWindow (Window): if queue is None: plugins = [] else: - plugins = queue.get_plugins() - - PluginWindow(self.window, plugins) + plugins = list(queue.get_plugins()) + + PluginWindow(self.window, plugins, self.queue) return True def cb_show_updates_clicked (self, *args): diff --git a/portato/gui/windows/plugin.py b/portato/gui/windows/plugin.py index fb9446e..a0694be 100644 --- a/portato/gui/windows/plugin.py +++ b/portato/gui/windows/plugin.py @@ -15,6 +15,9 @@ from __future__ import absolute_import import gtk from .basic import AbstractDialog +from ..dialogs import blocked_dialog, unmask_dialog +from ...backend import system +from ...backend.exceptions import PackageNotFoundException, BlockedException from ...helper import debug class PluginWindow (AbstractDialog): @@ -24,7 +27,7 @@ class PluginWindow (AbstractDialog): for s in (_("Disabled"), _("Temporarily enabled"), _("Enabled"), _("Temporarily disabled")): statsStore.append([s]) - def __init__ (self, parent, plugins): + def __init__ (self, parent, plugins, queue = None): """Constructor. @param parent: the parent window @@ -32,50 +35,77 @@ class PluginWindow (AbstractDialog): AbstractDialog.__init__(self, parent) self.plugins = plugins + self.queue = queue self.changedPlugins = {} + self.inst = [] + self.ninst = [] - view = self.tree.get_widget("pluginList") - self.store = gtk.ListStore(str,str,str) + self.buttons = map(self.tree.get_widget, ("disabledRB", "tempEnabledRB", "enabledRB", "tempDisabledRB")) + map(lambda b: b.set_mode(False), self.buttons) + + self.descrLabel = self.tree.get_widget("descrLabel") + self.authorLabel = self.tree.get_widget("authorLabel") + + self.depExpander = self.tree.get_widget("depExpander") + self.installBtn = self.tree.get_widget("installBtn") + self.depList = self.tree.get_widget("depList") + self.build_dep_list() + + self.buttonBox = self.tree.get_widget("buttonBox") + + self.instIcon = self.window.render_icon(gtk.STOCK_YES, gtk.ICON_SIZE_MENU) - view.set_model(self.store) + self.view = self.tree.get_widget("pluginList") + self.store = gtk.ListStore(str) - cell = gtk.CellRendererText() - col = gtk.TreeViewColumn(_("Plugin"), cell, markup = 0) - view.append_column(col) + self.view.set_model(self.store) - col = gtk.TreeViewColumn(_("Authors"), cell, text = 1) - view.append_column(col) - - ccell = gtk.CellRendererCombo() - ccell.set_property("model", self.statsStore) - ccell.set_property("text-column", 0) - ccell.set_property("has-entry", False) - ccell.set_property("editable", True) - ccell.connect("edited", self.cb_status_changed) - col = gtk.TreeViewColumn(_("Status"), ccell, markup = 2) - view.append_column(col) + cell = gtk.CellRendererText() + col = gtk.TreeViewColumn("Plugin", cell, markup = 0) + self.view.append_column(col) - for p in (("<b>"+p.name+"</b>", p.author, _(self.statsStore[p.status][0])) for p in plugins): - self.store.append(p) + for p in plugins: + self.store.append(["<b>%s</b>" % p.name]) + + self.view.get_selection().connect("changed", self.cb_list_selection) self.window.show_all() - def cb_status_changed (self, cell, path, new_text): - path = int(path) - - self.store[path][2] = "<b>%s</b>" % new_text + def build_dep_list (self): + store = gtk.ListStore(gtk.gdk.Pixbuf, str) + + self.depList.set_model(store) + + col = gtk.TreeViewColumn() + + cell = gtk.CellRendererPixbuf() + col.pack_start(cell, False) + col.add_attribute(cell, "pixbuf", 0) + + cell = gtk.CellRendererText() + col.pack_start(cell, True) + col.add_attribute(cell, "text", 1) + + self.depList.append_column(col) + + def fill_dep_list (self, inst = [], ninst = []): + store = self.depList.get_model() + store.clear() + + for dep in inst: + store.append([self.instIcon, dep]) + for dep in ninst: + store.append([None, dep]) - # convert string to constant - const = None - for num, val in enumerate(self.statsStore): - if val[0] == new_text: - const = num - break + def cb_state_toggled (self, rb): + + plugin = self.get_actual() - assert (const is not None) + if plugin: + state = self.buttons.index(rb) - self.changedPlugins.update({self.plugins[path] : const}) - debug("new changed plugins: %s => %d", self.plugins[path].name, const) + self.changedPlugins[plugin] = state + debug("new changed plugins: %s => %d", plugin.name, state) def cb_ok_clicked (self, btn): for plugin, val in self.changedPlugins.iteritems(): @@ -83,3 +113,69 @@ class PluginWindow (AbstractDialog): self.close() return True + + def cb_list_selection (self, selection): + plugin = self.get_actual() + self.inst = [] + self.ninst = [] + + if plugin: + if not plugin.description: + self.descrLabel.hide() + else: + self.descrLabel.set_markup(plugin.description) + self.descrLabel.show() + + self.authorLabel.set_label(plugin.author) + + status = self.changedPlugins.get(plugin, plugin.status) + self.buttons[status].set_active(True) + + if plugin.deps: + + for dep in plugin.deps: + if system.find_packages(dep, pkgSet = system.SET_INSTALLED, with_version = False): + self.inst.append(dep) + else: + self.ninst.append(dep) + + self.fill_dep_list(self.inst, self.ninst) + self.depExpander.show() + + self.installBtn.show() + self.installBtn.set_sensitive(bool(self.ninst)) + + else: + self.installBtn.hide() + self.depExpander.hide() + + self.buttonBox.set_sensitive(not plugin._unresolved_deps and plugin.status != plugin.STAT_HARD_DISABLED) + + def cb_install_clicked (self, *args): + if not self.queue: + return False + + for cpv in self.ninst: + + pkg = system.find_best_match(cpv, masked = False, only_cpv = True) + if not pkg: + pkg = system.find_best_match(cpv, masked = True, only_cpv = True) + + try: + try: + self.queue.append(pkg, type = "install") + except PackageNotFoundException, e: + if unmask_dialog(e[0]) == gtk.RESPONSE_YES: + self.queue.append(pkg, type = "install", unmask = True) + except BlockedException, e: + blocked_dialog(e[0], e[1]) + + return True + + def get_actual (self): + store, it = self.view.get_selection().get_selected() + + if it: + return self.plugins[int(store.get_path(it)[0])] + else: + return None diff --git a/portato/plugin.py b/portato/plugin.py index 5926922..0bb161c 100644 --- a/portato/plugin.py +++ b/portato/plugin.py @@ -3,349 +3,421 @@ # File: portato/plugin.py # This file is part of the Portato-Project, a graphical portage-frontend. # -# Copyright (C) 2007 René 'Necoro' Neumann +# Copyright (C) 2008 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> -"""A module containing the management of the plugin system.""" +""" +A module managing the plugins for Portato. +""" from __future__ import absolute_import - -import os, os.path -from xml.dom.minidom import parse -from lxml import etree - -from .constants import PLUGIN_DIR, XSD_LOCATION -from .helper import debug, info, warning, error, flatten - -class PluginImportException (ImportError): +__docformat__ = "restructuredtext" + +import os +import os.path as osp +import traceback +from collections import defaultdict +from functools import wraps + +from .helper import debug, warning, info, error +from .constants import PLUGIN_DIR +from .backend import system +from . import plugins as plugin_module + +class PluginLoadException (Exception): + """ + Exception signaling a failed plugin loading. + """ pass -class Options (object): - """The <options>-element.""" +class Menu (object): + """ + One single menu entry. - __options = ("disabled", "blocking") + :IVariables: - def __init__ (self, options = None): + label : string + The label of the entry. Can have underscores to define the shortcut. - self.disabled = False - self.blocking = False + call + The function to call, if the entry is clicked. + """ + __slots__ = ("label", "call") - if options: - self.parse(options) - - def parse (self, options): - for opt in options: - nodes = opt.childNodes - type = str(nodes[0].nodeValue.strip()) - if type in self.__options: - self.set(type, True) + def __init__ (self, label, call): + self.label = label + self.call = call - def get (self, name): - return self.__getattribute__(name) +class Call (object): + """ + This class represents an object, which is attached to a specified hook. - def set (self, name, value): - return self.__setattr__(name, value) + :IVariables: -class Menu: - """A single <menu>-element.""" - def __init__ (self, plugin, label, call): - """Constructor. + plugin : `Plugin` + The plugin where this call belongs to. - @param plugin: the plugin this menu belongs to - @type plugin: Plugin - @param label: the label to show - @type label: string - @param call: the function to call relative to the import statement - @type call: string + hook : string + The name of the corresponding hook. - @raises PluginImportException: if the plugin's import could not be imported""" + call + The function to call. - self.label = label - self.plugin = plugin + type : string + This is either ``before``, ``after`` or ``override`` and defines the type of the call: - if self.plugin.needs_import(): # get import - imp = self.plugin.get_import() - try: - mod = __import__(imp, globals(), locals(), [call]) - except ImportError: - raise PluginImportException, imp + before + access before the original function + override + access *instead of* the original function. **USE THIS ONLY IF YOU KNOW WHAT YOU ARE DOING** + after + access after the original function has been called - try: - self.call = eval("mod."+call) # build function - except AttributeError: - raise PluginImportException, imp - else: - try: - self.call = eval(call) - except AttributeError: - raise PluginImportException, imp + Default: ``before`` -class Connect: - """A single <connect>-element.""" + dep : string + This defines a plugin which should be executed after/before this one. + ``"*"`` means all and ``"-*"`` means none. + """ + __slots__ = ("plugin", "hook", "call", "type", "dep") - def __init__ (self, hook, type, depend_plugin): - """Constructor. + def __init__ (self, plugin, hook, call, type = "before", dep = None): + self.plugin = plugin + self.hook = hook + self.call = call + self.type = type + self.dep = dep - @param hook: the parent Hook - @type hook: Hook - @param type: the type of the connect ("before", "after", "override") - @type type: string - @param depend_plugin: a plugin we are dependant on - @type depend_plugin: string or None""" +class Hook (object): + """ + Representing a hook with all the `Call` s for the different types. + """ + + __slots__ = ("before", "override", "after") - self.type = type - self.hook = hook - self.depend_plugin = depend_plugin + def __init__ (self): + self.before = [] + self.override = None + self.after = [] - def is_before_type (self): - return self.type == "before" +class Plugin (object): + """ + This is the main plugin object. It is used where ever a plugin is wanted, and it is the one, which needs to be subclassed by plugin authors. - def is_after_type (self): - return self.type == "after" + :CVariables: - def is_override_type (self): - return self.type == "override" + STAT_DISABLED : status + Status: Disabled. -class Hook: - """A single <hook>-element.""" + STAT_TEMP_ENABLED : status + Status: Enabled for this session only. - def __init__ (self, plugin, hook, call): - """Constructor. + STAT_ENABLED : status + Status: Enabled. - @param plugin: the parent Plugin - @type plugin: Plugin - @param hook: the hook to add to - @type hook: string - @param call: the call to make - @type call: string""" + STAT_TEMP_DISABLED : status + Status: Disabled for this session only. - self.plugin = plugin - self.hook = hook - self.call = call - self.connects = [] + STAT_HARD_DISABLED : status + Status: Forced disabled by program (i.e. because of errors in the plugin). + """ - def parse_connects (self, connects): - """This gets a list of <connect>-elements and parses them. - - @param connects: the list of <connect>'s - @type connects: NodeList""" - - if not connects: # no connects - assume "before" connect - self.connects.append(Connect(self, "before", None)) + (STAT_DISABLED, STAT_TEMP_ENABLED, STAT_ENABLED, STAT_TEMP_DISABLED) = range(4) + STAT_HARD_DISABLED = -1 + + def __init__ (self, disable = False): + """ + :param disable: Forcefully disable the plugin + :type disable: bool + """ + self.__menus = [] #: List of `Menu` + self.__calls = [] #: List of `Call` + self._unresolved_deps = False #: Does this plugin has unresolved dependencies? + + self.status = self.STAT_ENABLED #: The status of this plugin - for c in connects: - type = c.getAttribute("type") - if type == '': - type = "before" - - # get dep_plugin if available - dep_plugin = None - if c.hasChildNodes(): - nodes = c.childNodes - dep_plugin = nodes[0].nodeValue.strip() - - connect = Connect(self, type, dep_plugin) - self.connects.append(connect) + if disable: + self.status = self.STAT_HARD_DISABLED + + def _init (self): + """ + Method called from outside to init the extension parts of this plugin. + If the current status is `STAT_HARD_DISABLED` or there are unresolved dependencies, the init process is not started. + """ + + for d in self.deps: + if not system.find_packages(d, pkgSet=system.SET_INSTALLED, with_version = False): + self._unresolved_deps = True + break -class Plugin: - """A complete plugin.""" - - (STAT_DISABLED, STAT_TEMP_ENABLED, STAT_ENABLED, STAT_TEMP_DISABLED) = range(4) - - def __init__ (self, file, name, author): - """Constructor. - - @param file: the file name of the plugin.xml - @type file: string - @param name: the name of the plugin - @type name: Node - @param author: the author of the plugin - @type author: Node""" + if self.status != self.STAT_HARD_DISABLED and not self._unresolved_deps: + self.init() + + def init (self): + """ + This method is called by `_init` and should be overriden by the plugin author. + + :precond: No unresolved deps and the status is not `STAT_HARD_DISABLED`. + """ + pass + + @property + def author (self): + """ + Returns the plugin's author. + The author is given by the ``__author__`` variable. + + :rtype: string + """ + return getattr(self, "__author__", "") + + @property + def description (self): + """ + Returns the description of this plugin. + It is given by either a ``__description__`` variable or by the normal class docstring. + + :rtype: string + """ + if hasattr(self, "__description__"): + return self.__description__ + else: + doc = getattr(self, "__doc__", "") - self.file = file - self.name = name.firstChild.nodeValue.strip() - self.author = author.firstChild.nodeValue.strip() - self._import = None - self.hooks = [] - self.menus = [] - self.options = Options() + if not doc or doc == Plugin.__doc__: + return "" + else: + return doc + + @property + def name (self): + """ + The name of the plugin. If no ``__name__`` variable is given, the class name is taken. + + :rtype: string + """ + return getattr(self, "__name__", self.__class__.__name__) + + @property + def menus (self): + """ + Returns an iterator over the menus for this plugin. + + :rtype: iter<`Menu`> + """ + return iter(self.__menus) + + @property + def calls (self): + """ + Returns an iterator over the registered calls for this plugin. + + :rtype: iter<`Call`> + """ + return iter(self.__calls) + + @property + def deps (self): + """ + Returns an iterator of the dependencies or ``[]`` if there are none. + The dependencies are given in the ``__dependency__`` variable. + + :rtype: [] or iter<string> + """ + if hasattr(self, "__dependency__"): + return iter(self.__dependency__) + else: + return [] - self.status = self.STAT_ENABLED + @property + def enabled (self): + """ + Returns ``True`` if the plugin is enabled. - def parse_hooks (self, hooks): - """Gets an <hooks>-elements and parses it. + :rtype: boolean + :see: `status` + """ + return (self.status in (self.STAT_ENABLED, self.STAT_TEMP_ENABLED)) + + def add_menu (self, label, callable): + """ + Adds a new menu item for this plugin. - @param hooks: the hooks node - @type hooks: NodeList""" + :see: `Menu` + """ + self.__menus.append(Menu(label, callable)) - if hooks: - for h in hooks[0].getElementsByTagName("hook"): - hook = Hook(self, str(h.getAttribute("type")), str(h.getAttribute("call"))) - hook.parse_connects(h.getElementsByTagName("connect")) - self.hooks.append(hook) + def add_call (self, hook, callable, type = "before", dep = None): + """ + Adds a new call for this plugin. - def parse_menus (self, menus): - """Get a list of <menu>-elements and parses them. + :see: `Call` + """ + self.__calls.append(Call(self, hook, callable, type, dep)) - @param menus: the menu nodelist - @type menus: NodeList""" - if menus: - for item in menus[0].getElementsByTagName("item"): - menu = Menu(self, item.firstChild.nodeValue.strip(), str(item.getAttribute("call"))) - self.menus.append(menu) +class PluginQueue (object): + """ + Class managing and loading the plugins. + + :IVariables: - def parse_options (self, options): - if options: - for o in options: - self.options.parse(o.getElementsByTagName("option")) + plugins : `Plugin` [] + The list of managed plugins. - self.status = self.STAT_DISABLED if self.options.get("disabled") else self.STAT_ENABLED - - def set_import (self, imports): - """This gets a list of imports and parses them - setting the import needed to call the plugin. + hooks : string -> `Hook` + For each hook name map to a `Hook` object holding the corresponding `Call` objects. + """ - @param imports: list of imports - @type imports: NodeList - - @raises PluginImportException: if the plugin's import could not be imported""" + def __init__ (self): + """ + Constructor. + """ - if imports: - self._import = str(imports[0].firstChild.nodeValue.strip()) + self.plugins = [] + self.hooks = defaultdict(Hook) - try: # try loading - mod = __import__(self._import) - del mod - except ImportError: - raise PluginImportException, self._import + def get_plugins (self, list_disabled = True): + """ + Returns the plugins. - def needs_import (self): - """Returns True if an import is required prior to calling the plugin. - @rtype: bool""" - return self._import is not None + :param list_disabled: Also list disabled plugins. + :type list_disabled: boolean - def get_import (self): - """Returns the module to import. - @rtype: string""" - return self._import + :rtype: iter<`Plugin`> + """ + return (x for x in self.plugins if (x.enabled or list_disabled)) - def get_option(self, name): - return self.options.get(name) + def load (self): + """ + Load the plugins. + + This method scans the `portato.constants.PLUGIN_DIR` for python modules and tries to load them. If the modules are real plugins, + they have called `register` and thus the plugins are added. + """ + + # look them up + plugins = [] + for f in os.listdir(PLUGIN_DIR): + path = osp.join(PLUGIN_DIR, f) + if osp.isdir(path): + if osp.isfile(osp.join(path, "__init__.py")): + plugins.append(f) + else: + debug("'%s' is not a plugin: __init__.py missing", path) + else: + if f.endswith(".py"): + plugins.append(f[:-3]) + elif f.endswith(".pyc") or f.endswith(".pyo"): + pass # ignore .pyc and .pyo + else: + debug("'%s' is not a plugin: not a .py file", path) + + # some magic ... + plugin_module.__path__.insert(0, PLUGIN_DIR.rstrip("/")) # make the plugins loadable as "portato.plugins.name" + # add Plugin and register to the builtins, so the plugins always have the correct version :) + plugin_module.__builtins__["Plugin"] = Plugin + plugin_module.__builtins__["register"] = register + + for p in plugins: # import them + try: + exec "from portato.plugins import %s" % p in {} + except PluginLoadException, e: + error(_("Loading plugin '%(plugin)s' failed: %(error)s"), {"plugin" : p, "error" : e.message}) + except: + tb = traceback.format_exc() + error(_("Loading plugin '%(plugin)s' failed: %(error)s"), {"plugin" : p, "error" : tb}) - def set_option (self, name, value): - return self.options.set(name, value) + self._organize() - def is_enabled (self): - return (self.status in (self.STAT_ENABLED, self.STAT_TEMP_ENABLED)) + def add (self, plugin, disable = False): + """ + Adds a plugin to the internal list. -class PluginQueue: - """Class managing and loading the plugins.""" + :Parameters: - def __init__ (self, frontend, load = True): - """Constructor. - - @param frontend: the frontend used - @type frontend: string - @param load: if False nothing is loaded - @type load: bool""" + plugin : `Plugin` + ``Plugin`` subclass or instance to add. If a class is passed, it is instantiated. - self.frontend = frontend - self.list = [] - self.hooks = {} - if load: - self._load() + disable : boolean + Disable the plugin. - def get_plugins (self, list_disabled = True): - return [x for x in self.list if (x.is_enabled() or list_disabled)] + :raise PluginLoadException: passed plugin is not of class `Plugin` + """ - def get_plugin_data (self, list_disabled = False): - return [(x.name, x.author) for x in self.list if (x.is_enabled() or list_disabled)] + if callable(plugin) and Plugin in plugin.__bases__: + p = plugin(disable = disable) # need an instance and not the class + elif isinstance(plugin, Plugin): + p = plugin + if disable: + p.status = p.STAT_HARD_DISABLED + else: + raise PluginLoadException, "Is neither a subclass nor an instance of Plugin." - def get_plugin_menus (self, list_disabled = False): - return flatten([x.menus for x in self.list if (x.is_enabled() or list_disabled)]) + p._init() - def hook (self, hook, *hargs, **hkwargs): - """This is a method taking care of calling the plugins. + self.plugins.append(p) - B{Example}:: - - @pluginQueue.hook("some_hook", data) - def function (a, b, c): - orig_call(b,c,data) - - def function (a, b, c): - hook = pluginQueue.hook("some_hook", data) - hook(orig_call)(b,c,data) + if p.status == p.STAT_HARD_DISABLED: + msg = _("Plugin is disabled!") + elif p._unresolved_deps: + msg = _("Plugin has unresolved dependencies - disabled!") + else: + msg = "" + + info("%s %s", _("Plugin '%s' loaded.") % p.name, msg) - @param hook: the name of the hook - @type hook: string""" - - def call (cmd): - """Convienience function for calling a connect. - @param cmd: the actual Connect - @type cmd: Connect""" - - imp = "" - if cmd.hook.plugin.needs_import(): # get import - imp = cmd.hook.plugin.get_import() - try: - mod = __import__(imp, globals(), locals(), [cmd.hook.call]) - except ImportError: - error(_("%s cannot be imported."), imp) - return - - try: - f = eval("mod."+cmd.hook.call) # build function - except AttributeError: - error(_("%s cannot be imported."), cmd.hook.call) - else: - try: - f = eval(cmd.hook.call) - except AttributeError: - error(_("%s cannot be imported."), cmd.hook.call) + def hook (self, hook, *hargs, **hkwargs): + """ + The decorator to use in the program. + All parameters except ``hook`` are passed to plugins. - return f(*hargs, **hkwargs) # call function + :param hook: the name of the hook + :type hook: string + """ def hook_decorator (func): - """This is the real decorator.""" - - if hook in self.hooks: - list = self.hooks[hook] - else: - list = ([],[],[]) + """ + The real decorator. + """ + h = self.hooks[hook] + + active = Hook() # remove disabled - _list = ([],[],[]) - for i in range(len(list)): - for cmd in list[i]: - if cmd.hook.plugin.is_enabled(): - _list[i].append(cmd) - - list = _list + for type in ("before", "after"): + calls = getattr(h, type) + aCalls = getattr(active, type) + for call in calls: + if call.plugin.enabled: + aCalls.append(call) + + if h.override and h.override.plugin.enabled: + active.override = h.override + @wraps(func) def wrapper (*args, **kwargs): - ret = None # before - for cmd in list[0]: - debug(_("Accessing hook '%(hook)s' of plugin '%(plugin)s' (before)."), {"hook" : hook, "plugin": cmd.hook.plugin.name}) - call(cmd) + for call in active.before: + debug("Accessing hook '%(hook)s' of plugin '%(plugin)s' (before).", {"hook" : hook, "plugin": call.plugin.name}) + call.call(*hargs, **hkwargs) - if list[1]: # override - info(_("Overriding hook '%(hook)s' with plugin '%(plugin)s'."), {"hook": hook, "plugin": list[1][0].hook.plugin.name}) - ret = call(list[1][0]) + if active.override: # override + info(_("Overriding hook '%(hook)s' with plugin '%(plugin)s'."), {"hook": hook, "plugin": active.override.plugin.name}) + ret = active.override.call(*hargs, **hkwargs) else: # normal ret = func(*args, **kwargs) # after - for cmd in list[2]: - debug(_("Accessing hook '%(hook)s' of plugin '%(plugin)s' (after)."), {"hook":hook, "plugin": cmd.hook.plugin.name}) - call(cmd) + for call in active.after: + debug("Accessing hook '%(hook)s' of plugin '%(plugin)s' (after).", {"hook": hook, "plugin": call.plugin.name}) + call.call(*hargs, **hkwargs) return ret @@ -353,178 +425,127 @@ class PluginQueue: return hook_decorator - def _load (self): - """Load the plugins.""" - plugins = filter(lambda x: x.endswith(".xml"), os.listdir(PLUGIN_DIR)) - plugins = map(lambda x: os.path.join(PLUGIN_DIR, x), plugins) - schema = etree.XMLSchema(file = XSD_LOCATION) - - for p in plugins: - - try: - schema.assertValid(etree.parse(p)) - except etree.XMLSyntaxError: - error(_("Loading plugin '%s' failed. Invalid XML syntax."), p) - continue - except etree.DocumentInvalid: - error(_("Loading plugin '%s' failed. Plugin does not comply with schema."), p) - continue - - doc = parse(p) - - try: - list = doc.getElementsByTagName("plugin") - elem = list[0] - - frontendOK = None - frontends = elem.getElementsByTagName("frontends") - if frontends: - nodes = frontends[0].childNodes - for f in nodes[0].nodeValue.strip().split(): - if f == self.frontend: - frontendOK = True # one positive is enough - break - elif frontendOK is None: # do not make negative if we already have a positive - frontendOK = False - - if frontendOK is None or frontendOK == True: - plugin = Plugin(p, elem.getElementsByTagName("name")[0], elem.getElementsByTagName("author")[0]) - plugin.parse_hooks(elem.getElementsByTagName("hooks")) - plugin.set_import(elem.getElementsByTagName("import")) - plugin.parse_menus(elem.getElementsByTagName("menu")) - plugin.parse_options(elem.getElementsByTagName("options")) - - self.list.append(plugin) - info(_("Plugin '%s' loaded."), p) - - except PluginImportException, e: - error(_("Loading plugin '%(plugin)s' failed: Could not import %(import)s"), {"plugin": p, "import": e[0]}) - finally: - doc.unlink() - - self._organize() - def _organize (self): - """Creates the lists of connects in a way, that all dependencies are fullfilled.""" - unresolved_before = {} - unresolved_after = {} - star_before = {} # should be _before_ all other - star_after = {} # should be _after_ all other - - for plugin in self.list: # plugins - for hook in plugin.hooks: # hooks in plugin - if not hook.hook in self.hooks: - self.hooks[hook.hook] = ([], [], []) - - for connect in hook.connects: # connects in hook - - # type="before" - if connect.is_before_type(): - 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] = [] - - star_before[hook.hook].append(connect) + """ + Organizes the lists of `Call` in a way, that all dependencies are fullfilled. + """ + unresolved_before = defaultdict(list) + unresolved_after = defaultdict(list) + star_before = defaultdict(Hook) # should be _before_ all other + star_after = defaultdict(Hook) # should be _after_ all other + + for plugin in self.plugins: # plugins + for call in plugin.calls: # hooks in plugin + if call.type == "before": + if call.dep is None: # no dependency -> straight add + self.hooks[call.hook].before.append(call) + elif call.dep == "*": + self.hooks[call.hook].before.insert(0, call) + elif call.dep == "-*": + star_before[call.hook].append(call) + else: + named = [x.plugin.name for x in self.hooks[call.hook].before] + if call.dep in named: + self.hooks[call.hook].before.insert(named.index(call.dep), call) else: - named = [x.plugin.name for x in self.hooks[hook.hook][0]] - if connect.depend_plugin in named: - self.hooks[hook.hook][0].insert(named.index(connect.depend_plugin), connect) - else: - if not hook.hook in unresolved_before: - unresolved_before[hook.hook] = [] - - unresolved_before[hook.hook].append(connect) - - # type = "after" - elif connect.is_after_type(): - if connect.depend_plugin is None: # no dependency -> straight add - self.hooks[hook.hook][2].append(connect) - elif connect.depend_plugin == "*": - if not hook.hook in star_after: - star_after[hook.hook] = [] - - star_after[hook.hook].append(connect) - elif connect.depend_plugin == "-*": - self.hooks[hook.hook][2][0:0] = [connect] + unresolved_before[call.hook].append(call) + + elif call.type == "after": + if call.dep is None: # no dependency -> straight add + self.hooks[call.hook].after.append(call) + elif call.dep == "*": + star_after[call.hook].append(call) + elif call.dep == "-*": + self.hooks[call.hook].after.insert(0, call) + else: + named = [x.plugin.name for x in self.hooks[call.hook].after] + if call.dep in named: + self.hooks[call.hook].after.insert(named.index(call.dep)+1, call) else: - named = [x.hook.plugin.name for x in self.hooks[hook.hook][2]] - if connect.depend_plugin in named: - self.hooks[hook.hook][2].insert(named.index(connect.depend_plugin)+1, connect) - else: - if not hook.hook in unresolved_after: - unresolved_after[hook.hook] = [] - - unresolved_after[hook.hook].append(connect) + unresolved_after[call.hook].append(call) + + # type = "override" + elif call.type == "override": + if self.hooks[call.hook].override: + warning(_("For hook '%(hook)s' an override is already defined by plugin '%(plugin)s'!"), {"hook": call.hook, "plugin": self.hooks[call.hook].override.plugin.name}) + warning(_("It is now replaced by the one from plugin '%s'!"), call.plugin.name) - # type = "override" - elif connect.is_override_type(): - if self.hooks[hook.hook][1]: - warning(_("For hook '%(hook)s' an override is already defined by plugin '%(plugin)s'!"), {"hook": hook.hook, "plugin": self.hooks[hook.hook][1][0]}) - - self.hooks[hook.hook][1][:1] = [connect] - continue + self.hooks[call.hook].override = call + continue self._resolve_unresolved(unresolved_before, unresolved_after) - for hook in star_before: - self.hooks[hook][0].extend(star_before[hook]) # append the list + for hook, calls in star_before.iteritems(): + self.hooks[hook].before.extend(calls) # append the list - for hook in star_after: - self.hooks[hook][2].extend(star_after[hook]) # append the list + for hook, calls in star_after.iteritems(): + self.hooks[hook].after.extend(calls) # append the list def _resolve_unresolved (self, before, after): - def resolve(hook, list, idx, add): + def resolve(hook, list, type, add): if not list: return - changed = False - for connect in list[:]: - named = [x.plugin.name for x in self.hooks[hook][idx]] - if connect.depend_plugin in named: - changed = True - self.hooks[hook][idx].insert(named.index(connect.depend_plugin)+add, connect) - list.remove(connect) + callList = getattr(self.hooks[hook], type) + named = [x.plugin.name for x in callList] - if changed: - resolve(hook, list, idx, add) + while list and named: + newNamed = [] # use newNamed, so in each iteration only the plugins inserted last are searched + for call in list[:]: + if call.dep in named: + callList.insert(named.index(call.dep)+add, call) + list.remove(call) + newNamed.append(call.plugin.name) + + named = newNamed for l in list: - warning("Command for hook '%(hook)s' in plugin '%(plugin)s' could not be added due to missing dependant: '%(dep)s'!", {"hook": hook, "plugin": l.hook.plugin.name, "dep": l.depend_plugin}) + warning(_("Command for hook '%(hook)s' in plugin '%(plugin)s' could not be added due to missing dependant: '%(dep)s'!"), {"hook": hook, "plugin": l.plugin.name, "dep": l.dep}) for hook in before: - resolve(hook, before[hook], 0, 0) + resolve(hook, before[hook], "before", 0) for hook in after: - resolve(hook, after[hook], 2, 1) + resolve(hook, after[hook], "after", 1) __plugins = None -def load_plugins(frontend): - """Loads the plugins for a given frontend. - @param frontend: The frontend. See L{constants.FRONTENDS} for the correct list of values. - @type frontend: string""" - +def load_plugins(): + """ + Loads the plugins. + """ + global __plugins - if __plugins is None or __plugins.frontend != frontend: - __plugins = PluginQueue(frontend) + if __plugins is None: + __plugins = PluginQueue() + __plugins.load() + def get_plugin_queue(): - """Returns the actual L{PluginQueue}. If it is C{None}, they are not being loaded yet. + """ + Returns the actual `PluginQueue`. If it is ``None``, they are not being loaded yet. - @returns: the actual L{PluginQueue} or C{None} - @rtype: PluginQueue""" + :rtype: `PluginQueue` or ``None``""" return __plugins def hook(hook, *args, **kwargs): + """ + Shortcut to `PluginQueue.hook`. If no `PluginQueue` is loaded, this does nothing. + """ if __plugins is None: def pseudo_decorator(f): return f return pseudo_decorator else: return __plugins.hook(hook, *args, **kwargs) + +def register (plugin, disable = False): + """ + Registers a plugin. + + :see: `PluginQueue.add` + """ + if __plugins is not None: + __plugins.add(plugin, disable) diff --git a/portato/plugins/__init__.py b/portato/plugins/__init__.py index fe95dbc..5080cec 100644 --- a/portato/plugins/__init__.py +++ b/portato/plugins/__init__.py @@ -3,9 +3,14 @@ # File: portato/plugins/__init__.py # This file is part of the Portato-Project, a graphical portage-frontend. # -# Copyright (C) 2007 René 'Necoro' Neumann +# Copyright (C) 2007-2008 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> + +""" +This is a dummy module. The plugins loaded from here are in reality stored +in /usr/share/portato/plugins or alike. +""" diff --git a/portato/plugins/etc_proposals.py b/portato/plugins/etc_proposals.py deleted file mode 100644 index d5f707f..0000000 --- a/portato/plugins/etc_proposals.py +++ /dev/null @@ -1,31 +0,0 @@ -# -*- coding: utf-8 -*- -# -# File: portato/plugins/etc_proposals.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> - -from portato.helper import error - -import os -from subprocess import Popen - -PROG=["/usr/sbin/etc-proposals"] - -def launch (options = []): - if os.getuid() == 0: - Popen(PROG+options) - else: - error("ETC_PROPOSALS :: %s",_("Cannot start etc-proposals. Not root!")) - -def etc_prop (*args, **kwargs): - """Entry point for this plugin.""" - launch(["--fastexit"]) - -def etc_prop_menu (*args, **kwargs): - launch() diff --git a/portato/plugins/exception.py b/portato/plugins/exception.py deleted file mode 100644 index 64bdb77..0000000 --- a/portato/plugins/exception.py +++ /dev/null @@ -1,2 +0,0 @@ -def throw (*args, **kwargs): - raise Exception, "As requested, Sir!" diff --git a/portato/plugins/new_version.py b/portato/plugins/new_version.py deleted file mode 100644 index fe69292..0000000 --- a/portato/plugins/new_version.py +++ /dev/null @@ -1,58 +0,0 @@ -try: - from bzrlib import plugin, branch -except ImportError: - plugin = branch = None -import gobject - -from portato.helper import debug, warning -from portato import get_listener -from portato.constants import VERSION, APP_ICON, APP -from portato.gui.utils import GtkThread - -def find_version (rev): - try: - b = branch.Branch.open("lp:portato") - except Exception, e: - warning("NEW_VERSION :: Exception occured while accessing the remote branch: %s", str(e)) - return - - debug("NEW_VERSION :: Installed rev: %s - Current rev: %s", rev, b.revno()) - if int(rev) < int(b.revno()): - def callback(): - get_listener().send_notify(base = "New Portato Live Version Found", descr = "You have rev. %s, but the most recent revision is %s." % (rev, b.revno()), icon = APP_ICON) - return False - - gobject.idle_add(callback) - -def start_thread(rev): - t = GtkThread(target = find_version, name = "Version Updater Thread", args = (rev,)) - t.setDaemon(True) - t.start() - return True - -def run_menu (*args, **kwargs): - """ - Run the thread once. - """ - if not all((plugin, branch)): - return None - - v = VERSION.split() - if len(v) != 3 or v[0] != "9999": - return None - - rev = v[-1] - - plugin.load_plugins() # to have lp: addresses parsed - - start_thread(rev) - return rev - -def run (*args, **kwargs): - """ - Run the thread once and add a 30 minutes timer. - """ - rev = run_menu() - - if rev is not None: - gobject.timeout_add(30*60*1000, start_thread, rev) # call it every 30 minutes diff --git a/portato/plugins/notify.py b/portato/plugins/notify.py deleted file mode 100644 index 78812b3..0000000 --- a/portato/plugins/notify.py +++ /dev/null @@ -1,22 +0,0 @@ -import pynotify - -from portato import get_listener - -from portato.helper import warning, error, debug -from portato.constants import APP_ICON, APP - -def notify (retcode, **kwargs): - if retcode is None: - warning("NOTIFY :: %s", _("Notify called while process is still running!")) - else: - icon = APP_ICON - if retcode == 0: - text = _("Emerge finished!") - descr = "" - urgency = pynotify.URGENCY_NORMAL - else: - text = _("Emerge failed!") - descr = _("Error Code: %d") % retcode - urgency = pynotify.URGENCY_CRITICAL - - get_listener().send_notify(base = text, descr = descr, icon = icon, urgency = urgency) @@ -17,14 +17,13 @@ from portato.constants import VERSION, DATA_DIR, ICON_DIR, PLUGIN_DIR, TEMPLATE_ def plugin_list (*args): """Creates a list of correct plugin pathes out of the arguments.""" - return [("plugins/%s.xml" % x) for x in args] + return [("plugins/%s.py" % x) for x in args] packages = ["portato", "portato.gui", "portato.gui.windows", "portato.plugins", "portato.backend", "portato.backend.portage"] data_files = [ (TEMPLATE_DIR, [os.path.join("portato/gui/templates",x) for x in os.listdir("portato/gui/templates") if x.endswith(".glade")]), (ICON_DIR, ["icons/portato-icon.png"]), -# (PLUGIN_DIR, plugin_list("dbus_init")), - (DATA_DIR, ["plugin.xsd"])] + (PLUGIN_DIR, plugin_list("gpytage", "notify", "etc_proposals", "reload_portage"))] # do the distutils setup setup(name="Portato", |