diff options
26 files changed, 2697 insertions, 1116 deletions
diff --git a/doc/AUTHORS b/doc/AUTHORS index cdb99ab..5901f92 100644 --- a/doc/AUTHORS +++ b/doc/AUTHORS @@ -3,7 +3,8 @@ Main author: René 'Necoro' Neumann <necoro@necoro.net> Application icon: P4r4D0X (after an idea by wolfden) Shipped with code from: - - Philip Semanchuk (shm module) + - Nicola Larosa & Michael Foord (portato.odict) + see: http://www.voidspace.org.uk/python/odict.html Many thanks to the Porthole team which often inspired me or gave me hints. (And sometimes I even copied files ^^ ;)) diff --git a/doc/Changelog b/doc/Changelog index 2d16030..3c95fe8 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,11 @@ +next: +- allowed collapsed categories -- similar to porthole +- make max. title length of the console changeable by the user +- added shortcut for "Reload Portage" +- increased the maximum scrollback lines +- now only use external "shm" package +- bug fixes + 0.10: - added interactive search - added uselist to the "Generals" Tab @@ -15,14 +15,11 @@ Backend: - rewrite flags handling -- do not alert a block, if a package blocks a version of one package which is updated to a new one not being blocked anymore - also check installed packages if they block the current one - make sure, a package being removed from the queue is not needed as a dependency by another package - binary package support -- move resume_loop and shutdown plugins into main code - GUI: ==== @@ -31,13 +28,11 @@ Main Point: user preferences: - rotating systray icon - if a package has been merged, but there are still dependencies to be merged - move the dependencies up the tree (Updater) -- use alternative way of displaying categories (tree-like - see Porthole) - move GUI prefs out of the system config - make oneshot better - show reverse dependencies - reload package table when emerge is finished - show which package has a dependency that causes a block -- make the max length of the console title a user setting [quote] I would like to be able to see at a glance: diff --git a/etc/portato.cfg b/etc/portato.cfg index 07e6bdb..ca20fd9 100644 --- a/etc/portato.cfg +++ b/etc/portato.cfg @@ -53,16 +53,16 @@ useperversion = False # [GUI] -; show emerge progress in window title - boolean values +; show emerge progress in window title - boolean value updatetitle = on -; show emerge process in console title - boolean values +; show emerge process in console title - boolean value updateconsole = on -; show the systray icon?- boolean values +; show the systray icon?- boolean value showsystray = true -; control whether to completely hide on minimization - boolean values +; control whether to completely hide on minimization - boolean value hideonminimize = true ; the browser to use - string value @@ -70,14 +70,21 @@ browserCmd = firefox ; search the packages while you are typing ; disable this if you own a slow machine +; - boolean value searchontype = true -; show slots in the version list +; show slots in the version list - boolean value showslots = true -; sets the font of the console - string values +; sets the font of the console - string value consolefont = Monospace 11 +; controls the maximum length of the console title - integer value +titlelength = 60 + +; collapse categories with the same prefix - boolean value +collapsecats = true + ; sets the position of the tabs of the two notebooks ; allowed positions: ; - 1 : top @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: Portato\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2008-04-09 01:21+0100\n" +"PO-Revision-Date: 2008-06-08 02:09+0100\n" "Last-Translator: René 'Necoro' Neumann <necoro@necoro.net>\n" "Language-Team: \n" "MIME-Version: 1.0\n" @@ -13,22 +13,50 @@ msgstr "" "X-Poedit-SourceCharset: utf-8\n" #: portato/gui/templates/popups.glade:12 -#: portato/gui/templates/MainWindow.glade:190 -#: portato/gui/templates/MainWindow.glade:282 +#: 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:198 -#: portato/gui/templates/MainWindow.glade:264 +#: 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:68 +#: 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" + +#: portato/gui/templates/AboutWindow.glade:18 +msgid "" +"This software is licensed under the terms of the GPLv2.\n" +"Copyright (C) 2006-2008 René 'Necoro' Neumann <necoro@necoro.net>" +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" @@ -113,7 +141,7 @@ 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:892 +#: portato/gui/templates/MainWindow.glade:895 msgid "General" msgstr "Allgemein" @@ -133,53 +161,77 @@ msgstr "Zeige den Emerge Prozess im Konsolentitel" 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:573 +#: portato/gui/templates/PreferenceWindow.glade:576 msgid "Console Font" msgstr "Schriftart in Konsole" -#: portato/gui/templates/PreferenceWindow.glade:583 +#: portato/gui/templates/PreferenceWindow.glade:587 msgid "Chose a console font" msgstr "Wähle eine Schriftart" -#: portato/gui/templates/PreferenceWindow.glade:600 +#: 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:640 +#: portato/gui/templates/PreferenceWindow.glade:675 msgid "Package Tabs" msgstr "Pakettabs" -#: portato/gui/templates/PreferenceWindow.glade:665 +#: portato/gui/templates/PreferenceWindow.glade:700 msgid "System Tabs" msgstr "Systemtabs" -#: portato/gui/templates/PreferenceWindow.glade:691 +#: portato/gui/templates/PreferenceWindow.glade:726 msgid "<b>Tab Options</b>" msgstr "<b>Tab Optionen</b>" -#: portato/gui/templates/PreferenceWindow.glade:721 +#: 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:732 +#: 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:756 +#: portato/gui/templates/PreferenceWindow.glade:811 msgid "Visual" msgstr "Oberfläche" -#: portato/gui/templates/PreferenceWindow.glade:774 +#: portato/gui/templates/PreferenceWindow.glade:829 #: portato/gui/templates/PluginWindow.glade:47 #: portato/gui/templates/SearchWindow.glade:48 msgid "gtk-cancel" msgstr "" -#: portato/gui/templates/PreferenceWindow.glade:783 +#: portato/gui/templates/PreferenceWindow.glade:838 #: portato/gui/templates/PluginWindow.glade:59 msgid "gtk-apply" msgstr "" @@ -188,34 +240,6 @@ msgstr "" msgid "Plugins" msgstr "Plugins" -#: portato/gui/templates/UpdateWindow.glade:7 -msgid "Updateble 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" - -#: portato/gui/templates/AboutWindow.glade:18 -msgid "" -"This software is licensed under the terms of the GPLv2.\n" -"Copyright (C) 2006-2007 René 'Necoro' Neumann <necoro@necoro.net>" -msgstr "" -"This software is licensed under the terms of the GPLv2.\n" -"Copyright (C) 2006-2007 René 'Necoro' Neumann <necoro@necoro.net>" - #: portato/gui/templates/SearchWindow.glade:8 msgid "Search Results" msgstr "Ergebnisse" @@ -240,267 +264,223 @@ msgstr "_Einstellungen" msgid "Re_load Portage" msgstr "Aktua_lisiere Portage-Cache" -#: portato/gui/templates/MainWindow.glade:81 +#: portato/gui/templates/MainWindow.glade:83 msgid "_Emerge" msgstr "_Emerge" -#: portato/gui/templates/MainWindow.glade:89 +#: portato/gui/templates/MainWindow.glade:91 msgid "_Install" msgstr "_Installieren" -#: portato/gui/templates/MainWindow.glade:105 +#: portato/gui/templates/MainWindow.glade:107 msgid "_Uninstall" msgstr "_Deinstallieren" -#: portato/gui/templates/MainWindow.glade:121 -#: portato/gui/templates/MainWindow.glade:1111 +#: portato/gui/templates/MainWindow.glade:123 +#: portato/gui/templates/MainWindow.glade:1114 msgid "Update _World" msgstr "Update _World" -#: portato/gui/templates/MainWindow.glade:130 +#: portato/gui/templates/MainWindow.glade:132 msgid "Show Updatable P_ackages" msgstr "Zeige Pakete mit _Updates" -#: portato/gui/templates/MainWindow.glade:139 +#: portato/gui/templates/MainWindow.glade:141 msgid "Show _Only Installed Packages" msgstr "_Zeige nur installierte Pakete" -#: portato/gui/templates/MainWindow.glade:153 +#: portato/gui/templates/MainWindow.glade:155 msgid "_Sync" msgstr "_Sync" -#: portato/gui/templates/MainWindow.glade:169 +#: portato/gui/templates/MainWindow.glade:171 msgid "Save _Flags" msgstr "Speichere _Flags" -#: portato/gui/templates/MainWindow.glade:221 -#: portato/gui/templates/MainWindow.glade:1167 -#: portato/gui/windows/main.py:852 +#: portato/gui/templates/MainWindow.glade:223 +#: portato/gui/templates/MainWindow.glade:1170 +#: portato/gui/windows/main.py:767 msgid "Queue" msgstr "Queue" -#: portato/gui/templates/MainWindow.glade:228 +#: portato/gui/templates/MainWindow.glade:230 msgid "Oneshot" msgstr "Oneshot" -#: portato/gui/templates/MainWindow.glade:240 -#: portato/gui/templates/MainWindow.glade:1192 -#: portato/gui/windows/main.py:1165 -#: portato/gui/windows/main.py:1167 +#: portato/gui/templates/MainWindow.glade:242 +#: portato/gui/templates/MainWindow.glade:1195 +#: portato/gui/windows/main.py:1206 +#: portato/gui/windows/main.py:1208 msgid "Console" msgstr "Konsole" -#: portato/gui/templates/MainWindow.glade:247 +#: portato/gui/templates/MainWindow.glade:249 msgid "_Copy" msgstr "_Kopieren" -#: portato/gui/templates/MainWindow.glade:293 +#: portato/gui/templates/MainWindow.glade:295 msgid "Plu_gins" msgstr "Plu_gins" -#: portato/gui/templates/MainWindow.glade:305 +#: portato/gui/templates/MainWindow.glade:307 msgid "_?" msgstr "_?" -#: portato/gui/templates/MainWindow.glade:313 +#: portato/gui/templates/MainWindow.glade:315 msgid "_About" msgstr "_Über" -#: portato/gui/templates/MainWindow.glade:331 +#: portato/gui/templates/MainWindow.glade:333 msgid "_Plugins" msgstr "_Plugins" -#: portato/gui/templates/MainWindow.glade:373 +#: portato/gui/templates/MainWindow.glade:375 msgid "gtk-delete" msgstr "" -#: portato/gui/templates/MainWindow.glade:387 +#: portato/gui/templates/MainWindow.glade:389 msgid "gtk-find" msgstr "" -#: portato/gui/templates/MainWindow.glade:504 -#: portato/gui/templates/MainWindow.glade:744 -#: portato/gui/templates/MainWindow.glade:760 -#: portato/gui/templates/MainWindow.glade:872 +#: portato/gui/templates/MainWindow.glade:505 +#: portato/gui/templates/MainWindow.glade:623 +#: portato/gui/templates/MainWindow.glade:736 +#: portato/gui/templates/MainWindow.glade:751 msgid "label" msgstr "" -#: portato/gui/templates/MainWindow.glade:513 +#: portato/gui/templates/MainWindow.glade:516 msgid "Install onto system" msgstr "Füge zum System hinzu" -#: portato/gui/templates/MainWindow.glade:536 +#: portato/gui/templates/MainWindow.glade:539 msgid "Uninstall from system" msgstr "Lösche vom System" -#: portato/gui/templates/MainWindow.glade:638 -msgid "<b>Use Flags:</b>" -msgstr "<b>Use Flags:</b>" +#: portato/gui/templates/MainWindow.glade:639 +msgid "<b>License:</b>" +msgstr "<b>Lizenz:</b>" #: portato/gui/templates/MainWindow.glade:656 -#: portato/gui/windows/main.py:524 -#: portato/gui/windows/main.py:527 -#: portato/gui/windows/main.py:587 -#: portato/gui/windows/main.py:591 -msgid "Testing" -msgstr "Testing" - -#: portato/gui/templates/MainWindow.glade:689 -#: portato/gui/windows/main.py:507 -#: portato/gui/windows/main.py:510 -#: portato/gui/windows/main.py:513 -#: portato/gui/windows/main.py:614 -#: portato/gui/windows/main.py:618 -#: portato/gui/windows/main.py:625 -#: portato/gui/windows/main.py:629 -msgid "Masked" -msgstr "Masked" +msgid "<b>Installed, but not in portage anymore</b>" +msgstr "<b>Installiert, aber nicht mehr im Portage-Tree vorhanden</b>" -#: portato/gui/templates/MainWindow.glade:708 -#: portato/gui/windows/main.py:344 -msgid "Installed" -msgstr "Installiert" +#: portato/gui/templates/MainWindow.glade:671 +msgid "<span foreground='red'><b>MISSING KEYWORD</b></span>" +msgstr "<span foreground='red'><b>FEHLENDES KEYWORD</b></span>" -#: portato/gui/templates/MainWindow.glade:726 -msgid "<b>Homepage:</b>" -msgstr "<b>Homepage:</b>" +#: portato/gui/templates/MainWindow.glade:703 +msgid "<b>Description:</b>" +msgstr "<b>Beschreibung:</b>" -#: portato/gui/templates/MainWindow.glade:775 +#: portato/gui/templates/MainWindow.glade:719 msgid "<b>Overlay:</b>" msgstr "<b>Overlay:</b>" -#: portato/gui/templates/MainWindow.glade:792 -msgid "<b>Description:</b>" -msgstr "<b>Beschreibung:</b>" +#: portato/gui/templates/MainWindow.glade:767 +msgid "<b>Homepage:</b>" +msgstr "<b>Homepage:</b>" -#: portato/gui/templates/MainWindow.glade:824 -msgid "<span foreground='red'><b>MISSING KEYWORD</b></span>" -msgstr "<span foreground='red'><b>FEHLENDES KEYWORD</b></span>" +#: portato/gui/templates/MainWindow.glade:785 +#: portato/gui/windows/main.py:327 +msgid "Installed" +msgstr "Installiert" -#: portato/gui/templates/MainWindow.glade:839 -msgid "<b>Installed, but not in portage anymore</b>" -msgstr "<b>Installiert, aber nicht mehr im Portage-Tree vorhanden</b>" +#: portato/gui/templates/MainWindow.glade:804 +#: portato/gui/windows/main.py:402 +#: portato/gui/windows/main.py:405 +#: portato/gui/windows/main.py:408 +#: portato/gui/windows/main.py:507 +#: portato/gui/windows/main.py:511 +#: portato/gui/windows/main.py:518 +#: portato/gui/windows/main.py:522 +msgid "Masked" +msgstr "Masked" -#: portato/gui/templates/MainWindow.glade:854 -msgid "<b>License:</b>" -msgstr "<b>Lizenz:</b>" +#: portato/gui/templates/MainWindow.glade:837 +#: portato/gui/windows/main.py:419 +#: portato/gui/windows/main.py:422 +#: portato/gui/windows/main.py:480 +#: portato/gui/windows/main.py:484 +msgid "Testing" +msgstr "Testing" + +#: portato/gui/templates/MainWindow.glade:856 +msgid "<b>Use Flags:</b>" +msgstr "<b>Use Flags:</b>" -#: portato/gui/templates/MainWindow.glade:923 +#: portato/gui/templates/MainWindow.glade:926 msgid "Use List" msgstr "Use-Flag-Liste" -#: portato/gui/templates/MainWindow.glade:957 +#: portato/gui/templates/MainWindow.glade:960 msgid "Dependencies" msgstr "Abhängigkeiten" -#: portato/gui/templates/MainWindow.glade:984 +#: portato/gui/templates/MainWindow.glade:987 msgid "Ebuild" msgstr "Ebuild" -#: portato/gui/templates/MainWindow.glade:1011 +#: portato/gui/templates/MainWindow.glade:1014 msgid "Changelog" msgstr "Changelog" -#: portato/gui/templates/MainWindow.glade:1038 +#: portato/gui/templates/MainWindow.glade:1041 msgid "Files" msgstr "Dateien" -#: portato/gui/templates/MainWindow.glade:1076 +#: portato/gui/templates/MainWindow.glade:1079 msgid "Execute the current selected queue" msgstr "Führe die aktuell gewählte Queue aus" -#: portato/gui/templates/MainWindow.glade:1095 +#: portato/gui/templates/MainWindow.glade:1098 msgid "E_xecute" msgstr "_Ausführen" -#: portato/gui/templates/MainWindow.glade:1110 +#: portato/gui/templates/MainWindow.glade:1113 msgid "Calculate the packages which will be installed during an \"update world\"" msgstr "Berechnet die Pakete, welche während eines \"update world\" installiert werden würden" -#: portato/gui/templates/MainWindow.glade:1123 +#: portato/gui/templates/MainWindow.glade:1126 msgid "Remove the selected package from the queue" msgstr "Löscht das gewählte Paket aus der Queue" -#: portato/gui/templates/MainWindow.glade:1124 +#: portato/gui/templates/MainWindow.glade:1127 msgid "_Remove" msgstr "_Löschen" -#: portato/gui/templates/MainWindow.glade:1226 +#: portato/gui/templates/MainWindow.glade:1230 msgid "Log" msgstr "Log" -#: portato/config_parser.py:271 -#, python-format -msgid "Unrecognized line in configuration: %s" -msgstr "Unbekannte Zeile in Konfiguration: %s" - -#: portato/plugins/notify.py:11 -msgid "Notify called while process is still running!" -msgstr "\"Notify\" aufgerufen, während Emerge noch lief." +#: portato.py:40 +msgid "validates the given plugin xml instead of launching Portato" +msgstr "Validiert die gegebene Plugin-XML. Startet nicht Portato." -#: portato/plugins/notify.py:15 -msgid "Emerge finished!" -msgstr "Emerge-Prozess erfolgreich beendet!" +#: portato.py:43 +msgid "do not fork off as root" +msgstr "erzeuge keinen Root-Prozess" -#: portato/plugins/notify.py:19 -msgid "Emerge failed!" -msgstr "Emerge fehlgeschlagen!" +#: portato.py:43 +msgid "-L is deprecated" +msgstr "-L ist veraltet" -#: portato/plugins/notify.py:20 +#: portato.py:53 #, python-format -msgid "Error Code: %d" -msgstr "Fehler-Code: %d" - -#: portato/plugins/resume_loop.py:30 -msgid "Resume-loop called while process is still running!" -msgstr "\"Resume-Loop\" aufgerufen, während Emerge noch lief. Bug!" - -#: portato/plugins/etc_proposals.py:25 -msgid "Cannot start etc-proposals. Not root!" -msgstr "Kann etc-proposals nicht starten. Nur root kann das!" - -#: portato/plistener.py:89 -msgid "Listener has not been started." -msgstr "Listener wurde nicht gestartet." - -#: portato/gui/exception_handling.py:45 -msgid "A programming error has been detected during the execution of this program." -msgstr "Ein Fehler ist aufgetreten." - -#: portato/gui/exception_handling.py:46 -msgid "Bug Detected" -msgstr "Fehler aufgetreten" - -#: portato/gui/exception_handling.py:47 -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:49 -msgid "Show Details" -msgstr "Details" +msgid "Validation failed. XML syntax error: %s." +msgstr "Validierung fehlgeschlagen. XML Syntax Fehler: %s." -#: portato/gui/exception_handling.py:72 -#: portato/gui/exception_handling.py:135 -#, python-format -msgid "" -"Exception in thread \"%(thread)s\":\n" -"%(trace)s" -msgstr "" -"Exception im Thread \"%(thread)s\":\n" -"%(trace)s" +#: portato.py:56 +msgid "Validation failed. Does not comply with schema." +msgstr "Validierung gegen das Schema fehlgeschlagen." -#: portato/gui/exception_handling.py:88 -msgid "Save traceback..." -msgstr "Speichere Traceback..." +#: portato.py:59 +msgid "Validation succeeded." +msgstr "Validierung erfolgreich." -#: portato/gui/exception_handling.py:137 -#, python-format -msgid "" -"Exception:\n" -"%s" -msgstr "" -"Exception:\n" -"%s" +#: portato.py:65 +msgid "Starting Portato" +msgstr "Starte Portato" #: portato/gui/wrapper.py:59 msgid "oneshot" @@ -544,11 +524,6 @@ msgstr "Deinstallieren" msgid "Update" msgstr "Update" -#: portato/gui/updater.py:95 -#, 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/dialogs.py:17 msgid "" "There are some packages in the emerge queue and/or an emerge process is running.\n" @@ -604,8 +579,85 @@ msgstr "" msgid "Do you really want to clear the whole queue?" msgstr "Wirklich die gesamte Queue löschen?" +#: portato/gui/views.py:70 +msgid "Package is not installed" +msgstr "Paket ist nicht installiert" + +#: 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/views.py:96 +#, python-format +msgid "No %(old)s language file installed. Disable highlighting." +msgstr "Keine \"%(old)s\" Syntaxdatei gefunden. Deaktiviere Hervorhebung." + +#: portato/gui/views.py:115 +#: portato/gui/windows/main.py:670 +#, python-format +msgid "Error: %s" +msgstr "Fehler: %s" + +#: 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/exception_handling.py:51 +msgid "A programming error has been detected during the execution of this program." +msgstr "Ein Fehler ist aufgetreten." + +#: portato/gui/exception_handling.py:52 +msgid "Bug Detected" +msgstr "Fehler aufgetreten" + +#: portato/gui/exception_handling.py:53 +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:55 +msgid "Show Details" +msgstr "Details" + +#: portato/gui/exception_handling.py:78 +#: portato/gui/exception_handling.py:142 +#, 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:94 +msgid "Save traceback..." +msgstr "Speichere Traceback..." + +#: portato/gui/exception_handling.py:144 +#, python-format +msgid "" +"Exception:\n" +"%s" +msgstr "" +"Exception:\n" +"%s" + +#: portato/gui/__init__.py:21 +msgid "Loading Backend" +msgstr "Lade Backend" + +#: portato/gui/windows/search.py:57 +msgid "Results" +msgstr "Ergebnisse" + #: portato/gui/windows/update.py:47 -#: portato/gui/windows/main.py:343 +#: portato/gui/windows/main.py:326 #: portato/gui/windows/plugin.py:24 msgid "Enabled" msgstr "Aktiviert" @@ -614,132 +666,142 @@ msgstr "Aktiviert" msgid "Package" msgstr "Paket" -#: portato/gui/windows/splash.py:36 +#: portato/gui/windows/splash.py:35 #, python-format msgid "... is starting up: %s" msgstr "... startet: %s" -#: portato/gui/windows/main.py:172 +#: portato/gui/windows/preference.py:124 +msgid "Top" +msgstr "Oben" + +#: portato/gui/windows/preference.py:124 +msgid "Bottom" +msgstr "Unten" + +#: portato/gui/windows/preference.py:124 +msgid "Left" +msgstr "Links" + +#: portato/gui/windows/preference.py:124 +msgid "Right" +msgstr "Rechts" + +#: portato/gui/windows/main.py:155 msgid "<no description>" msgstr "<keine Beschreibung>" -#: portato/gui/windows/main.py:198 +#: portato/gui/windows/main.py:181 msgid "The first homepage part does not start with 'http' or 'ftp'." msgstr "Der erste Teil der Homepage startet nicht mit 'http' oder 'ftp'." -#: portato/gui/windows/main.py:202 +#: portato/gui/windows/main.py:185 msgid "Blank inside homepage." msgstr "Leerzeichen innerhalb der Homepage-URL." -#: portato/gui/windows/main.py:243 +#: portato/gui/windows/main.py:226 #, python-format msgid "If '%s' is disabled" msgstr "Wenn '%s' deaktiviert ist" -#: portato/gui/windows/main.py:245 +#: portato/gui/windows/main.py:228 #, python-format msgid "If '%s' is enabled" msgstr "Wenn '%s' aktiviert ist" -#: portato/gui/windows/main.py:253 +#: portato/gui/windows/main.py:236 msgid "One of the following" msgstr "Eins der folgenden" -#: portato/gui/windows/main.py:257 +#: portato/gui/windows/main.py:240 msgid "All of the following" msgstr "Alle folgenden" -#: portato/gui/windows/main.py:280 +#: portato/gui/windows/main.py:263 msgid "Can't display dependencies: This package has an unsupported dependency string." msgstr "Kann Abhängigkeiten nicht anzeigen: Format der Abhängigkeiten des Pakets wird nicht unterstützt." -#: portato/gui/windows/main.py:304 +#: portato/gui/windows/main.py:287 msgid "This is an expanded use flag and cannot be selected" msgstr "Dies ist ein \"Expanded Use Flag\" und kann daher nicht ausgewählt werden." -#: portato/gui/windows/main.py:345 +#: portato/gui/windows/main.py:328 msgid "Flag" msgstr "Flag" -#: portato/gui/windows/main.py:346 +#: portato/gui/windows/main.py:329 msgid "Description" msgstr "Beschreibung" -#: portato/gui/windows/main.py:357 -msgid "Versions" -msgstr "Versionen" - -#: portato/gui/windows/main.py:360 -msgid "Slot" -msgstr "Slot" - -#: portato/gui/windows/main.py:454 +#: portato/gui/windows/main.py:349 #, python-format msgid "Package could not be found: %s" msgstr "Paket konnte nicht gefunden werden: %s" -#: portato/gui/windows/main.py:516 -#: portato/gui/windows/main.py:616 +#: portato/gui/windows/main.py:411 +#: portato/gui/windows/main.py:509 msgid "Masked by user" msgstr "Vom Benutzer maskiert" -#: portato/gui/windows/main.py:699 +#: portato/gui/windows/main.py:592 msgid "Loading Config" msgstr "Lade Konfiguration" -#: portato/gui/windows/main.py:711 +#: portato/gui/windows/main.py:604 msgid "Creating Database" msgstr "Erstelle Datenbank" -#: portato/gui/windows/main.py:716 +#: portato/gui/windows/main.py:609 msgid "Loading Plugins" msgstr "Lade Plugins" -#: portato/gui/windows/main.py:729 +#: portato/gui/windows/main.py:622 msgid "Building frontend" msgstr "Erstelle Oberfläche" -#: portato/gui/windows/main.py:772 -#: portato/gui/views.py:115 -#, python-format -msgid "Error: %s" -msgstr "Fehler: %s" - -#: portato/gui/windows/main.py:811 +#: portato/gui/windows/main.py:709 msgid "Restoring Session" msgstr "Lade Session" -#: portato/gui/windows/main.py:820 +#: portato/gui/windows/main.py:718 msgid "Finishing startup" msgstr "Erledige letzte Handgriffe :)" -#: portato/gui/windows/main.py:855 +#: portato/gui/windows/main.py:770 msgid "Options" msgstr "Optionen" -#: portato/gui/windows/main.py:867 +#: portato/gui/windows/main.py:784 msgid "Categories" msgstr "Kategorien" -#: portato/gui/windows/main.py:903 +#: portato/gui/windows/main.py:843 msgid "Packages" msgstr "Pakete" -#: portato/gui/windows/main.py:971 +#: portato/gui/windows/main.py:891 +msgid "Versions" +msgstr "Versionen" + +#: portato/gui/windows/main.py:894 +msgid "Slot" +msgstr "Slot" + +#: portato/gui/windows/main.py:992 #, python-format msgid "Translating session from version %d to %d." msgstr "Upgrade Session von Version %d auf Version %d." -#: portato/gui/windows/main.py:974 +#: portato/gui/windows/main.py:995 #, python-format msgid "Cannot translate session from version %d to %d." msgstr "Kann Session nicht von Version %d nach %d upgraden." -#: portato/gui/windows/main.py:1296 +#: portato/gui/windows/main.py:1358 msgid "use flags" msgstr "Use Flags" -#: portato/gui/windows/main.py:1307 +#: portato/gui/windows/main.py:1369 msgid "masking keywords" msgstr "Masking Keywords" @@ -767,26 +829,6 @@ msgstr "Entwickler" msgid "Status" msgstr "Status" -#: portato/gui/windows/search.py:57 -msgid "Results" -msgstr "Ergebnisse" - -#: portato/gui/windows/preference.py:116 -msgid "Top" -msgstr "Oben" - -#: portato/gui/windows/preference.py:116 -msgid "Bottom" -msgstr "Unten" - -#: portato/gui/windows/preference.py:116 -msgid "Left" -msgstr "Links" - -#: portato/gui/windows/preference.py:116 -msgid "Right" -msgstr "Rechts" - #: portato/gui/session.py:21 msgid "Version mismatch." msgstr "Versionen stimmen nicht überein." @@ -818,132 +860,119 @@ 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/gui/views.py:70 -msgid "Package is not installed" -msgstr "Paket ist nicht installiert" +#: portato/backend/flags.py:527 +#, python-format +msgid "Conflicting values for masking status: %s" +msgstr "Konflikt beim Masking-Status: %s" -#: portato/gui/views.py:87 +#: portato/backend/portage/system.py:343 #, python-format -msgid "No %(old)s language file installed. Falling back to %(new)s." -msgstr "Keine \"%(old)s\" Syntaxdatei gefunden. Benutze \"%(new)s\"-Syntax." +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/views.py:96 +#: portato/backend/portage/system.py:413 #, python-format -msgid "No %(old)s language file installed. Disable highlighting." -msgstr "Keine \"%(old)s\" Syntaxdatei gefunden. Deaktiviere Hervorhebung." +msgid "Found a not installed dependency: %s." +msgstr "Nicht installierte Abhängigkeit gefunden: %s" -#: portato/gui/__init__.py:22 -msgid "Loading Backend" -msgstr "Lade Backend" +#: portato/backend/portage/system.py:455 +#, 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/catapult/package.py:99 -#: portato/backend/portage/package.py:132 +#: portato/backend/portage/package.py:131 +#: portato/backend/catapult/package.py:98 #, 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/backend/flags.py:528 -#, python-format -msgid "Conflicting values for masking status: %s" -msgstr "Konflikt beim Masking-Status: %s" +#: portato/helper.py:76 +msgid "Invalid dependency string" +msgstr "Ungültiger Dependency-String." -#: portato/backend/portage/system.py:333 +#: portato/session.py:47 #, 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 "Loading session from '%s'." +msgstr "Lade Session von '%s'." + +#: portato/plistener.py:88 +msgid "Listener has not been started." +msgstr "Listener wurde nicht gestartet." + +#: portato/plugins/etc_proposals.py:24 +msgid "Cannot start etc-proposals. Not root!" +msgstr "Kann etc-proposals nicht starten. Nur root kann das!" + +#: portato/plugins/resume_loop.py:29 +msgid "Resume-loop called while process is still running!" +msgstr "\"Resume-Loop\" aufgerufen, während Emerge noch lief. Bug!" + +#: portato/plugins/notify.py:10 +msgid "Notify called while process is still running!" +msgstr "\"Notify\" aufgerufen, während Emerge noch lief." -#: portato/backend/portage/system.py:403 +#: portato/plugins/notify.py:14 +msgid "Emerge finished!" +msgstr "Emerge-Prozess erfolgreich beendet!" + +#: portato/plugins/notify.py:18 +msgid "Emerge failed!" +msgstr "Emerge fehlgeschlagen!" + +#: portato/plugins/notify.py:19 #, python-format -msgid "Found a not installed dependency: %s." -msgstr "Nicht installierte Abhängigkeit gefunden: %s" +msgid "Error Code: %d" +msgstr "Fehler-Code: %d" -#: portato/backend/portage/system.py:445 +#: portato/config_parser.py:270 #, 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?" +msgid "Unrecognized line in configuration: %s" +msgstr "Unbekannte Zeile in Konfiguration: %s" -#: portato/plugin.py:298 -#: portato/plugin.py:304 -#: portato/plugin.py:309 +#: portato/plugin.py:297 +#: portato/plugin.py:303 +#: portato/plugin.py:308 #, python-format msgid "%s cannot be imported." msgstr "%s kann nicht importiert werden." -#: portato/plugin.py:336 +#: portato/plugin.py:335 #, python-format msgid "Accessing hook '%(hook)s' of plugin '%(plugin)s' (before)." msgstr "Aufruf des Hooks '%(hook)s' vom Plugin '%(plugin)s'. (before)" -#: portato/plugin.py:340 +#: portato/plugin.py:339 #, python-format msgid "Overriding hook '%(hook)s' with plugin '%(plugin)s'." msgstr "Überschreibe den Hook '%(hook)s' mit Plugin '%(plugin)s'." -#: portato/plugin.py:347 +#: portato/plugin.py:346 #, python-format msgid "Accessing hook '%(hook)s' of plugin '%(plugin)s' (after)." msgstr "Aufruf des Hooks '%(hook)s' vom Plugin '%(plugin)s'. (after)" -#: portato/plugin.py:367 +#: portato/plugin.py:366 #, python-format msgid "Loading plugin '%s' failed. Invalid XML syntax." msgstr "Laden des Plugins '%s\" fehlgeschlagen: XML-Syntax-Fehler." -#: portato/plugin.py:370 +#: portato/plugin.py:369 #, 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:398 +#: portato/plugin.py:397 #, python-format msgid "Plugin '%s' loaded." msgstr "Plugin '%s' geladen." -#: portato/plugin.py:401 +#: portato/plugin.py:400 #, 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." -#: portato/plugin.py:466 +#: portato/plugin.py:465 #, 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/session.py:47 -#, python-format -msgid "Loading session from '%s'." -msgstr "Lade Session von '%s'." - -#: portato/helper.py:77 -msgid "Invalid dependency string" -msgstr "Ungültiger Dependency-String." - -#: portato.py:42 -msgid "validates the given plugin xml instead of launching Portato" -msgstr "Validiert die gegebene Plugin-XML. Startet nicht Portato." - -#: portato.py:45 -msgid "do not fork off as root" -msgstr "erzeuge keinen Root-Prozess" - -#: portato.py:45 -msgid "-L is deprecated" -msgstr "-L ist veraltet" - -#: portato.py:55 -#, python-format -msgid "Validation failed. XML syntax error: %s." -msgstr "Validierung fehlgeschlagen. XML Syntax Fehler: %s." - -#: portato.py:58 -msgid "Validation failed. Does not comply with schema." -msgstr "Validierung gegen das Schema fehlgeschlagen." - -#: portato.py:61 -msgid "Validation succeeded." -msgstr "Validierung erfolgreich." - -#: portato.py:67 -msgid "Starting Portato" -msgstr "Starte Portato" - diff --git a/i18n/messages.pot b/i18n/messages.pot index 0bf4561..ff0bf5d 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-04-09 00:53+0200\n" +"POT-Creation-Date: 2008-06-08 02:05+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" @@ -17,22 +17,48 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" #: portato/gui/templates/popups.glade:12 -#: portato/gui/templates/MainWindow.glade:190 -#: portato/gui/templates/MainWindow.glade:282 +#: 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:198 -#: portato/gui/templates/MainWindow.glade:264 +#: 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:68 +#: 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 "" + +#: portato/gui/templates/AboutWindow.glade:18 +msgid "" +"This software is licensed under the terms of the GPLv2.\n" +"Copyright (C) 2006-2008 René 'Necoro' Neumann <necoro@necoro.net>" +msgstr "" + #: portato/gui/templates/PreferenceWindow.glade:7 msgid "Preferences" msgstr "" @@ -112,7 +138,7 @@ msgid "<b>Use Flag and Keyword Options</b>" msgstr "" #: portato/gui/templates/PreferenceWindow.glade:427 -#: portato/gui/templates/MainWindow.glade:892 +#: portato/gui/templates/MainWindow.glade:895 msgid "General" msgstr "" @@ -132,53 +158,77 @@ msgstr "" 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:573 +#: portato/gui/templates/PreferenceWindow.glade:576 msgid "Console Font" msgstr "" -#: portato/gui/templates/PreferenceWindow.glade:583 +#: portato/gui/templates/PreferenceWindow.glade:587 msgid "Chose a console font" msgstr "" -#: portato/gui/templates/PreferenceWindow.glade:600 +#: 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:640 +#: portato/gui/templates/PreferenceWindow.glade:675 msgid "Package Tabs" msgstr "" -#: portato/gui/templates/PreferenceWindow.glade:665 +#: portato/gui/templates/PreferenceWindow.glade:700 msgid "System Tabs" msgstr "" -#: portato/gui/templates/PreferenceWindow.glade:691 +#: portato/gui/templates/PreferenceWindow.glade:726 msgid "<b>Tab Options</b>" msgstr "" -#: portato/gui/templates/PreferenceWindow.glade:721 +#: portato/gui/templates/PreferenceWindow.glade:759 msgid "Show slots in the version list" msgstr "" -#: portato/gui/templates/PreferenceWindow.glade:732 +#: 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:756 +#: portato/gui/templates/PreferenceWindow.glade:811 msgid "Visual" msgstr "" -#: portato/gui/templates/PreferenceWindow.glade:774 +#: portato/gui/templates/PreferenceWindow.glade:829 #: portato/gui/templates/PluginWindow.glade:47 #: portato/gui/templates/SearchWindow.glade:48 msgid "gtk-cancel" msgstr "" -#: portato/gui/templates/PreferenceWindow.glade:783 +#: portato/gui/templates/PreferenceWindow.glade:838 #: portato/gui/templates/PluginWindow.glade:59 msgid "gtk-apply" msgstr "" @@ -187,32 +237,6 @@ msgstr "" msgid "Plugins" msgstr "" -#: portato/gui/templates/UpdateWindow.glade:7 -msgid "Updateble 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 "" - -#: portato/gui/templates/AboutWindow.glade:18 -msgid "" -"This software is licensed under the terms of the GPLv2.\n" -"Copyright (C) 2006-2007 René 'Necoro' Neumann <necoro@necoro.net>" -msgstr "" - #: portato/gui/templates/SearchWindow.glade:8 msgid "Search Results" msgstr "" @@ -237,256 +261,214 @@ msgstr "" msgid "Re_load Portage" msgstr "" -#: portato/gui/templates/MainWindow.glade:81 +#: portato/gui/templates/MainWindow.glade:83 msgid "_Emerge" msgstr "" -#: portato/gui/templates/MainWindow.glade:89 +#: portato/gui/templates/MainWindow.glade:91 msgid "_Install" msgstr "" -#: portato/gui/templates/MainWindow.glade:105 +#: portato/gui/templates/MainWindow.glade:107 msgid "_Uninstall" msgstr "" -#: portato/gui/templates/MainWindow.glade:121 -#: portato/gui/templates/MainWindow.glade:1111 +#: portato/gui/templates/MainWindow.glade:123 +#: portato/gui/templates/MainWindow.glade:1114 msgid "Update _World" msgstr "" -#: portato/gui/templates/MainWindow.glade:130 +#: portato/gui/templates/MainWindow.glade:132 msgid "Show Updatable P_ackages" msgstr "" -#: portato/gui/templates/MainWindow.glade:139 +#: portato/gui/templates/MainWindow.glade:141 msgid "Show _Only Installed Packages" msgstr "" -#: portato/gui/templates/MainWindow.glade:153 +#: portato/gui/templates/MainWindow.glade:155 msgid "_Sync" msgstr "" -#: portato/gui/templates/MainWindow.glade:169 +#: portato/gui/templates/MainWindow.glade:171 msgid "Save _Flags" msgstr "" -#: portato/gui/templates/MainWindow.glade:221 -#: portato/gui/templates/MainWindow.glade:1167 portato/gui/windows/main.py:852 +#: portato/gui/templates/MainWindow.glade:223 +#: portato/gui/templates/MainWindow.glade:1170 portato/gui/windows/main.py:767 msgid "Queue" msgstr "" -#: portato/gui/templates/MainWindow.glade:228 +#: portato/gui/templates/MainWindow.glade:230 msgid "Oneshot" msgstr "" -#: portato/gui/templates/MainWindow.glade:240 -#: portato/gui/templates/MainWindow.glade:1192 -#: portato/gui/windows/main.py:1165 portato/gui/windows/main.py:1167 +#: portato/gui/templates/MainWindow.glade:242 +#: portato/gui/templates/MainWindow.glade:1195 +#: portato/gui/windows/main.py:1206 portato/gui/windows/main.py:1208 msgid "Console" msgstr "" -#: portato/gui/templates/MainWindow.glade:247 +#: portato/gui/templates/MainWindow.glade:249 msgid "_Copy" msgstr "" -#: portato/gui/templates/MainWindow.glade:293 +#: portato/gui/templates/MainWindow.glade:295 msgid "Plu_gins" msgstr "" -#: portato/gui/templates/MainWindow.glade:305 +#: portato/gui/templates/MainWindow.glade:307 msgid "_?" msgstr "" -#: portato/gui/templates/MainWindow.glade:313 +#: portato/gui/templates/MainWindow.glade:315 msgid "_About" msgstr "" -#: portato/gui/templates/MainWindow.glade:331 +#: portato/gui/templates/MainWindow.glade:333 msgid "_Plugins" msgstr "" -#: portato/gui/templates/MainWindow.glade:373 +#: portato/gui/templates/MainWindow.glade:375 msgid "gtk-delete" msgstr "" -#: portato/gui/templates/MainWindow.glade:387 +#: portato/gui/templates/MainWindow.glade:389 msgid "gtk-find" msgstr "" -#: portato/gui/templates/MainWindow.glade:504 -#: portato/gui/templates/MainWindow.glade:744 -#: portato/gui/templates/MainWindow.glade:760 -#: portato/gui/templates/MainWindow.glade:872 +#: portato/gui/templates/MainWindow.glade:505 +#: portato/gui/templates/MainWindow.glade:623 +#: portato/gui/templates/MainWindow.glade:736 +#: portato/gui/templates/MainWindow.glade:751 msgid "label" msgstr "" -#: portato/gui/templates/MainWindow.glade:513 +#: portato/gui/templates/MainWindow.glade:516 msgid "Install onto system" msgstr "" -#: portato/gui/templates/MainWindow.glade:536 +#: portato/gui/templates/MainWindow.glade:539 msgid "Uninstall from system" msgstr "" -#: portato/gui/templates/MainWindow.glade:638 -msgid "<b>Use Flags:</b>" +#: portato/gui/templates/MainWindow.glade:639 +msgid "<b>License:</b>" msgstr "" -#: portato/gui/templates/MainWindow.glade:656 portato/gui/windows/main.py:524 -#: portato/gui/windows/main.py:527 portato/gui/windows/main.py:587 -#: portato/gui/windows/main.py:591 -msgid "Testing" +#: portato/gui/templates/MainWindow.glade:656 +msgid "<b>Installed, but not in portage anymore</b>" msgstr "" -#: portato/gui/templates/MainWindow.glade:689 portato/gui/windows/main.py:507 -#: portato/gui/windows/main.py:510 portato/gui/windows/main.py:513 -#: portato/gui/windows/main.py:614 portato/gui/windows/main.py:618 -#: portato/gui/windows/main.py:625 portato/gui/windows/main.py:629 -msgid "Masked" +#: portato/gui/templates/MainWindow.glade:671 +msgid "<span foreground='red'><b>MISSING KEYWORD</b></span>" msgstr "" -#: portato/gui/templates/MainWindow.glade:708 portato/gui/windows/main.py:344 -msgid "Installed" +#: portato/gui/templates/MainWindow.glade:703 +msgid "<b>Description:</b>" msgstr "" -#: portato/gui/templates/MainWindow.glade:726 -msgid "<b>Homepage:</b>" +#: portato/gui/templates/MainWindow.glade:719 +msgid "<b>Overlay:</b>" msgstr "" -#: portato/gui/templates/MainWindow.glade:775 -msgid "<b>Overlay:</b>" +#: portato/gui/templates/MainWindow.glade:767 +msgid "<b>Homepage:</b>" msgstr "" -#: portato/gui/templates/MainWindow.glade:792 -msgid "<b>Description:</b>" +#: portato/gui/templates/MainWindow.glade:785 portato/gui/windows/main.py:327 +msgid "Installed" msgstr "" -#: portato/gui/templates/MainWindow.glade:824 -msgid "<span foreground='red'><b>MISSING KEYWORD</b></span>" +#: portato/gui/templates/MainWindow.glade:804 portato/gui/windows/main.py:402 +#: portato/gui/windows/main.py:405 portato/gui/windows/main.py:408 +#: portato/gui/windows/main.py:507 portato/gui/windows/main.py:511 +#: portato/gui/windows/main.py:518 portato/gui/windows/main.py:522 +msgid "Masked" msgstr "" -#: portato/gui/templates/MainWindow.glade:839 -msgid "<b>Installed, but not in portage anymore</b>" +#: portato/gui/templates/MainWindow.glade:837 portato/gui/windows/main.py:419 +#: portato/gui/windows/main.py:422 portato/gui/windows/main.py:480 +#: portato/gui/windows/main.py:484 +msgid "Testing" msgstr "" -#: portato/gui/templates/MainWindow.glade:854 -msgid "<b>License:</b>" +#: portato/gui/templates/MainWindow.glade:856 +msgid "<b>Use Flags:</b>" msgstr "" -#: portato/gui/templates/MainWindow.glade:923 +#: portato/gui/templates/MainWindow.glade:926 msgid "Use List" msgstr "" -#: portato/gui/templates/MainWindow.glade:957 +#: portato/gui/templates/MainWindow.glade:960 msgid "Dependencies" msgstr "" -#: portato/gui/templates/MainWindow.glade:984 +#: portato/gui/templates/MainWindow.glade:987 msgid "Ebuild" msgstr "" -#: portato/gui/templates/MainWindow.glade:1011 +#: portato/gui/templates/MainWindow.glade:1014 msgid "Changelog" msgstr "" -#: portato/gui/templates/MainWindow.glade:1038 +#: portato/gui/templates/MainWindow.glade:1041 msgid "Files" msgstr "" -#: portato/gui/templates/MainWindow.glade:1076 +#: portato/gui/templates/MainWindow.glade:1079 msgid "Execute the current selected queue" msgstr "" -#: portato/gui/templates/MainWindow.glade:1095 +#: portato/gui/templates/MainWindow.glade:1098 msgid "E_xecute" msgstr "" -#: portato/gui/templates/MainWindow.glade:1110 +#: portato/gui/templates/MainWindow.glade:1113 msgid "" "Calculate the packages which will be installed during an \"update world\"" msgstr "" -#: portato/gui/templates/MainWindow.glade:1123 +#: portato/gui/templates/MainWindow.glade:1126 msgid "Remove the selected package from the queue" msgstr "" -#: portato/gui/templates/MainWindow.glade:1124 +#: portato/gui/templates/MainWindow.glade:1127 msgid "_Remove" msgstr "" -#: portato/gui/templates/MainWindow.glade:1226 +#: portato/gui/templates/MainWindow.glade:1230 msgid "Log" msgstr "" -#: portato/config_parser.py:271 -#, python-format -msgid "Unrecognized line in configuration: %s" -msgstr "" - -#: portato/plugins/notify.py:11 -msgid "Notify called while process is still running!" +#: portato.py:40 +msgid "validates the given plugin xml instead of launching Portato" msgstr "" -#: portato/plugins/notify.py:15 -msgid "Emerge finished!" +#: portato.py:43 +msgid "do not fork off as root" msgstr "" -#: portato/plugins/notify.py:19 -msgid "Emerge failed!" +#: portato.py:43 +msgid "-L is deprecated" msgstr "" -#: portato/plugins/notify.py:20 +#: portato.py:53 #, python-format -msgid "Error Code: %d" -msgstr "" - -#: portato/plugins/resume_loop.py:30 -msgid "Resume-loop called while process is still running!" -msgstr "" - -#: portato/plugins/etc_proposals.py:25 -msgid "Cannot start etc-proposals. Not root!" -msgstr "" - -#: portato/plistener.py:89 -msgid "Listener has not been started." -msgstr "" - -#: portato/gui/exception_handling.py:45 -msgid "" -"A programming error has been detected during the execution of this program." -msgstr "" - -#: portato/gui/exception_handling.py:46 -msgid "Bug Detected" -msgstr "" - -#: portato/gui/exception_handling.py:47 -msgid "" -"It probably isn't fatal, but should be reported to the developers " -"nonetheless." -msgstr "" - -#: portato/gui/exception_handling.py:49 -msgid "Show Details" +msgid "Validation failed. XML syntax error: %s." msgstr "" -#: portato/gui/exception_handling.py:72 portato/gui/exception_handling.py:135 -#, python-format -msgid "" -"Exception in thread \"%(thread)s\":\n" -"%(trace)s" +#: portato.py:56 +msgid "Validation failed. Does not comply with schema." msgstr "" -#: portato/gui/exception_handling.py:88 -msgid "Save traceback..." +#: portato.py:59 +msgid "Validation succeeded." msgstr "" -#: portato/gui/exception_handling.py:137 -#, python-format -msgid "" -"Exception:\n" -"%s" +#: portato.py:65 +msgid "Starting Portato" msgstr "" #: portato/gui/wrapper.py:59 @@ -531,12 +513,6 @@ msgstr "" msgid "Update" msgstr "" -#: portato/gui/updater.py:95 -#, python-format -msgid "" -"Trying to remove package '%s' from queue which does not exist in system." -msgstr "" - #: portato/gui/dialogs.py:17 msgid "" "There are some packages in the emerge queue and/or an emerge process is " @@ -587,7 +563,84 @@ msgstr "" msgid "Do you really want to clear the whole queue?" msgstr "" -#: portato/gui/windows/update.py:47 portato/gui/windows/main.py:343 +#: portato/gui/views.py:70 +msgid "Package is not installed" +msgstr "" + +#: portato/gui/views.py:87 +#, python-format +msgid "No %(old)s language file installed. Falling back to %(new)s." +msgstr "" + +#: portato/gui/views.py:96 +#, python-format +msgid "No %(old)s language file installed. Disable highlighting." +msgstr "" + +#: portato/gui/views.py:115 portato/gui/windows/main.py:670 +#, python-format +msgid "Error: %s" +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/exception_handling.py:51 +msgid "" +"A programming error has been detected during the execution of this program." +msgstr "" + +#: portato/gui/exception_handling.py:52 +msgid "Bug Detected" +msgstr "" + +#: portato/gui/exception_handling.py:53 +msgid "" +"It probably isn't fatal, but should be reported to the developers " +"nonetheless." +msgstr "" + +#: portato/gui/exception_handling.py:55 +msgid "Show Details" +msgstr "" + +#: portato/gui/exception_handling.py:78 portato/gui/exception_handling.py:142 +#, python-format +msgid "" +"Exception in thread \"%(thread)s\":\n" +"%(trace)s" +msgstr "" + +#: portato/gui/exception_handling.py:94 +msgid "Save traceback..." +msgstr "" + +#: portato/gui/exception_handling.py:144 +#, python-format +msgid "" +"Exception:\n" +"%s" +msgstr "" + +#: portato/gui/__init__.py:21 +msgid "Loading Backend" +msgstr "" + +#: portato/gui/windows/search.py:57 +msgid "Results" +msgstr "" + +#: portato/gui/windows/update.py:47 portato/gui/windows/main.py:326 #: portato/gui/windows/plugin.py:24 msgid "Enabled" msgstr "" @@ -596,132 +649,143 @@ msgstr "" msgid "Package" msgstr "" -#: portato/gui/windows/splash.py:36 +#: portato/gui/windows/splash.py:35 #, python-format msgid "... is starting up: %s" msgstr "" -#: portato/gui/windows/main.py:172 +#: portato/gui/windows/preference.py:124 +msgid "Top" +msgstr "" + +#: portato/gui/windows/preference.py:124 +msgid "Bottom" +msgstr "" + +#: portato/gui/windows/preference.py:124 +msgid "Left" +msgstr "" + +#: portato/gui/windows/preference.py:124 +msgid "Right" +msgstr "" + +#: portato/gui/windows/main.py:155 msgid "<no description>" msgstr "" -#: portato/gui/windows/main.py:198 +#: portato/gui/windows/main.py:181 msgid "The first homepage part does not start with 'http' or 'ftp'." msgstr "" -#: portato/gui/windows/main.py:202 +#: portato/gui/windows/main.py:185 msgid "Blank inside homepage." msgstr "" -#: portato/gui/windows/main.py:243 +#: portato/gui/windows/main.py:226 #, python-format msgid "If '%s' is disabled" msgstr "" -#: portato/gui/windows/main.py:245 +#: portato/gui/windows/main.py:228 #, python-format msgid "If '%s' is enabled" msgstr "" -#: portato/gui/windows/main.py:253 +#: portato/gui/windows/main.py:236 msgid "One of the following" msgstr "" -#: portato/gui/windows/main.py:257 +#: portato/gui/windows/main.py:240 msgid "All of the following" msgstr "" -#: portato/gui/windows/main.py:280 +#: portato/gui/windows/main.py:263 msgid "" "Can't display dependencies: This package has an unsupported dependency " "string." msgstr "" -#: portato/gui/windows/main.py:304 +#: portato/gui/windows/main.py:287 msgid "This is an expanded use flag and cannot be selected" msgstr "" -#: portato/gui/windows/main.py:345 +#: portato/gui/windows/main.py:328 msgid "Flag" msgstr "" -#: portato/gui/windows/main.py:346 +#: portato/gui/windows/main.py:329 msgid "Description" msgstr "" -#: portato/gui/windows/main.py:357 -msgid "Versions" -msgstr "" - -#: portato/gui/windows/main.py:360 -msgid "Slot" -msgstr "" - -#: portato/gui/windows/main.py:454 +#: portato/gui/windows/main.py:349 #, python-format msgid "Package could not be found: %s" msgstr "" -#: portato/gui/windows/main.py:516 portato/gui/windows/main.py:616 +#: portato/gui/windows/main.py:411 portato/gui/windows/main.py:509 msgid "Masked by user" msgstr "" -#: portato/gui/windows/main.py:699 +#: portato/gui/windows/main.py:592 msgid "Loading Config" msgstr "" -#: portato/gui/windows/main.py:711 +#: portato/gui/windows/main.py:604 msgid "Creating Database" msgstr "" -#: portato/gui/windows/main.py:716 +#: portato/gui/windows/main.py:609 msgid "Loading Plugins" msgstr "" -#: portato/gui/windows/main.py:729 +#: portato/gui/windows/main.py:622 msgid "Building frontend" msgstr "" -#: portato/gui/windows/main.py:772 portato/gui/views.py:115 -#, python-format -msgid "Error: %s" -msgstr "" - -#: portato/gui/windows/main.py:811 +#: portato/gui/windows/main.py:709 msgid "Restoring Session" msgstr "" -#: portato/gui/windows/main.py:820 +#: portato/gui/windows/main.py:718 msgid "Finishing startup" msgstr "" -#: portato/gui/windows/main.py:855 +#: portato/gui/windows/main.py:770 msgid "Options" msgstr "" -#: portato/gui/windows/main.py:867 +#: portato/gui/windows/main.py:784 msgid "Categories" msgstr "" -#: portato/gui/windows/main.py:903 +#: portato/gui/windows/main.py:843 msgid "Packages" msgstr "" -#: portato/gui/windows/main.py:971 +#: portato/gui/windows/main.py:891 +msgid "Versions" +msgstr "" + +#: portato/gui/windows/main.py:894 +msgid "Slot" +msgstr "" + +#: portato/gui/windows/main.py:992 #, python-format msgid "Translating session from version %d to %d." msgstr "" -#: portato/gui/windows/main.py:974 +#: portato/gui/windows/main.py:995 #, python-format msgid "Cannot translate session from version %d to %d." msgstr "" -#: portato/gui/windows/main.py:1296 +#: portato/gui/windows/main.py:1358 msgid "use flags" msgstr "" -#: portato/gui/windows/main.py:1307 +#: portato/gui/windows/main.py:1369 msgid "masking keywords" msgstr "" @@ -749,26 +813,6 @@ msgstr "" msgid "Status" msgstr "" -#: portato/gui/windows/search.py:57 -msgid "Results" -msgstr "" - -#: portato/gui/windows/preference.py:116 -msgid "Top" -msgstr "" - -#: portato/gui/windows/preference.py:116 -msgid "Bottom" -msgstr "" - -#: portato/gui/windows/preference.py:116 -msgid "Left" -msgstr "" - -#: portato/gui/windows/preference.py:116 -msgid "Right" -msgstr "" - #: portato/gui/session.py:21 msgid "Version mismatch." msgstr "" @@ -802,131 +846,118 @@ msgstr "" msgid "Error while compiling search expression: '%s'." msgstr "" -#: portato/gui/views.py:70 -msgid "Package is not installed" +#: portato/backend/flags.py:527 +#, python-format +msgid "Conflicting values for masking status: %s" msgstr "" -#: portato/gui/views.py:87 +#: portato/backend/portage/system.py:343 #, python-format -msgid "No %(old)s language file installed. Falling back to %(new)s." +msgid "No best match for %s. It seems not to be in the tree anymore." msgstr "" -#: portato/gui/views.py:96 +#: portato/backend/portage/system.py:413 #, python-format -msgid "No %(old)s language file installed. Disable highlighting." +msgid "Found a not installed dependency: %s." msgstr "" -#: portato/gui/__init__.py:22 -msgid "Loading Backend" +#: portato/backend/portage/system.py:455 +#, python-format +msgid "" +"Bug? No best match could be found for '%(package)s'. Needed by: '%(cpv)s'." msgstr "" -#: portato/backend/catapult/package.py:99 -#: portato/backend/portage/package.py:132 +#: portato/backend/portage/package.py:131 +#: portato/backend/catapult/package.py:98 #, python-format msgid "BUG in flags.new_masking_status. It returns '%s'" msgstr "" -#: portato/backend/flags.py:528 -#, python-format -msgid "Conflicting values for masking status: %s" +#: portato/helper.py:76 +msgid "Invalid dependency string" msgstr "" -#: portato/backend/portage/system.py:333 +#: portato/session.py:47 #, python-format -msgid "No best match for %s. It seems not to be in the tree anymore." +msgid "Loading session from '%s'." +msgstr "" + +#: portato/plistener.py:88 +msgid "Listener has not been started." +msgstr "" + +#: portato/plugins/etc_proposals.py:24 +msgid "Cannot start etc-proposals. Not root!" +msgstr "" + +#: portato/plugins/resume_loop.py:29 +msgid "Resume-loop called while process is still running!" +msgstr "" + +#: portato/plugins/notify.py:10 +msgid "Notify called while process is still running!" +msgstr "" + +#: portato/plugins/notify.py:14 +msgid "Emerge finished!" msgstr "" -#: portato/backend/portage/system.py:403 +#: portato/plugins/notify.py:18 +msgid "Emerge failed!" +msgstr "" + +#: portato/plugins/notify.py:19 #, python-format -msgid "Found a not installed dependency: %s." +msgid "Error Code: %d" msgstr "" -#: portato/backend/portage/system.py:445 +#: portato/config_parser.py:270 #, python-format -msgid "" -"Bug? No best match could be found for '%(package)s'. Needed by: '%(cpv)s'." +msgid "Unrecognized line in configuration: %s" msgstr "" -#: portato/plugin.py:298 portato/plugin.py:304 portato/plugin.py:309 +#: portato/plugin.py:297 portato/plugin.py:303 portato/plugin.py:308 #, python-format msgid "%s cannot be imported." msgstr "" -#: portato/plugin.py:336 +#: portato/plugin.py:335 #, python-format msgid "Accessing hook '%(hook)s' of plugin '%(plugin)s' (before)." msgstr "" -#: portato/plugin.py:340 +#: portato/plugin.py:339 #, python-format msgid "Overriding hook '%(hook)s' with plugin '%(plugin)s'." msgstr "" -#: portato/plugin.py:347 +#: portato/plugin.py:346 #, python-format msgid "Accessing hook '%(hook)s' of plugin '%(plugin)s' (after)." msgstr "" -#: portato/plugin.py:367 +#: portato/plugin.py:366 #, python-format msgid "Loading plugin '%s' failed. Invalid XML syntax." msgstr "" -#: portato/plugin.py:370 +#: portato/plugin.py:369 #, python-format msgid "Loading plugin '%s' failed. Plugin does not comply with schema." msgstr "" -#: portato/plugin.py:398 +#: portato/plugin.py:397 #, python-format msgid "Plugin '%s' loaded." msgstr "" -#: portato/plugin.py:401 +#: portato/plugin.py:400 #, python-format msgid "Loading plugin '%(plugin)s' failed: Could not import %(import)s" msgstr "" -#: portato/plugin.py:466 +#: portato/plugin.py:465 #, python-format msgid "" "For hook '%(hook)s' an override is already defined by plugin '%(plugin)s'!" msgstr "" - -#: portato/session.py:47 -#, python-format -msgid "Loading session from '%s'." -msgstr "" - -#: portato/helper.py:77 -msgid "Invalid dependency string" -msgstr "" - -#: portato.py:42 -msgid "validates the given plugin xml instead of launching Portato" -msgstr "" - -#: portato.py:45 -msgid "do not fork off as root" -msgstr "" - -#: portato.py:45 -msgid "-L is deprecated" -msgstr "" - -#: portato.py:55 -#, python-format -msgid "Validation failed. XML syntax error: %s." -msgstr "" - -#: portato.py:58 -msgid "Validation failed. Does not comply with schema." -msgstr "" - -#: portato.py:61 -msgid "Validation succeeded." -msgstr "" - -#: portato.py:67 -msgid "Starting Portato" -msgstr "" @@ -7,7 +7,7 @@ <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"> + <xs:element name="hooks" minOccurs="0"> <xs:complexType> <xs:sequence> <xs:element name="hook" minOccurs="1" maxOccurs="unbounded"> diff --git a/plugins/dbus_init.xml b/plugins/gpytage.xml index b17f269..51d9104 100644 --- a/plugins/dbus_init.xml +++ b/plugins/gpytage.xml @@ -1,13 +1,13 @@ -<?xml version="1.0" encoding="UTF-8" ?> +<?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>DBus Initializer</name> + <name>Etc-proposals</name> - <import>portato.plugins.dbus_init</import> + <import>portato.plugins.gpytage</import> - <hooks> - <hook type="main" call="dbus_init" /> - </hooks> + <menu> + <item call="gpytage">Config _Editor</item> + </menu> </plugin> diff --git a/plugins/old/resume_loop.xml b/plugins/old/resume_loop.xml deleted file mode 100644 index 124d61d..0000000 --- a/plugins/old/resume_loop.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>Emerge Resume Loop</name> - <import>portato.plugins.resume_loop</import> - - <hooks> - <hook type="emerge" call="set_data" /> - - <hook type="after_emerge" call="resume_loop"> - <connect type="before">*</connect> - </hook> - </hooks> - - <options> - <option>disabled</option> - </options> - -</plugin> diff --git a/plugins/old/shutdown.xml b/plugins/old/shutdown.xml deleted file mode 100644 index 75323b1..0000000 --- a/plugins/old/shutdown.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>Shutdown</name> - <import>portato.plugins.shutdown</import> - - <hooks> - <hook type = "after_emerge" call = "shutdown"> - <connect type="after">*</connect> - </hook> - </hooks> - - <options> - <option>disabled</option> - </options> -</plugin> diff --git a/portato/backend/portage/package.py b/portato/backend/portage/package.py index 02d141a..3502306 100644 --- a/portato/backend/portage/package.py +++ b/portato/backend/portage/package.py @@ -189,7 +189,7 @@ class PortagePackage (Package): return [d for d in deps if d[0] != "!"] - def get_dep_packages (self, depvar = ["RDEPEND", "PDEPEND", "DEPEND"], with_criterions = False): + def get_dep_packages (self, depvar = ["RDEPEND", "PDEPEND", "DEPEND"], with_criterions = False, return_blocks = False): dep_pkgs = [] # the package list # change the useflags, because we have internally changed some, but not made them visible for portage @@ -221,28 +221,37 @@ class PortagePackage (Package): for dep in deps: if dep[0] == '!': # blocking sth - dep = dep[1:] - if dep != self.get_cp(): # not cpv, because a version might explicitly block another one - blocked = system.find_packages(dep, "installed", only_cpv = True) - if blocked != []: - raise BlockedException, (self.get_cpv(), blocked[0]) + blocked = system.find_packages(dep, "installed", only_cpv = True) + if len(blocked) == 1: # only exact one match allowed to be harmless + if blocked[0].get_slot_cp() == self.get_slot_cp(): # blocks in the same slot are harmless + continue + + if return_blocks: + if with_criterions: + dep_pkgs.append((dep, dep)) + else: + dep_pkgs.append(dep) + else: + raise BlockedException, (self.get_cpv(), blocked[0].get_cpv()) + continue # finished with the blocking one -> next pkg = system.find_best_match(dep) if not pkg: # try to find masked ones - list = system.find_packages(dep, masked = True) - if not list: + pkgs = system.find_packages(dep, masked = True) + if not pkgs: raise PackageNotFoundException, dep - list = system.sort_package_list(list) + pkgs = system.sort_package_list(pkgs) + pkgs.reverse() done = False - for p in reversed(list): + for p in pkgs: if not p.is_masked(): dep_pkgs.append(create_dep_pkgs_data(dep, p)) done = True break if not done: - dep_pkgs.append(create_dep_pkgs_data(dep, list[0])) + dep_pkgs.append(create_dep_pkgs_data(dep, pkgs[0])) else: dep_pkgs.append(create_dep_pkgs_data(dep, pkg)) @@ -256,7 +265,10 @@ class PortagePackage (Package): return v def get_ebuild_path(self): - return self._settings.porttree.dbapi.findname(self._cpv) + if self.is_in_system(): + return self._settings.porttree.dbapi.findname(self._cpv) + else: + return self._settings.vartree.dbapi.findname(self._cpv) def get_files (self): if self.is_installed(): diff --git a/portato/backend/portage/system.py b/portato/backend/portage/system.py index e14908c..0d81945 100644 --- a/portato/backend/portage/system.py +++ b/portato/backend/portage/system.py @@ -168,6 +168,8 @@ class PortageSystem (SystemInterface): if self._version >= (2,1,5): t += [pkg.get_cpv() for pkg in self.find_packages(search_key, "installed") if not (pkg.is_testing(True) or pkg.is_masked())] + else: + t = self.find_packages(search_key, "installed", only_cpv=True) if t: t = unique_array(t) diff --git a/portato/gui/queue.py b/portato/gui/queue.py index a75048d..ce7e620 100644 --- a/portato/gui/queue.py +++ b/portato/gui/queue.py @@ -15,13 +15,16 @@ from __future__ import absolute_import # some stuff needed import os, pty import signal, threading, time +import itertools as itt from subprocess import Popen # some backend things from .. import backend, plugin from ..backend import flags, system -from ..helper import debug, info, send_signal_to_group, unique_array +from ..backend.exceptions import BlockedException +from ..helper import debug, info, warning, send_signal_to_group, unique_array, flatten from ..waiting_queue import WaitingQueue +from ..odict import OrderedDict from .updater import Updater # the wrapper @@ -55,6 +58,7 @@ class EmergeQueue: # dictionaries with data about the packages in the queue self.iters = {"install" : {}, "uninstall" : {}, "update" : {}} # iterator in the tree self.deps = {"install" : {}, "update" : {}} # all the deps of the package + self.blocks = {"install" : OrderedDict(), "update" : OrderedDict()} # member vars self.tree = tree @@ -189,20 +193,21 @@ class EmergeQueue: # add iter subIt = self.tree.append(it, self.tree.build_append_value(cpv, oneshot = oneshot, update = update, downgrade = downgrade, version = uVersion, useChange = changedUse)) - self.iters[type].update({cpv: subIt}) + self.iters[type][cpv] = subIt # get dependencies - deps = pkg.get_dep_packages() # this might raise a BlockedException - self.deps[type].update({cpv : deps}) + deps = pkg.get_dep_packages(return_blocks = True) + self.deps[type][cpv] = deps - # recursive call for d in deps: - try: + if d[0] == "!": # block + dep = d[1:] + if not dep in self.blocks[type]: + self.blocks[type][dep] = set() + + self.blocks[type][dep].add(cpv) + else: # recursive call self.update_tree(subIt, d, unmask, type = type) - except backend.BlockedException, e: # BlockedException occured -> delete current tree and re-raise exception - debug("Something blocked: %s", e[0]) - self.remove_with_children(subIt) - raise def append (self, cpv, type = "install", update = False, forceUpdate = False, unmask = False, oneshot = False): """Appends a cpv either to the merge queue or to the unmerge-queue. @@ -226,7 +231,7 @@ class EmergeQueue: if type in ("install", "update"): # emerge if update: pkg = self._get_pkg_from_cpv(cpv, unmask) - deps = pkg.get_dep_packages() + deps = pkg.get_dep_packages(return_blocks = True) if not forceUpdate and cpv in self.deps[type] and deps == self.deps[type][cpv]: return # nothing changed - return @@ -248,6 +253,51 @@ class EmergeQueue: self.update_tree(self.tree.get_emerge_it(), cpv, unmask, type = type, oneshot = oneshot) elif type == "update" and self.tree: self.update_tree(self.tree.get_update_it(), cpv, unmask, type = type, oneshot = oneshot) + + # handle blocks + if self.blocks[type]: + # check whether anything blocks something in the queue + for block in self.blocks[type]: + for c in self.iters[type]: + if system.cpv_matches(c, block): + blocked = ", ".join(self.blocks[type][block]) + warning("'%s' is blocked by: %s", c, blocked) + self.remove_with_children(self.iters[type][c], False) + raise BlockedException(c, blocked) + + # + # check whether we block a version that we are going to replace nevertheless + # + + # get the blocks that block an installed package + inst = [] + for block in self.blocks[type]: + pkgs = system.find_packages(block, "installed") + if pkgs: + inst.append((pkgs, block)) + + # the slot-cp's of the packages in the queue + slots = {} + for c in self.iters[type]: + slots[system.new_package(c).get_slot_cp()] = cpv + + # check the installed blocks against the slot-cp's + for pkgs, block in inst[:]: + done = False + for pkg in pkgs: + done = False + if pkg.get_slot_cp() in slots: + debug("Block '%s' can be ignored, because the blocking package is going to be replaced with '%s'.", block, slots[pkg.get_slot_cp()]) + done = True + if done: + inst.remove((pkgs,block)) + + if inst: # there is still something left to block + for pkgs, block in inst: + blocked = ", ".join(self.blocks[type][block]) + warning("'%s' blocks the installation of: %s", pkgs[0].get_cpv(), blocked) + self.remove_with_children(self.iters[type][cpv], False) + raise BlockedException(blocked, pkgs[0].get_cpv()) else: # unmerge self.unmergequeue.append(cpv) @@ -544,15 +594,32 @@ class EmergeQueue: @type it: Iterator @param removeNewFlags: True if new flags should be removed; False otherwise. Default: True. @type removeNewFlags: boolean""" + + def __remove (type, cpv): + del self.iters[type][cpv] + try: + del self.deps[type][cpv] + except KeyError: # this seems to be removed due to a BlockedException - so no deps here atm ;) + debug("Catched KeyError => %s seems not to be in self.deps. Should be no harm in normal cases.", cpv) + + for key in self.blocks[type].keys(): + if cpv in self.blocks[type][key]: + self.blocks[type][key].remove(cpv) + + if not self.blocks[type][key]: # list is empty -> remove the whole key + del self.blocks[type][key] + + if removeNewFlags: # remove the changed flags + flags.remove_new_use_flags(cpv) + flags.remove_new_masked(cpv) + flags.remove_new_testing(cpv) if self.tree.iter_has_parent(it): cpv = self.tree.get_value(it, self.tree.get_cpv_column()) if self.tree.is_in_emerge(it): # Emerge - del self.iters["install"][cpv] - try: - del self.deps["install"][cpv] - except KeyError: # this seems to be removed due to a BlockedException - so no deps here atm ;) - debug("Catched KeyError => %s seems not to be in self.deps. Should be no harm in normal cases.", cpv) + + __remove("install", cpv) + try: self.mergequeue.remove(cpv) except ValueError: # this is a dependency - ignore @@ -560,27 +627,14 @@ class EmergeQueue: self.oneshotmerge.remove(cpv) except ValueError: debug("Catched ValueError => %s seems not to be in merge-queue. Should be no harm.", cpv) - - if removeNewFlags: # remove the changed flags - flags.remove_new_use_flags(cpv) - flags.remove_new_masked(cpv) - flags.remove_new_testing(cpv) elif self.tree.is_in_unmerge(it): # in Unmerge del self.iters["uninstall"][cpv] self.unmergequeue.remove(cpv) elif self.tree.is_in_update(it): - del self.iters["update"][cpv] - try: - del self.deps["update"][cpv] - except KeyError: # this seems to be removed due to a BlockedException - so no deps here atm ;) - debug("Catched KeyError => %s seems not to be in self.deps. Should be no harm in normal cases.", cpv) - - if removeNewFlags: # remove the changed flags - flags.remove_new_use_flags(cpv) - flags.remove_new_masked(cpv) - flags.remove_new_testing(cpv) + __remove("update", cpv) + self.tree.remove(it) diff --git a/portato/gui/templates/MainWindow.glade b/portato/gui/templates/MainWindow.glade index 788a339..54a08c9 100644 --- a/portato/gui/templates/MainWindow.glade +++ b/portato/gui/templates/MainWindow.glade @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd"> -<!--Generated with glade3 3.4.3 on Mon Apr 21 23:09:43 2008 --> +<!--Generated with glade3 3.4.4 on Sat Jun 7 19:07:21 2008 --> <glade-interface> <widget class="GtkWindow" id="MainWindow"> <property name="border_width">2</property> @@ -46,8 +46,8 @@ <property name="label" translatable="yes">Re_load Portage</property> <property name="use_underline">True</property> <signal name="activate" handler="cb_reload_clicked"/> - <accelerator key="R" modifiers="GDK_CONTROL_MASK" signal="activate"/> <accelerator key="F5" modifiers="" signal="activate"/> + <accelerator key="R" modifiers="GDK_CONTROL_MASK" signal="activate"/> <child internal-child="image"> <widget class="GtkImage" id="menu-item-image9"> <property name="visible">True</property> @@ -463,7 +463,6 @@ <property name="visible">True</property> <property name="headers_clickable">True</property> <property name="search_column">1</property> - <signal name="cursor_changed" handler="cb_version_list_changed"/> </widget> </child> </widget> @@ -616,271 +615,271 @@ <placeholder/> </child> <child> - <widget class="GtkLabel" id="useFlagsLabel"> + <widget class="GtkLabel" id="licenseLabel"> <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> <property name="no_show_all">True</property> <property name="xalign">0</property> - <property name="label">use flags</property> - <property name="ellipsize">PANGO_ELLIPSIZE_END</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">4</property> - <property name="bottom_attach">5</property> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> <property name="y_options"></property> </packing> </child> <child> - <widget class="GtkLabel" id="useFlagsLabelLabel"> + <widget class="GtkLabel" id="licenseLabelLabel"> <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> - <property name="no_show_all">True</property> <property name="xalign">0</property> - <property name="label" translatable="yes"><b>Use Flags:</b></property> + <property name="label" translatable="yes"><b>License:</b></property> <property name="use_markup">True</property> <property name="single_line_mode">True</property> </widget> <packing> - <property name="top_attach">4</property> - <property name="bottom_attach">5</property> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> <property name="x_options">GTK_FILL</property> <property name="y_options"></property> <property name="y_padding">5</property> </packing> </child> <child> - <widget class="GtkCheckButton" id="testingCheck"> + <widget class="GtkLabel" id="notInSysLabel"> <property name="visible">True</property> - <property name="can_focus">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="no_show_all">True</property> - <property name="label" translatable="yes">Testing</property> - <property name="xalign">0</property> - <property name="response_id">0</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="cb_testing_toggled"/> + <property name="label" translatable="yes"><b>Installed, but not in portage anymore</b></property> + <property name="use_markup">True</property> </widget> <packing> - <property name="top_attach">7</property> - <property name="bottom_attach">8</property> - <property name="x_options">GTK_FILL</property> + <property name="right_attach">2</property> + <property name="top_attach">5</property> + <property name="bottom_attach">6</property> <property name="y_options"></property> </packing> </child> <child> - <widget class="GtkLabel" id="maskedLabel"> + <widget class="GtkLabel" id="missingLabel"> <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> - <property name="xalign">0</property> + <property name="no_show_all">True</property> + <property name="label" translatable="yes"><span foreground='red'><b>MISSING KEYWORD</b></span></property> + <property name="use_markup">True</property> </widget> <packing> - <property name="left_attach">1</property> <property name="right_attach">2</property> - <property name="top_attach">8</property> - <property name="bottom_attach">9</property> + <property name="top_attach">5</property> + <property name="bottom_attach">6</property> <property name="y_options"></property> </packing> </child> <child> - <widget class="GtkCheckButton" id="maskedCheck"> + <widget class="GtkHBox" id="linkBox"> <property name="visible">True</property> - <property name="can_focus">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="no_show_all">True</property> - <property name="label" translatable="yes">Masked</property> - <property name="xalign">0</property> - <property name="response_id">0</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="cb_masked_toggled"/> + <property name="spacing">5</property> + <child> + <placeholder/> + </child> </widget> <packing> - <property name="top_attach">8</property> - <property name="bottom_attach">9</property> - <property name="x_options">GTK_FILL</property> + <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> </packing> </child> <child> - <widget class="GtkCheckButton" id="installedCheck"> + <widget class="GtkLabel" id="descLabelLabel"> <property name="visible">True</property> - <property name="can_focus">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="no_show_all">True</property> - <property name="label" translatable="yes">Installed</property> <property name="xalign">0</property> - <property name="response_id">0</property> - <property name="draw_indicator">True</property> - <signal name="button_press_event" handler="cb_button_pressed"/> + <property name="label" translatable="yes"><b>Description:</b></property> + <property name="use_markup">True</property> + <property name="single_line_mode">True</property> </widget> <packing> - <property name="top_attach">6</property> - <property name="bottom_attach">7</property> <property name="x_options">GTK_FILL</property> <property name="y_options"></property> + <property name="y_padding">5</property> </packing> </child> <child> - <widget class="GtkLabel" id="homepageLinkLabel"> + <widget class="GtkLabel" id="overlayLabelLabel"> <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> + <property name="no_show_all">True</property> <property name="xalign">0</property> - <property name="label" translatable="yes"><b>Homepage:</b></property> + <property name="label" translatable="yes"><b>Overlay:</b></property> <property name="use_markup">True</property> <property name="single_line_mode">True</property> </widget> <packing> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> <property name="x_options">GTK_FILL</property> <property name="y_options"></property> <property name="y_padding">5</property> </packing> </child> <child> - <widget class="GtkLabel" id="overlayLabel"> + <widget class="GtkLabel" id="descLabel"> <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> - <property name="no_show_all">True</property> <property name="xalign">0</property> <property name="label" translatable="yes">label</property> - <property name="single_line_mode">True</property> + <property name="wrap">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"></property> </packing> </child> <child> - <widget class="GtkLabel" id="descLabel"> + <widget class="GtkLabel" id="overlayLabel"> <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> + <property name="no_show_all">True</property> <property name="xalign">0</property> <property name="label" translatable="yes">label</property> - <property name="wrap">True</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"></property> </packing> </child> <child> - <widget class="GtkLabel" id="overlayLabelLabel"> + <widget class="GtkLabel" id="homepageLinkLabel"> <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> - <property name="no_show_all">True</property> <property name="xalign">0</property> - <property name="label" translatable="yes"><b>Overlay:</b></property> + <property name="label" translatable="yes"><b>Homepage:</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="top_attach">2</property> + <property name="bottom_attach">3</property> <property name="x_options">GTK_FILL</property> <property name="y_options"></property> <property name="y_padding">5</property> </packing> </child> <child> - <widget class="GtkLabel" id="descLabelLabel"> + <widget class="GtkCheckButton" id="installedCheck"> <property name="visible">True</property> + <property name="can_focus">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="no_show_all">True</property> + <property name="label" translatable="yes">Installed</property> <property name="xalign">0</property> - <property name="label" translatable="yes"><b>Description:</b></property> - <property name="use_markup">True</property> - <property name="single_line_mode">True</property> + <property name="response_id">0</property> + <property name="draw_indicator">True</property> + <signal name="button_press_event" handler="cb_button_pressed"/> </widget> <packing> + <property name="top_attach">6</property> + <property name="bottom_attach">7</property> <property name="x_options">GTK_FILL</property> <property name="y_options"></property> - <property name="y_padding">5</property> </packing> </child> <child> - <widget class="GtkHBox" id="linkBox"> + <widget class="GtkCheckButton" id="maskedCheck"> <property name="visible">True</property> + <property name="can_focus">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="spacing">5</property> - <child> - <placeholder/> - </child> + <property name="no_show_all">True</property> + <property name="label" translatable="yes">Masked</property> + <property name="xalign">0</property> + <property name="response_id">0</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="cb_masked_toggled"/> </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="top_attach">8</property> + <property name="bottom_attach">9</property> + <property name="x_options">GTK_FILL</property> <property name="y_options"></property> </packing> </child> <child> - <widget class="GtkLabel" id="missingLabel"> + <widget class="GtkLabel" id="maskedLabel"> <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> - <property name="no_show_all">True</property> - <property name="label" translatable="yes"><span foreground='red'><b>MISSING KEYWORD</b></span></property> - <property name="use_markup">True</property> + <property name="xalign">0</property> </widget> <packing> + <property name="left_attach">1</property> <property name="right_attach">2</property> - <property name="top_attach">5</property> - <property name="bottom_attach">6</property> + <property name="top_attach">8</property> + <property name="bottom_attach">9</property> <property name="y_options"></property> </packing> </child> <child> - <widget class="GtkLabel" id="notInSysLabel"> + <widget class="GtkCheckButton" id="testingCheck"> <property name="visible">True</property> + <property name="can_focus">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="no_show_all">True</property> - <property name="label" translatable="yes"><b>Installed, but not in portage anymore</b></property> - <property name="use_markup">True</property> + <property name="label" translatable="yes">Testing</property> + <property name="xalign">0</property> + <property name="response_id">0</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="cb_testing_toggled"/> </widget> <packing> - <property name="right_attach">2</property> - <property name="top_attach">5</property> - <property name="bottom_attach">6</property> + <property name="top_attach">7</property> + <property name="bottom_attach">8</property> + <property name="x_options">GTK_FILL</property> <property name="y_options"></property> </packing> </child> <child> - <widget class="GtkLabel" id="licenseLabelLabel"> + <widget class="GtkLabel" id="useFlagsLabelLabel"> <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> + <property name="no_show_all">True</property> <property name="xalign">0</property> - <property name="label" translatable="yes"><b>License:</b></property> + <property name="label" translatable="yes"><b>Use Flags:</b></property> <property name="use_markup">True</property> <property name="single_line_mode">True</property> </widget> <packing> - <property name="top_attach">3</property> - <property name="bottom_attach">4</property> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> <property name="x_options">GTK_FILL</property> <property name="y_options"></property> <property name="y_padding">5</property> </packing> </child> <child> - <widget class="GtkLabel" id="licenseLabel"> + <widget class="GtkLabel" id="useFlagsLabel"> <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> <property name="no_show_all">True</property> <property name="xalign">0</property> - <property name="label" translatable="yes">label</property> + <property name="label">use flags</property> + <property name="ellipsize">PANGO_ELLIPSIZE_END</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">3</property> - <property name="bottom_attach">4</property> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> <property name="y_options"></property> </packing> </child> diff --git a/portato/gui/templates/PreferenceWindow.glade b/portato/gui/templates/PreferenceWindow.glade index 4cc7dce..3dc556b 100644 --- a/portato/gui/templates/PreferenceWindow.glade +++ b/portato/gui/templates/PreferenceWindow.glade @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd"> -<!--Generated with glade3 3.4.1 on Tue Mar 25 16:14:57 2008 --> +<!--Generated with glade3 3.4.4 on Sun Jun 8 01:45:17 2008 --> <glade-interface> <widget class="GtkWindow" id="PreferenceWindow"> <property name="border_width">5</property> @@ -517,7 +517,7 @@ <property name="can_focus">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="has_tooltip">True</property> - <property name="tooltip_markup">Update the package list with the current search results while you are typing. + <property name="tooltip_markup" translatable="yes">Update the package list with the current search results while you are typing. <b>Note</b>: Will slow down the typing process.</property> <property name="label" translatable="yes">Search while typing</property> <property name="yalign">0.47999998927116394</property> @@ -561,31 +561,66 @@ <property name="left_padding">12</property> <property name="right_padding">5</property> <child> - <widget class="GtkHBox" id="hbox1"> + <widget class="GtkVBox" id="vbox1"> <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> - <property name="spacing">5</property> <child> - <widget class="GtkLabel" id="label11"> + <widget class="GtkHBox" id="hbox1"> <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> - <property name="xalign">0</property> - <property name="label" translatable="yes">Console Font</property> + <property name="spacing">5</property> + <child> + <widget class="GtkLabel" id="label11"> + <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> + <property name="xalign">0</property> + <property name="label" translatable="yes">Console Font</property> + <property name="single_line_mode">True</property> + </widget> + </child> + <child> + <widget class="GtkFontButton" id="consoleFontBtn"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="response_id">0</property> + <property name="title" translatable="yes">Chose a console font</property> + <property name="use_font">True</property> + <property name="use_size">True</property> + <property name="show_style">False</property> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> </widget> </child> <child> - <widget class="GtkFontButton" id="consoleFontBtn"> + <widget class="GtkHBox" id="hbox"> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="response_id">0</property> - <property name="title" translatable="yes">Chose a console font</property> - <property name="use_font">True</property> - <property name="use_size">True</property> - <property name="show_style">False</property> + <child> + <widget class="GtkLabel" id="label2"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Maximum length of the console title</property> + <property name="single_line_mode">True</property> + </widget> + </child> + <child> + <widget class="GtkSpinButton" id="titleLengthSpinBtn"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="adjustment">0 0 300 1 10 10</property> + <property name="snap_to_ticks">True</property> + <property name="numeric">True</property> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> </widget> <packing> + <property name="padding">4</property> <property name="position">1</property> </packing> </child> @@ -714,13 +749,33 @@ <property name="left_padding">12</property> <property name="right_padding">5</property> <child> - <widget class="GtkCheckButton" id="showSlotsCheck"> + <widget class="GtkVBox" id="vbox3"> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="label" translatable="yes">Show slots in the version list</property> - <property name="response_id">0</property> - <property name="draw_indicator">True</property> + <child> + <widget class="GtkCheckButton" id="showSlotsCheck"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="label" translatable="yes">Show slots in the version list</property> + <property name="response_id">0</property> + <property name="draw_indicator">True</property> + </widget> + </child> + <child> + <widget class="GtkCheckButton" id="collapseCatCheck"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="has_tooltip">True</property> + <property name="tooltip_markup" translatable="yes">Organize the categories in a tree. Thereby collapse categories with the same prefix: +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.</property> + <property name="label" translatable="yes">Collapse categories with same prefix</property> + <property name="response_id">0</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> </widget> </child> </widget> diff --git a/portato/gui/templates/popups.glade b/portato/gui/templates/popups.glade index 270449b..63169c3 100644 --- a/portato/gui/templates/popups.glade +++ b/portato/gui/templates/popups.glade @@ -1,6 +1,6 @@ <?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:01:56 2008 --> +<!--Generated with glade3 3.4.4 on Mon Jun 9 20:49:26 2008 --> <glade-interface> <widget class="GtkMenu" id="systrayPopup"> <property name="visible">True</property> @@ -18,7 +18,7 @@ <property name="visible">True</property> <property name="label" translatable="yes">_Kill Emerge</property> <property name="use_underline">True</property> - <signal name="activate" handler="cb_kill_emerge"/> + <signal name="activate" handler="cb_kill_clicked"/> <child internal-child="image"> <widget class="GtkImage" id="menu-item-image16"> <property name="visible">True</property> diff --git a/portato/gui/updater.py b/portato/gui/updater.py index f293fbc..ba46ffd 100644 --- a/portato/gui/updater.py +++ b/portato/gui/updater.py @@ -15,7 +15,7 @@ from __future__ import absolute_import from ..backend import system import threading, subprocess, time -from ..helper import debug, error +from ..helper import debug, warning, error class Updater (object): """ @@ -79,21 +79,26 @@ q """ self.stopEvent.set() - def find (self, pv): + def find (self, pv, masked = False): """ As qlop only returns 'package-version' we need to assign it to a cpv. This is done here. """ - pkgs = system.find_packages("=%s" % pv, only_cpv = True) + pkgs = system.find_packages("=%s" % pv, only_cpv = True, masked = masked) if len(pkgs) > 1: # ambigous - try to find the one which is also in the iterators for p in pkgs: if p in self.iterators: return p elif not pkgs: # nothing found =| - error(_("Trying to remove package '%s' from queue which does not exist in system."), pv) - return None + if not masked: + warning(_("No unmasked version of package '%s' found. Trying masked ones. This normally should not happen..."), pv) + return self.find(pv, True) + + else: + error(_("Trying to remove package '%s' from queue which does not exist in system."), pv) + return None else: # only one choice =) return pkgs[0] diff --git a/portato/gui/windows/main.py b/portato/gui/windows/main.py index 36054a1..b675d17 100644 --- a/portato/gui/windows/main.py +++ b/portato/gui/windows/main.py @@ -19,6 +19,7 @@ import gobject # other import os.path import itertools as itt +from collections import defaultdict # our backend stuff from ...backend import flags, system # must be the first to avoid circular deps @@ -67,10 +68,6 @@ class PackageTable: # the notebook self.notebook = self.tree.get_widget("packageNotebook") - # the version combo - self.versionList = self.tree.get_widget("versionList") - self.build_version_list() - # chechboxes self.installedCheck = self.tree.get_widget("installedCheck") self.maskedCheck = self.tree.get_widget("maskedCheck") @@ -117,15 +114,13 @@ class PackageTable: self.icons["or"] = self.window.render_icon(gtk.STOCK_MEDIA_PAUSE, gtk.ICON_SIZE_MENU) self.icons["block"] = self.window.render_icon(gtk.STOCK_NO, gtk.ICON_SIZE_MENU) - def update (self, cp, queue = None, version = None, doEmerge = True, instantChange = False, type = None): + def update (self, pkg, queue = None, doEmerge = True, instantChange = False, type = None): """Updates the table to show the contents for the package. - @param cp: the selected package - @type cp: string (cp) + @param pkg: the selected package + @type pkg: Package @param queue: emerge-queue (if None the emerge-buttons are disabled) @type queue: EmergeQueue - @param version: if not None, specifies the version to select - @type version: string @param doEmerge: if False, the emerge buttons are disabled @type doEmerge: boolean @param instantChange: if True the changed keywords are updated instantly @@ -133,40 +128,28 @@ class PackageTable: @param type: the type of the queue this package is in; if None there is no queue :) @type type: string""" - self.cp = cp # category/package - self.version = version # version - if not None this is used + self.pkg = pkg self.queue = queue self.doEmerge = doEmerge self.instantChange = instantChange self.type = type - # packages and installed packages - if not self.doEmerge: - self.instPackages = self.packages = system.find_packages("=%s-%s" % (cp, version), masked = True) - else: - self.packages = system.sort_package_list(system.find_packages(cp, masked = True)) - self.instPackages = system.sort_package_list(system.find_packages(cp, "installed", masked = True)) - - # version-combo-box - self.versionList.get_model().clear() - self.fill_version_list() - if not self.queue or not self.doEmerge: self.emergeBtn.set_sensitive(False) self.unmergeBtn.set_sensitive(False) # current status - self.cb_version_list_changed() + self._update_table() self.vb.show_all() def hide (self): self.vb.hide_all() def set_labels (self): - pkg = self.actual_package() + pkg = self.pkg # name - self.nameLabel.set_markup("<b>%s</b>" % pkg.get_cp()) + self.nameLabel.set_markup("<b>%s</b>" % pkg.get_cpv()) # description desc = pkg.get_package_settings("DESCRIPTION") or _("<no description>") @@ -275,7 +258,7 @@ class PackageTable: store.append(it, [get_icon(dep), dep.dep]) try: - deptree = self.actual_package().get_dependencies() + deptree = self.pkg.get_dependencies() except AssertionError: w = _("Can't display dependencies: This package has an unsupported dependency string.") error(w) @@ -285,7 +268,7 @@ class PackageTable: def fill_use_list(self): - pkg = self.actual_package() + pkg = self.pkg pkg_flags = pkg.get_iuse_flags() pkg_flags.sort() @@ -309,7 +292,7 @@ class PackageTable: enabled = use in euse installed = use in instuse - store.append(actual_exp_it, [enabled, installed, use, system.get_use_desc(use, self.cp)]) + store.append(actual_exp_it, [enabled, installed, use, system.get_use_desc(use, self.pkg.get_cp())]) def build_dep_list (self): store = gtk.TreeStore(gtk.gdk.Pixbuf, str) @@ -348,115 +331,27 @@ class PackageTable: self.useList.set_search_column(2) self.useList.set_enable_tree_lines(True) - def build_version_list (self): - store = gtk.ListStore(gtk.gdk.Pixbuf, str, str) - - # build view - self.versionList.set_model(store) - - col = gtk.TreeViewColumn(_("Versions")) - col.set_property("expand", True) - - self.slotcol = gtk.TreeViewColumn(_("Slot")) - self.slotcol.set_property("expand", True) - - # adding the pixbuf - cell = gtk.CellRendererPixbuf() - col.pack_start(cell, False) - col.add_attribute(cell, "pixbuf", 0) - - # adding the package name - cell = gtk.CellRendererText() - col.pack_start(cell, True) - col.add_attribute(cell, "text", 1) - - # adding the slot - cell = gtk.CellRendererText() - self.slotcol.pack_start(cell, True) - self.slotcol.add_attribute(cell, "text", 2) - - self.versionList.append_column(col) - self.versionList.append_column(self.slotcol) - - def fill_version_list (self): - - store = self.versionList.get_model() - - # this is here for performance reasons - # to not query the package with info, we do not need - if self.main.cfg.get_boolean("showSlots", "GUI"): - def get_slot(pkg): - return pkg.get_package_settings("SLOT") - - self.slotcol.set_visible(True) - - else: - def get_slot(*args): - return "" - - self.slotcol.set_visible(False) - - # append versions - for vers, inst, slot in ((x.get_version(), x.is_installed(), get_slot(x)) for x in self.packages): - if inst: - icon = self.main.instPixbuf - else: - icon = None - - store.append([icon, vers, slot]) - - pos = ((0,)) # default - - # activate the first one - try: - best_version = "" - if self.version: - best_version = self.version - else: - best_version = system.find_best_match(self.packages[0].get_cp(), only_installed = (self.instPackages != [])).get_version() - for i in range(len(self.packages)): - if self.packages[i].get_version() == best_version: - pos = (i,) - break - except AttributeError: # no package found - pass - - self.versionList.get_selection().select_path(pos) - self.versionList.scroll_to_cell(pos) - - def actual_package (self): - """Returns the actual selected package. - - @returns: the actual selected package - @rtype: backend.Package""" - - model, iter = self.versionList.get_selection().get_selected() - if iter: - return self.packages[model.get_path(iter)[0]] - else: - return self.packages[0] - def _update_keywords (self, emerge, update = False): if emerge: type = "install" if not self.type else self.type try: try: - self.queue.append(self.actual_package().get_cpv(), type = type, update = update) + self.queue.append(self.pkg.get_cpv(), type = type, update = update) except PackageNotFoundException, e: if unmask_dialog(e[0]) == gtk.RESPONSE_YES: - self.queue.append(self.actual_package().get_cpv(), type = type, unmask = True, update = update) + self.queue.append(self.pkg.get_cpv(), type = type, unmask = True, update = update) except BlockedException, e: blocked_dialog(e[0], e[1]) else: try: - self.queue.append(self.actual_package().get_cpv(), type = "uninstall") + self.queue.append(self.pkg.get_cpv(), type = "uninstall") except PackageNotFoundException, e: error(_("Package could not be found: %s"), e[0]) #masked_dialog(e[0]) - def cb_version_list_changed (self, *args): + def _update_table (self, *args): - pkg = self.actual_package() + pkg = self.pkg # set the views for v in (self.ebuildView, self.changelogView, self.filesView): @@ -530,7 +425,7 @@ class PackageTable: if self.doEmerge: # set emerge-button-label - if not self.actual_package().is_installed(): + if not pkg.is_installed(): self.unmergeBtn.set_sensitive(False) else: self.unmergeBtn.set_sensitive(True) @@ -546,12 +441,10 @@ class PackageTable: def cb_package_revert_clicked (self, button): """Callback for pressed revert-button.""" - self.actual_package().remove_new_use_flags() - self.actual_package().remove_new_masked() - self.actual_package().remove_new_testing() - self.versionList.get_model().clear() - self.fill_version_list() - self.cb_version_list_changed() + self.pkg.remove_new_use_flags() + self.pkg.remove_new_masked() + self.pkg.remove_new_testing() + self._update_table() if self.instantChange: self._update_keywords(True, update = True) return True @@ -573,21 +466,21 @@ class PackageTable: status = button.get_active() # end of recursion :) - if self.actual_package().is_testing(use_keywords = False) == status: + if self.pkg.is_testing(use_keywords = False) == status: return False # if the package is not testing - don't allow to set it as such - if not self.actual_package().is_testing(use_keywords = False): + if not self.pkg.is_testing(use_keywords = False): button.set_active(False) return True # re-set to testing status - if not self.actual_package().is_testing(use_keywords = True): - self.actual_package().set_testing(False) + if not self.pkg.is_testing(use_keywords = True): + self.pkg.set_testing(False) button.set_label(_("Testing")) button.set_active(True) else: # disable testing - self.actual_package().set_testing(True) + self.pkg.set_testing(True) button.set_label("<i>(%s)</i>" % _("Testing")) button.get_child().set_use_markup(True) button.set_active(True) @@ -600,7 +493,7 @@ class PackageTable: def cb_masked_toggled (self, button): """Callback for toggled masking-checkbox.""" status = button.get_active() - pkg = self.actual_package() + pkg = self.pkg if pkg.is_masked(use_changed = False) == status and not pkg.is_locally_masked(): return False @@ -637,7 +530,7 @@ class PackageTable: def cb_use_flag_toggled (self, cell, path, store): """Callback for a toggled use-flag button.""" flag = store[path][2] - pkg = self.actual_package() + pkg = self.pkg if pkg.use_expanded(flag): # ignore expanded flags return False @@ -733,12 +626,17 @@ class MainWindow (Window): self.hpaned = self.tree.get_widget("hpaned") self.hpaned.set_position(int(self.window.get_size()[0]/1.5)) - # cat and pkg list + # lists + self.selCatName = "" + self.selCP = "" + self.selCPV = "" self.sortPkgListByName = True self.catList = self.tree.get_widget("catList") self.pkgList = self.tree.get_widget("pkgList") + self.versionList = self.tree.get_widget("versionList") self.build_cat_list() self.build_pkg_list() + self.build_version_list() # search entry self.searchEntry = self.tree.get_widget("searchEntry") @@ -804,9 +702,6 @@ class MainWindow (Window): self.queueTree = GtkTree(self.queueList.get_model()) self.queue = EmergeQueue(console = self.console, tree = self.queueTree, db = self.db, title_update = self.title_update, threadClass = GtkThread) - self.catList.get_selection().select_path(1) - self.pkgList.get_selection().select_path(0) - # session splash(_("Restoring Session")) try: @@ -821,8 +716,25 @@ class MainWindow (Window): self.window.show_all() - def show_package (self, *args, **kwargs): - self.packageTable.update(*args, **kwargs) + def show_package (self, pkg = None, cpv = None, cp = None, version = None, **kwargs): + p = None + + if pkg: + p = pkg + elif cpv: + p = system.find_packages("="+cpv, masked = True)[0] + elif cp: + if version: + p = system.find_packages("=%s-%s" % (cp, version), masked = True)[0] + + else: + best = system.find_best_match(cp) + if best: + p = best + else: + p = system.find_packages(cp)[0] + + self.packageTable.update(p, **kwargs) def build_terminal (self): """ @@ -860,28 +772,53 @@ class MainWindow (Window): Builds the category list. """ - store = gtk.ListStore(str) + store = gtk.TreeStore(str) + + self.fill_cat_store(store) self.catList.set_model(store) cell = gtk.CellRendererText() col = gtk.TreeViewColumn(_("Categories"), cell, text = 0) self.catList.append_column(col) - self.fill_cat_store(store) self.catList.get_selection().connect("changed", self.cb_cat_list_selection) - def fill_cat_store (self, store): + def fill_cat_store (self, store = None): """ Fills the category store with data. - + @param store: the store to fill @type store: gtk.ListStore """ + if store is None: + store = self.catList.get_model() + + store.clear() + cats = self.db.get_categories(installed = not self.showAll) - for p in cats: - store.append([p]) + if not self.cfg.get_boolean("collapseCats", "GUI"): + for p in cats: + store.append(None, [p]) + else: + splitCats = defaultdict(list) + for c in cats: + try: + pre, post = c.split("-", 1) + except ValueError: # no "-" in cat name -- do not split + debug("Category '%s' can't be split up. Should be no harm.", c) + splitCats["not-split"].append(c) + else: + splitCats[pre].append(post) + + for sc in splitCats: + if sc == "not-split": + it = None # append not splitted stuff to root + else: + it = store.append(None, [sc]) + for cat in splitCats[sc]: + store.append(it, [cat]) # sort them alphabetically store.set_sort_column_id(0, gtk.SORT_ASCENDING) @@ -895,7 +832,7 @@ class MainWindow (Window): """ store = gtk.ListStore(gtk.gdk.Pixbuf, str, str) - self.fill_pkg_store(store,name) + self.fill_pkg_store(store, name) # build view self.pkgList.set_model(store) @@ -918,7 +855,7 @@ class MainWindow (Window): self.pkgList.get_selection().connect("changed", self.cb_pkg_list_selection) - def fill_pkg_store (self, store, name = None): + def fill_pkg_store (self, store = None, name = None): """ Fills a given ListStore with the packages in a category. @@ -927,6 +864,10 @@ class MainWindow (Window): @param name: the name of the category @type name: string """ + + if store is None: + store = self.pkgList.get_model() + store.clear() if name: for cat, pkg, is_inst in self.db.get_cat(name, self.sortPkgListByName): @@ -938,19 +879,96 @@ class MainWindow (Window): icon = None store.append([icon, pkg, cat]) + def build_version_list (self): + store = gtk.ListStore(gtk.gdk.Pixbuf, str, str) + + # build view + self.versionList.set_model(store) + + col = gtk.TreeViewColumn(_("Versions")) + col.set_property("expand", True) + + self.slotcol = gtk.TreeViewColumn(_("Slot")) + self.slotcol.set_property("expand", True) + + # adding the pixbuf + cell = gtk.CellRendererPixbuf() + col.pack_start(cell, False) + col.add_attribute(cell, "pixbuf", 0) + + # adding the package name + cell = gtk.CellRendererText() + col.pack_start(cell, True) + col.add_attribute(cell, "text", 1) + + # adding the slot + cell = gtk.CellRendererText() + self.slotcol.pack_start(cell, True) + self.slotcol.add_attribute(cell, "text", 2) + + self.versionList.append_column(col) + self.versionList.append_column(self.slotcol) + + self.versionList.get_selection().connect("changed", self.cb_vers_list_selection) + + def fill_version_list (self, cp, version = None): + + store = self.versionList.get_model() + store.clear() + + # this is here for performance reasons + # to not query the package with info, we do not need + if self.cfg.get_boolean("showSlots", "GUI"): + def get_slot(pkg): + return pkg.get_package_settings("SLOT") + + self.slotcol.set_visible(True) + + else: + def get_slot(*args): + return "" + + self.slotcol.set_visible(False) + + packages = system.sort_package_list(system.find_packages(cp, masked=True)) + + # append versions + for vers, inst, slot in ((x.get_version(), x.is_installed(), get_slot(x)) for x in packages): + if inst: + icon = self.instPixbuf + else: + icon = None + + store.append([icon, vers, slot]) + + pos = ((0,)) # default + + # activate the first one + try: + best_version = "" + if version: + best_version = version + else: + best_version = system.find_best_match(packages[0].get_cp()).get_version() + for i, p in enumerate(packages): + if p.get_version() == best_version: + pos = (i,) + break + except AttributeError: # no package found + pass + + self.versionList.get_selection().select_path(pos) + self.versionList.scroll_to_cell(pos) + def refresh_stores (self): """ Refreshes the category and package stores. """ - store = self.catList.get_model() - store.clear() - self.fill_cat_store(store) + self.fill_cat_store() - store = self.pkgList.get_model() - store.clear() - try: - self.fill_pkg_store(store, self.selCatName) - except AttributeError: # no selCatName -> so no category selected --> ignore + if self.selCatName: + self.fill_pkg_store(name = self.selCatName) + else: # no selCatName -> so no category selected --> ignore debug("No category selected --> should be no harm.") def load_session(self, sessionEx = None): @@ -1012,7 +1030,27 @@ class MainWindow (Window): for cname, path in ((x[col], x.path) for x in list.get_model()): if cname == name: pos = path + break + + if self.cfg.get_boolean("collapseCats", "GUI") and \ + pos == "0" and isinstance(list.get_model(), gtk.TreeStore): # try the new split up + + try: + pre, post = name.split("-", 1) + except ValueError: # nothing to split + pass + else: + for row in list.get_model(): + if row[col] == pre: # found first part + pos = row.path + list.expand_row(pos, False) + for cname, path in ((x[col], x.path) for x in row.iterchildren()): + if cname == post: # found second + pos = ":".join(map(str,path)) + break + break + debug("Selecting path '%s'.", pos) list.get_selection().select_path(pos) list.scroll_to_cell(pos) @@ -1066,13 +1104,19 @@ class MainWindow (Window): elif version > SESSION_VERSION: raise NewSessionException(version, SESSION_VERSION) + def _add (value): + if len(value) == 4: + self.session.add_handler(value[:3], default = value[3]) + else: + self.session.add_handler(value) + # set the simple ones :) - map(self.session.add_handler,[ + map(_add,[ ([("gtksessionversion", "session")], load_session_version, lambda: SESSION_VERSION), ([("width", "window"), ("height", "window")], lambda w,h: self.window.resize(int(w), int(h)), self.window.get_size), ([("vpanedpos", "window"), ("hpanedpos", "window")], load_paned, save_paned), - ([("catsel", "window")], load_selection(self.catList, 0), save_cat_selection), - ([("pkgsel", "window")], load_selection(self.pkgList, 1), save_pkg_selection) + ([("catsel", "window")], load_selection(self.catList, 0), save_cat_selection, ["app-portage"]), + ([("pkgsel", "window")], load_selection(self.pkgList, 1), save_pkg_selection, ["portato"]) #([("merge", "queue"), ("unmerge", "queue"), ("oneshot", "queue")], load_queue, save_queue), ]) @@ -1107,7 +1151,7 @@ class MainWindow (Window): debug("Unexpected number of %s returned after search: %d", what, len(pathes)) break - self.show_package(cp, self.queue, version = version) + self.show_package(cp = cp, version = version, queue = self.queue) def set_uri_hook (self, browser): """ @@ -1166,7 +1210,8 @@ class MainWindow (Window): else: title = ("%s (%s)") % (_("Console"), title) - if (len(title) > 60): title = "%s..." % title[:57] + tlength = int(self.cfg.get("titlelength", "GUI")) + if (len(title) > tlength): title = "%s..." % title[:tlength-3] self.sysNotebook.set_tab_label_text(self.termHB, title) return False @@ -1182,26 +1227,47 @@ class MainWindow (Window): # get the selected category store, it = selection.get_selected() if it: - self.selCatName = store.get_value(it, 0) - self.pkgList.get_model().clear() - self.fill_pkg_store(self.pkgList.get_model(), self.selCatName) + if not self.cfg.get_boolean("collapseCats", "GUI"): + self.selCatName = store.get_value(it, 0) + else: + parent = store.iter_parent(it) + if parent is None: + if store.iter_has_child(it): # this is a split up selector -> do nothing + return True + else: + self.selCatName = store.get_value(it, 0) # this is a non-split up top + else: + self.selCatName = ("%s-%s" % (store.get_value(parent, 0), store.get_value(it, 0))) + + self.fill_pkg_store(name = self.selCatName) return True def cb_pkg_list_selection (self, selection): """ Callback for a package-list selection. - Updates the package info. + Updates the version list. """ store, it = selection.get_selected() if it: - cp = "%s/%s" % (store.get_value(it, 2), store.get_value(it, 1)) - self.show_package(cp, self.queue) + self.selCP = "%s/%s" % (store.get_value(it, 2), store.get_value(it, 1)) + self.fill_version_list(self.selCP) return True def cb_pkg_list_header_clicked(self, col): self.sortPkgListByName = not self.sortPkgListByName - self.pkgList.get_model().clear() - self.fill_pkg_store(self.pkgList.get_model(), self.selCatName) + self.fill_pkg_store(name = self.selCatName) + return True + + def cb_vers_list_selection (self, selection): + """ + Callback for a package-list selection. + Updates the version list. + """ + store, it = selection.get_selected() + if it: + self.selCPV = "%s-%s" % (self.selCP, store.get_value(it, 1)) + self.show_package(cpv = self.selCPV, queue = self.queue) + return True def cb_queue_row_activated (self, view, path, *args): @@ -1211,8 +1277,6 @@ class MainWindow (Window): iterator = store.get_original().get_iter(path) if store.iter_has_parent(iterator): package = store.get_value(iterator, store.get_cpv_column()) - cat, name, vers, rev = system.split_cpv(package) - if rev != "r0": vers = vers+"-"+rev if store.is_in_emerge(iterator): type = "install" @@ -1221,7 +1285,7 @@ class MainWindow (Window): elif store.is_in_update(iterator): type = "update" - self.show_package(cat+"/"+name, queue = self.queue, version = vers, instantChange = True, doEmerge = False, type = type) + self.show_package(cpv = package, queue = self.queue, instantChange = True, doEmerge = False, type = type) return True def cb_queue_tooltip_queried (self, view, x, y, is_keyboard, tooltip): @@ -1470,7 +1534,7 @@ class MainWindow (Window): """ User wants to open preferences. """ - PreferenceWindow(self.window, self.cfg, self.console.set_font_from_string, self.set_uri_hook, self.set_notebook_tabpos) + PreferenceWindow(self.window, self.cfg, self.console.set_font_from_string, self.set_uri_hook, self.set_notebook_tabpos, self.fill_cat_store) return True def cb_about_clicked (self, *args): @@ -1599,10 +1663,10 @@ class MainWindow (Window): self.emergePaused = cb.get_active() if not self.emergePaused: self.queue.continue_emerge() - self.tray.set_from_file(APP_ICON) + #self.tray.set_from_file(APP_ICON) else: self.queue.stop_emerge() - self.tray.set_from_file(os.path.join(ICON_DIR, "pausing.png")) + #self.tray.set_from_file(os.path.join(ICON_DIR, "pausing.png")) # block the handlers of the other buttons # so that calling "set_active" does not call this callback recursivly diff --git a/portato/gui/windows/preference.py b/portato/gui/windows/preference.py index 991e7b3..d35666d 100644 --- a/portato/gui/windows/preference.py +++ b/portato/gui/windows/preference.py @@ -24,6 +24,7 @@ class PreferenceWindow (AbstractDialog): # all checkboxes in the window # widget name -> option name checkboxes = { + "collapseCatCheck" : ("collapseCats", "GUI"), "consoleUpdateCheck" : ("updateConsole", "GUI"), "debugCheck" : "debug", "deepCheck" : "deep", @@ -56,7 +57,7 @@ class PreferenceWindow (AbstractDialog): 4 : gtk.POS_RIGHT } - def __init__ (self, parent, cfg, console_fn, linkbtn_fn, tabpos_fn): + def __init__ (self, parent, cfg, console_fn, linkbtn_fn, tabpos_fn, catmodel_fn): """Constructor. @param parent: parent window @@ -68,7 +69,9 @@ class PreferenceWindow (AbstractDialog): @param linkbtn_fn: function to call to set the linkbutton behavior @type linkbtn_fn: function(string) @param tabpos_fn: function to call to set the tabposition of the notebooks - @type tabpos_fn: function(gtk.ComboBox,int)""" + @type tabpos_fn: function(gtk.ComboBox,int) + @param catmodel_fn: function to call to set the model of the cat list (collapsed/not collapsed) + @type catmodel_fn: function()""" AbstractDialog.__init__(self, parent) @@ -79,6 +82,7 @@ class PreferenceWindow (AbstractDialog): self.console_fn = console_fn self.linkbtn_fn = linkbtn_fn self.tabpos_fn = tabpos_fn + self.catmodel_fn = catmodel_fn # set the bg-color of the hint hintEB = self.tree.get_widget("hintEB") @@ -106,6 +110,10 @@ class PreferenceWindow (AbstractDialog): self.consoleFontBtn = self.tree.get_widget("consoleFontBtn") self.consoleFontBtn.set_font_name(self.cfg.get("consolefont", section = "GUI")) + # the console title length spin button + self.titleLengthSpinBtn = self.tree.get_widget("titleLengthSpinBtn") + self.titleLengthSpinBtn.set_value(int(self.cfg.get("titlelength", section = "GUI"))) + # the comboboxes self.systemTabCombo = self.tree.get_widget("systemTabCombo") self.pkgTabCombo = self.tree.get_widget("packageTabCombo") @@ -140,6 +148,8 @@ class PreferenceWindow (AbstractDialog): self.cfg.set("consolefont", font, section = "GUI") self.console_fn(font) + self.cfg.set("titlelength", str(self.titleLengthSpinBtn.get_value_as_int()), section = "GUI") + pkgPos = self.pkgTabCombo.get_active()+1 sysPos = self.systemTabCombo.get_active()+1 @@ -150,6 +160,8 @@ class PreferenceWindow (AbstractDialog): self.linkbtn_fn(self.cfg.get("browserCmd", section="GUI")) + self.catmodel_fn() + def cb_ok_clicked(self, button): """Saves, writes to config-file and closes the window.""" self._save() diff --git a/portato/odict.py b/portato/odict.py new file mode 100644 index 0000000..2c8391d --- /dev/null +++ b/portato/odict.py @@ -0,0 +1,1399 @@ +# odict.py +# An Ordered Dictionary object +# Copyright (C) 2005 Nicola Larosa, Michael Foord +# E-mail: nico AT tekNico DOT net, fuzzyman AT voidspace DOT org DOT uk + +# This software is licensed under the terms of the BSD license. +# http://www.voidspace.org.uk/python/license.shtml +# Basically you're free to copy, modify, distribute and relicense it, +# So long as you keep a copy of the license with it. + +# Documentation at http://www.voidspace.org.uk/python/odict.html +# For information about bugfixes, updates and support, please join the +# Pythonutils mailing list: +# http://groups.google.com/group/pythonutils/ +# Comments, suggestions and bug reports welcome. + +"""A dict that keeps keys in insertion order""" +from __future__ import generators + +__author__ = ('Nicola Larosa <nico-NoSp@m-tekNico.net>,' + 'Michael Foord <fuzzyman AT voidspace DOT org DOT uk>') + +__docformat__ = "restructuredtext en" + +__revision__ = '$Id: odict.py 129 2005-09-12 18:15:28Z teknico $' + +__version__ = '0.2.2' + +__all__ = ['OrderedDict', 'SequenceOrderedDict'] + +import sys +INTP_VER = sys.version_info[:2] +if INTP_VER < (2, 2): + raise RuntimeError("Python v.2.2 or later required") + +import types, warnings + +class OrderedDict(dict): + """ + A class of dictionary that keeps the insertion order of keys. + + All appropriate methods return keys, items, or values in an ordered way. + + All normal dictionary methods are available. Update and comparison is + restricted to other OrderedDict objects. + + Various sequence methods are available, including the ability to explicitly + mutate the key ordering. + + __contains__ tests: + + >>> d = OrderedDict(((1, 3),)) + >>> 1 in d + 1 + >>> 4 in d + 0 + + __getitem__ tests: + + >>> OrderedDict(((1, 3), (3, 2), (2, 1)))[2] + 1 + >>> OrderedDict(((1, 3), (3, 2), (2, 1)))[4] + Traceback (most recent call last): + KeyError: 4 + + __len__ tests: + + >>> len(OrderedDict()) + 0 + >>> len(OrderedDict(((1, 3), (3, 2), (2, 1)))) + 3 + + get tests: + + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> d.get(1) + 3 + >>> d.get(4) is None + 1 + >>> d.get(4, 5) + 5 + >>> d + OrderedDict([(1, 3), (3, 2), (2, 1)]) + + has_key tests: + + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> d.has_key(1) + 1 + >>> d.has_key(4) + 0 + """ + + def __init__(self, init_val=(), strict=False): + """ + Create a new ordered dictionary. Cannot init from a normal dict, + nor from kwargs, since items order is undefined in those cases. + + If the ``strict`` keyword argument is ``True`` (``False`` is the + default) then when doing slice assignment - the ``OrderedDict`` you are + assigning from *must not* contain any keys in the remaining dict. + + >>> OrderedDict() + OrderedDict([]) + >>> OrderedDict({1: 1}) + Traceback (most recent call last): + TypeError: undefined order, cannot get items from dict + >>> OrderedDict({1: 1}.items()) + OrderedDict([(1, 1)]) + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> d + OrderedDict([(1, 3), (3, 2), (2, 1)]) + >>> OrderedDict(d) + OrderedDict([(1, 3), (3, 2), (2, 1)]) + """ + self.strict = strict + dict.__init__(self) + if isinstance(init_val, OrderedDict): + self._sequence = init_val.keys() + dict.update(self, init_val) + elif isinstance(init_val, dict): + # we lose compatibility with other ordered dict types this way + raise TypeError('undefined order, cannot get items from dict') + else: + self._sequence = [] + self.update(init_val) + +### Special methods ### + + def __delitem__(self, key): + """ + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> del d[3] + >>> d + OrderedDict([(1, 3), (2, 1)]) + >>> del d[3] + Traceback (most recent call last): + KeyError: 3 + >>> d[3] = 2 + >>> d + OrderedDict([(1, 3), (2, 1), (3, 2)]) + >>> del d[0:1] + >>> d + OrderedDict([(2, 1), (3, 2)]) + """ + if isinstance(key, types.SliceType): + # FIXME: efficiency? + keys = self._sequence[key] + for entry in keys: + dict.__delitem__(self, entry) + del self._sequence[key] + else: + # do the dict.__delitem__ *first* as it raises + # the more appropriate error + dict.__delitem__(self, key) + self._sequence.remove(key) + + def __eq__(self, other): + """ + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> d == OrderedDict(d) + True + >>> d == OrderedDict(((1, 3), (2, 1), (3, 2))) + False + >>> d == OrderedDict(((1, 0), (3, 2), (2, 1))) + False + >>> d == OrderedDict(((0, 3), (3, 2), (2, 1))) + False + >>> d == dict(d) + False + >>> d == False + False + """ + if isinstance(other, OrderedDict): + # FIXME: efficiency? + # Generate both item lists for each compare + return (self.items() == other.items()) + else: + return False + + def __lt__(self, other): + """ + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> c = OrderedDict(((0, 3), (3, 2), (2, 1))) + >>> c < d + True + >>> d < c + False + >>> d < dict(c) + Traceback (most recent call last): + TypeError: Can only compare with other OrderedDicts + """ + if not isinstance(other, OrderedDict): + raise TypeError('Can only compare with other OrderedDicts') + # FIXME: efficiency? + # Generate both item lists for each compare + return (self.items() < other.items()) + + def __le__(self, other): + """ + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> c = OrderedDict(((0, 3), (3, 2), (2, 1))) + >>> e = OrderedDict(d) + >>> c <= d + True + >>> d <= c + False + >>> d <= dict(c) + Traceback (most recent call last): + TypeError: Can only compare with other OrderedDicts + >>> d <= e + True + """ + if not isinstance(other, OrderedDict): + raise TypeError('Can only compare with other OrderedDicts') + # FIXME: efficiency? + # Generate both item lists for each compare + return (self.items() <= other.items()) + + def __ne__(self, other): + """ + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> d != OrderedDict(d) + False + >>> d != OrderedDict(((1, 3), (2, 1), (3, 2))) + True + >>> d != OrderedDict(((1, 0), (3, 2), (2, 1))) + True + >>> d == OrderedDict(((0, 3), (3, 2), (2, 1))) + False + >>> d != dict(d) + True + >>> d != False + True + """ + if isinstance(other, OrderedDict): + # FIXME: efficiency? + # Generate both item lists for each compare + return not (self.items() == other.items()) + else: + return True + + def __gt__(self, other): + """ + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> c = OrderedDict(((0, 3), (3, 2), (2, 1))) + >>> d > c + True + >>> c > d + False + >>> d > dict(c) + Traceback (most recent call last): + TypeError: Can only compare with other OrderedDicts + """ + if not isinstance(other, OrderedDict): + raise TypeError('Can only compare with other OrderedDicts') + # FIXME: efficiency? + # Generate both item lists for each compare + return (self.items() > other.items()) + + def __ge__(self, other): + """ + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> c = OrderedDict(((0, 3), (3, 2), (2, 1))) + >>> e = OrderedDict(d) + >>> c >= d + False + >>> d >= c + True + >>> d >= dict(c) + Traceback (most recent call last): + TypeError: Can only compare with other OrderedDicts + >>> e >= d + True + """ + if not isinstance(other, OrderedDict): + raise TypeError('Can only compare with other OrderedDicts') + # FIXME: efficiency? + # Generate both item lists for each compare + return (self.items() >= other.items()) + + def __repr__(self): + """ + Used for __repr__ and __str__ + + >>> r1 = repr(OrderedDict((('a', 'b'), ('c', 'd'), ('e', 'f')))) + >>> r1 + "OrderedDict([('a', 'b'), ('c', 'd'), ('e', 'f')])" + >>> r2 = repr(OrderedDict((('a', 'b'), ('e', 'f'), ('c', 'd')))) + >>> r2 + "OrderedDict([('a', 'b'), ('e', 'f'), ('c', 'd')])" + >>> r1 == str(OrderedDict((('a', 'b'), ('c', 'd'), ('e', 'f')))) + True + >>> r2 == str(OrderedDict((('a', 'b'), ('e', 'f'), ('c', 'd')))) + True + """ + return '%s([%s])' % (self.__class__.__name__, ', '.join( + ['(%r, %r)' % (key, self[key]) for key in self._sequence])) + + def __setitem__(self, key, val): + """ + Allows slice assignment, so long as the slice is an OrderedDict + >>> d = OrderedDict() + >>> d['a'] = 'b' + >>> d['b'] = 'a' + >>> d[3] = 12 + >>> d + OrderedDict([('a', 'b'), ('b', 'a'), (3, 12)]) + >>> d[:] = OrderedDict(((1, 2), (2, 3), (3, 4))) + >>> d + OrderedDict([(1, 2), (2, 3), (3, 4)]) + >>> d[::2] = OrderedDict(((7, 8), (9, 10))) + >>> d + OrderedDict([(7, 8), (2, 3), (9, 10)]) + >>> d = OrderedDict(((0, 1), (1, 2), (2, 3), (3, 4))) + >>> d[1:3] = OrderedDict(((1, 2), (5, 6), (7, 8))) + >>> d + OrderedDict([(0, 1), (1, 2), (5, 6), (7, 8), (3, 4)]) + >>> d = OrderedDict(((0, 1), (1, 2), (2, 3), (3, 4)), strict=True) + >>> d[1:3] = OrderedDict(((1, 2), (5, 6), (7, 8))) + >>> d + OrderedDict([(0, 1), (1, 2), (5, 6), (7, 8), (3, 4)]) + + >>> a = OrderedDict(((0, 1), (1, 2), (2, 3)), strict=True) + >>> a[3] = 4 + >>> a + OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) + >>> a[::1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) + >>> a + OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) + >>> a[:2] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]) + Traceback (most recent call last): + ValueError: slice assignment must be from unique keys + >>> a = OrderedDict(((0, 1), (1, 2), (2, 3))) + >>> a[3] = 4 + >>> a + OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) + >>> a[::1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) + >>> a + OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) + >>> a[:2] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) + >>> a + OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) + >>> a[::-1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) + >>> a + OrderedDict([(3, 4), (2, 3), (1, 2), (0, 1)]) + + >>> d = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) + >>> d[:1] = 3 + Traceback (most recent call last): + TypeError: slice assignment requires an OrderedDict + + >>> d = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) + >>> d[:1] = OrderedDict([(9, 8)]) + >>> d + OrderedDict([(9, 8), (1, 2), (2, 3), (3, 4)]) + """ + if isinstance(key, types.SliceType): + if not isinstance(val, OrderedDict): + # FIXME: allow a list of tuples? + raise TypeError('slice assignment requires an OrderedDict') + keys = self._sequence[key] + # NOTE: Could use ``range(*key.indices(len(self._sequence)))`` + indexes = range(len(self._sequence))[key] + if key.step is None: + # NOTE: new slice may not be the same size as the one being + # overwritten ! + # NOTE: What is the algorithm for an impossible slice? + # e.g. d[5:3] + pos = key.start or 0 + del self[key] + newkeys = val.keys() + for k in newkeys: + if k in self: + if self.strict: + raise ValueError('slice assignment must be from ' + 'unique keys') + else: + # NOTE: This removes duplicate keys *first* + # so start position might have changed? + del self[k] + self._sequence = (self._sequence[:pos] + newkeys + + self._sequence[pos:]) + dict.update(self, val) + else: + # extended slice - length of new slice must be the same + # as the one being replaced + if len(keys) != len(val): + raise ValueError('attempt to assign sequence of size %s ' + 'to extended slice of size %s' % (len(val), len(keys))) + # FIXME: efficiency? + del self[key] + item_list = zip(indexes, val.items()) + # smallest indexes first - higher indexes not guaranteed to + # exist + item_list.sort() + for pos, (newkey, newval) in item_list: + if self.strict and newkey in self: + raise ValueError('slice assignment must be from unique' + ' keys') + self.insert(pos, newkey, newval) + else: + if key not in self: + self._sequence.append(key) + dict.__setitem__(self, key, val) + + def __getitem__(self, key): + """ + Allows slicing. Returns an OrderedDict if you slice. + >>> b = OrderedDict([(7, 0), (6, 1), (5, 2), (4, 3), (3, 4), (2, 5), (1, 6)]) + >>> b[::-1] + OrderedDict([(1, 6), (2, 5), (3, 4), (4, 3), (5, 2), (6, 1), (7, 0)]) + >>> b[2:5] + OrderedDict([(5, 2), (4, 3), (3, 4)]) + >>> type(b[2:4]) + <class '__main__.OrderedDict'> + """ + if isinstance(key, types.SliceType): + # FIXME: does this raise the error we want? + keys = self._sequence[key] + # FIXME: efficiency? + return OrderedDict([(entry, self[entry]) for entry in keys]) + else: + return dict.__getitem__(self, key) + + __str__ = __repr__ + + def __setattr__(self, name, value): + """ + Implemented so that accesses to ``sequence`` raise a warning and are + diverted to the new ``setkeys`` method. + """ + if name == 'sequence': + warnings.warn('Use of the sequence attribute is deprecated.' + ' Use the keys method instead.', DeprecationWarning) + # NOTE: doesn't return anything + self.setkeys(value) + else: + # FIXME: do we want to allow arbitrary setting of attributes? + # Or do we want to manage it? + object.__setattr__(self, name, value) + + def __getattr__(self, name): + """ + Implemented so that access to ``sequence`` raises a warning. + + >>> d = OrderedDict() + >>> d.sequence + [] + """ + if name == 'sequence': + warnings.warn('Use of the sequence attribute is deprecated.' + ' Use the keys method instead.', DeprecationWarning) + # NOTE: Still (currently) returns a direct reference. Need to + # because code that uses sequence will expect to be able to + # mutate it in place. + return self._sequence + else: + # raise the appropriate error + raise AttributeError("OrderedDict has no '%s' attribute" % name) + + def __deepcopy__(self, memo): + """ + To allow deepcopy to work with OrderedDict. + + >>> from copy import deepcopy + >>> a = OrderedDict([(1, 1), (2, 2), (3, 3)]) + >>> a['test'] = {} + >>> b = deepcopy(a) + >>> b == a + True + >>> b is a + False + >>> a['test'] is b['test'] + False + """ + from copy import deepcopy + return self.__class__(deepcopy(self.items(), memo), self.strict) + + +### Read-only methods ### + + def copy(self): + """ + >>> OrderedDict(((1, 3), (3, 2), (2, 1))).copy() + OrderedDict([(1, 3), (3, 2), (2, 1)]) + """ + return OrderedDict(self) + + def items(self): + """ + ``items`` returns a list of tuples representing all the + ``(key, value)`` pairs in the dictionary. + + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> d.items() + [(1, 3), (3, 2), (2, 1)] + >>> d.clear() + >>> d.items() + [] + """ + return zip(self._sequence, self.values()) + + def keys(self): + """ + Return a list of keys in the ``OrderedDict``. + + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> d.keys() + [1, 3, 2] + """ + return self._sequence[:] + + def values(self, values=None): + """ + Return a list of all the values in the OrderedDict. + + Optionally you can pass in a list of values, which will replace the + current list. The value list must be the same len as the OrderedDict. + + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> d.values() + [3, 2, 1] + """ + return [self[key] for key in self._sequence] + + def iteritems(self): + """ + >>> ii = OrderedDict(((1, 3), (3, 2), (2, 1))).iteritems() + >>> ii.next() + (1, 3) + >>> ii.next() + (3, 2) + >>> ii.next() + (2, 1) + >>> ii.next() + Traceback (most recent call last): + StopIteration + """ + def make_iter(self=self): + keys = self.iterkeys() + while True: + key = keys.next() + yield (key, self[key]) + return make_iter() + + def iterkeys(self): + """ + >>> ii = OrderedDict(((1, 3), (3, 2), (2, 1))).iterkeys() + >>> ii.next() + 1 + >>> ii.next() + 3 + >>> ii.next() + 2 + >>> ii.next() + Traceback (most recent call last): + StopIteration + """ + return iter(self._sequence) + + __iter__ = iterkeys + + def itervalues(self): + """ + >>> iv = OrderedDict(((1, 3), (3, 2), (2, 1))).itervalues() + >>> iv.next() + 3 + >>> iv.next() + 2 + >>> iv.next() + 1 + >>> iv.next() + Traceback (most recent call last): + StopIteration + """ + def make_iter(self=self): + keys = self.iterkeys() + while True: + yield self[keys.next()] + return make_iter() + +### Read-write methods ### + + def clear(self): + """ + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> d.clear() + >>> d + OrderedDict([]) + """ + dict.clear(self) + self._sequence = [] + + def pop(self, key, *args): + """ + No dict.pop in Python 2.2, gotta reimplement it + + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> d.pop(3) + 2 + >>> d + OrderedDict([(1, 3), (2, 1)]) + >>> d.pop(4) + Traceback (most recent call last): + KeyError: 4 + >>> d.pop(4, 0) + 0 + >>> d.pop(4, 0, 1) + Traceback (most recent call last): + TypeError: pop expected at most 2 arguments, got 3 + """ + if len(args) > 1: + raise TypeError, ('pop expected at most 2 arguments, got %s' % + (len(args) + 1)) + if key in self: + val = self[key] + del self[key] + else: + try: + val = args[0] + except IndexError: + raise KeyError(key) + return val + + def popitem(self, i=-1): + """ + Delete and return an item specified by index, not a random one as in + dict. The index is -1 by default (the last item). + + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> d.popitem() + (2, 1) + >>> d + OrderedDict([(1, 3), (3, 2)]) + >>> d.popitem(0) + (1, 3) + >>> OrderedDict().popitem() + Traceback (most recent call last): + KeyError: 'popitem(): dictionary is empty' + >>> d.popitem(2) + Traceback (most recent call last): + IndexError: popitem(): index 2 not valid + """ + if not self._sequence: + raise KeyError('popitem(): dictionary is empty') + try: + key = self._sequence[i] + except IndexError: + raise IndexError('popitem(): index %s not valid' % i) + return (key, self.pop(key)) + + def setdefault(self, key, defval = None): + """ + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> d.setdefault(1) + 3 + >>> d.setdefault(4) is None + True + >>> d + OrderedDict([(1, 3), (3, 2), (2, 1), (4, None)]) + >>> d.setdefault(5, 0) + 0 + >>> d + OrderedDict([(1, 3), (3, 2), (2, 1), (4, None), (5, 0)]) + """ + if key in self: + return self[key] + else: + self[key] = defval + return defval + + def update(self, from_od): + """ + Update from another OrderedDict or sequence of (key, value) pairs + + >>> d = OrderedDict(((1, 0), (0, 1))) + >>> d.update(OrderedDict(((1, 3), (3, 2), (2, 1)))) + >>> d + OrderedDict([(1, 3), (0, 1), (3, 2), (2, 1)]) + >>> d.update({4: 4}) + Traceback (most recent call last): + TypeError: undefined order, cannot get items from dict + >>> d.update((4, 4)) + Traceback (most recent call last): + TypeError: cannot convert dictionary update sequence element "4" to a 2-item sequence + """ + if isinstance(from_od, OrderedDict): + for key, val in from_od.items(): + self[key] = val + elif isinstance(from_od, dict): + # we lose compatibility with other ordered dict types this way + raise TypeError('undefined order, cannot get items from dict') + else: + # FIXME: efficiency? + # sequence of 2-item sequences, or error + for item in from_od: + try: + key, val = item + except TypeError: + raise TypeError('cannot convert dictionary update' + ' sequence element "%s" to a 2-item sequence' % item) + self[key] = val + + def rename(self, old_key, new_key): + """ + Rename the key for a given value, without modifying sequence order. + + For the case where new_key already exists this raise an exception, + since if new_key exists, it is ambiguous as to what happens to the + associated values, and the position of new_key in the sequence. + + >>> od = OrderedDict() + >>> od['a'] = 1 + >>> od['b'] = 2 + >>> od.items() + [('a', 1), ('b', 2)] + >>> od.rename('b', 'c') + >>> od.items() + [('a', 1), ('c', 2)] + >>> od.rename('c', 'a') + Traceback (most recent call last): + ValueError: New key already exists: 'a' + >>> od.rename('d', 'b') + Traceback (most recent call last): + KeyError: 'd' + """ + if new_key == old_key: + # no-op + return + if new_key in self: + raise ValueError("New key already exists: %r" % new_key) + # rename sequence entry + value = self[old_key] + old_idx = self._sequence.index(old_key) + self._sequence[old_idx] = new_key + # rename internal dict entry + dict.__delitem__(self, old_key) + dict.__setitem__(self, new_key, value) + + def setitems(self, items): + """ + This method allows you to set the items in the dict. + + It takes a list of tuples - of the same sort returned by the ``items`` + method. + + >>> d = OrderedDict() + >>> d.setitems(((3, 1), (2, 3), (1, 2))) + >>> d + OrderedDict([(3, 1), (2, 3), (1, 2)]) + """ + self.clear() + # FIXME: this allows you to pass in an OrderedDict as well :-) + self.update(items) + + def setkeys(self, keys): + """ + ``setkeys`` all ows you to pass in a new list of keys which will + replace the current set. This must contain the same set of keys, but + need not be in the same order. + + If you pass in new keys that don't match, a ``KeyError`` will be + raised. + + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> d.keys() + [1, 3, 2] + >>> d.setkeys((1, 2, 3)) + >>> d + OrderedDict([(1, 3), (2, 1), (3, 2)]) + >>> d.setkeys(['a', 'b', 'c']) + Traceback (most recent call last): + KeyError: 'Keylist is not the same as current keylist.' + """ + # FIXME: Efficiency? (use set for Python 2.4 :-) + # NOTE: list(keys) rather than keys[:] because keys[:] returns + # a tuple, if keys is a tuple. + kcopy = list(keys) + kcopy.sort() + self._sequence.sort() + if kcopy != self._sequence: + raise KeyError('Keylist is not the same as current keylist.') + # NOTE: This makes the _sequence attribute a new object, instead + # of changing it in place. + # FIXME: efficiency? + self._sequence = list(keys) + + def setvalues(self, values): + """ + You can pass in a list of values, which will replace the + current list. The value list must be the same len as the OrderedDict. + + (Or a ``ValueError`` is raised.) + + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> d.setvalues((1, 2, 3)) + >>> d + OrderedDict([(1, 1), (3, 2), (2, 3)]) + >>> d.setvalues([6]) + Traceback (most recent call last): + ValueError: Value list is not the same length as the OrderedDict. + """ + if len(values) != len(self): + # FIXME: correct error to raise? + raise ValueError('Value list is not the same length as the ' + 'OrderedDict.') + self.update(zip(self, values)) + +### Sequence Methods ### + + def index(self, key): + """ + Return the position of the specified key in the OrderedDict. + + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> d.index(3) + 1 + >>> d.index(4) + Traceback (most recent call last): + ValueError: list.index(x): x not in list + """ + return self._sequence.index(key) + + def insert(self, index, key, value): + """ + Takes ``index``, ``key``, and ``value`` as arguments. + + Sets ``key`` to ``value``, so that ``key`` is at position ``index`` in + the OrderedDict. + + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> d.insert(0, 4, 0) + >>> d + OrderedDict([(4, 0), (1, 3), (3, 2), (2, 1)]) + >>> d.insert(0, 2, 1) + >>> d + OrderedDict([(2, 1), (4, 0), (1, 3), (3, 2)]) + >>> d.insert(8, 8, 1) + >>> d + OrderedDict([(2, 1), (4, 0), (1, 3), (3, 2), (8, 1)]) + """ + if key in self: + # FIXME: efficiency? + del self[key] + self._sequence.insert(index, key) + dict.__setitem__(self, key, value) + + def reverse(self): + """ + Reverse the order of the OrderedDict. + + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> d.reverse() + >>> d + OrderedDict([(2, 1), (3, 2), (1, 3)]) + """ + self._sequence.reverse() + + def sort(self, *args, **kwargs): + """ + Sort the key order in the OrderedDict. + + This method takes the same arguments as the ``list.sort`` method on + your version of Python. + + >>> d = OrderedDict(((4, 1), (2, 2), (3, 3), (1, 4))) + >>> d.sort() + >>> d + OrderedDict([(1, 4), (2, 2), (3, 3), (4, 1)]) + """ + self._sequence.sort(*args, **kwargs) + +class Keys(object): + # FIXME: should this object be a subclass of list? + """ + Custom object for accessing the keys of an OrderedDict. + + Can be called like the normal ``OrderedDict.keys`` method, but also + supports indexing and sequence methods. + """ + + def __init__(self, main): + self._main = main + + def __call__(self): + """Pretend to be the keys method.""" + return self._main._keys() + + def __getitem__(self, index): + """Fetch the key at position i.""" + # NOTE: this automatically supports slicing :-) + return self._main._sequence[index] + + def __setitem__(self, index, name): + """ + You cannot assign to keys, but you can do slice assignment to re-order + them. + + You can only do slice assignment if the new set of keys is a reordering + of the original set. + """ + if isinstance(index, types.SliceType): + # FIXME: efficiency? + # check length is the same + indexes = range(len(self._main._sequence))[index] + if len(indexes) != len(name): + raise ValueError('attempt to assign sequence of size %s ' + 'to slice of size %s' % (len(name), len(indexes))) + # check they are the same keys + # FIXME: Use set + old_keys = self._main._sequence[index] + new_keys = list(name) + old_keys.sort() + new_keys.sort() + if old_keys != new_keys: + raise KeyError('Keylist is not the same as current keylist.') + orig_vals = [self._main[k] for k in name] + del self._main[index] + vals = zip(indexes, name, orig_vals) + vals.sort() + for i, k, v in vals: + if self._main.strict and k in self._main: + raise ValueError('slice assignment must be from ' + 'unique keys') + self._main.insert(i, k, v) + else: + raise ValueError('Cannot assign to keys') + + ### following methods pinched from UserList and adapted ### + def __repr__(self): return repr(self._main._sequence) + + # FIXME: do we need to check if we are comparing with another ``Keys`` + # object? (like the __cast method of UserList) + def __lt__(self, other): return self._main._sequence < other + def __le__(self, other): return self._main._sequence <= other + def __eq__(self, other): return self._main._sequence == other + def __ne__(self, other): return self._main._sequence != other + def __gt__(self, other): return self._main._sequence > other + def __ge__(self, other): return self._main._sequence >= other + # FIXME: do we need __cmp__ as well as rich comparisons? + def __cmp__(self, other): return cmp(self._main._sequence, other) + + def __contains__(self, item): return item in self._main._sequence + def __len__(self): return len(self._main._sequence) + def __iter__(self): return self._main.iterkeys() + def count(self, item): return self._main._sequence.count(item) + def index(self, item, *args): return self._main._sequence.index(item, *args) + def reverse(self): self._main._sequence.reverse() + def sort(self, *args, **kwds): self._main._sequence.sort(*args, **kwds) + def __mul__(self, n): return self._main._sequence*n + __rmul__ = __mul__ + def __add__(self, other): return self._main._sequence + other + def __radd__(self, other): return other + self._main._sequence + + ## following methods not implemented for keys ## + def __delitem__(self, i): raise TypeError('Can\'t delete items from keys') + def __iadd__(self, other): raise TypeError('Can\'t add in place to keys') + def __imul__(self, n): raise TypeError('Can\'t multiply keys in place') + def append(self, item): raise TypeError('Can\'t append items to keys') + def insert(self, i, item): raise TypeError('Can\'t insert items into keys') + def pop(self, i=-1): raise TypeError('Can\'t pop items from keys') + def remove(self, item): raise TypeError('Can\'t remove items from keys') + def extend(self, other): raise TypeError('Can\'t extend keys') + +class Items(object): + """ + Custom object for accessing the items of an OrderedDict. + + Can be called like the normal ``OrderedDict.items`` method, but also + supports indexing and sequence methods. + """ + + def __init__(self, main): + self._main = main + + def __call__(self): + """Pretend to be the items method.""" + return self._main._items() + + def __getitem__(self, index): + """Fetch the item at position i.""" + if isinstance(index, types.SliceType): + # fetching a slice returns an OrderedDict + return self._main[index].items() + key = self._main._sequence[index] + return (key, self._main[key]) + + def __setitem__(self, index, item): + """Set item at position i to item.""" + if isinstance(index, types.SliceType): + # NOTE: item must be an iterable (list of tuples) + self._main[index] = OrderedDict(item) + else: + # FIXME: Does this raise a sensible error? + orig = self._main.keys[index] + key, value = item + if self._main.strict and key in self and (key != orig): + raise ValueError('slice assignment must be from ' + 'unique keys') + # delete the current one + del self._main[self._main._sequence[index]] + self._main.insert(index, key, value) + + def __delitem__(self, i): + """Delete the item at position i.""" + key = self._main._sequence[i] + if isinstance(i, types.SliceType): + for k in key: + # FIXME: efficiency? + del self._main[k] + else: + del self._main[key] + + ### following methods pinched from UserList and adapted ### + def __repr__(self): return repr(self._main.items()) + + # FIXME: do we need to check if we are comparing with another ``Items`` + # object? (like the __cast method of UserList) + def __lt__(self, other): return self._main.items() < other + def __le__(self, other): return self._main.items() <= other + def __eq__(self, other): return self._main.items() == other + def __ne__(self, other): return self._main.items() != other + def __gt__(self, other): return self._main.items() > other + def __ge__(self, other): return self._main.items() >= other + def __cmp__(self, other): return cmp(self._main.items(), other) + + def __contains__(self, item): return item in self._main.items() + def __len__(self): return len(self._main._sequence) # easier :-) + def __iter__(self): return self._main.iteritems() + def count(self, item): return self._main.items().count(item) + def index(self, item, *args): return self._main.items().index(item, *args) + def reverse(self): self._main.reverse() + def sort(self, *args, **kwds): self._main.sort(*args, **kwds) + def __mul__(self, n): return self._main.items()*n + __rmul__ = __mul__ + def __add__(self, other): return self._main.items() + other + def __radd__(self, other): return other + self._main.items() + + def append(self, item): + """Add an item to the end.""" + # FIXME: this is only append if the key isn't already present + key, value = item + self._main[key] = value + + def insert(self, i, item): + key, value = item + self._main.insert(i, key, value) + + def pop(self, i=-1): + key = self._main._sequence[i] + return (key, self._main.pop(key)) + + def remove(self, item): + key, value = item + try: + assert value == self._main[key] + except (KeyError, AssertionError): + raise ValueError('ValueError: list.remove(x): x not in list') + else: + del self._main[key] + + def extend(self, other): + # FIXME: is only a true extend if none of the keys already present + for item in other: + key, value = item + self._main[key] = value + + def __iadd__(self, other): + self.extend(other) + + ## following methods not implemented for items ## + + def __imul__(self, n): raise TypeError('Can\'t multiply items in place') + +class Values(object): + """ + Custom object for accessing the values of an OrderedDict. + + Can be called like the normal ``OrderedDict.values`` method, but also + supports indexing and sequence methods. + """ + + def __init__(self, main): + self._main = main + + def __call__(self): + """Pretend to be the values method.""" + return self._main._values() + + def __getitem__(self, index): + """Fetch the value at position i.""" + if isinstance(index, types.SliceType): + return [self._main[key] for key in self._main._sequence[index]] + else: + return self._main[self._main._sequence[index]] + + def __setitem__(self, index, value): + """ + Set the value at position i to value. + + You can only do slice assignment to values if you supply a sequence of + equal length to the slice you are replacing. + """ + if isinstance(index, types.SliceType): + keys = self._main._sequence[index] + if len(keys) != len(value): + raise ValueError('attempt to assign sequence of size %s ' + 'to slice of size %s' % (len(name), len(keys))) + # FIXME: efficiency? Would be better to calculate the indexes + # directly from the slice object + # NOTE: the new keys can collide with existing keys (or even + # contain duplicates) - these will overwrite + for key, val in zip(keys, value): + self._main[key] = val + else: + self._main[self._main._sequence[index]] = value + + ### following methods pinched from UserList and adapted ### + def __repr__(self): return repr(self._main.values()) + + # FIXME: do we need to check if we are comparing with another ``Values`` + # object? (like the __cast method of UserList) + def __lt__(self, other): return self._main.values() < other + def __le__(self, other): return self._main.values() <= other + def __eq__(self, other): return self._main.values() == other + def __ne__(self, other): return self._main.values() != other + def __gt__(self, other): return self._main.values() > other + def __ge__(self, other): return self._main.values() >= other + def __cmp__(self, other): return cmp(self._main.values(), other) + + def __contains__(self, item): return item in self._main.values() + def __len__(self): return len(self._main._sequence) # easier :-) + def __iter__(self): return self._main.itervalues() + def count(self, item): return self._main.values().count(item) + def index(self, item, *args): return self._main.values().index(item, *args) + + def reverse(self): + """Reverse the values""" + vals = self._main.values() + vals.reverse() + # FIXME: efficiency + self[:] = vals + + def sort(self, *args, **kwds): + """Sort the values.""" + vals = self._main.values() + vals.sort(*args, **kwds) + self[:] = vals + + def __mul__(self, n): return self._main.values()*n + __rmul__ = __mul__ + def __add__(self, other): return self._main.values() + other + def __radd__(self, other): return other + self._main.values() + + ## following methods not implemented for values ## + def __delitem__(self, i): raise TypeError('Can\'t delete items from values') + def __iadd__(self, other): raise TypeError('Can\'t add in place to values') + def __imul__(self, n): raise TypeError('Can\'t multiply values in place') + def append(self, item): raise TypeError('Can\'t append items to values') + def insert(self, i, item): raise TypeError('Can\'t insert items into values') + def pop(self, i=-1): raise TypeError('Can\'t pop items from values') + def remove(self, item): raise TypeError('Can\'t remove items from values') + def extend(self, other): raise TypeError('Can\'t extend values') + +class SequenceOrderedDict(OrderedDict): + """ + Experimental version of OrderedDict that has a custom object for ``keys``, + ``values``, and ``items``. + + These are callable sequence objects that work as methods, or can be + manipulated directly as sequences. + + Test for ``keys``, ``items`` and ``values``. + + >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4))) + >>> d + SequenceOrderedDict([(1, 2), (2, 3), (3, 4)]) + >>> d.keys + [1, 2, 3] + >>> d.keys() + [1, 2, 3] + >>> d.setkeys((3, 2, 1)) + >>> d + SequenceOrderedDict([(3, 4), (2, 3), (1, 2)]) + >>> d.setkeys((1, 2, 3)) + >>> d.keys[0] + 1 + >>> d.keys[:] + [1, 2, 3] + >>> d.keys[-1] + 3 + >>> d.keys[-2] + 2 + >>> d.keys[0:2] = [2, 1] + >>> d + SequenceOrderedDict([(2, 3), (1, 2), (3, 4)]) + >>> d.keys.reverse() + >>> d.keys + [3, 1, 2] + >>> d.keys = [1, 2, 3] + >>> d + SequenceOrderedDict([(1, 2), (2, 3), (3, 4)]) + >>> d.keys = [3, 1, 2] + >>> d + SequenceOrderedDict([(3, 4), (1, 2), (2, 3)]) + >>> a = SequenceOrderedDict() + >>> b = SequenceOrderedDict() + >>> a.keys == b.keys + 1 + >>> a['a'] = 3 + >>> a.keys == b.keys + 0 + >>> b['a'] = 3 + >>> a.keys == b.keys + 1 + >>> b['b'] = 3 + >>> a.keys == b.keys + 0 + >>> a.keys > b.keys + 0 + >>> a.keys < b.keys + 1 + >>> 'a' in a.keys + 1 + >>> len(b.keys) + 2 + >>> 'c' in d.keys + 0 + >>> 1 in d.keys + 1 + >>> [v for v in d.keys] + [3, 1, 2] + >>> d.keys.sort() + >>> d.keys + [1, 2, 3] + >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4)), strict=True) + >>> d.keys[::-1] = [1, 2, 3] + >>> d + SequenceOrderedDict([(3, 4), (2, 3), (1, 2)]) + >>> d.keys[:2] + [3, 2] + >>> d.keys[:2] = [1, 3] + Traceback (most recent call last): + KeyError: 'Keylist is not the same as current keylist.' + + >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4))) + >>> d + SequenceOrderedDict([(1, 2), (2, 3), (3, 4)]) + >>> d.values + [2, 3, 4] + >>> d.values() + [2, 3, 4] + >>> d.setvalues((4, 3, 2)) + >>> d + SequenceOrderedDict([(1, 4), (2, 3), (3, 2)]) + >>> d.values[::-1] + [2, 3, 4] + >>> d.values[0] + 4 + >>> d.values[-2] + 3 + >>> del d.values[0] + Traceback (most recent call last): + TypeError: Can't delete items from values + >>> d.values[::2] = [2, 4] + >>> d + SequenceOrderedDict([(1, 2), (2, 3), (3, 4)]) + >>> 7 in d.values + 0 + >>> len(d.values) + 3 + >>> [val for val in d.values] + [2, 3, 4] + >>> d.values[-1] = 2 + >>> d.values.count(2) + 2 + >>> d.values.index(2) + 0 + >>> d.values[-1] = 7 + >>> d.values + [2, 3, 7] + >>> d.values.reverse() + >>> d.values + [7, 3, 2] + >>> d.values.sort() + >>> d.values + [2, 3, 7] + >>> d.values.append('anything') + Traceback (most recent call last): + TypeError: Can't append items to values + >>> d.values = (1, 2, 3) + >>> d + SequenceOrderedDict([(1, 1), (2, 2), (3, 3)]) + + >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4))) + >>> d + SequenceOrderedDict([(1, 2), (2, 3), (3, 4)]) + >>> d.items() + [(1, 2), (2, 3), (3, 4)] + >>> d.setitems([(3, 4), (2 ,3), (1, 2)]) + >>> d + SequenceOrderedDict([(3, 4), (2, 3), (1, 2)]) + >>> d.items[0] + (3, 4) + >>> d.items[:-1] + [(3, 4), (2, 3)] + >>> d.items[1] = (6, 3) + >>> d.items + [(3, 4), (6, 3), (1, 2)] + >>> d.items[1:2] = [(9, 9)] + >>> d + SequenceOrderedDict([(3, 4), (9, 9), (1, 2)]) + >>> del d.items[1:2] + >>> d + SequenceOrderedDict([(3, 4), (1, 2)]) + >>> (3, 4) in d.items + 1 + >>> (4, 3) in d.items + 0 + >>> len(d.items) + 2 + >>> [v for v in d.items] + [(3, 4), (1, 2)] + >>> d.items.count((3, 4)) + 1 + >>> d.items.index((1, 2)) + 1 + >>> d.items.index((2, 1)) + Traceback (most recent call last): + ValueError: list.index(x): x not in list + >>> d.items.reverse() + >>> d.items + [(1, 2), (3, 4)] + >>> d.items.reverse() + >>> d.items.sort() + >>> d.items + [(1, 2), (3, 4)] + >>> d.items.append((5, 6)) + >>> d.items + [(1, 2), (3, 4), (5, 6)] + >>> d.items.insert(0, (0, 0)) + >>> d.items + [(0, 0), (1, 2), (3, 4), (5, 6)] + >>> d.items.insert(-1, (7, 8)) + >>> d.items + [(0, 0), (1, 2), (3, 4), (7, 8), (5, 6)] + >>> d.items.pop() + (5, 6) + >>> d.items + [(0, 0), (1, 2), (3, 4), (7, 8)] + >>> d.items.remove((1, 2)) + >>> d.items + [(0, 0), (3, 4), (7, 8)] + >>> d.items.extend([(1, 2), (5, 6)]) + >>> d.items + [(0, 0), (3, 4), (7, 8), (1, 2), (5, 6)] + """ + + def __init__(self, init_val=(), strict=True): + OrderedDict.__init__(self, init_val, strict=strict) + self._keys = self.keys + self._values = self.values + self._items = self.items + self.keys = Keys(self) + self.values = Values(self) + self.items = Items(self) + self._att_dict = { + 'keys': self.setkeys, + 'items': self.setitems, + 'values': self.setvalues, + } + + def __setattr__(self, name, value): + """Protect keys, items, and values.""" + if not '_att_dict' in self.__dict__: + object.__setattr__(self, name, value) + else: + try: + fun = self._att_dict[name] + except KeyError: + OrderedDict.__setattr__(self, name, value) + else: + fun(value) + +if __name__ == '__main__': + if INTP_VER < (2, 3): + raise RuntimeError("Tests require Python v.2.3 or later") + # turn off warnings for tests + warnings.filterwarnings('ignore') + # run the code tests in doctest format + import doctest + m = sys.modules.get('__main__') + globs = m.__dict__.copy() + globs.update({ + 'INTP_VER': INTP_VER, + }) + doctest.testmod(m, globs=globs) + diff --git a/portato/plugin.py b/portato/plugin.py index bf9dc91..5926922 100644 --- a/portato/plugin.py +++ b/portato/plugin.py @@ -180,12 +180,13 @@ class Plugin: """Gets an <hooks>-elements and parses it. @param hooks: the hooks node - @type hooks: Node""" + @type hooks: NodeList""" - for h in hooks.getElementsByTagName("hook"): - hook = Hook(self, str(h.getAttribute("type")), str(h.getAttribute("call"))) - hook.parse_connects(h.getElementsByTagName("connect")) - self.hooks.append(hook) + 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 parse_menus (self, menus): """Get a list of <menu>-elements and parses them. @@ -388,7 +389,7 @@ class PluginQueue: if frontendOK is None or frontendOK == True: plugin = Plugin(p, elem.getElementsByTagName("name")[0], elem.getElementsByTagName("author")[0]) - plugin.parse_hooks(elem.getElementsByTagName("hooks")[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")) diff --git a/portato/plugins/dbus_init.py b/portato/plugins/dbus_init.py deleted file mode 100644 index 653af31..0000000 --- a/portato/plugins/dbus_init.py +++ /dev/null @@ -1,10 +0,0 @@ -try: - from dbus.mainloop.glib import threads_init -except ImportError: - threads_init = None - -from portato.constants import USE_CATAPULT - -def dbus_init (*args): - if USE_CATAPULT and threads_init is not None: - threads_init() diff --git a/portato/plugins/shutdown.py b/portato/plugins/gpytage.py index 120bac8..22cc7ef 100644 --- a/portato/plugins/shutdown.py +++ b/portato/plugins/gpytage.py @@ -1,17 +1,16 @@ # -*- coding: utf-8 -*- # -# File: portato/plugins/shutdown.py +# File: portato/plugins/gpytage.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> -import os +from subprocess import Popen -def shutdown (*args, **kwargs): - """Shutdown the computer. May not work if not root.""" - os.system("shutdown -h now") +def gpytage(*args, **kwargs): + Popen(["/usr/bin/gpytage"]) diff --git a/portato/plugins/resume_loop.py b/portato/plugins/resume_loop.py deleted file mode 100644 index e4531d0..0000000 --- a/portato/plugins/resume_loop.py +++ /dev/null @@ -1,52 +0,0 @@ -# -*- coding: utf-8 -*- -# -# File: portato/plugins/resume_loop.py -# This file is part of the Portato-Project, a graphical portage-frontend. -# -# Copyright (C) 2007 René 'Necoro' Neumann -# This is free software. You may redistribute copies of it under the terms of -# the GNU General Public License version 2. -# There is NO WARRANTY, to the extent permitted by law. -# -# Written by René 'Necoro' Neumann <necoro@necoro.net> - -import pty, time -from subprocess import Popen, STDOUT -from portato.backend import system -from portato.helper import debug, warning - -console = None -title_update = None -command = "until emerge --resume --skipfirst; do : ; done" - -def set_data (*args, **kwargs): - global console, title_update - console = kwargs["console"] - title_update = kwargs["title_update"] - -def resume_loop (retcode, *args, **kwargs): - if retcode is None: - warning(_("Resume-loop called while process is still running!")) - elif retcode == 0: - # everything ok - ignore - #pass - debug("Everything is ok.") - else: - if console is None: - debug("No console for the resume loop...") - else: - # open tty - (master, slave) = pty.openpty() - console.set_pty(master) - p = Popen(command, stdout = slave, stderr = STDOUT, shell = True, env = system.get_environment()) - - # update titles - old_title = console.get_window_title() - while p and p.poll() is None: - if title_update : - title = console.get_window_title() - if title != old_title: - title_update(title) - time.sleep(0.5) - - if title_update: title_update(None) diff --git a/portato/session.py b/portato/session.py index 6abd899..56f0af8 100644 --- a/portato/session.py +++ b/portato/session.py @@ -54,20 +54,20 @@ class Session (object): # add version check self.add_handler(([("version", "session")], self.check_version, lambda: self.VERSION)) - def add_handler (self, (options, load_fn, save_fn)): + def add_handler (self, (options, load_fn, save_fn), default = None): """ Adds a handler to this session. A handler is a three-tuple consisting of: - a list of (key,section) values - a function getting number of option arguments and applying them to the program - a function returning the number of option return values - getting them out of the program """ - self._handlers.append((options, load_fn, save_fn)) + self._handlers.append((options, load_fn, save_fn, default)) def load (self): """ Loads and applies all values of the session. """ - for options, lfn, sfn in self._handlers: + for options, lfn, sfn, default in self._handlers: try: loaded = [self._cfg.get(*x) for x in options] except KeyError: # does not exist -> ignore @@ -75,13 +75,18 @@ class Session (object): else: debug("Loading %s with values %s.", options, loaded) lfn(*loaded) + continue + + if default: + debug("Loading %s with defaults %s.", options, default) + lfn(*default) def save (self): """ Saves all options into the file. """ - for options, lfn, sfn in self._handlers: + for options, lfn, sfn, default in self._handlers: vals = sfn() # map into list if necessairy @@ -23,7 +23,7 @@ packages = ["portato", "portato.gui", "portato.gui.windows", "portato.plugins", 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")), +# (PLUGIN_DIR, plugin_list("dbus_init")), (DATA_DIR, ["plugin.xsd"])] # do the distutils setup |