diff --git a/locale/pl.po b/locale/pl.po index c3e0a22..fc19af5 100644 --- a/locale/pl.po +++ b/locale/pl.po @@ -32,14 +32,17 @@ msgid "Add _Directory" msgstr "Dodaj kata_log" #: pygtktalog/views/glade/main.glade.h:6 +#: pygtktalog/views/glade/files.glade.h:1 msgid "Add _Images" msgstr "Dodaj _obrazy" #: pygtktalog/views/glade/main.glade.h:7 +#: pygtktalog/views/glade/files.glade.h:2 msgid "Add _Thumbnail" msgstr "Dodaj miniaturę" #: pygtktalog/views/glade/main.glade.h:8 +#: pygtktalog/views/glade/files.glade.h:3 msgid "" "Add images to file. If file have no thumbnail,\n" "thumbnail from first image will be generated." @@ -48,182 +51,234 @@ msgstr "" "zostanie wygenerowana miniatura z pierwszego obrazu." #: pygtktalog/views/glade/main.glade.h:10 +#: pygtktalog/views/glade/main.glade.h:6 msgid "Cancel" msgstr "Analuj" #: pygtktalog/views/glade/main.glade.h:11 +#: pygtktalog/views/glade/main.glade.h:7 msgid "Catalog _statistics" msgstr "_Statystyka katalogu" #: pygtktalog/views/glade/main.glade.h:12 +#: pygtktalog/views/glade/discs.glade.h:1 msgid "Collapse all nodes" msgstr "Zwiń wszystkie gałęzie" #: pygtktalog/views/glade/main.glade.h:13 +#: pygtktalog/views/glade/main.glade.h:8 msgid "Create new catalog" msgstr "Utwórz nowy katalog" #: pygtktalog/views/glade/main.glade.h:14 +#: pygtktalog/views/glade/main.glade.h:9 msgid "Delete all images" msgstr "Usuń wszystkie obrazy" #: pygtktalog/views/glade/main.glade.h:15 +#: pygtktalog/views/glade/main.glade.h:10 msgid "Delete all thumbnals" msgstr "Usuń wszystkie miniatury" #: pygtktalog/views/glade/main.glade.h:16 +#: pygtktalog/views/glade/main.glade.h:11 msgid "Delete tag" msgstr "Usuń tag" #: pygtktalog/views/glade/main.glade.h:17 +#: pygtktalog/views/glade/main.glade.h:12 msgid "Deletes all images from files in current colection" msgstr "Usuwa wszystkie obrazy z plików w bieżącej kolekcji" #: pygtktalog/views/glade/main.glade.h:18 +#: pygtktalog/views/glade/main.glade.h:13 msgid "Discs" msgstr "Dyski" #: pygtktalog/views/glade/main.glade.h:19 +#: pygtktalog/views/glade/details.glade.h:1 msgid "Double click to open image" msgstr "Dwukrotne kliknięcie otwiera obraz" #: pygtktalog/views/glade/main.glade.h:20 +#: pygtktalog/views/glade/details.glade.h:2 msgid "EXIF" msgstr "EXIF" #: pygtktalog/views/glade/main.glade.h:21 +#: pygtktalog/views/glade/discs.glade.h:2 msgid "Expand all nodes" msgstr "Rozwiń wszystkie gałęzie" #: pygtktalog/views/glade/main.glade.h:22 +#: pygtktalog/views/glade/main.glade.h:14 msgid "Export" msgstr "Eksport" #: pygtktalog/views/glade/main.glade.h:23 +#: pygtktalog/views/glade/details.glade.h:3 msgid "File info" msgstr "Informacje o pliku" #: pygtktalog/views/glade/main.glade.h:24 +#: pygtktalog/views/glade/main.glade.h:15 msgid "Find file" msgstr "Znajdź plik" #: pygtktalog/views/glade/main.glade.h:25 +#: pygtktalog/views/glade/details.glade.h:4 msgid "Images" msgstr "Obrazy" #: pygtktalog/views/glade/main.glade.h:26 +#: pygtktalog/views/glade/main.glade.h:16 msgid "Import" msgstr "Import" #: pygtktalog/views/glade/main.glade.h:27 +#: pygtktalog/views/glade/main.glade.h:17 msgid "Open catalog file" msgstr "Otwórz plik katalogu" #: pygtktalog/views/glade/main.glade.h:28 +#: pygtktalog/views/glade/main.glade.h:18 msgid "Quit pyGTKtalog" msgstr "Zakończ pyGTKtalog" #: pygtktalog/views/glade/main.glade.h:29 +#: pygtktalog/views/glade/files.glade.h:5 msgid "Re_move Thumbnail" msgstr "Usuń miniaturę" #: pygtktalog/views/glade/main.glade.h:30 +#: pygtktalog/views/glade/main.glade.h:19 msgid "Recent files" msgstr "Ostatnio używane pliki" #: pygtktalog/views/glade/main.glade.h:31 +#: pygtktalog/views/glade/files.glade.h:6 msgid "Rem_ove All Images" msgstr "Usuń wszystkie obrazy" #: pygtktalog/views/glade/main.glade.h:32 +#: pygtktalog/views/glade/files.glade.h:7 msgid "Remo_ve tag" msgstr "Usuń tag" #: pygtktalog/views/glade/main.glade.h:33 +#: pygtktalog/views/glade/main.glade.h:20 msgid "Save all images..." msgstr "Zapisz wszytkie obrazy..." #: pygtktalog/views/glade/main.glade.h:34 +#: pygtktalog/views/glade/main.glade.h:21 msgid "Save catalog" msgstr "Zapisz katalog" #: pygtktalog/views/glade/main.glade.h:35 +#: pygtktalog/views/glade/main.glade.h:22 msgid "Set as _thumbnail" msgstr "Ustaw jako miniaturę" #: pygtktalog/views/glade/main.glade.h:36 +#: pygtktalog/views/glade/main.glade.h:23 msgid "Status bar" msgstr "Pasek stanu" #: pygtktalog/views/glade/main.glade.h:37 +#: pygtktalog/views/glade/main.glade.h:24 msgid "Tags" msgstr "Tagi" #: pygtktalog/views/glade/main.glade.h:38 +#: pygtktalog/views/glade/main.glade.h:25 msgid "Toolbar" msgstr "Pasek narzędzi" #: pygtktalog/views/glade/main.glade.h:39 +#: pygtktalog/views/glade/main.glade.h:26 msgid "_Add images" msgstr "Dodaj obrazy" #: pygtktalog/views/glade/main.glade.h:40 +#: pygtktalog/views/glade/files.glade.h:8 msgid "_Add tag" msgstr "_Dodaj tag" #: pygtktalog/views/glade/main.glade.h:41 +#: pygtktalog/views/glade/main.glade.h:27 msgid "_Catalog" msgstr "_Katalog" #: pygtktalog/views/glade/main.glade.h:42 +#: pygtktalog/views/glade/discs.glade.h:3 msgid "_Collapse all" msgstr "_Zwiń wszystko" #: pygtktalog/views/glade/main.glade.h:43 +#: pygtktalog/views/glade/discs.glade.h:4 +#: pygtktalog/views/glade/files.glade.h:9 msgid "_Delete" msgstr "_Usuń" #: pygtktalog/views/glade/main.glade.h:44 +#: pygtktalog/views/glade/main.glade.h:28 msgid "_Delete images" msgstr "Usuń obrazy" #: pygtktalog/views/glade/main.glade.h:45 +#: pygtktalog/views/glade/main.glade.h:29 +#: pygtktalog/views/glade/files.glade.h:10 +#: pygtktalog/views/glade/test.glade.h:1 msgid "_Edit" msgstr "_Edycja" #: pygtktalog/views/glade/main.glade.h:46 +#: pygtktalog/views/glade/discs.glade.h:5 msgid "_Expand all" msgstr "_Rozwiń wszystko" -#: pygtktalog/views/glade/main.glade.h:47 +#: pygtktalog/views/glade/main.glade.h:47 pygtktalog/views/main_menu.py:19 +#: pygtktalog/views/glade/main.glade.h:30 +#: pygtktalog/views/glade/test.glade.h:2 msgid "_File" msgstr "_Plik" #: pygtktalog/views/glade/main.glade.h:48 +#: pygtktalog/views/glade/main.glade.h:31 +#: pygtktalog/views/glade/test.glade.h:3 msgid "_Help" msgstr "_Pomoc" #: pygtktalog/views/glade/main.glade.h:49 +#: pygtktalog/views/glade/main.glade.h:32 msgid "_Remove Thumbnail" msgstr "Usuń miniaturę" #: pygtktalog/views/glade/main.glade.h:50 +#: pygtktalog/views/glade/discs.glade.h:6 +#: pygtktalog/views/glade/files.glade.h:11 msgid "_Rename" msgstr "_Zmień nazwę" #: pygtktalog/views/glade/main.glade.h:51 +#: pygtktalog/views/glade/main.glade.h:33 msgid "_Save images to..." msgstr "Zapisz obrazy do..." #: pygtktalog/views/glade/main.glade.h:52 +#: pygtktalog/views/glade/discs.glade.h:7 msgid "_Statistics" msgstr "_Statystyka" #: pygtktalog/views/glade/main.glade.h:53 +#: pygtktalog/views/glade/discs.glade.h:8 msgid "_Update" msgstr "_Uaktualnij" #: pygtktalog/views/glade/main.glade.h:54 +#: pygtktalog/views/glade/main.glade.h:34 +#: pygtktalog/views/glade/test.glade.h:4 msgid "_View" msgstr "_Widok" @@ -232,10 +287,12 @@ msgid "gtk-clear" msgstr "" #: pygtktalog/views/glade/main.glade.h:56 +#: pygtktalog/views/glade/main.glade.h:35 msgid "pyGTKtalog" msgstr "pyGTKtalog" #: pygtktalog/views/glade/main.glade.h:57 +#: pygtktalog/views/glade/main.glade.h:36 msgid "pyGTKtalog - Image" msgstr "pyGTKtalog - Obraz" @@ -251,6 +308,68 @@ msgstr "Bieżący katalog nie jest zapisany, wszelkie zmiany zostaną utracone." msgid "Quit application" msgstr "Zakończ aplikację" -#: pygtktalog/models/main.py:16 +#: pygtktalog/models/main.py:16 pygtktalog/models/main.py:31 msgid "Idle" msgstr "Bezczynny" + +#: pygtktalog/controllers/files.py:33 +#, fuzzy +msgid "Disc" +msgstr "Dyski" + +#: pygtktalog/controllers/files.py:39 +#, fuzzy +msgid "Filename" +msgstr "_Zmień nazwę" + +#: pygtktalog/controllers/files.py:50 +msgid "Path" +msgstr "" + +#: pygtktalog/controllers/files.py:56 +msgid "Size" +msgstr "" + +#: pygtktalog/controllers/files.py:61 +msgid "Date" +msgstr "" + +#: pygtktalog/views/glade/test.glade.h:5 +msgid "gtk-about" +msgstr "" + +#: pygtktalog/views/glade/test.glade.h:6 +msgid "gtk-copy" +msgstr "" + +#: pygtktalog/views/glade/test.glade.h:7 +msgid "gtk-cut" +msgstr "" + +#: pygtktalog/views/glade/test.glade.h:8 +msgid "gtk-delete" +msgstr "" + +#: pygtktalog/views/glade/test.glade.h:9 +msgid "gtk-new" +msgstr "" + +#: pygtktalog/views/glade/test.glade.h:10 +msgid "gtk-open" +msgstr "" + +#: pygtktalog/views/glade/test.glade.h:11 +msgid "gtk-paste" +msgstr "" + +#: pygtktalog/views/glade/test.glade.h:12 +msgid "gtk-quit" +msgstr "" + +#: pygtktalog/views/glade/test.glade.h:13 +msgid "gtk-save" +msgstr "" + +#: pygtktalog/views/glade/test.glade.h:14 +msgid "gtk-save-as" +msgstr "" diff --git a/locale/pygtktalog.pot b/locale/pygtktalog.pot index 5aa856f..5d4dd4e 100644 --- a/locale/pygtktalog.pot +++ b/locale/pygtktalog.pot @@ -31,196 +31,251 @@ msgid "Add _Directory" msgstr "" #: pygtktalog/views/glade/main.glade.h:6 +#: pygtktalog/views/glade/files.glade.h:1 msgid "Add _Images" msgstr "" #: pygtktalog/views/glade/main.glade.h:7 +#: pygtktalog/views/glade/files.glade.h:2 msgid "Add _Thumbnail" msgstr "" #: pygtktalog/views/glade/main.glade.h:8 +#: pygtktalog/views/glade/files.glade.h:3 msgid "" "Add images to file. If file have no thumbnail,\n" "thumbnail from first image will be generated." msgstr "" #: pygtktalog/views/glade/main.glade.h:10 +#: pygtktalog/views/glade/main.glade.h:6 msgid "Cancel" msgstr "" #: pygtktalog/views/glade/main.glade.h:11 +#: pygtktalog/views/glade/main.glade.h:7 msgid "Catalog _statistics" msgstr "" #: pygtktalog/views/glade/main.glade.h:12 +#: pygtktalog/views/glade/discs.glade.h:1 msgid "Collapse all nodes" msgstr "" #: pygtktalog/views/glade/main.glade.h:13 +#: pygtktalog/views/glade/main.glade.h:8 msgid "Create new catalog" msgstr "" #: pygtktalog/views/glade/main.glade.h:14 +#: pygtktalog/views/glade/main.glade.h:9 msgid "Delete all images" msgstr "" #: pygtktalog/views/glade/main.glade.h:15 +#: pygtktalog/views/glade/main.glade.h:10 msgid "Delete all thumbnals" msgstr "" #: pygtktalog/views/glade/main.glade.h:16 +#: pygtktalog/views/glade/main.glade.h:11 msgid "Delete tag" msgstr "" #: pygtktalog/views/glade/main.glade.h:17 +#: pygtktalog/views/glade/main.glade.h:12 msgid "Deletes all images from files in current colection" msgstr "" #: pygtktalog/views/glade/main.glade.h:18 +#: pygtktalog/views/glade/main.glade.h:13 msgid "Discs" msgstr "" #: pygtktalog/views/glade/main.glade.h:19 +#: pygtktalog/views/glade/details.glade.h:1 msgid "Double click to open image" msgstr "" #: pygtktalog/views/glade/main.glade.h:20 +#: pygtktalog/views/glade/details.glade.h:2 msgid "EXIF" msgstr "" #: pygtktalog/views/glade/main.glade.h:21 +#: pygtktalog/views/glade/discs.glade.h:2 msgid "Expand all nodes" msgstr "" #: pygtktalog/views/glade/main.glade.h:22 +#: pygtktalog/views/glade/main.glade.h:14 msgid "Export" msgstr "" #: pygtktalog/views/glade/main.glade.h:23 +#: pygtktalog/views/glade/details.glade.h:3 msgid "File info" msgstr "" #: pygtktalog/views/glade/main.glade.h:24 +#: pygtktalog/views/glade/main.glade.h:15 msgid "Find file" msgstr "" #: pygtktalog/views/glade/main.glade.h:25 +#: pygtktalog/views/glade/details.glade.h:4 msgid "Images" msgstr "" #: pygtktalog/views/glade/main.glade.h:26 +#: pygtktalog/views/glade/main.glade.h:16 msgid "Import" msgstr "" #: pygtktalog/views/glade/main.glade.h:27 +#: pygtktalog/views/glade/main.glade.h:17 msgid "Open catalog file" msgstr "" #: pygtktalog/views/glade/main.glade.h:28 +#: pygtktalog/views/glade/main.glade.h:18 msgid "Quit pyGTKtalog" msgstr "" #: pygtktalog/views/glade/main.glade.h:29 +#: pygtktalog/views/glade/files.glade.h:5 msgid "Re_move Thumbnail" msgstr "" #: pygtktalog/views/glade/main.glade.h:30 +#: pygtktalog/views/glade/main.glade.h:19 msgid "Recent files" msgstr "" #: pygtktalog/views/glade/main.glade.h:31 +#: pygtktalog/views/glade/files.glade.h:6 msgid "Rem_ove All Images" msgstr "" #: pygtktalog/views/glade/main.glade.h:32 +#: pygtktalog/views/glade/files.glade.h:7 msgid "Remo_ve tag" msgstr "" #: pygtktalog/views/glade/main.glade.h:33 +#: pygtktalog/views/glade/main.glade.h:20 msgid "Save all images..." msgstr "" #: pygtktalog/views/glade/main.glade.h:34 +#: pygtktalog/views/glade/main.glade.h:21 msgid "Save catalog" msgstr "" #: pygtktalog/views/glade/main.glade.h:35 +#: pygtktalog/views/glade/main.glade.h:22 msgid "Set as _thumbnail" msgstr "" #: pygtktalog/views/glade/main.glade.h:36 +#: pygtktalog/views/glade/main.glade.h:23 msgid "Status bar" msgstr "" #: pygtktalog/views/glade/main.glade.h:37 +#: pygtktalog/views/glade/main.glade.h:24 msgid "Tags" msgstr "" #: pygtktalog/views/glade/main.glade.h:38 +#: pygtktalog/views/glade/main.glade.h:25 msgid "Toolbar" msgstr "" #: pygtktalog/views/glade/main.glade.h:39 +#: pygtktalog/views/glade/main.glade.h:26 msgid "_Add images" msgstr "" #: pygtktalog/views/glade/main.glade.h:40 +#: pygtktalog/views/glade/files.glade.h:8 msgid "_Add tag" msgstr "" #: pygtktalog/views/glade/main.glade.h:41 +#: pygtktalog/views/glade/main.glade.h:27 msgid "_Catalog" msgstr "" #: pygtktalog/views/glade/main.glade.h:42 +#: pygtktalog/views/glade/discs.glade.h:3 msgid "_Collapse all" msgstr "" #: pygtktalog/views/glade/main.glade.h:43 +#: pygtktalog/views/glade/discs.glade.h:4 +#: pygtktalog/views/glade/files.glade.h:9 msgid "_Delete" msgstr "" #: pygtktalog/views/glade/main.glade.h:44 +#: pygtktalog/views/glade/main.glade.h:28 msgid "_Delete images" msgstr "" #: pygtktalog/views/glade/main.glade.h:45 +#: pygtktalog/views/glade/main.glade.h:29 +#: pygtktalog/views/glade/files.glade.h:10 +#: pygtktalog/views/glade/test.glade.h:1 msgid "_Edit" msgstr "" #: pygtktalog/views/glade/main.glade.h:46 +#: pygtktalog/views/glade/discs.glade.h:5 msgid "_Expand all" msgstr "" -#: pygtktalog/views/glade/main.glade.h:47 +#: pygtktalog/views/glade/main.glade.h:47 pygtktalog/views/main_menu.py:19 +#: pygtktalog/views/glade/main.glade.h:30 +#: pygtktalog/views/glade/test.glade.h:2 msgid "_File" msgstr "" #: pygtktalog/views/glade/main.glade.h:48 +#: pygtktalog/views/glade/main.glade.h:31 +#: pygtktalog/views/glade/test.glade.h:3 msgid "_Help" msgstr "" #: pygtktalog/views/glade/main.glade.h:49 +#: pygtktalog/views/glade/main.glade.h:32 msgid "_Remove Thumbnail" msgstr "" #: pygtktalog/views/glade/main.glade.h:50 +#: pygtktalog/views/glade/discs.glade.h:6 +#: pygtktalog/views/glade/files.glade.h:11 msgid "_Rename" msgstr "" #: pygtktalog/views/glade/main.glade.h:51 +#: pygtktalog/views/glade/main.glade.h:33 msgid "_Save images to..." msgstr "" #: pygtktalog/views/glade/main.glade.h:52 +#: pygtktalog/views/glade/discs.glade.h:7 msgid "_Statistics" msgstr "" #: pygtktalog/views/glade/main.glade.h:53 +#: pygtktalog/views/glade/discs.glade.h:8 msgid "_Update" msgstr "" #: pygtktalog/views/glade/main.glade.h:54 +#: pygtktalog/views/glade/main.glade.h:34 +#: pygtktalog/views/glade/test.glade.h:4 msgid "_View" msgstr "" @@ -229,10 +284,12 @@ msgid "gtk-clear" msgstr "" #: pygtktalog/views/glade/main.glade.h:56 +#: pygtktalog/views/glade/main.glade.h:35 msgid "pyGTKtalog" msgstr "" #: pygtktalog/views/glade/main.glade.h:57 +#: pygtktalog/views/glade/main.glade.h:36 msgid "pyGTKtalog - Image" msgstr "" @@ -248,6 +305,66 @@ msgstr "" msgid "Quit application" msgstr "" -#: pygtktalog/models/main.py:16 +#: pygtktalog/models/main.py:16 pygtktalog/models/main.py:31 msgid "Idle" msgstr "" + +#: pygtktalog/controllers/files.py:33 +msgid "Disc" +msgstr "" + +#: pygtktalog/controllers/files.py:39 +msgid "Filename" +msgstr "" + +#: pygtktalog/controllers/files.py:50 +msgid "Path" +msgstr "" + +#: pygtktalog/controllers/files.py:56 +msgid "Size" +msgstr "" + +#: pygtktalog/controllers/files.py:61 +msgid "Date" +msgstr "" + +#: pygtktalog/views/glade/test.glade.h:5 +msgid "gtk-about" +msgstr "" + +#: pygtktalog/views/glade/test.glade.h:6 +msgid "gtk-copy" +msgstr "" + +#: pygtktalog/views/glade/test.glade.h:7 +msgid "gtk-cut" +msgstr "" + +#: pygtktalog/views/glade/test.glade.h:8 +msgid "gtk-delete" +msgstr "" + +#: pygtktalog/views/glade/test.glade.h:9 +msgid "gtk-new" +msgstr "" + +#: pygtktalog/views/glade/test.glade.h:10 +msgid "gtk-open" +msgstr "" + +#: pygtktalog/views/glade/test.glade.h:11 +msgid "gtk-paste" +msgstr "" + +#: pygtktalog/views/glade/test.glade.h:12 +msgid "gtk-quit" +msgstr "" + +#: pygtktalog/views/glade/test.glade.h:13 +msgid "gtk-save" +msgstr "" + +#: pygtktalog/views/glade/test.glade.h:14 +msgid "gtk-save-as" +msgstr "" diff --git a/pygtktalog/controllers/files.py b/pygtktalog/controllers/files.py index e57465e..ecf97f2 100644 --- a/pygtktalog/controllers/files.py +++ b/pygtktalog/controllers/files.py @@ -9,6 +9,11 @@ import gtk from gtkmvc import Controller +from pygtktalog.pygtkutils import get_tv_item_under_cursor +from pygtktalog.logger import get_logger + +LOG = get_logger("files ctrl") + class FilesController(Controller): """ @@ -28,6 +33,20 @@ class FilesController(Controller): Register view, and setup columns for files treeview """ view['files'].set_model(self.model.files) + + sigs = {"add_tag": ("activate", self.on_add_tag1_activate), + "delete_tag": ("activate", self.on_delete_tag_activate), + "add_thumb": ("activate", self.on_add_thumb1_activate), + "remove_thumb": ("activate", self.on_remove_thumb1_activate), + "add_image": ("activate", self.on_add_image1_activate), + "remove_image": ("activate", self.on_remove_image1_activate), + "edit": ("activate", self.on_edit2_activate), + "delete": ("activate", self.on_delete3_activate), + "rename": ("activate", self.on_rename2_activate)} + for signal in sigs: + view.menu[signal].connect(sigs[signal][0], sigs[signal][1]) + + view['files'].get_selection().set_mode(gtk.SELECTION_MULTIPLE) col = gtk.TreeViewColumn(_('Disc'), gtk.CellRendererText(), text=1) @@ -65,7 +84,178 @@ class FilesController(Controller): self.view['files'].set_search_column(2) # setup d'n'd support - view['files'].drag_source_set(gtk.gdk.BUTTON1_MASK, - self.DND_TARGETS, - gtk.gdk.ACTION_COPY) + self.view['files'].drag_source_set(gtk.gdk.BUTTON1_MASK, + self.DND_TARGETS, + gtk.gdk.ACTION_COPY) + + # signals + def on_files_drag_data_get(self, treeview, context, selection, + targetType, eventTime): + """responce to "data get" DnD signal""" + # get selection, and send it to the client + if targetType == self.DND_TARGETS[0][2]: + # get selection + treesrl = treeview.get_selection() + model, list_of_paths = treesrl.get_selected_rows() + ids = [] + for path in list_of_paths: + fid = model.get_value(model.get_iter(path), 0) + ids.append(fid) + string = str(tuple(ids)).replace(",)", ")") + selection.set(selection.target, 8, string) + + def on_files_button_press_event(self, treeview, event): + """ + Handle right click on files treeview - show popup menu. + """ + LOG.debug(self.on_files_button_press_event.__doc__.strip()) + pathinfo = treeview.get_path_at_pos(int(event.x), int(event.y)) + + if event.button == 3: # Right mouse button. Show context menu. + if pathinfo: + path = pathinfo[0] + + # Make sure, that there is selected row + sel = treeview.get_selection() + sel.unselect_all() + sel.select_path(path) + + self._popup_menu(sel, event, event.button) + else: + self._popup_menu(None, event, event.button) + return True + #try: + # selection = tree.get_selection() + # model, list_of_paths = selection.get_selected_rows() + #except TypeError: + # list_of_paths = [] + + #if len(list_of_paths) == 0: + # # try to select item under cursor + # try: + # path, column, x, y = tree.get_path_at_pos(int(event.x), + # int(event.y)) + # except TypeError: + # # failed, do not show any popup and return + # tree.get_selection().unselect_all() + # return False + # selection.select_path(path[0]) + + #if len(list_of_paths) > 1: + # self.view['add_image'].set_sensitive(False) + # self.view['rename'].set_sensitive(False) + # self.view['edit'].set_sensitive(False) + #else: + # self.view['add_image'].set_sensitive(True) + # self.view['rename'].set_sensitive(True) + # self.view['edit'].set_sensitive(True) + #self.__popup_menu(event, 'files_popup') + #return True + + def on_files_cursor_changed(self, treeview): + """Show details of selected file/directory""" + file_id = get_tv_item_under_cursor(treeview) + LOG.debug("found item: %s" % file_id) + return + + def on_files_key_release_event(self, treeview, event): + """do something with pressed keys""" + if gtk.gdk.keyval_name(event.keyval) == 'Menu': + try: + selection = treeview.get_selection() + model, list_of_paths = selection.get_selected_rows() + if not list_of_paths: + return + except TypeError: + return + self._popup_menu(selection, event, 0) + + if gtk.gdk.keyval_name(event.keyval) == 'BackSpace': + d_path, d_column = self.view['discs'].get_cursor() + if d_path and d_column: + # easy way + model = self.view['discs'].get_model() + child_iter = model.get_iter(d_path) + parent_iter = model.iter_parent(child_iter) + if parent_iter: + self.view['discs'].set_cursor(model.get_path(parent_iter)) + else: + # hard way + f_model = treeview.get_model() + first_iter = f_model.get_iter_first() + first_child_value = f_model.get_value(first_iter, 0) + # get two steps up + val = self.model.get_parent_id(first_child_value) + parent_value = self.model.get_parent_id(val) + iter = self.model.discs_tree.get_iter_first() + while iter: + current_value = self.model.discs_tree.get_value(iter, + 0) + if current_value == parent_value: + path = self.model.discs_tree.get_path(iter) + self.view['discs'].set_cursor(path) + iter = None + else: + iter = self.model.discs_tree.iter_next(iter) + #if gtk.gdk.keyval_name(event.keyval) == 'Delete': + # for file_id in self.__get_tv_selection_ids(treeview): + # self.main.delete(file_id) + + #ids = self.__get_tv_selection_ids(self.view['files']) + + def on_files_row_activated(self, files_obj, row, column): + """On directory doubleclick in files listview dive into desired + branch.""" + f_iter = self.model.files_list.get_iter(row) + current_id = self.model.files_list.get_value(f_iter, 0) + + if self.model.files_list.get_value(f_iter, 6) == 1: + # ONLY directories. files are omitted. + self.__set_files_hiden_columns_visible(False) + self.model.get_root_entries(current_id) + + d_path, d_column = self.view['discs'].get_cursor() + if d_path: + if not self.view['discs'].row_expanded(d_path): + self.view['discs'].expand_row(d_path, False) + + discs_model = self.model.discs_tree + iterator = discs_model.get_iter(d_path) + new_iter = discs_model.iter_children(iterator) + if new_iter: + while new_iter: + current_value = discs_model.get_value(new_iter, 0) + if current_value == current_id: + path = discs_model.get_path(new_iter) + self.view['discs'].set_cursor(path) + new_iter = discs_model.iter_next(new_iter) + return + + def on_add_tag1_activate(self, menu_item): pass + def on_delete_tag_activate(self, menuitem): pass + def on_add_thumb1_activate(self, menuitem): pass + def on_remove_thumb1_activate(self, menuitem): pass + def on_add_image1_activate(self, menuitem): pass + def on_remove_image1_activate(self, menuitem): pass + def on_edit2_activate(self, menuitem): pass + def on_delete3_activate(self, menuitem): pass + def on_rename2_activate(self, menuitem): pass + + # private methods + def _popup_menu(self, selection, event, button): + """ + Popup menu for files treeview. Gather information from discs model, + and trigger menu popup. + """ + LOG.debug(self._popup_menu.__doc__.strip()) + if selection is None: + self.view.menu.set_menu_items_sensitivity(False) + else: + model, list_of_paths = selection.get_selected_rows() + + for path in list_of_paths: + self.view.menu.set_menu_items_sensitivity(True) + + self.view.menu['files_popup'].popup(None, None, None, + button, event.time) diff --git a/pygtktalog/controllers/main.py b/pygtktalog/controllers/main.py index 980148c..3e28c12 100644 --- a/pygtktalog/controllers/main.py +++ b/pygtktalog/controllers/main.py @@ -9,15 +9,17 @@ import gtk from gtkmvc import Controller -#from pygtktalog.dialogs import yesno -from pygtktalog.dialogs import About from pygtktalog.controllers.discs import DiscsController from pygtktalog.controllers.files import FilesController #from pygtktalog.controllers.details import DetailsController #from pygtktalog.controllers.tags import TagcloudController #from pygtktalog.dialogs import yesno, okcancel, info, warn, error +from pygtktalog.dialogs import open_catalog, save_catalog, error, yesno +from pygtktalog.dialogs import About # TODO: how about make it like a functions above? from pygtktalog.logger import get_logger from pygtktalog import __version__ +# although it seems to be unused, it is necessary, because it contains +# definitions for additional connectable widgets for observers. LOG = get_logger("main controller") @@ -25,6 +27,8 @@ class MainController(Controller): """ Controller for main application window """ + TITLE = "pyGTKtalog" + UNTITLED = _("untitled") def __init__(self, model, view): """ @@ -48,6 +52,8 @@ class MainController(Controller): LOG.debug("replace hardcoded defaults with configured!") view['main'].set_default_size(800, 600) view['hpaned1'].set_position(200) + if not self.model.tmp_filename: + view.set_widgets_app_sensitivity(False) view['main'].show() def register_adapters(self): @@ -55,6 +61,10 @@ class MainController(Controller): progress bar/status bar adapters goes here """ LOG.debug(self.register_adapters.__doc__.strip()) + #title_ad = Adapter(self.model, "cat_fname") + #title_ad.connect_widget(self.view["main"], + # setter=lambda w,v: \ + # w.set_title(self._get_title())) pass # signals @@ -79,10 +89,123 @@ class MainController(Controller): gtk.main_quit() return False + def on_new_activate(self, widget): + """ + Create new catalog file + """ + LOG.debug(self.on_new_activate.__doc__.strip()) + self.model.new() + self.view.discs['discs'].set_model(self.model.discs) + self.view.files['files'].set_model(self.model.files) + self._set_title() + self.view.set_widgets_app_sensitivity(True) + + def on_open_activate(self, widget): + """ + Open catalog file + """ + LOG.debug(self.on_open_activate.__doc__.strip()) + + if self.model.db_unsaved and 'confirm' in self.model.config and \ + self.model.config['confirm']: + if not yesno(_("Current database is not saved"), + _("Do you really want to open new file and abandon " + "current one?"), + _("Unsaved data")): + LOG.debug("Cancel opening catalog file - unsaved data remain") + return + + initial_path = None + #if self.model.config.recent and self.model.config.recent[0]: + # initial_path = os.path.dirname(self.model.config.recent[0]) + + #if not path: + path = open_catalog(path=initial_path) + if not path: + return + + # cleanup files and details + try: + self.model.files_list.clear() + except: + pass + #self.__hide_details() + #self.view['tag_path_box'].hide() + #buf = self.view['tag_cloud_textview'].get_buffer() + #buf.set_text('') + #self.view['tag_cloud_textview'].set_buffer(buf) + + if not self.model.open(path): + error(_("Cannot open file."), + _("File %s cannot be open") % path, + _("Error opening file")) + else: + #self.__generate_recent_menu() + #self.__activate_ui(path) + #self.__tag_cloud() + self.view.set_widgets_app_sensitivity() + self._set_title() + + return + def on_about1_activate(self, widget): """Show about dialog""" - About("pyGTKtalog", - "%s" % __version__, - "About", - ["Roman 'gryf' Dobosz"], - '') + About("pyGTKtalog", "%s" % __version__, "About", + ["Roman 'gryf' Dobosz"], '') + + def on_save_activate(self, widget): + """ + Save current catalog + """ + LOG.debug(self.on_save_activate.__doc__.strip()) + if not self.model.cat_fname: + self.on_save_as_activate(widget) + else: + self.model.save() + self._set_title() + + def on_save_as_activate(self, widget): + """ + Save current catalog under differnet file + """ + LOG.debug(self.on_save_as_activate.__doc__.strip()) + initial_path = None + #if self.model.config.recent[0]: + # initial_path = os.path.dirname(self.model.config.recent[0]) + + path = save_catalog(path=initial_path) + if path: + ret, err = self.model.save(path) + if ret: + #self.model.config.add_recent(path) + self._set_title() + pass + else: + error(_("Cannot write file %s.") % path, + "%s" % err, + _("Error writing file")) + + + # helpers + def _set_title(self): + """ + Get title of the main window, to reflect state of the catalog file + Returns: + String with apropriate title for main form + """ + LOG.debug("change the title") + + if not self.model.tmp_filename: + LOG.debug("application has been initialized, title should" + " be empty") + fname = "" + + elif not self.model.cat_fname: + fname = self.UNTITLED + else: + fname = self.model.cat_fname + + modified = self.model.db_unsaved and "*" or "" + + self.view['main'].set_title("%s%s - %s" % (fname, + modified, self.TITLE)) diff --git a/pygtktalog/dbcommon.py b/pygtktalog/dbcommon.py index f6e18bc..630888d 100644 --- a/pygtktalog/dbcommon.py +++ b/pygtktalog/dbcommon.py @@ -22,6 +22,8 @@ Meta = MetaData() Base = declarative_base(metadata=Meta) Session = sessionmaker() +LOG = get_logger("dbcommon") + def connect(filename): """ @@ -30,8 +32,7 @@ def connect(filename): @filename - string with absolute or relative path to sqlite database file. """ - get_logger("dbcommon").info("db filename: %s" % filename) + LOG.info("db filename: %s" % filename) engine = create_engine("sqlite:///%s" % filename) Meta.bind = engine - Meta.create_all() - + Meta.create_all(engine) diff --git a/pygtktalog/dialogs.py b/pygtktalog/dialogs.py index eb08110..cae939f 100644 --- a/pygtktalog/dialogs.py +++ b/pygtktalog/dialogs.py @@ -5,13 +5,15 @@ Author: Roman 'gryf' Dobosz, gryf73@gmail.com Created: 2009-05-12 """ +import os + import gtk class Dialog(object): - """Show simple dialog for questions - if "OK" button pressed, return "True" - "Cancel" button return "False" + """ + Show simple dialog for questions + Returns: Bool - True, if "OK" button pressed, False otherwise. """ def __init__(self, dialog_type, message, secondary_msg="", title=""): @@ -29,7 +31,7 @@ class Dialog(object): def run(self): """Show the dialog""" if self.dialog is None: - self.__create_dialog() + self._create_dialog() # Change default/focus from cancel/no to ok/yes. Suitable only for # Question dialog. @@ -45,7 +47,7 @@ class Dialog(object): return True return False - def __create_dialog(self): + def _create_dialog(self): """Create MessageDialog widgt""" if self.type == gtk.MESSAGE_QUESTION and \ self.buttons not in (gtk.BUTTONS_YES_NO, gtk.BUTTONS_OK_CANCEL): @@ -60,7 +62,7 @@ class About(object): """ Show About dialog """ - def __init__(self, name=None, ver="", title="", authors=[],licence=""): + def __init__(self, name=None, ver="", title="", authors=[], licence=""): self.dialog = gtk.AboutDialog() self.dialog.set_title(title) self.dialog.set_version(ver) @@ -74,6 +76,90 @@ class About(object): # TODO: finish this, re-use Dialog class instead of copy/paste of old classes! # def about(name, version, ) +class ChooseFile(object): + """ + Common file chooser + """ + URI = None + BUTTON_PAIRS = {'cancel': (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL), + 'ok': (gtk.STOCK_OK, gtk.RESPONSE_APPLY), + 'save': (gtk.STOCK_SAVE, gtk.RESPONSE_APPLY), + 'open': (gtk.STOCK_OPEN, gtk.RESPONSE_APPLY)} + CHOOSER_TYPES = {'open': gtk.FILE_CHOOSER_ACTION_OPEN, + 'save': gtk.FILE_CHOOSER_ACTION_SAVE} + FILTERS = {'catalogs': {'name': "Catalog files", + 'patterns': ("*.sqlite", "*.sqlite.bz2")}, + 'all': {'name': "All files", 'patterns': ("*.*",)}} + + def __init__(self, title="", buttons=('cancel', 'ok'), path=None, + chooser_type="open"): + super(ChooseFile, self).__init__() + self.path = path + self.title = title + self.action = self.CHOOSER_TYPES[chooser_type] + self.buttons=[] + for button in buttons: + self.buttons.append(self.BUTTON_PAIRS[button][0]) + self.buttons.append(self.BUTTON_PAIRS[button][1]) + self.buttons = tuple(self.buttons) + self.confirmation = False + self.dialog = None + self.filters = [] + + def _mk_dialog(self): + """ + Create FileChooserDialog object + """ + self.dialog = gtk.FileChooserDialog(self.title, None, self.action, + self.buttons) + self.dialog.set_action(gtk.FILE_CHOOSER_ACTION_SAVE) + self.dialog.set_default_response(gtk.RESPONSE_OK) + self.dialog.set_do_overwrite_confirmation(self.confirmation) + self.dialog.set_title(self.title) + + if self.URI: + self.dialog.set_current_folder_uri(self.URI) + elif self.path and os.path.exists(self.path): + self.path = "file://"+os.path.abspath(self.path) + self.dialog.set_current_folder_uri(self.path) + + for filtr in self._get_filters(): + self.dialog.add_filter(filtr) + + def _get_filters(self): + """ + """ + filters = [] + for filter_def in self.filters: + filtr = gtk.FileFilter() + filtr.set_name(self.FILTERS[filter_def]['name']) + for pat in self.FILTERS[filter_def]['patterns']: + filtr.add_pattern(pat) + filters.append(filtr) + return filters + + def run(self): + """ + Show dialog, get response. + Returns: + + Returns: String - with filename, None otherwise. + """ + + if self.dialog is None: + self._mk_dialog() + + response = self.dialog.run() + filename = None + + if response == gtk.RESPONSE_APPLY: + filename = self.dialog.get_filename() + self.__class__.URI = self.dialog.get_current_folder_uri() + + self.dialog.destroy() + return filename + + def yesno(message, secondarymsg="", title="", default=False): """Question with yes-no buttons. Returns False on 'no', True on 'yes'""" dialog = Dialog(gtk.MESSAGE_QUESTION, message, secondarymsg, title) @@ -116,3 +202,21 @@ def error(message, secondarymsg="", title="", button=gtk.BUTTONS_OK): dialog.run() return True +def open_catalog(title=_("Open catalog"), path=None): + """ + Request filename from user to open. + Returns: string - full path and filename or None + """ + requester = ChooseFile(title) + requester.filters = ['catalogs', 'all'] + return requester.run() + +def save_catalog(title=_("Open catalog"), path=None): + """ + Request filename from user for save. + Returns: string - full path and filename or None + """ + requester = ChooseFile(title, chooser_type="save") + requester.filters = ['catalogs', 'all'] + requester.confirmation = True + return requester.run() diff --git a/pygtktalog/logger.py b/pygtktalog/logger.py index fae2994..f9c726a 100644 --- a/pygtktalog/logger.py +++ b/pygtktalog/logger.py @@ -40,12 +40,11 @@ def get_logger(module_name, level=None, to_file=False): if to_file: log_handler = logging.FileHandler(path) - formatter = logging.Formatter("%(asctime)s %(filename)s:%(lineno) - " - "%(name)s - %(levelname)s - " - "%(message)s") + formatter = logging.Formatter("%(asctime)s %(filename)s:%(lineno)s - " + "%(levelname)s - %(message)s") else: log_handler = logging.StreamHandler(sys.stderr) - formatter = logging.Formatter("%(name)s - %(filename)s:%(lineno)s - " + formatter = logging.Formatter("%(filename)s:%(lineno)s - " "%(levelname)s - %(message)s") log_handler.setFormatter(formatter) diff --git a/pygtktalog/models/main.py b/pygtktalog/models/main.py index 3990c34..99d1c17 100644 --- a/pygtktalog/models/main.py +++ b/pygtktalog/models/main.py @@ -13,6 +13,7 @@ from tempfile import mkstemp import gtk import gobject from gtkmvc import ModelMT +from sqlalchemy import create_engine from pygtktalog.dbobjects import File, Exif, Group, Gthumb from pygtktalog.dbobjects import Image, Tag, Thumbnail @@ -45,17 +46,38 @@ class MainModel(ModelMT): self.cat_fname = filename # Temporary (usually in /tmp) working database. self.tmp_filename = None + self.config = {} # SQLAlchemy session object for internal use self._session = None # Flag indicates, that db was compressed # TODO: make it depend on configuration self.compressed = False - self.db_unsaved = False + self.db_unsaved = None + self.discs = None + self.files = None + + self._init_discs() + self._init_files() + + if self.cat_fname: + self.open(self.cat_fname) + + + def _init_discs(self): + """ + Create TreeStore model for the discs + """ self.discs = gtk.TreeStore(gobject.TYPE_PYOBJECT, gobject.TYPE_STRING, str) + + + def _init_files(self): + """ + Create ListStore model for the diles + """ self.files = gtk.ListStore(gobject.TYPE_PYOBJECT, gobject.TYPE_STRING, gobject.TYPE_STRING, @@ -64,8 +86,6 @@ class MainModel(ModelMT): gobject.TYPE_STRING, gobject.TYPE_INT, str) - if self.cat_fname: - self.open(self.cat_fname) def open(self, filename): """ @@ -87,12 +107,45 @@ class MainModel(ModelMT): else: return False + def save(self, filename=None): + """ + Save tared directory at given catalog fielname + Arguments: + @filename - see MainModel __init__ docstring. + Returns: tuple: + Bool - true for success, false otherwise. + String or None - error message + """ + + if not filename and not self.cat_fname: + LOG.debug("no filename detected!") + return False, None + + if filename: + if not '.sqlite' in filename: + filename += '.sqlite' + else: + filename = filename[:filename.rindex('.sqlite')] + '.sqlite' + + if 'compress' in self.config and self.config['compress']: + filename += '.bz2' + + self.cat_fname = filename + val, err = self._compress_and_save() + if not val: + self.cat_fname = None + return val, err + def new(self): """ Create new catalog """ self.cleanup() self._create_temp_db_file() + self._create_schema() + self._init_discs() + self._init_files() + self.db_unsaved = False def cleanup(self): """ @@ -108,20 +161,13 @@ class MainModel(ModelMT): try: os.unlink(self.tmp_filename) - LOG.debug("file %s succesfully deleted", self.tmp_filename) except OSError: - LOG.exception("temporary db file doesn't exists!") + LOG.error("temporary db file doesn't exists!") except TypeError: - # TODO: file not exist - create? print error message? - LOG.exception("temporary db file doesn't exists!") - - - def _create_empty_db(self): - """ - Create new DB - """ - self.cleanup() - self._create_temp_db_file() + # TODO: file does not exist - create? print error message? + LOG.error("temporary db file doesn't exists!") + else: + LOG.debug("file %s succesfully deleted", self.tmp_filename) def _examine_file(self, filename): """ @@ -179,7 +225,7 @@ class MainModel(ModelMT): open_file.close() except IOError: self.cleanup() - self.filename = None + self.cat_fname = None self.internal_dirname = None LOG.exception("File is probably not a bz2!") return False @@ -195,7 +241,7 @@ class MainModel(ModelMT): LOG.error("Error opening file '%s' - not a catalog file!", self.tmp_filename) self.cleanup() - self.filename = None + self.cat_fname = None self.internal_dirname = None return False @@ -204,11 +250,33 @@ class MainModel(ModelMT): return True def _create_temp_db_file(self): + """ + Create new DB file, populate schema. + """ fd, self.tmp_filename = mkstemp() + LOG.debug("new db filename: %s" % self.tmp_filename) # close file descriptor, otherwise it can be source of app crash! # http://www.logilab.org/blogentry/17873 os.close(fd) + def _create_schema(self): + """ + """ + self._session = Session() + + connect(os.path.abspath(self.tmp_filename)) + + root = File() + root.id = 1 + root.filename = 'root' + root.size = 0 + root.source = 0 + root.type = 0 + root.parent_id = 1 + + self._session.add(root) + self._session.commit() + def _populate_discs_from_db(self): """ Read objects from database, fill TreeStore model with discs @@ -222,7 +290,7 @@ class MainModel(ModelMT): Get all children of the selected parent. Arguments: @parent_id - integer with id of the parent (from db) - @iterator - TODO + @iterator - gtk.TreeIter, which points to a path inside model """ for fileob in dirs: if fileob.parent_id == parent_id: @@ -263,5 +331,30 @@ class MainModel(ModelMT): self.files.set_value(myiter, 7, gtk.STOCK_DIRECTORY \ if child.type==1 else gtk.STOCK_FILE) + def _compress_and_save(self): + """ + Create (and optionaly compress) tar archive from working directory and + write it to specified file. + """ + # flush all changes + self._session.commit() + try: + if 'compress' in self.config and self.config['compress']: + output_file = bz2.BZ2File(self.cat_fname, "w") + else: + output_file = open(self.cat_fname, "w") + LOG.debug("save (and optionally compress) successed") + + except IOError, (errno, strerror): + LOG.error("error saving or compressing file", errno, strerror) + return False, strerror + + dbpath = open(self.tmp_filename) + output_file.write(dbpath.read()) + dbpath.close() + output_file.close() + + self.db_unsaved = False + return True, None diff --git a/pygtktalog/pygtkutils.py b/pygtktalog/pygtkutils.py new file mode 100644 index 0000000..0af047a --- /dev/null +++ b/pygtktalog/pygtkutils.py @@ -0,0 +1,25 @@ +""" +Project: pyGTKtalog +Description: pyGTK common utility functions +Type: tility +Author: Roman 'gryf' Dobosz, gryf73@gmail.com +Created: 2010-11-07 13:30:37 +""" + +def get_tv_item_under_cursor(treeview): + """ + Get item (most probably id of the row) form tree view under cursor. + Arguments: + @treeview - gtk.TreeView + Returns: + Item in first column of TreeModel, which TreeView is connected with, + None in other cases + """ + path, column = treeview.get_cursor() + if path and column: + model = treeview.get_model() + tm_iter = model.get_iter(path) + item_id = model.get_value(tm_iter, 0) + return item_id + return None + diff --git a/pygtktalog/video.py b/pygtktalog/video.py index a1e8aa3..fc73988 100644 --- a/pygtktalog/video.py +++ b/pygtktalog/video.py @@ -12,6 +12,7 @@ import shutil from tempfile import mkdtemp, mkstemp import math +import Image from pygtktalog.misc import float_to_string @@ -21,9 +22,15 @@ class Video(object): Midentify script belongs to mplayer package. """ - def __init__(self, filename): - """Init class instance. Filename of a video file is required.""" + def __init__(self, filename, out_width=1024): + """ + Init class instance. + Arguments: + @filename - Filename of a video file (required). + @out_width - width of final image to be scaled to. + """ self.filename = filename + self.out_width = out_width self.tags = {} output = self.__get_movie_info() @@ -52,14 +59,12 @@ class Video(object): length_str = "%02d:%02d:%02d" % (hours, minutes, seconds) self.tags['duration'] = length_str - def capture(self, out_width=1024): + def capture(self): """ Extract images for given video filename and montage it into one, big picture, similar to output from Windows Media Player thing, but without captions and time (who need it anyway?). - Arguments: - @out_width - width of generated image. If actual image width - exceeds this number scale is performed. + Returns: image filename or None NOTE: You should remove returned file manually, or move it in some @@ -93,7 +98,7 @@ class Video(object): file_desc, image_fn = mkstemp() os.close(file_desc) self.__make_captures(tempdir, no_pictures) - self.__make_montage(tempdir, image_fn, no_pictures, out_width) + self.__make_montage(tempdir, image_fn, no_pictures, self.out_width) shutil.rmtree(tempdir) return image_fn @@ -149,26 +154,71 @@ class Video(object): shutil.move(os.path.join(directory, "00000001.jpg"), os.path.join(directory, "picture_%s.jpg" % time)) - def __make_montage(self, directory, image_fn, no_pictures, out_width): + def __make_montage2(self, directory, image_fn, no_pictures): """ Generate one big image from screnshots and optionally resize it. Arguments: @directory - source directory containing images @image_fn - destination final image @no_pictures - number of pictures - @out_width - width of final image to be scaled to. """ scale = False row_length = 4 if no_pictures < 8: row_length = 2 - if (self.tags['width'] * row_length) > out_width: + if (self.tags['width'] * row_length) > self.out_width: scale = True else: for i in [8, 6, 5]: if (no_pictures % i) == 0 and \ - (i * self.tags['width']) <= out_width: + (i * self.tags['width']) <= self.out_width: + row_length = i + break + + tile = "%dx%d" % (row_length, no_pictures / row_length) + + _curdir = os.path.abspath(os.path.curdir) + os.chdir(directory) + + # composite pictures + # readlines trick will make to wait for process end + #cmd = "montage -tile %s -geometry +2+2 picture_*.jpg montage.jpg" + imgs = [Image.open(fn).resize((photow,photoh)) for fn in fnames] + + os.popen(cmd % tile).readlines() + + # scale it to minimum 'modern' width: 1024 + if scale: + cmd = "convert -scale %s montage.jpg montage_scaled.jpg" + os.popen(cmd % out_width).readlines() + shutil.move(os.path.join(directory, 'montage_scaled.jpg'), + image_fn) + else: + shutil.move(os.path.join(directory, 'montage.jpg'), + image_fn) + + os.chdir(_curdir) + + def __make_montage(self, directory, image_fn, no_pictures): + """ + Generate one big image from screnshots and optionally resize it. + Arguments: + @directory - source directory containing images + @image_fn - destination final image + @no_pictures - number of pictures + """ + scale = False + row_length = 4 + if no_pictures < 8: + row_length = 2 + + if (self.tags['width'] * row_length) > self.out_width: + scale = True + else: + for i in [8, 6, 5]: + if (no_pictures % i) == 0 and \ + (i * self.tags['width']) <= self.ut_width: row_length = i break @@ -185,7 +235,7 @@ class Video(object): # scale it to minimum 'modern' width: 1024 if scale: cmd = "convert -scale %s montage.jpg montage_scaled.jpg" - os.popen(cmd % out_width).readlines() + os.popen(cmd % self.out_width).readlines() shutil.move(os.path.join(directory, 'montage_scaled.jpg'), image_fn) else: diff --git a/pygtktalog/views/main.py b/pygtktalog/views/main.py index ea48be2..2e2db36 100644 --- a/pygtktalog/views/main.py +++ b/pygtktalog/views/main.py @@ -30,14 +30,20 @@ class MainView(View): Initialize view """ View.__init__(self) + self.app_sensitive = None self['tag_path_box'].hide() self.discs = DiscsView() - #self['scrolledwindow_discs'].add_with_viewport(self.discs.get_top_widget()) + #self['scrolledwindow_discs'].add_with_viewport(\ + # self.discs.get_top_widget()) self['scrolledwindow_discs'].add(self.discs.get_top_widget()) self.files = FilesView() - self['scrolledwindow_files'].add_with_viewport(self.files.get_top_widget()) + self['scrolledwindow_files'].add_with_viewport(\ + self.files.get_top_widget()) + + self.details = DetailsView() + self['vpaned1'].add2(self.details.get_top_widget()) def set_widgets_scan_sensitivity(self, sensitive=True): """ @@ -45,6 +51,23 @@ class MainView(View): """ pass + def set_widgets_app_sensitivity(self, sensitive=True): + """ + Enable/disable widgets for empty application. Usefull for first run + of an application (without any db filename as an argument). + """ + if self.app_sensitive is sensitive: + return + + for widget in ['scrolledwindow_discs', 'scrolledwindow_files', + 'tb_save', 'tb_addcd', 'tb_adddir', 'tb_find', + 'edit1', 'catalog1', 'save1', 'save_as1', 'import', + 'export']: + self[widget].set_sensitive(sensitive) + + # widgets from subclasses + self.details['notebook_details'].set_sensitive(sensitive) + class DiscsView(View): """ @@ -94,6 +117,7 @@ class DiscsPopupView(View): for item in ['update', 'rename', 'delete', 'statistics']: self[item].set_sensitive(state) + class FilesView(View): """ Separate subview of Files TreeView as a table. @@ -106,6 +130,8 @@ class FilesView(View): Initialize view """ View.__init__(self) + self.menu = FilesPopupView() + class FilesPopupView(View): """ @@ -120,6 +146,19 @@ class FilesPopupView(View): """ View.__init__(self) + def set_menu_items_sensitivity(self, state): + """ + Set sensitivity for couple of popup menu items, which should be + disabled if user right-clicks on no item in treeview. + Arguments: + @state - Bool, if True update menu item will be sensitive, + otherwise not + """ + for item in ["add_tag", "delete_tag", "add_thumb", "remove_thumb", + "add_image", "remove_image", "edit", "delete", "rename"]: + self[item].set_sensitive(state) + + class TagcloudView(View): """ Textview subview with clickable tags.