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
@@ -24,6 +24,7 @@ REQUIREMENTS
pyGTKtalog is written in python with following dependencies:
- python 2.4 or higher
- pygtk <http://www.pygtk.org>
- 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.
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.
BeOS/ZETA/Haiku, QNX or MacOSX).
@@ -57,11 +58,14 @@ Then, just run pyGTKtalog script.
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
- tagging files (90%)
- user definied group of tags (represented by color in cloud tag)
x tagging files
x remove nasty bug in redraw of tag cloud
x file details:
x files properties
@@ -75,6 +79,10 @@ x generating/saving thumbnails
x moving hardcoded files extensions into config
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.
For version 2.0:
@@ -82,6 +90,9 @@ For version 2.0:
- command line support: query, adding media to collection etc
- internationalization support
- 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:
- 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
be enough for good and fast information search.
- file information (date, size, etc) (50%) (no need for?)
NOTES
=====

View File

@@ -93,35 +93,9 @@
<property name="visible">True</property>
<property name="label" translatable="yes">_Edit</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_edit1_activate"/>
<child>
<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>
<widget class="GtkImageMenuItem" id="delete1">
<property name="visible">True</property>
@@ -129,6 +103,7 @@
<property name="use_underline">True</property>
<property name="use_stock">True</property>
<signal name="activate" handler="on_delete1_activate"/>
<accelerator key="Delete" modifiers="" signal="activate"/>
</widget>
</child>
<child>
@@ -177,7 +152,7 @@
<property name="label" translatable="yes">Add _CD/DVD</property>
<property name="use_underline">True</property>
<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>
</child>
<child>
@@ -185,7 +160,7 @@
<property name="visible">True</property>
<property name="label" translatable="yes">Add _Directory</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"/>
</widget>
</child>
@@ -371,6 +346,18 @@
<property name="expand">False</property>
</packing>
</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>
<widget class="GtkToolButton" id="tb_find">
<property name="visible">True</property>
@@ -501,6 +488,7 @@
<signal name="button_press_event" handler="on_discs_button_press_event"/>
<signal name="row_activated" handler="on_discs_row_activated"/>
<signal name="cursor_changed" handler="on_discs_cursor_changed"/>
<signal name="key_release_event" handler="on_discs_key_release_event"/>
</widget>
</child>
</widget>
@@ -672,6 +660,7 @@
<property name="tooltip" translatable="yes">Double click to open image</property>
<signal name="button_press_event" handler="on_images_button_press_event"/>
<signal name="item_activated" handler="on_images_item_activated"/>
<signal name="key_release_event" handler="on_images_key_release_event"/>
</widget>
</child>
</widget>
@@ -1025,4 +1014,17 @@
</widget>
</child>
</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>

View File

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

View File

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

View File

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

View File

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