1
0
mirror of https://github.com/gryf/pygtktalog.git synced 2025-12-17 11:30:19 +01:00

* Added support for "menu" key on keyboard.

* Small changes in behaviour of individual popup menus.
This commit is contained in:
2008-05-06 19:17:10 +00:00
parent aea871b30e
commit 33b5e76f99
6 changed files with 328 additions and 192 deletions

23
README
View File

@@ -1,4 +1,4 @@
pyGTKtalog 0.8 pyGTKtalog 0.9
============== ==============
pyGTKtalog Linux/FreeBSD program for indexing CD/DVD or directories on pyGTKtalog Linux/FreeBSD program for indexing CD/DVD or directories on
@@ -24,6 +24,7 @@ REQUIREMENTS
pyGTKtalog is written in python with following dependencies: pyGTKtalog is written in python with following dependencies:
- python 2.4 or higher
- pygtk <http://www.pygtk.org> - pygtk <http://www.pygtk.org>
- pysqlite2 <http://pysqlite.org/> (unnecessary, if python 2.5 is used) - pysqlite2 <http://pysqlite.org/> (unnecessary, if python 2.5 is used)
@@ -37,7 +38,7 @@ Additional pyGTKtalog uses EXIF module by Gene Cash which is included in
sources. sources.
pyGTKtalog extensivly uses external programs in unix spirit, however there is pyGTKtalog extensivly uses external programs in unix spirit, however there is
small possibility of using it Windows (probably with liitations) and quite big small possibility of using it Windows (probably with limitations) and quite big
possiblity to run it on other sofisticated unix-like systems (i.e. possiblity to run it on other sofisticated unix-like systems (i.e.
BeOS/ZETA/Haiku, QNX or MacOSX). BeOS/ZETA/Haiku, QNX or MacOSX).
@@ -57,11 +58,14 @@ Then, just run pyGTKtalog script.
TODO TODO
==== ====
For version 1.0 following aims have to be done: PyGTKtalog is still under heavy development, however there is small chance to
change structure of catalogs (and if it'll change, there will be transparent
function to update DB schema).
For version 1.0 following major aims have to be done:
- searching database - searching database
- tagging files (90%) x tagging files
- user definied group of tags (represented by color in cloud tag)
x remove nasty bug in redraw of tag cloud x remove nasty bug in redraw of tag cloud
x file details: x file details:
x files properties x files properties
@@ -75,6 +79,10 @@ x generating/saving thumbnails
x moving hardcoded files extensions into config x moving hardcoded files extensions into config
x statistics x statistics
There are still minor aims for versions 1.x to be done:
- consolidate popup-menus with edit menu
- add popup menu for directly removing tag from tag cloud
Legend: [-] not done, [x] done. Legend: [-] not done, [x] done.
For version 2.0: For version 2.0:
@@ -82,6 +90,9 @@ For version 2.0:
- command line support: query, adding media to collection etc - command line support: query, adding media to collection etc
- internationalization support - internationalization support
- export to XLS - export to XLS
- user definied group of tags (represented by color in cloud tag)
- hiding specified files - configurable, like dot prefixed, cfg and manualy
selected
Removed: Removed:
- filetypes handling (movies, images, archives, documents etc). Now it have - filetypes handling (movies, images, archives, documents etc). Now it have
@@ -100,7 +111,7 @@ Removed:
Maybe in future versions. Now text file descriptions/notes and tags have to Maybe in future versions. Now text file descriptions/notes and tags have to
be enough for good and fast information search. be enough for good and fast information search.
- file information (date, size, etc) (50%) (no need for?) - file information (date, size, etc) (50%) (no need for?)
NOTES NOTES
===== =====

View File

@@ -93,35 +93,9 @@
<property name="visible">True</property> <property name="visible">True</property>
<property name="label" translatable="yes">_Edit</property> <property name="label" translatable="yes">_Edit</property>
<property name="use_underline">True</property> <property name="use_underline">True</property>
<signal name="activate" handler="on_edit1_activate"/>
<child> <child>
<widget class="GtkMenu" id="edit1_menu"> <widget class="GtkMenu" id="edit1_menu">
<child>
<widget class="GtkImageMenuItem" id="cut1">
<property name="visible">True</property>
<property name="label">gtk-cut</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
<signal name="activate" handler="on_cut1_activate"/>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="copy1">
<property name="visible">True</property>
<property name="label">gtk-copy</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
<signal name="activate" handler="on_copy1_activate"/>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="paste1">
<property name="visible">True</property>
<property name="label">gtk-paste</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
<signal name="activate" handler="on_paste1_activate"/>
</widget>
</child>
<child> <child>
<widget class="GtkImageMenuItem" id="delete1"> <widget class="GtkImageMenuItem" id="delete1">
<property name="visible">True</property> <property name="visible">True</property>
@@ -129,6 +103,7 @@
<property name="use_underline">True</property> <property name="use_underline">True</property>
<property name="use_stock">True</property> <property name="use_stock">True</property>
<signal name="activate" handler="on_delete1_activate"/> <signal name="activate" handler="on_delete1_activate"/>
<accelerator key="Delete" modifiers="" signal="activate"/>
</widget> </widget>
</child> </child>
<child> <child>
@@ -177,7 +152,7 @@
<property name="label" translatable="yes">Add _CD/DVD</property> <property name="label" translatable="yes">Add _CD/DVD</property>
<property name="use_underline">True</property> <property name="use_underline">True</property>
<signal name="activate" handler="on_add_cd_activate"/> <signal name="activate" handler="on_add_cd_activate"/>
<accelerator key="a" modifiers="GDK_CONTROL_MASK" signal="activate"/> <accelerator key="c" modifiers="GDK_CONTROL_MASK" signal="activate"/>
</widget> </widget>
</child> </child>
<child> <child>
@@ -185,7 +160,7 @@
<property name="visible">True</property> <property name="visible">True</property>
<property name="label" translatable="yes">Add _Directory</property> <property name="label" translatable="yes">Add _Directory</property>
<property name="use_underline">True</property> <property name="use_underline">True</property>
<signal name="activate" handler="on_add_directory1_activate"/> <signal name="activate" handler="on_add_directory_activate"/>
<accelerator key="d" modifiers="GDK_CONTROL_MASK" signal="activate"/> <accelerator key="d" modifiers="GDK_CONTROL_MASK" signal="activate"/>
</widget> </widget>
</child> </child>
@@ -371,6 +346,18 @@
<property name="expand">False</property> <property name="expand">False</property>
</packing> </packing>
</child> </child>
<child>
<widget class="GtkToolButton" id="tb_adddir">
<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="label" translatable="yes">Add Dir</property>
<property name="stock_id">gtk-directory</property>
<signal name="clicked" handler="on_add_directory_activate"/>
</widget>
<packing>
<property name="expand">False</property>
</packing>
</child>
<child> <child>
<widget class="GtkToolButton" id="tb_find"> <widget class="GtkToolButton" id="tb_find">
<property name="visible">True</property> <property name="visible">True</property>
@@ -501,6 +488,7 @@
<signal name="button_press_event" handler="on_discs_button_press_event"/> <signal name="button_press_event" handler="on_discs_button_press_event"/>
<signal name="row_activated" handler="on_discs_row_activated"/> <signal name="row_activated" handler="on_discs_row_activated"/>
<signal name="cursor_changed" handler="on_discs_cursor_changed"/> <signal name="cursor_changed" handler="on_discs_cursor_changed"/>
<signal name="key_release_event" handler="on_discs_key_release_event"/>
</widget> </widget>
</child> </child>
</widget> </widget>
@@ -672,6 +660,7 @@
<property name="tooltip" translatable="yes">Double click to open image</property> <property name="tooltip" translatable="yes">Double click to open image</property>
<signal name="button_press_event" handler="on_images_button_press_event"/> <signal name="button_press_event" handler="on_images_button_press_event"/>
<signal name="item_activated" handler="on_images_item_activated"/> <signal name="item_activated" handler="on_images_item_activated"/>
<signal name="key_release_event" handler="on_images_key_release_event"/>
</widget> </widget>
</child> </child>
</widget> </widget>
@@ -1025,4 +1014,17 @@
</widget> </widget>
</child> </child>
</widget> </widget>
<widget class="GtkMenu" id="tag_popup">
<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>
<child>
<widget class="GtkMenuItem" id="delete_tag2">
<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="label" translatable="yes">Delete tag</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_delete_tag2_activate"/>
</widget>
</child>
</widget>
</glade-interface> </glade-interface>

View File

@@ -47,9 +47,9 @@ import pango
class MainController(Controller): class MainController(Controller):
"""Controller for main application window""" """Controller for main application window"""
scan_cd = False scan_cd = False
widgets = ("discs", "files", 'save1', 'save_as1', 'cut1', 'copy1', widgets = ("discs", "files", 'save1', 'save_as1', 'delete1', 'add_cd',
'paste1', 'delete1', 'add_cd', 'add_directory1', 'tb_save', 'add_directory1', 'tb_save', 'tb_addcd', 'tb_find', 'nb_dirs',
'tb_addcd', 'tb_find', 'nb_dirs', 'description', 'stat1') 'description', 'stat1')
widgets_all = ("discs", "files", 'file1', 'edit1', 'add_cd', widgets_all = ("discs", "files", 'file1', 'edit1', 'add_cd',
'add_directory1', 'help1', 'tb_save', 'tb_addcd', 'tb_find', 'add_directory1', 'help1', 'tb_save', 'tb_addcd', 'tb_find',
'tb_new', 'tb_open', 'tb_quit', 'nb_dirs', 'description', 'tb_new', 'tb_open', 'tb_quit', 'nb_dirs', 'description',
@@ -72,7 +72,7 @@ class MainController(Controller):
for widget in self.widgets: for widget in self.widgets:
self.view[widget].set_sensitive(False) self.view[widget].set_sensitive(False)
# Make not active "Cancel" button and menuitem # Make not active "Cancel" button and menu_item
for widget in self.widgets_cancel: for widget in self.widgets_cancel:
self.view[widget].set_sensitive(False) self.view[widget].set_sensitive(False)
@@ -123,11 +123,14 @@ class MainController(Controller):
# generate recent menu # generate recent menu
self.__generate_recent_menu() self.__generate_recent_menu()
self.view['tag_cloud_textview'].connect("populate-popup",
self.on_tag_cloud_textview_popup)
# in case model has opened file, register tags # in case model has opened file, register tags
if self.model.internal_dirname: if self.model.internal_dirname:
self.__tag_cloud() self.__tag_cloud()
# Show main window # Show main window
self.view['main'].show(); self.view['main'].show();
self.view['main'].drag_dest_set(0, [], 0) self.view['main'].drag_dest_set(0, [], 0)
@@ -144,13 +147,13 @@ class MainController(Controller):
iter = filestv.get_iter_at_location(x, y) iter = filestv.get_iter_at_location(x, y)
buff = filestv.get_buffer() buff = filestv.get_buffer()
tag_table = buff.get_tag_table() tag_table = buff.get_tag_table()
# clear weight of tags # clear weight of tags
def foreach_tag(texttag, user_data): def foreach_tag(texttag, user_data):
"""set every text tag's weight to normal""" """set every text tag's weight to normal"""
texttag.set_property("underline", pango.UNDERLINE_NONE) texttag.set_property("underline", pango.UNDERLINE_NONE)
tag_table.foreach(foreach_tag, None) tag_table.foreach(foreach_tag, None)
try: try:
tag = iter.get_tags()[0] tag = iter.get_tags()[0]
tag.set_property("underline", pango.UNDERLINE_LOW) tag.set_property("underline", pango.UNDERLINE_LOW)
@@ -173,12 +176,16 @@ class MainController(Controller):
string = str(tuple(ids)).replace(",)", ")") string = str(tuple(ids)).replace(",)", ")")
selection.set(selection.target, 8, string) selection.set(selection.target, 8, string)
def on_tag_cloud_textview_popup(self, textview, menu):
menu = None
return True
def on_tag_cloud_textview_event_after(self, textview, event): def on_tag_cloud_textview_event_after(self, textview, event):
if event.type != gtk.gdk.BUTTON_RELEASE: if event.type != gtk.gdk.BUTTON_RELEASE:
return False return False
if event.button != 1: if event.button != 1:
return False return False
buff = textview.get_buffer() buff = textview.get_buffer()
try: try:
(start, end) = buff.get_selection_bounds() (start, end) = buff.get_selection_bounds()
@@ -192,20 +199,20 @@ class MainController(Controller):
(x, y) = textview.window_to_buffer_coords(gtk.TEXT_WINDOW_WIDGET, (x, y) = textview.window_to_buffer_coords(gtk.TEXT_WINDOW_WIDGET,
int(event.x), int(event.y)) int(event.x), int(event.y))
iterator = textview.get_iter_at_location(x, y) iterator = textview.get_iter_at_location(x, y)
tags = iterator.get_tags() tags = iterator.get_tags()
if len(tags) == 1: if len(tags) == 1:
tag = tags[0] tag = tags[0]
self.model.add_tag_to_path(tag.get_property('name')) self.model.add_tag_to_path(tag.get_property('name'))
self.view['tag_path_box'].show() self.view['tag_path_box'].show()
# fill the path of tag # fill the path of tag
self.view['tag_path'].set_text('') self.view['tag_path'].set_text('')
temp = self.model.selected_tags.values() temp = self.model.selected_tags.values()
self.model.refresh_discs_tree() self.model.refresh_discs_tree()
#self.on_discs_cursor_changed(textview) #self.on_discs_cursor_changed(textview)
temp.sort() temp.sort()
for tag1 in temp: for tag1 in temp:
txt = self.view['tag_path'].get_text() txt = self.view['tag_path'].get_text()
@@ -220,7 +227,7 @@ class MainController(Controller):
def on_tag_cloud_textview_drag_data_received(self, widget, context, x, y, def on_tag_cloud_textview_drag_data_received(self, widget, context, x, y,
selection, targetType, time): selection, targetType, time):
"""recive data from source TV""" """recive data from source TreeView"""
if targetType == self.DND_TARGETS[0][2]: if targetType == self.DND_TARGETS[0][2]:
iter = widget.get_iter_at_location(x, y) iter = widget.get_iter_at_location(x, y)
ids = selection.data.rstrip(")").lstrip("(").split(",") ids = selection.data.rstrip(")").lstrip("(").split(",")
@@ -402,7 +409,7 @@ class MainController(Controller):
self.view['tag_path_box'].hide() self.view['tag_path_box'].hide()
self.model.selected_tags = [] self.model.selected_tags = []
self.model.refresh_discs_tree() self.model.refresh_discs_tree()
# cleanup files and detiles # cleanup files and detiles
try: try:
self.model.files_list.clear() self.model.files_list.clear()
@@ -411,18 +418,18 @@ class MainController(Controller):
self.__hide_details() self.__hide_details()
self.on_discs_cursor_changed(w) self.on_discs_cursor_changed(w)
self.__tag_cloud() self.__tag_cloud()
def on_tag_cloud_textview_drag_leave(self, textview, dragcontext, time): def on_tag_cloud_textview_drag_leave(self, textview, dragcontext, time):
"""clean up tags properties""" """clean up tags properties"""
buff = textview.get_buffer() buff = textview.get_buffer()
tag_table = buff.get_tag_table() tag_table = buff.get_tag_table()
# clear weight of tags # clear weight of tags
def foreach_tag(texttag, user_data): def foreach_tag(texttag, user_data):
"""set every text tag's weight to normal""" """set every text tag's weight to normal"""
texttag.set_property("underline", pango.UNDERLINE_NONE) texttag.set_property("underline", pango.UNDERLINE_NONE)
tag_table.foreach(foreach_tag, None) tag_table.foreach(foreach_tag, None)
# NOTE: text view "links" functions # NOTE: text view "links" functions
def on_tag_cloud_textview_visibility_notify_event(self, textview, event): def on_tag_cloud_textview_visibility_notify_event(self, textview, event):
(wx, wy, mod) = textview.window.get_pointer() (wx, wy, mod) = textview.window.get_pointer()
@@ -438,7 +445,7 @@ class MainController(Controller):
textview = self.view['tag_cloud_textview'] textview = self.view['tag_cloud_textview']
# get the iter at the mouse position # get the iter at the mouse position
iter = textview.get_iter_at_location(x, y) iter = textview.get_iter_at_location(x, y)
# set _hovering if the iter has the tag "url" # set _hovering if the iter has the tag "url"
tags = iter.get_tags() tags = iter.get_tags()
for tag in tags: for tag in tags:
@@ -457,20 +464,20 @@ class MainController(Controller):
textview.get_window(gtk.TEXT_WINDOW_TEXT).\ textview.get_window(gtk.TEXT_WINDOW_TEXT).\
set_cursor(gtk.gdk.Cursor(gtk.gdk.LEFT_PTR)) set_cursor(gtk.gdk.Cursor(gtk.gdk.LEFT_PTR))
def on_tag_cloud_click(self, tag, textview, event, b_iter, data): def on_tag_cloud_click(self, tag, textview, event, b_iter, data):
"""react on click on connected tag items""" """react on click on connected tag items"""
tag_cloud = self.view['tag_cloud_textview'] tag_cloud = self.view['tag_cloud_textview']
if event.type == gtk.gdk.BUTTON_RELEASE: if event.type == gtk.gdk.BUTTON_RELEASE:
self.model.add_tag_to_path(tag.get_property('name')) self.model.add_tag_to_path(tag.get_property('name'))
self.view['tag_path_box'].show() self.view['tag_path_box'].show()
# fill the path of tag # fill the path of tag
self.view['tag_path'].set_text('') self.view['tag_path'].set_text('')
temp = self.model.selected_tags.values() temp = self.model.selected_tags.values()
self.model.refresh_discs_tree() self.model.refresh_discs_tree()
#self.on_discs_cursor_changed(textview) #self.on_discs_cursor_changed(textview)
temp.sort() temp.sort()
for tag1 in temp: for tag1 in temp:
txt = self.view['tag_path'].get_text() txt = self.view['tag_path'].get_text()
@@ -479,8 +486,8 @@ class MainController(Controller):
else: else:
self.view['tag_path'].set_text(txt + ", " +tag1) self.view['tag_path'].set_text(txt + ", " +tag1)
self.__tag_cloud() self.__tag_cloud()
#elif event.type == gtk.gdk.MOTION_NOTIFY: #elif event.type == gtk.gdk.MOTION_NOTIFY:
# window = tag_cloud.get_window(gtk.TEXT_WINDOW_TEXT) # window = tag_cloud.get_window(gtk.TEXT_WINDOW_TEXT)
# if window: # if window:
@@ -489,7 +496,7 @@ class MainController(Controller):
# window = tag_cloud.get_window(gtk.TEXT_WINDOW_TEXT) # window = tag_cloud.get_window(gtk.TEXT_WINDOW_TEXT)
# if window: # if window:
# window.set_cursor(None) # window.set_cursor(None)
# NOTE: quit / close window # NOTE: quit / close window
def on_main_destroy_event(self, window, event): def on_main_destroy_event(self, window, event):
self.on_quit_activate(window) self.on_quit_activate(window)
@@ -556,7 +563,7 @@ class MainController(Controller):
"Last mount message:\n%s" % mount) "Last mount message:\n%s" % mount)
return False return False
def on_add_directory1_activate(self, widget, path=None, label=None, def on_add_directory_activate(self, widget, path=None, label=None,
current_id=None): current_id=None):
"""Show dialog for choose drectory to add from filesystem.""" """Show dialog for choose drectory to add from filesystem."""
if not label or not path: if not label or not path:
@@ -651,7 +658,7 @@ class MainController(Controller):
if not path: if not path:
path = Dialogs.LoadDBFile().run() path = Dialogs.LoadDBFile().run()
# cleanup files and details # cleanup files and details
try: try:
self.model.files_list.clear() self.model.files_list.clear()
@@ -662,7 +669,7 @@ class MainController(Controller):
buf = self.view['tag_cloud_textview'].get_buffer() buf = self.view['tag_cloud_textview'].get_buffer()
buf.set_text('') buf.set_text('')
self.view['tag_cloud_textview'].set_buffer(buf) self.view['tag_cloud_textview'].set_buffer(buf)
if path: if path:
if not self.model.open(path): if not self.model.open(path):
Dialogs.Err("Error opening file - pyGTKtalog", Dialogs.Err("Error opening file - pyGTKtalog",
@@ -683,7 +690,7 @@ class MainController(Controller):
id = self.model.discs_tree.get_value(iter, 0) id = self.model.discs_tree.get_value(iter, 0)
self.model.get_root_entries(id) self.model.get_root_entries(id)
self.__get_item_info(id) self.__get_item_info(id)
return return
def on_discs_row_activated(self, treeview, path, treecolumn): def on_discs_row_activated(self, treeview, path, treecolumn):
@@ -693,37 +700,60 @@ class MainController(Controller):
else: else:
treeview.expand_row(path, False) treeview.expand_row(path, False)
return return
def on_discs_key_release_event(self, treeview, event):
if gtk.gdk.keyval_name(event.keyval) == 'Menu':
ids = self.__get_tv_selection_ids(treeview)
menu_items = ['update1','rename1','delete2', 'statistics1']
for menu_item in menu_items:
self.view[menu_item].set_sensitive(not not ids)
self.__popup_menu(event, 'discs_popup')
return True
return False
def on_images_key_release_event(self, iconview, event):
if gtk.gdk.keyval_name(event.keyval) == 'Menu':
self.__popup_menu(event, 'img_popup')
return True
return False
def on_images_button_press_event(self, iconview, event): def on_images_button_press_event(self, iconview, event):
try: #try:
path_and_cell = iconview.get_item_at_pos(int(event.x), # path_and_cell = iconview.get_item_at_pos(int(event.x),
int(event.y)) # int(event.y))
except TypeError: #except TypeError:
return False # return False
if event.button == 3: # Right mouse button. Show context menu. if event.button == 3: # Right mouse button. Show context menu.
try: #try:
iconview.select_path(path_and_cell[0]) # iconview.select_path(path_and_cell[0])
except TypeError: #except TypeError:
return False # return False
self.__popup_menu(event, 'img_popup') self.__popup_menu(event, 'img_popup')
return True return True
return False return False
def on_img_delete_activate(self, menu_item): def on_img_delete_activate(self, menu_item):
"""delete selected images (with thumbnails)"""
list_of_paths = self.view['images'].get_selected_items()
if not list_of_paths:
Dialogs.Inf("Delete images", "No images selected",
"You have to select at least one image to delete.")
return
if self.model.config.confd['delwarn']: if self.model.config.confd['delwarn']:
obj = Dialogs.Qst('Delete images', 'Delete selected images?', obj = Dialogs.Qst('Delete images', 'Delete selected images?',
'Selected images will be permanently removed from catalog.') 'Selected images will be permanently removed from catalog.')
if not obj.run(): if not obj.run():
return return
list_of_paths = self.view['images'].get_selected_items()
model = self.view['images'].get_model() model = self.view['images'].get_model()
for path in list_of_paths: for path in list_of_paths:
iter = model.get_iter(path) iter = model.get_iter(path)
id = model.get_value(iter, 0) id = model.get_value(iter, 0)
self.model.delete_image(id) self.model.delete_image(id)
# refresh files tree # refresh files tree
try: try:
path, column = self.view['files'].get_cursor() path, column = self.view['files'].get_cursor()
@@ -742,41 +772,55 @@ class MainController(Controller):
"""export images (not thumbnails) into desired direcotry""" """export images (not thumbnails) into desired direcotry"""
dialog = Dialogs.SelectDirectory("Choose directory to save images") dialog = Dialogs.SelectDirectory("Choose directory to save images")
filepath = dialog.run() filepath = dialog.run()
if not filepath: if not filepath:
return return
list_of_paths = self.view['images'].get_selected_items() list_of_paths = self.view['images'].get_selected_items()
model = self.view['images'].get_model() model = self.view['images'].get_model()
count = 0 count = 0
for path in list_of_paths: if len(list_of_paths) == 0:
icon_iter = model.get_iter(path) # no picture was selected. default to save all of them
img_id = model.get_value(icon_iter, 0) for image in model:
if self.model.save_image(image[0], filepath):
count += 1
else:
# some pictures was selected, save them
for path in list_of_paths:
icon_iter = model.get_iter(path)
img_id = model.get_value(icon_iter, 0)
if self.model.save_image(img_id, filepath): if self.model.save_image(img_id, filepath):
count += 1 count += 1
if len(list_of_paths) > 0:
if count > 0: if count > 0:
Dialogs.Inf("Save images", Dialogs.Inf("Save images",
"%d images was succsefully saved." % count, "%d images was succsefully saved." % count,
"Images are placed in directory:\n%s." % filepath) "Images are placed in directory:\n%s." % filepath)
else: else:
Dialogs.Inf("Save images", Dialogs.Inf("Save images",
"No images was saved.", "No images was saved.",
"Images probably don't have real images - only" + \ "Images probably don't have real images - only" + \
" thumbnails.") " thumbnails.")
return
def on_img_delete2_activate(self, menu_item): def on_img_delete2_activate(self, menu_item):
"""remove images, but keep thumbnails""" """remove images, but keep thumbnails"""
list_of_paths = self.view['images'].get_selected_items()
if not list_of_paths:
Dialogs.Inf("Delete images", "No images selected",
"You have to select at least one image to delete.")
return
if self.model.config.confd['delwarn']: if self.model.config.confd['delwarn']:
obj = Dialogs.Qst('Delete images', 'Delete selected images?', obj = Dialogs.Qst('Delete images', 'Delete selected images?',
'Selected images will be permanently removed from ' + \ 'Selected images will be permanently removed from ' + \
'catalog,\nthumbnails will be keeped.') 'catalog,\nthumbnails will be keeped.')
if not obj.run(): if not obj.run():
return return
list_of_paths = self.view['images'].get_selected_items()
model = self.view['images'].get_model() model = self.view['images'].get_model()
for path in list_of_paths: for path in list_of_paths:
iter = model.get_iter(path) iter = model.get_iter(path)
@@ -795,7 +839,7 @@ class MainController(Controller):
self.model.unsaved_project = True self.model.unsaved_project = True
self.__set_title(filepath=self.model.filename, modified=True) self.__set_title(filepath=self.model.filename, modified=True)
return return
def on_img_add_activate(self, menu_item): def on_img_add_activate(self, menu_item):
self.on_add_image1_activate(menu_item) self.on_add_image1_activate(menu_item)
@@ -822,31 +866,29 @@ class MainController(Controller):
if path not in list_of_paths: if path not in list_of_paths:
treeview.get_selection().unselect_all() treeview.get_selection().unselect_all()
treeview.get_selection().select_path(path) treeview.get_selection().select_path(path)
# setup menu
iter = self.model.discs_tree.get_iter(path) ids = self.__get_tv_selection_ids(treeview)
if self.model.discs_tree.get_value(iter, 3) == 1: menu_items = ['update1','rename1','delete2', 'statistics1']
# if ancestor is 'root', then activate "update" menu item for menu_item in menu_items:
self.view['update1'].set_sensitive(True) self.view[menu_item].set_sensitive(not not ids)
else:
self.view['update1'].set_sensitive(False) # checkout, if we dealing with disc or directory
# if ancestor is 'root', then activate "update" menu item
treeiter = self.model.discs_tree.get_iter(path)
ancestor = self.model.discs_tree.get_value(treeiter, 3) == 1
self.view['update1'].set_sensitive(ancestor)
self.__popup_menu(event) self.__popup_menu(event)
def on_expand_all1_activate(self, menuitem): def on_expand_all1_activate(self, menu_item):
self.view['discs'].expand_all() self.view['discs'].expand_all()
return return
def on_collapse_all1_activate(self, menuitem): def on_collapse_all1_activate(self, menu_item):
self.view['discs'].collapse_all() self.view['discs'].collapse_all()
return return
def on_files_button_press_event(self, tree, event): def on_files_button_press_event(self, tree, event):
try:
path, column, x, y = tree.get_path_at_pos(int(event.x),
int(event.y))
except TypeError:
tree.get_selection().unselect_all()
return False
if event.button == 3: # Right mouse button. Show context menu. if event.button == 3: # Right mouse button. Show context menu.
try: try:
selection = tree.get_selection() selection = tree.get_selection()
@@ -855,6 +897,14 @@ class MainController(Controller):
list_of_paths = [] list_of_paths = []
if len(list_of_paths) == 0: 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]) selection.select_path(path[0])
if len(list_of_paths) > 1: if len(list_of_paths) > 1:
@@ -867,8 +917,8 @@ class MainController(Controller):
self.view['edit2'].set_sensitive(True) self.view['edit2'].set_sensitive(True)
self.__popup_menu(event, 'files_popup') self.__popup_menu(event, 'files_popup')
return True return True
if event.button == 1: #if event.button == 1:
return False # return False
def on_files_cursor_changed(self, treeview): def on_files_cursor_changed(self, treeview):
"""Show details of selected file/directory""" """Show details of selected file/directory"""
@@ -877,6 +927,17 @@ class MainController(Controller):
return return
def on_files_key_release_event(self, treeview, event): 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(event, 'files_popup')
if gtk.gdk.keyval_name(event.keyval) == 'BackSpace': if gtk.gdk.keyval_name(event.keyval) == 'BackSpace':
d_path, d_column = self.view['discs'].get_cursor() d_path, d_column = self.view['discs'].get_cursor()
if d_path and d_column: if d_path and d_column:
@@ -903,9 +964,9 @@ class MainController(Controller):
iter = None iter = None
else: else:
iter = self.model.discs_tree.iter_next(iter) iter = self.model.discs_tree.iter_next(iter)
if gtk.gdk.keyval_name(event.keyval) == 'Delete': #if gtk.gdk.keyval_name(event.keyval) == 'Delete':
for file_id in self.__get_tv_selection_ids(treeview): # for file_id in self.__get_tv_selection_ids(treeview):
self.main.delete(file_id) # self.main.delete(file_id)
ids = self.__get_tv_selection_ids(self.view['files']) ids = self.__get_tv_selection_ids(self.view['files'])
@@ -951,13 +1012,16 @@ class MainController(Controller):
return return
# NOTE: add tags / images # NOTE: add tags / images
def on_delete_tag2_activate(self, menu_item):
pass
def on_delete_tag_activate(self, menu_item): def on_delete_tag_activate(self, menu_item):
ids = self.__get_tv_selection_ids(self.view['files']) ids = self.__get_tv_selection_ids(self.view['files'])
if not ids: if not ids:
Dialogs.Inf("Remove tags", "No files selected", Dialogs.Inf("Remove tags", "No files selected",
"You have to select some files first.") "You have to select some files first.")
return return
tags = self.model.get_tags_by_file_id(ids) tags = self.model.get_tags_by_file_id(ids)
if tags: if tags:
d = Dialogs.TagsRemoveDialog(tags) d = Dialogs.TagsRemoveDialog(tags)
@@ -972,7 +1036,7 @@ class MainController(Controller):
self.view['files'].set_model(self.model.files_list) self.view['files'].set_model(self.model.files_list)
self.__tag_cloud() self.__tag_cloud()
return return
def on_add_tag1_activate(self, menu_item): def on_add_tag1_activate(self, menu_item):
#try: #try:
tags = Dialogs.TagsDialog().run() tags = Dialogs.TagsDialog().run()
@@ -996,8 +1060,8 @@ class MainController(Controller):
def on_add_image1_activate(self, menu_item): def on_add_image1_activate(self, menu_item):
dialog = Dialogs.LoadImageFile(True) dialog = Dialogs.LoadImageFile(True)
toggle = gtk.CheckButton("Don't copy images. \ toggle = gtk.CheckButton("Don't copy images. " + \
Generate only thumbnails.") "Generate only thumbnails.")
toggle.show() toggle.show()
dialog.dialog.set_extra_widget(toggle) dialog.dialog.set_extra_widget(toggle)
@@ -1039,17 +1103,32 @@ class MainController(Controller):
if self.model.get_source(path) == self.model.CD: if self.model.get_source(path) == self.model.CD:
self.on_add_cd_activate(menu_item, label, fid) self.on_add_cd_activate(menu_item, label, fid)
elif self.model.get_source(path) == self.model.DR: elif self.model.get_source(path) == self.model.DR:
self.on_add_directory1_activate(menu_item, filepath, label, fid) self.on_add_directory_activate(menu_item, filepath, label, fid)
return return
def on_delete1_activate(self, menu_item):
"""Main menu delete dispatcher"""
if self.view['files'].is_focus():
self.on_delete3_activate(menu_item)
if self.view['discs'].is_focus():
self.on_delete2_activate(menu_item)
if self.view['images'].is_focus():
self.on_img_delete_activate(menu_item)
def on_delete2_activate(self, menu_item): def on_delete2_activate(self, menu_item):
try: try:
selection = self.view['discs'].get_selection() selection = self.view['discs'].get_selection()
model, selected_iter = selection.get_selected() model, selected_iter = selection.get_selected()
except: except:
return return
if not selected_iter:
Dialogs.Inf("Delete disc", "No disc selected",
"You have to select disc first before you " +\
"can delete it")
return
if self.model.config.confd['delwarn']: if self.model.config.confd['delwarn']:
name = model.get_value(selected_iter, 1) name = model.get_value(selected_iter, 1)
obj = Dialogs.Qst('Delete %s' % name, 'Delete %s?' % name, obj = Dialogs.Qst('Delete %s' % name, 'Delete %s?' % name,
@@ -1093,11 +1172,16 @@ class MainController(Controller):
model, list_of_paths = selection.get_selected_rows() model, list_of_paths = selection.get_selected_rows()
except TypeError: except TypeError:
return return
if not list_of_paths:
Dialogs.Inf("Delete files", "No files selected",
"You have to select at least one file to delete.")
return
if self.model.config.confd['delwarn']: if self.model.config.confd['delwarn']:
obj = Dialogs.Qst("Delete elements", "Delete items?", obj = Dialogs.Qst("Delete files", "Delete files?",
"Items will be permanently \ "Selected files and directories will be" + \
removed from catalog.") " permanently\nremoved from catalog.")
if not obj.run(): if not obj.run():
return return
@@ -1155,7 +1239,15 @@ class MainController(Controller):
self.model.unsaved_project = True self.model.unsaved_project = True
self.__set_title(filepath=self.model.filename, modified=True) self.__set_title(filepath=self.model.filename, modified=True)
return return
def on_edit1_activate(self, menu_item):
"""Make sufficient menu items sensitive in right cases"""
# TODO: consolidate popup-menus with edit menu
if self.view['tag_cloud_textview'].is_focus():
self.view['delete1'].set_sensitive(False)
else:
self.view['delete1'].set_sensitive(True)
def on_debugbtn_clicked(self, widget): def on_debugbtn_clicked(self, widget):
"""Debug. To remove in stable version, including button in GUI""" """Debug. To remove in stable version, including button in GUI"""
if __debug__: if __debug__:
@@ -1168,7 +1260,10 @@ class MainController(Controller):
print "abort = %s" % self.model.abort print "abort = %s" % self.model.abort
print "self.model.config.recent = %s" % self.model.config.recent print "self.model.config.recent = %s" % self.model.config.recent
print "source: %s" % self.model.source print "source: %s" % self.model.source
print "files have focus", self.view['files'].is_focus()
print "discs have focus", self.view['discs'].is_focus()
print "images have focus", self.view['images'].is_focus()
##################### #####################
# observed properetis # observed properetis
@@ -1229,7 +1324,7 @@ class MainController(Controller):
print "getting selected items" print "getting selected items"
return return
return None return None
def __get_tv_id_under_cursor(self, treeview): def __get_tv_id_under_cursor(self, treeview):
"""get id of item form tree view under cursor""" """get id of item form tree view under cursor"""
path, column = treeview.get_cursor() path, column = treeview.get_cursor()
@@ -1239,7 +1334,7 @@ class MainController(Controller):
item_id = model.get_value(tm_iter, 0) item_id = model.get_value(tm_iter, 0)
return item_id return item_id
return None return None
def __setup_disc_treeview(self): def __setup_disc_treeview(self):
"""Setup TreeView discs widget as tree.""" """Setup TreeView discs widget as tree."""
self.view['discs'].set_model(self.model.discs_tree) self.view['discs'].set_model(self.model.discs_tree)
@@ -1360,8 +1455,9 @@ class MainController(Controller):
def __popup_menu(self, event, menu='discs_popup'): def __popup_menu(self, event, menu='discs_popup'):
"""Popoup desired menu""" """Popoup desired menu"""
self.view[menu].popup(None, None, None, event.button, self.view[menu].popup(None, None, None, 0, 0)
event.time) #self.view[menu].popup(None, None, None, event.button,
# event.time)
self.view[menu].show_all() self.view[menu].show_all()
return return
@@ -1377,7 +1473,7 @@ class MainController(Controller):
return return
def __get_item_info(self, file_id): def __get_item_info(self, file_id):
buf = gtk.TextBuffer() buf = gtk.TextBuffer()
if not file_id: if not file_id:
self.__hide_details() self.__hide_details()
@@ -1423,7 +1519,7 @@ class MainController(Controller):
tag.set_property('weight', pango.WEIGHT_BOLD) tag.set_property('weight', pango.WEIGHT_BOLD)
buf.insert_with_tags(buf.get_end_iter(), "Note:\n", tag) buf.insert_with_tags(buf.get_end_iter(), "Note:\n", tag)
buf.insert(buf.get_end_iter(), set['note']) buf.insert(buf.get_end_iter(), set['note'])
tags = self.model.get_file_tags(file_id) tags = self.model.get_file_tags(file_id)
if tags: if tags:
buf.insert(buf.get_end_iter(), "\n") buf.insert(buf.get_end_iter(), "\n")
@@ -1460,7 +1556,7 @@ class MainController(Controller):
"""generate tag cloud""" """generate tag cloud"""
tag_cloud = self.view['tag_cloud_textview'] tag_cloud = self.view['tag_cloud_textview']
self.model.get_tags() self.model.get_tags()
def insert_blank(buff, b_iter): def insert_blank(buff, b_iter):
if b_iter.is_end() and b_iter.is_start(): if b_iter.is_end() and b_iter.is_start():
return b_iter return b_iter
@@ -1470,18 +1566,18 @@ class MainController(Controller):
return b_iter return b_iter
buff = tag_cloud.get_buffer() buff = tag_cloud.get_buffer()
# NOTE: remove old tags # NOTE: remove old tags
def foreach_rem(texttag, data): def foreach_rem(texttag, data):
"""remove old tags""" """remove old tags"""
tag_table.remove(texttag) tag_table.remove(texttag)
tag_table = buff.get_tag_table() tag_table = buff.get_tag_table()
while tag_table.get_size() > 0: while tag_table.get_size() > 0:
tag_table.foreach(foreach_rem, None) tag_table.foreach(foreach_rem, None)
buff.set_text('') buff.set_text('')
if len(self.model.tag_cloud) > 0: if len(self.model.tag_cloud) > 0:
for cloud in self.model.tag_cloud: for cloud in self.model.tag_cloud:
buff_iter = insert_blank(buff, buff.get_end_iter()) buff_iter = insert_blank(buff, buff.get_end_iter())
@@ -1490,7 +1586,8 @@ class MainController(Controller):
tag.set_property('size-points', cloud['size']) tag.set_property('size-points', cloud['size'])
#tag.connect('event', self.on_tag_cloud_click, tag) #tag.connect('event', self.on_tag_cloud_click, tag)
buff.insert_with_tags(buff_iter, buff.insert_with_tags(buff_iter,
cloud['name'] + "(%d)" % cloud['count'], cloud['name'] + "(%d)" % \
cloud['count'],
tag) tag)
except: except:
if __debug__: if __debug__:

View File

@@ -175,12 +175,12 @@ class MainModel(ModelMT):
ORDER BY t.tag""" ORDER BY t.tag"""
self.db_cursor.execute(sql) self.db_cursor.execute(sql)
res = self.db_cursor.fetchall() res = self.db_cursor.fetchall()
retval = {} retval = {}
for tag in res: for tag in res:
retval[tag[0]] = tag[1] retval[tag[0]] = tag[1]
return retval return retval
def get_tag_by_id(self, tag_id): def get_tag_by_id(self, tag_id):
"""get tag (string) by its id""" """get tag (string) by its id"""
# SQL: get tag by id # SQL: get tag by id
@@ -190,10 +190,10 @@ class MainModel(ModelMT):
if not res: if not res:
return None return None
return res[0] return res[0]
def get_file_tags(self, file_id): def get_file_tags(self, file_id):
"""get tags of file""" """get tags of file"""
# SQL: get tag by id # SQL: get tag by id
sql = """SELECT t.id, t.tag FROM tags t sql = """SELECT t.id, t.tag FROM tags t
LEFT JOIN tags_files f ON t.id=f.tag_id LEFT JOIN tags_files f ON t.id=f.tag_id
@@ -201,16 +201,16 @@ class MainModel(ModelMT):
ORDER BY t.tag""" ORDER BY t.tag"""
self.db_cursor.execute(sql, (int(file_id), )) self.db_cursor.execute(sql, (int(file_id), ))
res = self.db_cursor.fetchall() res = self.db_cursor.fetchall()
tmp = {} tmp = {}
if len(res) == 0: if len(res) == 0:
return None return None
for row in res: for row in res:
tmp[row[0]] = row[1] tmp[row[0]] = row[1]
return tmp return tmp
def delete_tags(self, file_id_list, tag_id_list): def delete_tags(self, file_id_list, tag_id_list):
"""remove tags from selected files""" """remove tags from selected files"""
for file_id in file_id_list: for file_id in file_id_list:
@@ -222,7 +222,17 @@ class MainModel(ModelMT):
sql = """DELETE FROM tags_files WHERE file_id = ? sql = """DELETE FROM tags_files WHERE file_id = ?
AND tag_id IN """ + sql AND tag_id IN """ + sql
self.db_cursor.execute(sql, (int(file_id), )) self.db_cursor.execute(sql, (int(file_id), ))
self.db_connection.commit()
for tag_id in tag_id_list:
sql = """SELECT count(*) FROM tags_files WHERE tag_id=?"""
self.db_cursor.execute(sql, (int(tag_id),))
res = self.db_cursor.fetchone()
if res[0] == 0:
sql = """DELETE FROM tags WHERE id=?"""
self.db_cursor.execute(sql, (int(tag_id),))
self.db_connection.commit()
def get_tags(self): def get_tags(self):
"""fill tags dict with values from db""" """fill tags dict with values from db"""
if not self.selected_tags: if not self.selected_tags:
@@ -237,7 +247,7 @@ class MainModel(ModelMT):
WHERE f.file_id in """ + str(tuple(id_filter)) + \ WHERE f.file_id in """ + str(tuple(id_filter)) + \
"""GROUP BY f.tag_id """GROUP BY f.tag_id
ORDER BY t.tag""" ORDER BY t.tag"""
self.db_cursor.execute(sql) self.db_cursor.execute(sql)
res = self.db_cursor.fetchall() res = self.db_cursor.fetchall()
@@ -267,17 +277,17 @@ class MainModel(ModelMT):
tmp = 1 tmp = 1
self.tag_cloud[count]['size'] = tmp + 8 self.tag_cloud[count]['size'] = tmp + 8
count += 1 count += 1
def add_tag_to_path(self, tag_id): def add_tag_to_path(self, tag_id):
"""add tag to filter""" """add tag to filter"""
temp = {} temp = {}
tag_name = self.get_tag_by_id(tag_id) tag_name = self.get_tag_by_id(tag_id)
for i in self.selected_tags: for i in self.selected_tags:
temp[i] = self.selected_tags[i] temp[i] = self.selected_tags[i]
temp[int(tag_id)] = tag_name temp[int(tag_id)] = tag_name
self.selected_tags = temp self.selected_tags = temp
def add_image(self, image, file_id, only_thumbs=False): def add_image(self, image, file_id, only_thumbs=False):
"""add single image to file/directory""" """add single image to file/directory"""
sql = """INSERT INTO images(file_id, thumbnail, filename) sql = """INSERT INTO images(file_id, thumbnail, filename)
@@ -320,7 +330,7 @@ class MainModel(ModelMT):
sql = """DELETE FROM images WHERE file_id = ?""" sql = """DELETE FROM images WHERE file_id = ?"""
self.db_cursor.execute(sql, (file_id,)) self.db_cursor.execute(sql, (file_id,))
self.db_connection.commit() self.db_connection.commit()
def save_image(self, image_id, file_path): def save_image(self, image_id, file_path):
"""save image with specified id into file path (directory)""" """save image with specified id into file path (directory)"""
sql = """SELECT i.filename, f.filename FROM images i sql = """SELECT i.filename, f.filename FROM images i
@@ -333,18 +343,18 @@ class MainModel(ModelMT):
count = 1 count = 1
dest = os.path.join(file_path, res[1] + "_%d." % count + \ dest = os.path.join(file_path, res[1] + "_%d." % count + \
res[0].split('.')[-1]) res[0].split('.')[-1])
while os.path.exists(dest): while os.path.exists(dest):
count += 1 count += 1
dest = os.path.join(file_path, res[1] + "_%d." % count + \ dest = os.path.join(file_path, res[1] + "_%d." % count + \
res[0].split('.')[-1]) res[0].split('.')[-1])
shutil.copy(source, dest) shutil.copy(source, dest)
return True return True
#os.unlink() #os.unlink()
else: else:
return False return False
def delete_images_wth_thumbs(self, image_id): def delete_images_wth_thumbs(self, image_id):
"""removes image (without thumbnail) on specified image id""" """removes image (without thumbnail) on specified image id"""
sql = """SELECT filename FROM images WHERE id=?""" sql = """SELECT filename FROM images WHERE id=?"""
@@ -361,7 +371,7 @@ class MainModel(ModelMT):
sql = """UPDATE images set filename=NULL WHERE id = ?""" sql = """UPDATE images set filename=NULL WHERE id = ?"""
self.db_cursor.execute(sql, (image_id,)) self.db_cursor.execute(sql, (image_id,))
self.db_connection.commit() self.db_connection.commit()
def delete_image(self, image_id): def delete_image(self, image_id):
"""removes image on specified image id""" """removes image on specified image id"""
sql = """SELECT filename, thumbnail FROM images WHERE id=?""" sql = """SELECT filename, thumbnail FROM images WHERE id=?"""
@@ -531,18 +541,18 @@ class MainModel(ModelMT):
self.db_cursor.execute("update files set filename=? \ self.db_cursor.execute("update files set filename=? \
WHERE id=?", (new_name, file_id)) WHERE id=?", (new_name, file_id))
self.db_connection.commit() self.db_connection.commit()
for row in self.files_list: for row in self.files_list:
if row[0] == file_id: if row[0] == file_id:
row[1] = new_name row[1] = new_name
break break
def foreach_discs_tree(model, path, iterator, data): def foreach_discs_tree(model, path, iterator, data):
if model.get_value(iterator, 0) == data[0]: if model.get_value(iterator, 0) == data[0]:
model.set_value(iterator, 1, data[1]) model.set_value(iterator, 1, data[1])
self.discs_tree.foreach(foreach_discs_tree, (file_id, new_name)) self.discs_tree.foreach(foreach_discs_tree, (file_id, new_name))
#self.__fetch_db_into_treestore() #self.__fetch_db_into_treestore()
self.unsaved_project = True self.unsaved_project = True
else: else:
@@ -585,12 +595,12 @@ class MainModel(ModelMT):
else: else:
sql="""SELECT id, filename, size, date FROM files sql="""SELECT id, filename, size, date FROM files
WHERE 1=0""" WHERE 1=0"""
if not parent_id and self.selected_tags: if not parent_id and self.selected_tags:
self.db_cursor.execute(sql) self.db_cursor.execute(sql)
else: else:
self.db_cursor.execute(sql, (parent_id,)) self.db_cursor.execute(sql, (parent_id,))
data = self.db_cursor.fetchall() data = self.db_cursor.fetchall()
for row in data: for row in data:
myiter = self.files_list.insert_before(None, None) myiter = self.files_list.insert_before(None, None)
@@ -633,7 +643,7 @@ class MainModel(ModelMT):
self.db_cursor.execute(sql) self.db_cursor.execute(sql)
else: else:
self.db_cursor.execute(sql, (parent_id,)) self.db_cursor.execute(sql, (parent_id,))
data = self.db_cursor.fetchall() data = self.db_cursor.fetchall()
for row in data: for row in data:
myiter = self.files_list.insert_before(None, None) myiter = self.files_list.insert_before(None, None)
@@ -754,7 +764,7 @@ class MainModel(ModelMT):
"""Remove subtree (item and its children) from main tree, remove tags """Remove subtree (item and its children) from main tree, remove tags
from database remove all possible data, like thumbnails, images, gthumb from database remove all possible data, like thumbnails, images, gthumb
info, exif etc""" info, exif etc"""
fids = [] fids = []
if not db_cursor: if not db_cursor:
@@ -793,7 +803,13 @@ class MainModel(ModelMT):
sql = """DELETE FROM tags_files WHERE file_id = ?""" sql = """DELETE FROM tags_files WHERE file_id = ?"""
db_cursor.executemany(sql, generator()) db_cursor.executemany(sql, generator())
arg = str(tuple(fids)) if __debug__:
print "m_main.py: delete(): deleting:", fids
if len(fids) == 1:
arg = "(%d)" % fids[0]
else:
arg = str(tuple(fids))
# remove thumbnails # remove thumbnails
sql = """SELECT filename FROM thumbnails WHERE file_id IN %s""" % arg sql = """SELECT filename FROM thumbnails WHERE file_id IN %s""" % arg
@@ -946,6 +962,7 @@ class MainModel(ModelMT):
"""update note and description""" """update note and description"""
sql = """UPDATE files SET description=?, note=? WHERE id=?""" sql = """UPDATE files SET description=?, note=? WHERE id=?"""
self.db_cursor.execute(sql, (desc, note, file_id)) self.db_cursor.execute(sql, (desc, note, file_id))
self.db_connection.commit()
return return
# private class functions # private class functions
@@ -1101,11 +1118,11 @@ class MainModel(ModelMT):
self.db_cursor.execute(sql) self.db_cursor.execute(sql)
self.db_connection.commit() self.db_connection.commit()
def __filter(self): def __filter(self):
"""return list of ids of files (AND their parent, even if they have no """return list of ids of files (AND their parent, even if they have no
assigned tags) that corresponds to tags""" assigned tags) that corresponds to tags"""
filtered_ids = [] filtered_ids = []
count = 0 count = 0
for tid in self.selected_tags: for tid in self.selected_tags:
@@ -1117,13 +1134,13 @@ class MainModel(ModelMT):
data = self.db_cursor.fetchall() data = self.db_cursor.fetchall()
for row in data: for row in data:
temp1.append(row[0]) temp1.append(row[0])
if count > 0: if count > 0:
filtered_ids = list(set(filtered_ids).intersection(temp1)) filtered_ids = list(set(filtered_ids).intersection(temp1))
else: else:
filtered_ids = temp1 filtered_ids = temp1
count += 1 count += 1
parents = [] parents = []
for i in filtered_ids: for i in filtered_ids:
sql = """SELECT parent_id sql = """SELECT parent_id
@@ -1143,13 +1160,13 @@ class MainModel(ModelMT):
break break
else: else:
parents.append(data[0]) parents.append(data[0])
return list(set(parents).union(filtered_ids)) return list(set(parents).union(filtered_ids))
def __filter2(self): def __filter2(self):
"""return list of ids of files (WITHOUT their parent) that """return list of ids of files (WITHOUT their parent) that
corresponds to tags""" corresponds to tags"""
filtered_ids = [] filtered_ids = []
count = 0 count = 0
for tid in self.selected_tags: for tid in self.selected_tags:
@@ -1161,7 +1178,7 @@ class MainModel(ModelMT):
data = self.db_cursor.fetchall() data = self.db_cursor.fetchall()
for row in data: for row in data:
temp1.append(row[0]) temp1.append(row[0])
if count > 0: if count > 0:
filtered_ids = list(set(filtered_ids).intersection(temp1)) filtered_ids = list(set(filtered_ids).intersection(temp1))
else: else:
@@ -1169,7 +1186,7 @@ class MainModel(ModelMT):
count += 1 count += 1
return filtered_ids return filtered_ids
def __scan(self): def __scan(self):
"""scan content of the given path""" """scan content of the given path"""
self.busy = True self.busy = True

View File

@@ -231,7 +231,7 @@ class SelectDirectory(object):
self.title = title self.title = title
else: else:
self.title = "Choose directory" self.title = "Choose directory"
def run(self): def run(self):
"""dialog for point the mountpoint""" """dialog for point the mountpoint"""
dialog = gtk.FileChooserDialog( dialog = gtk.FileChooserDialog(
@@ -247,7 +247,7 @@ class SelectDirectory(object):
dialog.set_default_response(gtk.RESPONSE_OK) dialog.set_default_response(gtk.RESPONSE_OK)
retval = None retval = None
if self.URI: if self.URI:
dialog.set_current_folder_uri(self.URI) dialog.set_current_folder_uri(self.URI)
response = dialog.run() response = dialog.run()
@@ -479,7 +479,7 @@ class StatsDialog(object):
if result == gtk.RESPONSE_OK: if result == gtk.RESPONSE_OK:
return entry.get_text() return entry.get_text()
return None return None
class TagsDialog(object): class TagsDialog(object):
"""Sepcific dialog for display stats""" """Sepcific dialog for display stats"""
@@ -498,7 +498,7 @@ class TagsDialog(object):
if result == gtk.RESPONSE_OK: if result == gtk.RESPONSE_OK:
return entry.get_text() return entry.get_text()
return None return None
class TagsRemoveDialog(object): class TagsRemoveDialog(object):
"""Sepcific dialog for display stats""" """Sepcific dialog for display stats"""
@@ -509,34 +509,44 @@ class TagsRemoveDialog(object):
def run(self): def run(self):
if not self.tag_dict: if not self.tag_dict:
return None return None
gladexml = gtk.glade.XML(self.gladefile, "tagRemove") gladexml = gtk.glade.XML(self.gladefile, "tagRemove")
dialog = gladexml.get_widget("tagRemove") dialog = gladexml.get_widget("tagRemove")
# fill model with dict # declare model
model = gtk.ListStore(gobject.TYPE_INT, model = gtk.ListStore(gobject.TYPE_INT,
gobject.TYPE_STRING, gobject.TYPE_BOOLEAN) gobject.TYPE_STRING, gobject.TYPE_BOOLEAN)
for tag in self.tag_dict: # sort dict
values = self.tag_dict.values()
values.sort()
keys = []
for val in values:
for d_key, d_value in self.tag_dict.items():
if d_value == val:
keys.append(d_key)
# fill model with dict
for count in range(len(keys)):
myiter = model.insert_before(None, None) myiter = model.insert_before(None, None)
model.set_value(myiter, 0, tag) model.set_value(myiter, 0, keys[count])
model.set_value(myiter, 1, self.tag_dict[tag]) model.set_value(myiter, 1, values[count])
model.set_value(myiter, 2, None) model.set_value(myiter, 2, None)
def toggle(cell, path, model): def toggle(cell, path, model):
model[path][2] = not model[path][2] model[path][2] = not model[path][2]
def toggle_all(column, model): def toggle_all(column, model):
for row in model: for row in model:
row[2] = not row[2] row[2] = not row[2]
treeview = gladexml.get_widget("treeview1") treeview = gladexml.get_widget("treeview1")
treeview.set_model(model) treeview.set_model(model)
renderer = gtk.CellRendererText() renderer = gtk.CellRendererText()
column = gtk.TreeViewColumn("Tag", renderer, text=1) column = gtk.TreeViewColumn("Tag", renderer, text=1)
column.set_property('expand', True) column.set_property('expand', True)
treeview.append_column(column) treeview.append_column(column)
renderer = gtk.CellRendererToggle() renderer = gtk.CellRendererToggle()
renderer.set_property('activatable', True) renderer.set_property('activatable', True)
renderer.connect('toggled', toggle, model) renderer.connect('toggled', toggle, model)
@@ -546,7 +556,7 @@ class TagsRemoveDialog(object):
column.set_property("clickable", True) column.set_property("clickable", True)
column.connect("clicked", toggle_all, model) column.connect("clicked", toggle_all, model)
treeview.append_column(column) treeview.append_column(column)
result = dialog.run() result = dialog.run()
dialog.destroy() dialog.destroy()

View File

@@ -39,7 +39,6 @@ class MainView(View):
self['separatormenuitem4'].hide() self['separatormenuitem4'].hide()
self['list1'].hide() self['list1'].hide()
self['thumbnails1'].hide() self['thumbnails1'].hide()
#self['tag_cloud_textview'].drag_dest_set(0, [], 0)
return return
pass # end of class pass # end of class