mirror of
https://github.com/gryf/pygtktalog.git
synced 2025-12-17 11:30:19 +01:00
* Added drag and drop support from files TreeView to tags cloud.
* Added tags add. * Code clean up. * Removed unnecessary imports. * Adapted to PEP8.
This commit is contained in:
4
README
4
README
@@ -60,8 +60,8 @@ TODO
|
|||||||
For version 1.0 following aims have to be done:
|
For version 1.0 following aims have to be done:
|
||||||
|
|
||||||
- searching database
|
- searching database
|
||||||
- tagging files
|
- tagging files (40%)
|
||||||
- user definied group of tags (represented by color in cloud tag)
|
- user definied group of tags (represented by color in cloud tag) (10%)
|
||||||
x file details:
|
x file details:
|
||||||
x files properties
|
x files properties
|
||||||
x thumbnail
|
x thumbnail
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
# -------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
|
|
||||||
__version__ = "0.8"
|
__version__ = "0.8"
|
||||||
licence = \
|
LICENCE = \
|
||||||
"""
|
"""
|
||||||
GPL v2
|
GPL v2
|
||||||
http://www.gnu.org/licenses/gpl.txt
|
http://www.gnu.org/licenses/gpl.txt
|
||||||
@@ -34,9 +34,12 @@ from os import popen
|
|||||||
from utils import deviceHelper
|
from utils import deviceHelper
|
||||||
from gtkmvc import Controller
|
from gtkmvc import Controller
|
||||||
|
|
||||||
|
from time import time, ctime
|
||||||
|
|
||||||
from c_config import ConfigController
|
from c_config import ConfigController
|
||||||
from views.v_config import ConfigView
|
from views.v_config import ConfigView
|
||||||
from models.m_config import ConfigModel
|
from c_tags import TagsController
|
||||||
|
from views.v_tags import TagsView
|
||||||
|
|
||||||
import views.v_dialogs as Dialogs
|
import views.v_dialogs as Dialogs
|
||||||
|
|
||||||
@@ -45,27 +48,22 @@ from views.v_image import ImageView
|
|||||||
import gtk
|
import gtk
|
||||||
import pango
|
import pango
|
||||||
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
class MainController(Controller):
|
class MainController(Controller):
|
||||||
"""Controller for main application window"""
|
"""Controller for main application window"""
|
||||||
scan_cd = False
|
scan_cd = False
|
||||||
widgets = (
|
widgets = ("discs", "files", 'save1', 'save_as1', 'cut1', 'copy1',
|
||||||
"discs","files",
|
'paste1', 'delete1', 'add_cd', 'add_directory1', 'tb_save',
|
||||||
'save1','save_as1','cut1','copy1','paste1','delete1','add_cd','add_directory1',
|
'tb_addcd', 'tb_find', 'nb_dirs', 'description', 'stat1')
|
||||||
'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',
|
||||||
widgets_all = (
|
'tb_new', 'tb_open', 'tb_quit', 'nb_dirs', 'description',
|
||||||
"discs","files",
|
'stat1')
|
||||||
'file1','edit1','add_cd','add_directory1','help1',
|
|
||||||
'tb_save','tb_addcd','tb_find','tb_new','tb_open','tb_quit',
|
|
||||||
'nb_dirs','description','stat1',
|
|
||||||
)
|
|
||||||
|
|
||||||
widgets_cancel = ('cancel','cancel1')
|
widgets_cancel = ('cancel','cancel1')
|
||||||
|
|
||||||
def __init__(self, model):
|
def __init__(self, model):
|
||||||
"""Initialize controller"""
|
"""Initialize controller"""
|
||||||
|
self.DND_TARGETS = [('files_tags', 0, 69)]
|
||||||
Controller.__init__(self, model)
|
Controller.__init__(self, model)
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -88,8 +86,9 @@ class MainController(Controller):
|
|||||||
self.view['debugbtn'].hide()
|
self.view['debugbtn'].hide()
|
||||||
|
|
||||||
# load configuration/defaults and set it to properties
|
# load configuration/defaults and set it to properties
|
||||||
self.view['toolbar1'].set_active(self.model.config.confd['showtoolbar'])
|
bo = self.model.config.confd['showtoolbar']
|
||||||
if self.model.config.confd['showtoolbar']:
|
self.view['toolbar1'].set_active(bo)
|
||||||
|
if bo:
|
||||||
self.view['maintoolbar'].show()
|
self.view['maintoolbar'].show()
|
||||||
else:
|
else:
|
||||||
self.view['maintoolbar'].hide()
|
self.view['maintoolbar'].hide()
|
||||||
@@ -105,15 +104,24 @@ class MainController(Controller):
|
|||||||
self.model.config.confd['wy'])
|
self.model.config.confd['wy'])
|
||||||
|
|
||||||
# initialize statusbar
|
# initialize statusbar
|
||||||
self.context_id = self.view['mainStatus'].get_context_id('detailed res')
|
context = self.view['mainStatus'].get_context_id('detailed res')
|
||||||
|
self.context_id = context
|
||||||
self.statusbar_id = self.view['mainStatus'].push(self.context_id,
|
self.statusbar_id = self.view['mainStatus'].push(self.context_id,
|
||||||
"Idle")
|
"Idle")
|
||||||
|
|
||||||
|
# make tag_cloud_textview recive dnd signals
|
||||||
|
self.view['tag_cloud_textview'].drag_dest_set(gtk.DEST_DEFAULT_ALL,
|
||||||
|
self.DND_TARGETS,
|
||||||
|
gtk.gdk.ACTION_COPY)
|
||||||
|
#ttv.connect('drag_motion', self.on_tag_cloud_textview_drag_motion)
|
||||||
|
#ttv.connect('drag_drop', self.on_tag_cloud_textview_drag_drop)
|
||||||
|
|
||||||
# initialize treeviews
|
# initialize treeviews
|
||||||
self.__setup_disc_treeview()
|
self.__setup_disc_treeview()
|
||||||
self.__setup_files_treeview()
|
self.__setup_files_treeview()
|
||||||
self.__setup_exif_treeview()
|
self.__setup_exif_treeview()
|
||||||
|
|
||||||
|
|
||||||
# in case passing catalog filename in command line, unlock gui
|
# in case passing catalog filename in command line, unlock gui
|
||||||
if self.model.filename != None:
|
if self.model.filename != None:
|
||||||
self.__activate_ui(self.model.filename)
|
self.__activate_ui(self.model.filename)
|
||||||
@@ -121,26 +129,60 @@ class MainController(Controller):
|
|||||||
# generate recent menu
|
# generate recent menu
|
||||||
self.__generate_recent_menu()
|
self.__generate_recent_menu()
|
||||||
|
|
||||||
|
# initialoze 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)
|
||||||
return
|
return
|
||||||
|
|
||||||
#########################################################################
|
#########################################################################
|
||||||
# Connect signals from GUI, like menu objects, toolbar buttons and so on.
|
# Connect signals from GUI, like menu objects, toolbar buttons and so on.
|
||||||
|
def on_tag_cloud_textview_drag_drop(self, wid, context, x, y, time):
|
||||||
|
#print '\n'.join([str(t) for t in context.targets])
|
||||||
|
#print context.drag_get_selection()
|
||||||
|
#print context.get_source_widget()
|
||||||
|
#print x, y
|
||||||
|
#print wid
|
||||||
|
context.finish(True, False, time)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def on_tag_cloud_textview_drag_motion(self, wid, context, x, y, time):
|
||||||
|
context.drag_status(gtk.gdk.ACTION_COPY, time)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def on_files_drag_data_get(self, widget, context, selection,
|
||||||
|
targetType, eventTime):
|
||||||
|
# get selection, and send it to the client
|
||||||
|
if targetType == self.DND_TARGETS[0][2]:
|
||||||
|
str = "1,2,3,4,57,9,0,"
|
||||||
|
selection.set(selection.target, 8, str)
|
||||||
|
|
||||||
|
def on_tag_cloud_textview_drag_data_received(self, widget, context, x, y,
|
||||||
|
selection, targetType, time):
|
||||||
|
if targetType == self.DND_TARGETS[0][2]:
|
||||||
|
print selection.data
|
||||||
|
print "kupa"
|
||||||
|
|
||||||
def on_edit2_activate(self, menu_item):
|
def on_edit2_activate(self, menu_item):
|
||||||
try:
|
try:
|
||||||
selection = self.view['files'].get_selection()
|
selection = self.view['files'].get_selection()
|
||||||
model, list_of_paths = selection.get_selected_rows()
|
model, list_of_paths = selection.get_selected_rows()
|
||||||
id = model.get_value(model.get_iter(list_of_paths[0]), 0)
|
id = model.get_value(model.get_iter(list_of_paths[0]), 0)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
if __debug__: print "c_main.py: on_edit2_activate(): 0 zaznaczonych wierszy"
|
if __debug__:
|
||||||
|
print "c_main.py: on_edit2_activate(): 0",
|
||||||
|
print "zaznaczonych wierszy"
|
||||||
return
|
return
|
||||||
|
|
||||||
val = self.model.get_file_info(id)
|
val = self.model.get_file_info(id)
|
||||||
ret = Dialogs.EditDialog(val).run()
|
ret = Dialogs.EditDialog(val).run()
|
||||||
if ret:
|
if ret:
|
||||||
self.model.rename(id, ret['filename'])
|
self.model.rename(id, ret['filename'])
|
||||||
self.model.update_desc_and_note(id, ret['description'], ret['note'])
|
self.model.update_desc_and_note(id,
|
||||||
|
ret['description'], ret['note'])
|
||||||
self.__get_item_info(id)
|
self.__get_item_info(id)
|
||||||
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)
|
||||||
@@ -158,7 +200,9 @@ 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)
|
||||||
except:
|
except:
|
||||||
if __debug__: print "c_main.py: on_add_thumb1_activate(): error on getting selected items or creating thumbnails"
|
if __debug__:
|
||||||
|
print "c_main.py: on_add_thumb1_activate(): error on getting",
|
||||||
|
print "selected items or creating thumbnails"
|
||||||
return
|
return
|
||||||
self.__get_item_info(id)
|
self.__get_item_info(id)
|
||||||
return
|
return
|
||||||
@@ -166,7 +210,8 @@ class MainController(Controller):
|
|||||||
def on_remove_thumb1_activate(self, menu_item):
|
def on_remove_thumb1_activate(self, menu_item):
|
||||||
if self.model.config.confd['delwarn']:
|
if self.model.config.confd['delwarn']:
|
||||||
obj = Dialogs.Qst('Delete thumbnails', 'Delete thumbnails?',
|
obj = Dialogs.Qst('Delete thumbnails', 'Delete thumbnails?',
|
||||||
'Thumbnails for selected items will be permanently removed from catalog.')
|
"Thumbnails for selected items will be \
|
||||||
|
permanently removed from catalog.")
|
||||||
if not obj.run():
|
if not obj.run():
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
@@ -176,7 +221,9 @@ class MainController(Controller):
|
|||||||
id = model.get_value(model.get_iter(path),0)
|
id = model.get_value(model.get_iter(path),0)
|
||||||
self.model.del_thumbnail(id)
|
self.model.del_thumbnail(id)
|
||||||
except:
|
except:
|
||||||
if __debug__: print "c_main.py: on_remove_thumb1_activate(): error on getting selected items or removing thumbnails"
|
if __debug__:
|
||||||
|
print "c_main.py: on_remove_thumb1_activate(): error on",
|
||||||
|
print "getting selected items or removing thumbnails"
|
||||||
return
|
return
|
||||||
|
|
||||||
self.model.unsaved_project = True
|
self.model.unsaved_project = True
|
||||||
@@ -187,7 +234,8 @@ class MainController(Controller):
|
|||||||
def on_remove_image1_activate(self, menu_item):
|
def on_remove_image1_activate(self, menu_item):
|
||||||
if self.model.config.confd['delwarn']:
|
if self.model.config.confd['delwarn']:
|
||||||
obj = Dialogs.Qst('Delete images', 'Delete all images?',
|
obj = Dialogs.Qst('Delete images', 'Delete all images?',
|
||||||
'All images for selected items will be permanently removed from catalog.')
|
'All images for selected items will be \
|
||||||
|
permanently removed from catalog.')
|
||||||
if not obj.run():
|
if not obj.run():
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
@@ -197,7 +245,9 @@ class MainController(Controller):
|
|||||||
id = model.get_value(model.get_iter(path),0)
|
id = model.get_value(model.get_iter(path),0)
|
||||||
self.model.del_images(id)
|
self.model.del_images(id)
|
||||||
except:
|
except:
|
||||||
if __debug__: print "c_main.py: on_remove_thumb1_activate(): error on getting selected items or removing thumbnails"
|
if __debug__:
|
||||||
|
print "c_main.py: on_remove_thumb1_activate(): error on",
|
||||||
|
print "getting selected items or removing thumbnails"
|
||||||
return
|
return
|
||||||
|
|
||||||
self.model.unsaved_project = True
|
self.model.unsaved_project = True
|
||||||
@@ -211,12 +261,14 @@ class MainController(Controller):
|
|||||||
id = model.get_value(iter, 0)
|
id = model.get_value(iter, 0)
|
||||||
img = self.model.get_image_path(id)
|
img = self.model.get_image_path(id)
|
||||||
if img:
|
if img:
|
||||||
if self.model.config.confd['imgview'] and len(self.model.config.confd['imgprog'])>0:
|
if self.model.config.confd['imgview'] and \
|
||||||
|
len(self.model.config.confd['imgprog'])>0:
|
||||||
popen("%s %s" % (self.model.config.confd['imgprog'], img))
|
popen("%s %s" % (self.model.config.confd['imgprog'], img))
|
||||||
else:
|
else:
|
||||||
ImageView(img)
|
ImageView(img)
|
||||||
else:
|
else:
|
||||||
Dialogs.Inf("Image view", "No Image", "This item have no real image, only thumbnail.")
|
Dialogs.Inf("Image view", "No Image",
|
||||||
|
"This item have no real image, only thumbnail.")
|
||||||
|
|
||||||
def on_rename1_activate(self, widget):
|
def on_rename1_activate(self, widget):
|
||||||
model, iter = self.view['discs'].get_selection().get_selected()
|
model, iter = self.view['discs'].get_selection().get_selected()
|
||||||
@@ -224,7 +276,8 @@ class MainController(Controller):
|
|||||||
id = model.get_value(iter, 0)
|
id = model.get_value(iter, 0)
|
||||||
new_name = Dialogs.InputNewName(name).run()
|
new_name = Dialogs.InputNewName(name).run()
|
||||||
|
|
||||||
if __debug__: print "c_main.py: on_rename1_activate(): label:", new_name
|
if __debug__:
|
||||||
|
print "c_main.py: on_rename1_activate(): label:", new_name
|
||||||
|
|
||||||
if new_name != None and new_name != name:
|
if new_name != None and new_name != name:
|
||||||
self.model.rename(id, new_name)
|
self.model.rename(id, new_name)
|
||||||
@@ -246,7 +299,8 @@ class MainController(Controller):
|
|||||||
name = model.get_value(model.get_iter(list_of_paths[0]),1)
|
name = model.get_value(model.get_iter(list_of_paths[0]),1)
|
||||||
|
|
||||||
new_name = Dialogs.InputNewName(name).run()
|
new_name = Dialogs.InputNewName(name).run()
|
||||||
if __debug__: print "c_main.py: on_rename1_activate(): label:", new_name
|
if __debug__:
|
||||||
|
print "c_main.py: on_rename1_activate(): label:", new_name
|
||||||
|
|
||||||
if new_name != None and new_name != name:
|
if new_name != None and new_name != name:
|
||||||
self.model.rename(fid, new_name)
|
self.model.rename(fid, new_name)
|
||||||
@@ -265,20 +319,34 @@ class MainController(Controller):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def on_tag_cloud_textview_motion_notify_event(self, widget):
|
def on_tag_cloud_textview_motion_notify_event(self, widget):
|
||||||
if __debug__: print "c_main.py: on_tag_cloud_textview_motion_notify_event():"
|
if __debug__:
|
||||||
|
print "c_main.py: on_tag_cloud_textview_motion_notify_event():"
|
||||||
w = self.view['tag_cloud_textview'].get_window(gtk.TEXT_WINDOW_TEXT)
|
w = self.view['tag_cloud_textview'].get_window(gtk.TEXT_WINDOW_TEXT)
|
||||||
if w:
|
if w:
|
||||||
w.set_cursor(None)
|
w.set_cursor(None)
|
||||||
|
|
||||||
def on_main_destroy_event(self, window, event):
|
def on_main_destroy_event(self, window, event):
|
||||||
self.__do_quit()
|
self.on_quit1_activate(widget)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def on_tb_quit_clicked(self, widget):
|
def on_tb_quit_clicked(self, widget):
|
||||||
self.__do_quit()
|
self.on_quit1_activate(widget)
|
||||||
|
|
||||||
def on_quit1_activate(self, widget):
|
def on_quit1_activate(self, widget):
|
||||||
self.__do_quit()
|
"""Quit and save window parameters to config file"""
|
||||||
|
# check if any unsaved project is on go.
|
||||||
|
if self.model.unsaved_project and \
|
||||||
|
self.model.config.confd['confirmquit']:
|
||||||
|
if not Dialogs.Qst('Quit application - pyGTKtalog',
|
||||||
|
'Do you really want to quit?',
|
||||||
|
"Current database is not saved, any changes \
|
||||||
|
will be lost.").run():
|
||||||
|
return
|
||||||
|
self.__store_settings()
|
||||||
|
self.model.cleanup()
|
||||||
|
gtk.main_quit()
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def on_new1_activate(self, widget):
|
def on_new1_activate(self, widget):
|
||||||
self.__new_db()
|
self.__new_db()
|
||||||
@@ -286,11 +354,34 @@ class MainController(Controller):
|
|||||||
def on_tb_new_clicked(self, widget):
|
def on_tb_new_clicked(self, widget):
|
||||||
self.__new_db()
|
self.__new_db()
|
||||||
|
|
||||||
def on_add_cd_activate(self, widget):
|
def on_add_cd_activate(self, widget, label=None, current_id=None):
|
||||||
self.__add_cd()
|
"""Add directory structure from cd/dvd disc"""
|
||||||
|
mount = deviceHelper.volmount(self.model.config.confd['cd'])
|
||||||
|
if mount == 'ok':
|
||||||
|
guessed_label = deviceHelper.volname(self.model.config.confd['cd'])
|
||||||
|
if not label:
|
||||||
|
label = Dialogs.InputDiskLabel(guessed_label).run()
|
||||||
|
if label != None:
|
||||||
|
self.scan_cd = True
|
||||||
|
for widget in self.widgets_all:
|
||||||
|
self.view[widget].set_sensitive(False)
|
||||||
|
self.model.source = self.model.CD
|
||||||
|
self.model.scan(self.model.config.confd['cd'], label,
|
||||||
|
current_id)
|
||||||
|
self.model.unsaved_project = True
|
||||||
|
self.__set_title(filepath=self.model.filename, modified=True)
|
||||||
|
else:
|
||||||
|
deviceHelper.volumount(self.model.config.confd['cd'])
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
Dialogs.Wrn("Error mounting device - pyGTKtalog",
|
||||||
|
"Cannot mount device pointed to %s" %
|
||||||
|
self.model.config.confd['cd'],
|
||||||
|
"Last mount message:\n%s" % mount)
|
||||||
|
return False
|
||||||
|
|
||||||
def on_tb_addcd_clicked(self, widget):
|
def on_tb_addcd_clicked(self, widget):
|
||||||
self.__add_cd()
|
self.on_add_cd_activate(widget)
|
||||||
|
|
||||||
def on_add_directory1_activate(self, widget):
|
def on_add_directory1_activate(self, widget):
|
||||||
"""Show dialog for choose drectory to add from filesystem."""
|
"""Show dialog for choose drectory to add from filesystem."""
|
||||||
@@ -300,7 +391,7 @@ class MainController(Controller):
|
|||||||
def on_about1_activate(self, widget):
|
def on_about1_activate(self, widget):
|
||||||
"""Show about dialog"""
|
"""Show about dialog"""
|
||||||
Dialogs.Abt("pyGTKtalog", __version__, "About",
|
Dialogs.Abt("pyGTKtalog", __version__, "About",
|
||||||
["Roman 'gryf' Dobosz"], licence)
|
["Roman 'gryf' Dobosz"], LICENCE)
|
||||||
return
|
return
|
||||||
|
|
||||||
def on_preferences_activate(self, widget):
|
def on_preferences_activate(self, widget):
|
||||||
@@ -327,13 +418,27 @@ class MainController(Controller):
|
|||||||
self.view['maintoolbar'].hide()
|
self.view['maintoolbar'].hide()
|
||||||
|
|
||||||
def on_save1_activate(self, widget):
|
def on_save1_activate(self, widget):
|
||||||
self.__save()
|
"""Save catalog to file"""
|
||||||
|
if self.model.filename:
|
||||||
|
self.model.save()
|
||||||
|
self.__set_title(filepath=self.model.filename)
|
||||||
|
else:
|
||||||
|
self.on_save_as1_activate(widget)
|
||||||
|
|
||||||
def on_tb_save_clicked(self, widget):
|
def on_tb_save_clicked(self, widget):
|
||||||
self.__save()
|
self.on_save1_activate(widget)
|
||||||
|
|
||||||
def on_save_as1_activate(self, widget):
|
def on_save_as1_activate(self, widget):
|
||||||
self.__save_as()
|
"""Save database to file under different filename."""
|
||||||
|
path = Dialogs.ChooseDBFilename().run()
|
||||||
|
if path:
|
||||||
|
ret, err = self.model.save(path)
|
||||||
|
if ret:
|
||||||
|
self.model.config.add_recent(path)
|
||||||
|
self.__set_title(filepath=path)
|
||||||
|
else:
|
||||||
|
Dialogs.Err("Error writing file - pyGTKtalog",
|
||||||
|
"Cannot write file %s." % path, "%s" % err)
|
||||||
|
|
||||||
def on_stat1_activate(self, menu_item):
|
def on_stat1_activate(self, menu_item):
|
||||||
self.__show_stats()
|
self.__show_stats()
|
||||||
@@ -350,10 +455,30 @@ class MainController(Controller):
|
|||||||
self.__show_stats(selected_id)
|
self.__show_stats(selected_id)
|
||||||
|
|
||||||
def on_tb_open_clicked(self, widget):
|
def on_tb_open_clicked(self, widget):
|
||||||
self.__open()
|
self.on_open1_activate(widget)
|
||||||
|
return
|
||||||
|
|
||||||
def on_open1_activate(self, widget):
|
def on_open1_activate(self, widget, path=None):
|
||||||
self.__open()
|
"""Open catalog file"""
|
||||||
|
confirm = self.model.config.confd['confirmabandon']
|
||||||
|
if self.model.unsaved_project and confirm:
|
||||||
|
obj = Dialogs.Qst('Unsaved data - pyGTKtalog',
|
||||||
|
'There is not saved database',
|
||||||
|
'Pressing "Ok" will abandon catalog.')
|
||||||
|
if not obj.run():
|
||||||
|
return
|
||||||
|
|
||||||
|
if not path:
|
||||||
|
path = Dialogs.LoadDBFile().run()
|
||||||
|
|
||||||
|
if path:
|
||||||
|
if not self.model.open(path):
|
||||||
|
Dialogs.Err("Error opening file - pyGTKtalog",
|
||||||
|
"Cannot open file %s." % path)
|
||||||
|
else:
|
||||||
|
self.__generate_recent_menu()
|
||||||
|
self.__activate_ui(path)
|
||||||
|
return
|
||||||
|
|
||||||
def on_discs_cursor_changed(self, widget):
|
def on_discs_cursor_changed(self, widget):
|
||||||
"""Show files on right treeview, after clicking the left disc
|
"""Show files on right treeview, after clicking the left disc
|
||||||
@@ -377,7 +502,8 @@ class MainController(Controller):
|
|||||||
|
|
||||||
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), int(event.y))
|
path_and_cell = iconview.get_item_at_pos(int(event.x),
|
||||||
|
int(event.y))
|
||||||
except TypeError:
|
except TypeError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -487,6 +613,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:
|
||||||
|
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"""
|
||||||
@@ -497,7 +625,9 @@ class MainController(Controller):
|
|||||||
selected_item = self.model.files_list.get_value(iter, 0)
|
selected_item = self.model.files_list.get_value(iter, 0)
|
||||||
self.__get_item_info(selected_item)
|
self.__get_item_info(selected_item)
|
||||||
except:
|
except:
|
||||||
if __debug__: print "c_main.py: on_files_cursor_changed() insufficient iterator"
|
if __debug__:
|
||||||
|
print "c_main.py: on_files_cursor_changed() insufficient",
|
||||||
|
print "iterator"
|
||||||
return
|
return
|
||||||
|
|
||||||
def on_files_key_release_event(self, a, event):
|
def on_files_key_release_event(self, a, event):
|
||||||
@@ -516,8 +646,8 @@ class MainController(Controller):
|
|||||||
first_iter = f_model.get_iter_first()
|
first_iter = f_model.get_iter_first()
|
||||||
first_child_value = f_model.get_value(first_iter, 0)
|
first_child_value = f_model.get_value(first_iter, 0)
|
||||||
# get two steps up
|
# get two steps up
|
||||||
value = self.model.get_parent_discs_value(first_child_value)
|
val = self.model.get_parent_discs_value(first_child_value)
|
||||||
parent_value = self.model.get_parent_discs_value(value)
|
parent_value = self.model.get_parent_discs_value(val)
|
||||||
iter = self.model.discs_tree.get_iter_first()
|
iter = self.model.discs_tree.get_iter_first()
|
||||||
while iter:
|
while iter:
|
||||||
current_value = self.model.discs_tree.get_value(iter,0)
|
current_value = self.model.discs_tree.get_value(iter,0)
|
||||||
@@ -565,15 +695,36 @@ class MainController(Controller):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def recent_item_response(self, path):
|
def recent_item_response(self, path):
|
||||||
self.__open(path)
|
self.on_open1_activate(widget)
|
||||||
return
|
return
|
||||||
|
|
||||||
def on_add_tag1_activate(self, menu_item):
|
def on_add_tag1_activate(self, menu_item):
|
||||||
print self.view['files'].get_cursor()
|
#try:
|
||||||
|
selection = self.view['files'].get_selection()
|
||||||
|
model, list_of_paths = selection.get_selected_rows()
|
||||||
|
tags = Dialogs.TagsDialog().run()
|
||||||
|
if not tags:
|
||||||
|
return
|
||||||
|
|
||||||
|
for path in list_of_paths:
|
||||||
|
id = model.get_value(model.get_iter(path),0)
|
||||||
|
self.model.add_tags(id, tags)
|
||||||
|
#except:
|
||||||
|
# if __debug__:
|
||||||
|
# print "c_main.py: on_remove_thumb1_activate(): error on",
|
||||||
|
# print "getting selected items or removing thumbnails"
|
||||||
|
# return
|
||||||
|
self.__tag_cloud()
|
||||||
|
self.model.unsaved_project = True
|
||||||
|
self.__set_title(filepath=self.model.filename, modified=True)
|
||||||
|
self.__get_item_info(id)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
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. Generate only thumbnails.")
|
toggle = gtk.CheckButton("Don't copy images. \
|
||||||
|
Generate only thumbnails.")
|
||||||
toggle.show()
|
toggle.show()
|
||||||
dialog.dialog.set_extra_widget(toggle)
|
dialog.dialog.set_extra_widget(toggle)
|
||||||
|
|
||||||
@@ -613,8 +764,7 @@ class MainController(Controller):
|
|||||||
fid = model.get_value(model.get_iter(path), 0)
|
fid = model.get_value(model.get_iter(path), 0)
|
||||||
|
|
||||||
if self.model.get_source(path) == self.model.CD:
|
if self.model.get_source(path) == self.model.CD:
|
||||||
self.__add_cd(label, fid)
|
self.on_add_cd_activate(widget, label, fid)
|
||||||
|
|
||||||
elif self.model.get_source(path) == self.model.DR:
|
elif self.model.get_source(path) == self.model.DR:
|
||||||
self.__add_directory(filepath, label, fid)
|
self.__add_directory(filepath, label, fid)
|
||||||
|
|
||||||
@@ -672,8 +822,9 @@ class MainController(Controller):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if self.model.config.confd['delwarn']:
|
if self.model.config.confd['delwarn']:
|
||||||
obj = Dialogs.Qst('Delete elements', 'Delete items?',
|
obj = Dialogs.Qst("Delete elements", "Delete items?",
|
||||||
'Items will be permanently removed from catalog.')
|
"Items will be permanently \
|
||||||
|
removed from catalog.")
|
||||||
if not obj.run():
|
if not obj.run():
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -699,7 +850,8 @@ class MainController(Controller):
|
|||||||
model, list_of_paths = selection.get_selected_rows()
|
model, list_of_paths = selection.get_selected_rows()
|
||||||
if not list_of_paths:
|
if not list_of_paths:
|
||||||
list_of_paths = [1]
|
list_of_paths = [1]
|
||||||
self.model.get_root_entries(model.get_value(model.get_iter(list_of_paths[0]),0))
|
iter = model.get_iter(list_of_paths[0])
|
||||||
|
self.model.get_root_entries(model.get_value(iter,0))
|
||||||
except TypeError:
|
except TypeError:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -716,7 +868,8 @@ class MainController(Controller):
|
|||||||
def on_th_delete_activate(self, menu_item):
|
def on_th_delete_activate(self, menu_item):
|
||||||
if self.model.config.confd['delwarn']:
|
if self.model.config.confd['delwarn']:
|
||||||
obj = Dialogs.Qst('Delete thumbnail', 'Delete thumbnail?',
|
obj = Dialogs.Qst('Delete thumbnail', 'Delete thumbnail?',
|
||||||
'Current thumbnail will be permanently removed from catalog.')
|
"Current thumbnail will be permanently removed\
|
||||||
|
from catalog.")
|
||||||
if not obj.run():
|
if not obj.run():
|
||||||
return
|
return
|
||||||
path, column = self.view['files'].get_cursor()
|
path, column = self.view['files'].get_cursor()
|
||||||
@@ -785,73 +938,6 @@ class MainController(Controller):
|
|||||||
|
|
||||||
#########################
|
#########################
|
||||||
# private class functions
|
# private class functions
|
||||||
def __open(self, path=None):
|
|
||||||
"""Open catalog file"""
|
|
||||||
confirm = self.model.config.confd['confirmabandon']
|
|
||||||
if self.model.unsaved_project and confirm:
|
|
||||||
obj = Dialogs.Qst('Unsaved data - pyGTKtalog','There is not saved database','Pressing "Ok" will abandon catalog.')
|
|
||||||
if not obj.run():
|
|
||||||
return
|
|
||||||
|
|
||||||
if not path:
|
|
||||||
path = Dialogs.LoadDBFile().run()
|
|
||||||
|
|
||||||
if path:
|
|
||||||
if not self.model.open(path):
|
|
||||||
Dialogs.Err("Error opening file - pyGTKtalog","Cannot open \
|
|
||||||
file %s." % path)
|
|
||||||
else:
|
|
||||||
self.__generate_recent_menu()
|
|
||||||
self.__activate_ui(path)
|
|
||||||
return
|
|
||||||
|
|
||||||
def __save(self):
|
|
||||||
"""Save catalog to file"""
|
|
||||||
if self.model.filename:
|
|
||||||
self.model.save()
|
|
||||||
self.__set_title(filepath=self.model.filename)
|
|
||||||
else:
|
|
||||||
self.__save_as()
|
|
||||||
pass
|
|
||||||
|
|
||||||
def __save_as(self):
|
|
||||||
"""Save database to file under different filename."""
|
|
||||||
path = Dialogs.ChooseDBFilename().run()
|
|
||||||
if path:
|
|
||||||
ret, err = self.model.save(path)
|
|
||||||
if ret:
|
|
||||||
self.model.config.add_recent(path)
|
|
||||||
self.__set_title(filepath=path)
|
|
||||||
else:
|
|
||||||
Dialogs.Err("Error writing file - pyGTKtalog","Cannot write \
|
|
||||||
file %s." % path, "%s" % err)
|
|
||||||
|
|
||||||
def __add_cd(self, label=None, current_id=None):
|
|
||||||
"""Add directory structure from cd/dvd disc"""
|
|
||||||
mount = deviceHelper.volmount(self.model.config.confd['cd'])
|
|
||||||
if mount == 'ok':
|
|
||||||
guessed_label = deviceHelper.volname(self.model.config.confd['cd'])
|
|
||||||
if not label:
|
|
||||||
label = Dialogs.InputDiskLabel(guessed_label).run()
|
|
||||||
if label != None:
|
|
||||||
self.scan_cd = True
|
|
||||||
for widget in self.widgets_all:
|
|
||||||
self.view[widget].set_sensitive(False)
|
|
||||||
self.model.source = self.model.CD
|
|
||||||
self.model.scan(self.model.config.confd['cd'], label,
|
|
||||||
current_id)
|
|
||||||
self.model.unsaved_project = True
|
|
||||||
self.__set_title(filepath=self.model.filename, modified=True)
|
|
||||||
else:
|
|
||||||
deviceHelper.volumount(self.model.config.confd['cd'])
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
Dialogs.Wrn("Error mounting device - pyGTKtalog",
|
|
||||||
"Cannot mount device pointed to %s" %
|
|
||||||
self.model.config.confd['cd'],
|
|
||||||
"Last mount message:\n%s" % mount)
|
|
||||||
return False
|
|
||||||
|
|
||||||
def __add_directory(self, path=None, label=None, current_id=None):
|
def __add_directory(self, path=None, label=None, current_id=None):
|
||||||
if not label or not path:
|
if not label or not path:
|
||||||
res = Dialogs.PointDirectoryToAdd().run()
|
res = Dialogs.PointDirectoryToAdd().run()
|
||||||
@@ -868,30 +954,13 @@ class MainController(Controller):
|
|||||||
self.__set_title(filepath=self.model.filename, modified=True)
|
self.__set_title(filepath=self.model.filename, modified=True)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def __do_quit(self):
|
|
||||||
"""Quit and save window parameters to config file"""
|
|
||||||
# check if any unsaved project is on go.
|
|
||||||
if self.model.unsaved_project and \
|
|
||||||
self.model.config.confd['confirmquit']:
|
|
||||||
if not Dialogs.Qst('Quit application - pyGTKtalog',
|
|
||||||
'Do you really want to quit?',
|
|
||||||
"Current database is not saved, any changes will be lost.").run():
|
|
||||||
return
|
|
||||||
|
|
||||||
self.__store_settings()
|
|
||||||
self.model.cleanup()
|
|
||||||
gtk.main_quit()
|
|
||||||
return False
|
|
||||||
|
|
||||||
def __new_db(self):
|
def __new_db(self):
|
||||||
|
|
||||||
self.__tag_cloud()
|
|
||||||
|
|
||||||
"""Create new database file"""
|
"""Create new database file"""
|
||||||
if self.model.unsaved_project:
|
if self.model.unsaved_project:
|
||||||
if not Dialogs.Qst('Unsaved data - pyGTKtalog',
|
if not Dialogs.Qst('Unsaved data - pyGTKtalog',
|
||||||
"Current database isn't saved",
|
"Current database isn't saved",
|
||||||
'All changes will be lost. Do you really want to abandon it?').run():
|
"All changes will be lost. Do you really \
|
||||||
|
want to abandon it?").run():
|
||||||
return
|
return
|
||||||
self.model.new()
|
self.model.new()
|
||||||
|
|
||||||
@@ -931,9 +1000,10 @@ class MainController(Controller):
|
|||||||
|
|
||||||
def __setup_files_treeview(self):
|
def __setup_files_treeview(self):
|
||||||
"""Setup TreeView files widget, as columned list."""
|
"""Setup TreeView files widget, as columned list."""
|
||||||
self.view['files'].set_model(self.model.files_list)
|
v = self.view['files']
|
||||||
|
v.set_model(self.model.files_list)
|
||||||
|
|
||||||
self.view['files'].get_selection().set_mode(gtk.SELECTION_MULTIPLE)
|
v.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
|
||||||
|
|
||||||
c = gtk.TreeViewColumn('Filename')
|
c = gtk.TreeViewColumn('Filename')
|
||||||
cellpb = gtk.CellRendererPixbuf()
|
cellpb = gtk.CellRendererPixbuf()
|
||||||
@@ -956,6 +1026,12 @@ class MainController(Controller):
|
|||||||
c.set_sort_column_id(3)
|
c.set_sort_column_id(3)
|
||||||
c.set_resizable(True)
|
c.set_resizable(True)
|
||||||
self.view['files'].append_column(c)
|
self.view['files'].append_column(c)
|
||||||
|
|
||||||
|
#v.enable_model_drag_source(gtk.gdk.BUTTON1_MASK,
|
||||||
|
# self.DND_TARGETS,
|
||||||
|
# gtk.gdk.ACTION_DEFAULT)
|
||||||
|
v.drag_source_set(gtk.gdk.BUTTON1_MASK, self.DND_TARGETS,
|
||||||
|
gtk.gdk.ACTION_COPY)
|
||||||
return
|
return
|
||||||
|
|
||||||
def __setup_exif_treeview(self):
|
def __setup_exif_treeview(self):
|
||||||
@@ -1040,7 +1116,7 @@ class MainController(Controller):
|
|||||||
set = self.model.get_file_info(item)
|
set = self.model.get_file_info(item)
|
||||||
buf = gtk.TextBuffer()
|
buf = gtk.TextBuffer()
|
||||||
|
|
||||||
if __debug__ and set.has_key('debug'):
|
if __debug__ and 'debug' in set:
|
||||||
tag = buf.create_tag()
|
tag = buf.create_tag()
|
||||||
tag.set_property('weight', pango.WEIGHT_BOLD)
|
tag.set_property('weight', pango.WEIGHT_BOLD)
|
||||||
buf.insert_with_tags(buf.get_end_iter(), "ID: ", tag)
|
buf.insert_with_tags(buf.get_end_iter(), "ID: ", tag)
|
||||||
@@ -1054,7 +1130,7 @@ class MainController(Controller):
|
|||||||
buf.insert_with_tags(buf.get_end_iter(), "Type: ", tag)
|
buf.insert_with_tags(buf.get_end_iter(), "Type: ", tag)
|
||||||
buf.insert(buf.get_end_iter(), str(set['debug']['type']) + "\n\n")
|
buf.insert(buf.get_end_iter(), str(set['debug']['type']) + "\n\n")
|
||||||
|
|
||||||
if set.has_key('gthumb'):
|
if 'gthumb' in set:
|
||||||
tag = buf.create_tag()
|
tag = buf.create_tag()
|
||||||
tag.set_property('weight', pango.WEIGHT_BOLD)
|
tag.set_property('weight', pango.WEIGHT_BOLD)
|
||||||
buf.insert_with_tags(buf.get_end_iter(), "gThumb comment:\n", tag)
|
buf.insert_with_tags(buf.get_end_iter(), "gThumb comment:\n", tag)
|
||||||
@@ -1066,14 +1142,14 @@ class MainController(Controller):
|
|||||||
buf.insert(buf.get_end_iter(), set['gthumb']['date'] + "\n")
|
buf.insert(buf.get_end_iter(), set['gthumb']['date'] + "\n")
|
||||||
buf.insert(buf.get_end_iter(), "\n")
|
buf.insert(buf.get_end_iter(), "\n")
|
||||||
|
|
||||||
if set.has_key('description'):
|
if 'description' in set:
|
||||||
tag = buf.create_tag()
|
tag = buf.create_tag()
|
||||||
tag.set_property('weight', pango.WEIGHT_BOLD)
|
tag.set_property('weight', pango.WEIGHT_BOLD)
|
||||||
buf.insert_with_tags(buf.get_end_iter(), "Details:\n", tag)
|
buf.insert_with_tags(buf.get_end_iter(), "Details:\n", tag)
|
||||||
buf.insert(buf.get_end_iter(), set['description'])
|
buf.insert(buf.get_end_iter(), set['description'])
|
||||||
buf.insert(buf.get_end_iter(), "\n")
|
buf.insert(buf.get_end_iter(), "\n")
|
||||||
|
|
||||||
if set.has_key('note'):
|
if 'note' in set:
|
||||||
tag = buf.create_tag()
|
tag = buf.create_tag()
|
||||||
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)
|
||||||
@@ -1081,42 +1157,43 @@ class MainController(Controller):
|
|||||||
|
|
||||||
self.view['description'].set_buffer(buf)
|
self.view['description'].set_buffer(buf)
|
||||||
|
|
||||||
if set.has_key('images'):
|
if 'images' in set:
|
||||||
self.__setup_iconview()
|
self.__setup_iconview()
|
||||||
self.view['img_container'].show()
|
self.view['img_container'].show()
|
||||||
else:
|
else:
|
||||||
self.view['img_container'].hide()
|
self.view['img_container'].hide()
|
||||||
|
|
||||||
if set.has_key('exif'):
|
if 'exif' in set:
|
||||||
self.view['exif_tree'].set_model(self.model.exif_list)
|
self.view['exif_tree'].set_model(self.model.exif_list)
|
||||||
self.view['exifinfo'].show()
|
self.view['exifinfo'].show()
|
||||||
else:
|
else:
|
||||||
self.view['exifinfo'].hide()
|
self.view['exifinfo'].hide()
|
||||||
|
|
||||||
if set.has_key('thumbnail'):
|
if 'thumbnail' in set:
|
||||||
self.view['thumb'].set_from_file(set['thumbnail'])
|
self.view['thumb'].set_from_file(set['thumbnail'])
|
||||||
self.view['thumb_box'].show()
|
self.view['thumb_box'].show()
|
||||||
#self.view['thumb'].show()
|
|
||||||
else:
|
else:
|
||||||
#self.view['thumb'].hide()
|
|
||||||
self.view['thumb_box'].hide()
|
self.view['thumb_box'].hide()
|
||||||
return
|
return
|
||||||
|
'''
|
||||||
|
def on_tag_cloud_textview_drag_motion(self, widget, context, x, y, time):
|
||||||
|
context.drag_status(gtk.gdk.ACTION_COPY, time)
|
||||||
|
print "motion", x, y
|
||||||
|
'''
|
||||||
def __tag_cloud(self):
|
def __tag_cloud(self):
|
||||||
"""generate tag cloud"""
|
"""generate tag cloud"""
|
||||||
# TODO: checkit!
|
# TODO: checkit!
|
||||||
|
v = self.view['tag_cloud_textview']
|
||||||
def tag_cloud_click(tag, textview, event, iter, e):
|
def tag_cloud_click(tag, textview, event, iter, e):
|
||||||
"""react on click on connected tag items"""
|
"""react on click on connected tag items"""
|
||||||
if event.type == gtk.gdk.BUTTON_RELEASE:
|
if event.type == gtk.gdk.BUTTON_RELEASE:
|
||||||
print tag.get_property('name')
|
print tag.get_property('name')
|
||||||
elif event.type == gtk.gdk.MOTION_NOTIFY:
|
elif event.type == gtk.gdk.MOTION_NOTIFY:
|
||||||
w = \
|
w = v.get_window(gtk.TEXT_WINDOW_TEXT)
|
||||||
self.view['tag_cloud_textview'].get_window(gtk.TEXT_WINDOW_TEXT)
|
|
||||||
if w:
|
if w:
|
||||||
w.set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND2))
|
w.set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND2))
|
||||||
else:
|
else:
|
||||||
w = \
|
w = v.get_window(gtk.TEXT_WINDOW_TEXT)
|
||||||
self.view['tag_cloud_textview'].get_window(gtk.TEXT_WINDOW_TEXT)
|
|
||||||
if w:
|
if w:
|
||||||
w.set_cursor(None)
|
w.set_cursor(None)
|
||||||
|
|
||||||
@@ -1129,18 +1206,22 @@ class MainController(Controller):
|
|||||||
return iter
|
return iter
|
||||||
|
|
||||||
if len(self.model.tag_cloud) > 0:
|
if len(self.model.tag_cloud) > 0:
|
||||||
buff = gtk.TextBuffer()
|
buff = v.get_buffer()
|
||||||
|
buff.set_text('')
|
||||||
for cloud in self.model.tag_cloud:
|
for cloud in self.model.tag_cloud:
|
||||||
iter = insert_blank(buff, buff.get_end_iter())
|
iter = insert_blank(buff, buff.get_end_iter())
|
||||||
tag = buff.create_tag(cloud['id'])
|
tag = buff.create_tag(str(cloud['id']))
|
||||||
tag.set_property('size-points', cloud['size'])
|
tag.set_property('size-points', cloud['size'])
|
||||||
tag.set_property('foreground', cloud['color'])
|
tag.set_property('foreground', cloud['color'])
|
||||||
tag.connect('event', tag_cloud_click, tag)
|
tag.connect('event', tag_cloud_click, tag)
|
||||||
buff.insert_with_tags(iter, cloud['name'], tag)
|
buff.insert_with_tags(iter, cloud['name'], tag)
|
||||||
self.view['tag_cloud_textview'].set_buffer(buff)
|
v.set_buffer(buff)
|
||||||
|
|
||||||
def __show_stats(self, selected_id=None):
|
def __show_stats(self, selected_id=None):
|
||||||
data = self.model.get_stats(selected_id)
|
data = self.model.get_stats(selected_id)
|
||||||
label = Dialogs.StatsDialog(data).run()
|
label = Dialogs.StatsDialog(data).run()
|
||||||
|
|
||||||
|
def __find_tag_in_textview(self, widget, x, y):
|
||||||
|
pass
|
||||||
|
|
||||||
pass # end of class
|
pass # end of class
|
||||||
|
|||||||
@@ -24,11 +24,10 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import base64
|
|
||||||
import shutil
|
import shutil
|
||||||
import tarfile
|
import tarfile
|
||||||
import tempfile
|
|
||||||
import string
|
import string
|
||||||
|
import math
|
||||||
|
|
||||||
import gtk
|
import gtk
|
||||||
import gobject
|
import gobject
|
||||||
@@ -38,10 +37,7 @@ from gtkmvc.model_mt import ModelMT
|
|||||||
from pysqlite2 import dbapi2 as sqlite
|
from pysqlite2 import dbapi2 as sqlite
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
try:
|
|
||||||
import threading as _threading
|
import threading as _threading
|
||||||
except ImportError:
|
|
||||||
import dummy_threading as _threading
|
|
||||||
|
|
||||||
from m_config import ConfigModel
|
from m_config import ConfigModel
|
||||||
try:
|
try:
|
||||||
@@ -120,33 +116,91 @@ class MainModel(ModelMT):
|
|||||||
# - #rgb
|
# - #rgb
|
||||||
# - #rrggbb
|
# - #rrggbb
|
||||||
self.tag_cloud = []
|
self.tag_cloud = []
|
||||||
'''{'id': str(1), 'name': "bezpieczeństwo", 'size': 10, 'color': '#666'},
|
|
||||||
{'id': str(2), 'name': "bsd", 'size': 14, 'color': '#333'},
|
|
||||||
{'id': str(3), 'name': "debian", 'size': 18, 'color': '#333'},
|
|
||||||
{'id': str(4), 'name': "fedora", 'size': 12, 'color': '#666'},
|
|
||||||
{'id': str(5), 'name': "firefox", 'size': 40, 'color': '#666'},
|
|
||||||
{'id': str(6), 'name': "gnome", 'size': 26, 'color': '#333'},
|
|
||||||
{'id': str(7), 'name': "gentoo", 'size': 30, 'color': '#000'},
|
|
||||||
{'id': str(8), 'name': "kde", 'size': 20, 'color': '#333'},
|
|
||||||
{'id': str(9), 'name': "kernel", 'size': 10, 'color': '#666'},
|
|
||||||
{'id': str(10), 'name': "windows", 'size': 18, 'color': '#333'},
|
|
||||||
]'''
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def add_tags(self, id, tags):
|
||||||
|
for tag in tags.split(','):
|
||||||
|
tag = tag.strip()
|
||||||
|
|
||||||
|
# first, checkerap if we already have tag in tags table
|
||||||
|
sql = """SELECT id FROM tags WHERE tag = ?"""
|
||||||
|
self.db_cursor.execute(sql, (tag, ))
|
||||||
|
res = self.db_cursor.fetchone()
|
||||||
|
if not res:
|
||||||
|
# insert new tag
|
||||||
|
sql = """INSERT INTO tags(tag, group_id)
|
||||||
|
VALUES(?, ?)"""
|
||||||
|
self.db_cursor.execute(sql, (tag, 1))
|
||||||
|
self.db_connection.commit()
|
||||||
|
sql = """SELECT id FROM tags WHERE tag = ?"""
|
||||||
|
self.db_cursor.execute(sql, (tag, ))
|
||||||
|
res = self.db_cursor.fetchone()
|
||||||
|
|
||||||
|
tag_id = res[0]
|
||||||
|
|
||||||
|
# then checkout if file have already tag assigned
|
||||||
|
sql = """SELECT file_id FROM tags_files
|
||||||
|
WHERE file_id = ? AND tag_id = ?"""
|
||||||
|
self.db_cursor.execute(sql, (id, tag_id))
|
||||||
|
res = self.db_cursor.fetchone()
|
||||||
|
if not res:
|
||||||
|
sql = """INSERT INTO tags_files(file_id, tag_id)
|
||||||
|
VALUES(?, ?)"""
|
||||||
|
self.db_cursor.execute(sql, (id, tag_id))
|
||||||
|
self.db_connection.commit()
|
||||||
|
self.get_tags()
|
||||||
|
return
|
||||||
|
|
||||||
|
def get_tags(self):
|
||||||
|
sql = """SELECT COUNT(f.file_id), t.id, t.tag FROM tags_files f
|
||||||
|
LEFT JOIN tags t ON f.tag_id = t.id
|
||||||
|
GROUP BY f.tag_id
|
||||||
|
ORDER BY t.tag"""
|
||||||
|
self.db_cursor.execute(sql)
|
||||||
|
res = self.db_cursor.fetchall()
|
||||||
|
|
||||||
|
if len(res) > 0:
|
||||||
|
self.tag_cloud = []
|
||||||
|
for row in res:
|
||||||
|
self.tag_cloud.append({'id': row[1],
|
||||||
|
'name': row[2],
|
||||||
|
'size': row[0],
|
||||||
|
'color':'black'})
|
||||||
|
|
||||||
|
def tag_weight(x):
|
||||||
|
"""Calculate 'weight' of tag.
|
||||||
|
Tags can have sizes between 9 to ~40. Upper size is calculated with
|
||||||
|
logarythm and can take in extereme situation around value 55 like
|
||||||
|
for 1 milion tags."""
|
||||||
|
if x==None or x==0:
|
||||||
|
x = 1
|
||||||
|
return 4 * math.log(x, math.e)
|
||||||
|
|
||||||
|
# correct font sizes with tag_weight function.
|
||||||
|
count = 0
|
||||||
|
for ta in self.tag_cloud:
|
||||||
|
tmp = int(tag_weight(ta['size']))
|
||||||
|
if tmp == 0:
|
||||||
|
tmp = 1
|
||||||
|
self.tag_cloud[count]['size'] = tmp + 8
|
||||||
|
count+=1
|
||||||
|
|
||||||
def add_image(self, image, id, only_thumbs=False):
|
def add_image(self, image, 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)
|
||||||
values(?, null, null)"""
|
VALUES(?, null, null)"""
|
||||||
self.db_cursor.execute(sql, (id,))
|
self.db_cursor.execute(sql, (id,))
|
||||||
self.db_connection.commit()
|
self.db_connection.commit()
|
||||||
|
|
||||||
sql = """select id from images where thumbnail is null and filename is null and file_id=?"""
|
sql = """SELECT id FROM images WHERE thumbnail is null
|
||||||
|
AND filename IS null AND file_id=?"""
|
||||||
self.db_cursor.execute(sql, (id,))
|
self.db_cursor.execute(sql, (id,))
|
||||||
res = self.db_cursor.fetchone()
|
res = self.db_cursor.fetchone()
|
||||||
if res:
|
if res:
|
||||||
tp, ip, rc = Img(image, self.internal_dirname).save(res[0])
|
tp, ip, rc = Img(image, self.internal_dirname).save(res[0])
|
||||||
if rc != -1:
|
if rc != -1:
|
||||||
sql = """update images set filename=?, thumbnail=? where id=?"""
|
sql = """UPDATE images SET filename=?,
|
||||||
|
thumbnail=? WHERE id=?"""
|
||||||
if only_thumbs:
|
if only_thumbs:
|
||||||
img = None
|
img = None
|
||||||
else:
|
else:
|
||||||
@@ -160,7 +214,7 @@ class MainModel(ModelMT):
|
|||||||
def del_images(self, id):
|
def del_images(self, id):
|
||||||
"""removes images and their thumbnails from selected file/dir"""
|
"""removes images and their thumbnails from selected file/dir"""
|
||||||
# remove images
|
# remove images
|
||||||
sql = """select filename, thumbnail from images where file_id =?"""
|
sql = """SELECT filename, thumbnail FROM images WHERE file_id =?"""
|
||||||
self.db_cursor.execute(sql, (id,))
|
self.db_cursor.execute(sql, (id,))
|
||||||
res = self.db_cursor.fetchall()
|
res = self.db_cursor.fetchall()
|
||||||
if len(res) > 0:
|
if len(res) > 0:
|
||||||
@@ -170,13 +224,13 @@ class MainModel(ModelMT):
|
|||||||
os.unlink(os.path.join(self.internal_dirname, fn[1]))
|
os.unlink(os.path.join(self.internal_dirname, fn[1]))
|
||||||
|
|
||||||
# remove images records
|
# remove images records
|
||||||
sql = """delete from images where file_id = ?"""
|
sql = """DELETE FROM images WHERE file_id = ?"""
|
||||||
self.db_cursor.execute(sql, (id,))
|
self.db_cursor.execute(sql, (id,))
|
||||||
self.db_connection.commit()
|
self.db_connection.commit()
|
||||||
|
|
||||||
def delete_image(self, id):
|
def delete_image(self, 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=?"""
|
||||||
self.db_cursor.execute(sql, (id,))
|
self.db_cursor.execute(sql, (id,))
|
||||||
res = self.db_cursor.fetchone()
|
res = self.db_cursor.fetchone()
|
||||||
if res:
|
if res:
|
||||||
@@ -189,7 +243,7 @@ class MainModel(ModelMT):
|
|||||||
print res[0]
|
print res[0]
|
||||||
print res[1]
|
print res[1]
|
||||||
# remove images records
|
# remove images records
|
||||||
sql = """delete from images where id = ?"""
|
sql = """DELETE FROM images WHERE id = ?"""
|
||||||
self.db_cursor.execute(sql, (id,))
|
self.db_cursor.execute(sql, (id,))
|
||||||
self.db_connection.commit()
|
self.db_connection.commit()
|
||||||
|
|
||||||
@@ -200,7 +254,8 @@ class MainModel(ModelMT):
|
|||||||
p, e, ret_code = Thumbnail(img_fn,
|
p, e, ret_code = Thumbnail(img_fn,
|
||||||
base=self.internal_dirname).save(id)
|
base=self.internal_dirname).save(id)
|
||||||
if ret_code != -1:
|
if ret_code != -1:
|
||||||
sql = """insert into thumbnails(file_id, filename) values (?, ?)"""
|
sql = """insert into thumbnails(file_id, filename)
|
||||||
|
values (?, ?)"""
|
||||||
self.db_cursor.execute(sql,
|
self.db_cursor.execute(sql,
|
||||||
(id,
|
(id,
|
||||||
p.split(self.internal_dirname)[1][1:]))
|
p.split(self.internal_dirname)[1][1:]))
|
||||||
@@ -212,14 +267,14 @@ class MainModel(ModelMT):
|
|||||||
"""removes thumbnail from selected file/dir"""
|
"""removes thumbnail from selected file/dir"""
|
||||||
|
|
||||||
# remove thumbnail files
|
# remove thumbnail files
|
||||||
sql = """select filename from thumbnails where file_id=?"""
|
sql = """SELECT filename FROM thumbnails WHERE file_id=?"""
|
||||||
self.db_cursor.execute(sql, (id,))
|
self.db_cursor.execute(sql, (id,))
|
||||||
res = self.db_cursor.fetchone()
|
res = self.db_cursor.fetchone()
|
||||||
if res:
|
if res:
|
||||||
os.unlink(os.path.join(self.internal_dirname, res[0]))
|
os.unlink(os.path.join(self.internal_dirname, res[0]))
|
||||||
|
|
||||||
# remove thumbs records
|
# remove thumbs records
|
||||||
sql = """delete from thumbnails where file_id=?"""
|
sql = """DELETE FROM thumbnails WHERE file_id=?"""
|
||||||
self.db_cursor.execute(sql, (id,))
|
self.db_cursor.execute(sql, (id,))
|
||||||
self.db_connection.commit()
|
self.db_connection.commit()
|
||||||
|
|
||||||
@@ -276,7 +331,8 @@ class MainModel(ModelMT):
|
|||||||
try:
|
try:
|
||||||
tar.extractall()
|
tar.extractall()
|
||||||
if __debug__:
|
if __debug__:
|
||||||
print "m_main.py: extracted tarfile into", self.internal_dirname
|
print "m_main.py: extracted tarfile into",
|
||||||
|
print self.internal_dirname
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# python's 2.4 tarfile module lacks of method extractall()
|
# python's 2.4 tarfile module lacks of method extractall()
|
||||||
directories = []
|
directories = []
|
||||||
@@ -305,14 +361,15 @@ class MainModel(ModelMT):
|
|||||||
os.chmod(tarinfo, '.')
|
os.chmod(tarinfo, '.')
|
||||||
except:
|
except:
|
||||||
if __debug__:
|
if __debug__:
|
||||||
print "m_main.py: open(): setting corrext owner, mtime etc"
|
print "m_main.py: open(): setting corrext owner,",
|
||||||
|
print "mtime etc"
|
||||||
pass
|
pass
|
||||||
tar.close()
|
tar.close()
|
||||||
|
|
||||||
self.__connect_to_db()
|
self.__connect_to_db()
|
||||||
self.__fetch_db_into_treestore()
|
self.__fetch_db_into_treestore()
|
||||||
self.config.add_recent(filename)
|
self.config.add_recent(filename)
|
||||||
|
self.get_tags()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def scan(self, path, label, currentid):
|
def scan(self, path, label, currentid):
|
||||||
@@ -334,7 +391,7 @@ class MainModel(ModelMT):
|
|||||||
def rename(self, id, new_name=None):
|
def rename(self, id, new_name=None):
|
||||||
if new_name:
|
if new_name:
|
||||||
self.db_cursor.execute("update files set filename=? \
|
self.db_cursor.execute("update files set filename=? \
|
||||||
where id=?", (new_name, id))
|
WHERE id=?", (new_name, id))
|
||||||
self.db_connection.commit()
|
self.db_connection.commit()
|
||||||
self.__fetch_db_into_treestore()
|
self.__fetch_db_into_treestore()
|
||||||
self.unsaved_project = True
|
self.unsaved_project = True
|
||||||
@@ -354,9 +411,10 @@ class MainModel(ModelMT):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
# directories first
|
# directories first
|
||||||
self.db_cursor.execute("SELECT id, filename, size, date FROM files \
|
sql = """SELECT id, filename, size, date FROM files
|
||||||
WHERE parent_id=? AND type=1 \
|
WHERE parent_id=? AND type=1
|
||||||
ORDER BY filename", (id,))
|
ORDER BY filename"""
|
||||||
|
self.db_cursor.execute(sql, (id,))
|
||||||
data = self.db_cursor.fetchall()
|
data = self.db_cursor.fetchall()
|
||||||
for ch in data:
|
for ch in data:
|
||||||
myiter = self.files_list.insert_before(None, None)
|
myiter = self.files_list.insert_before(None, None)
|
||||||
@@ -370,10 +428,11 @@ class MainModel(ModelMT):
|
|||||||
self.files_list.set_value(myiter, 6, gtk.STOCK_DIRECTORY)
|
self.files_list.set_value(myiter, 6, gtk.STOCK_DIRECTORY)
|
||||||
|
|
||||||
# all the rest
|
# all the rest
|
||||||
self.db_cursor.execute("SELECT f.id, f.filename, f.size, f.date, \
|
sql = """SELECT f.id, f.filename, f.size, f.date, f.type
|
||||||
f.type FROM files f \
|
FROM files f
|
||||||
WHERE f.parent_id=? AND f.type!=1 \
|
WHERE f.parent_id=? AND f.type!=1
|
||||||
ORDER BY f.filename", (id,))
|
ORDER BY f.filename"""
|
||||||
|
self.db_cursor.execute(sql, (id,))
|
||||||
data = self.db_cursor.fetchall()
|
data = self.db_cursor.fetchall()
|
||||||
for ch in data:
|
for ch in data:
|
||||||
myiter = self.files_list.insert_before(None, None)
|
myiter = self.files_list.insert_before(None, None)
|
||||||
@@ -391,7 +450,8 @@ class MainModel(ModelMT):
|
|||||||
|
|
||||||
def get_parent_discs_value(self, child_id):
|
def get_parent_discs_value(self, child_id):
|
||||||
if child_id:
|
if child_id:
|
||||||
self.db_cursor.execute("SELECT parent_id FROM files where id=?", (child_id,))
|
sql = """SELECT parent_id FROM files WHERE id=?"""
|
||||||
|
self.db_cursor.execute(sql, (child_id,))
|
||||||
set = self.db_cursor.fetchone()
|
set = self.db_cursor.fetchone()
|
||||||
if set:
|
if set:
|
||||||
return set[0]
|
return set[0]
|
||||||
@@ -421,9 +481,11 @@ class MainModel(ModelMT):
|
|||||||
retval['note'] = set[6]
|
retval['note'] = set[6]
|
||||||
|
|
||||||
if set[4]:
|
if set[4]:
|
||||||
retval['thumbnail'] = os.path.join(self.internal_dirname, set[4])
|
pa = os.path.join(self.internal_dirname, set[4])
|
||||||
|
retval['thumbnail'] = pa
|
||||||
|
|
||||||
sql = """SELECT id, filename, thumbnail from images WHERE file_id = ?"""
|
sql = """SELECT id, filename, thumbnail FROM images
|
||||||
|
WHERE file_id = ?"""
|
||||||
self.db_cursor.execute(sql, (id,))
|
self.db_cursor.execute(sql, (id,))
|
||||||
set = self.db_cursor.fetchall()
|
set = self.db_cursor.fetchall()
|
||||||
if set:
|
if set:
|
||||||
@@ -437,7 +499,7 @@ class MainModel(ModelMT):
|
|||||||
sql = """SELECT camera, date, aperture, exposure_program,
|
sql = """SELECT camera, date, aperture, exposure_program,
|
||||||
exposure_bias, iso, focal_length, subject_distance, metering_mode,
|
exposure_bias, iso, focal_length, subject_distance, metering_mode,
|
||||||
flash, light_source, resolution, orientation
|
flash, light_source, resolution, orientation
|
||||||
from exif
|
FROM exif
|
||||||
WHERE file_id = ?"""
|
WHERE file_id = ?"""
|
||||||
self.db_cursor.execute(sql, (id,))
|
self.db_cursor.execute(sql, (id,))
|
||||||
set = self.db_cursor.fetchone()
|
set = self.db_cursor.fetchone()
|
||||||
@@ -452,20 +514,22 @@ class MainModel(ModelMT):
|
|||||||
retval['exif'] = True
|
retval['exif'] = True
|
||||||
|
|
||||||
# gthumb
|
# gthumb
|
||||||
sql = """SELECT note, place, date from gthumb WHERE file_id = ?"""
|
sql = """SELECT note, place, date FROM gthumb WHERE file_id = ?"""
|
||||||
self.db_cursor.execute(sql, (id,))
|
self.db_cursor.execute(sql, (id,))
|
||||||
set = self.db_cursor.fetchone()
|
set = self.db_cursor.fetchone()
|
||||||
|
|
||||||
if set:
|
if set:
|
||||||
retval['gthumb'] = {'note': set[0], 'place': set[1], 'date': set[2]}
|
retval['gthumb'] = {'note': set[0],
|
||||||
|
'place': set[1],
|
||||||
|
'date': set[2]}
|
||||||
|
|
||||||
return retval
|
return retval
|
||||||
|
|
||||||
def get_source(self, path):
|
def get_source(self, path):
|
||||||
"""get source of top level directory"""
|
"""get source of top level directory"""
|
||||||
bid = self.discs_tree.get_value(self.discs_tree.get_iter(path[0]),
|
bid = self.discs_tree.get_value(self.discs_tree.get_iter(path[0]), 0)
|
||||||
0)
|
sql = """SELECT source FROM files WHERE id = ?"""
|
||||||
self.db_cursor.execute("select source from files where id = ?",
|
self.db_cursor.execute(sql,
|
||||||
(bid,))
|
(bid,))
|
||||||
res = self.db_cursor.fetchone()
|
res = self.db_cursor.fetchone()
|
||||||
if res == None:
|
if res == None:
|
||||||
@@ -474,10 +538,10 @@ class MainModel(ModelMT):
|
|||||||
|
|
||||||
def get_label_and_filepath(self, path):
|
def get_label_and_filepath(self, path):
|
||||||
"""get source of top level directory"""
|
"""get source of top level directory"""
|
||||||
bid = self.discs_tree.get_value(self.discs_tree.get_iter(path),
|
bid = self.discs_tree.get_value(self.discs_tree.get_iter(path), 0)
|
||||||
0)
|
sql = """SELECT filepath, filename FROM files
|
||||||
self.db_cursor.execute("select filepath, filename from files \
|
WHERE id = ? AND parent_id = 1"""
|
||||||
where id = ? and parent_id = 1", (bid,))
|
self.db_cursor.execute(sql, (bid,))
|
||||||
res = self.db_cursor.fetchone()
|
res = self.db_cursor.fetchone()
|
||||||
if res == None:
|
if res == None:
|
||||||
return None, None
|
return None, None
|
||||||
@@ -496,14 +560,14 @@ class MainModel(ModelMT):
|
|||||||
if not db_connection:
|
if not db_connection:
|
||||||
db_connection = self.db_connection
|
db_connection = self.db_connection
|
||||||
|
|
||||||
sql = """select parent_id from files where id = ?"""
|
sql = """SELECT parent_id FROM files WHERE id = ?"""
|
||||||
db_cursor.execute(sql, (root_id,))
|
db_cursor.execute(sql, (root_id,))
|
||||||
res = db_cursor.fetchone()
|
res = db_cursor.fetchone()
|
||||||
parent_id = res[0]
|
parent_id = res[0]
|
||||||
|
|
||||||
def get_children(fid):
|
def get_children(fid):
|
||||||
fids.append(fid)
|
fids.append(fid)
|
||||||
sql = """select id from files where parent_id = ?"""
|
sql = """SELECT id FROM files where parent_id = ?"""
|
||||||
db_cursor.execute(sql, (fid,))
|
db_cursor.execute(sql, (fid,))
|
||||||
res = db_cursor.fetchall()
|
res = db_cursor.fetchall()
|
||||||
if len(res)>0:
|
if len(res)>0:
|
||||||
@@ -517,17 +581,17 @@ class MainModel(ModelMT):
|
|||||||
yield (c,)
|
yield (c,)
|
||||||
|
|
||||||
# remove files records
|
# remove files records
|
||||||
sql = """delete from files where id = ?"""
|
sql = """DELETE FROM files WHERE id = ?"""
|
||||||
db_cursor.executemany(sql, generator())
|
db_cursor.executemany(sql, generator())
|
||||||
|
|
||||||
# remove tags records
|
# remove tags records
|
||||||
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 = self.__list_to_string(fids)
|
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
|
||||||
db_cursor.execute(sql)
|
db_cursor.execute(sql)
|
||||||
res = db_cursor.fetchall()
|
res = db_cursor.fetchall()
|
||||||
if len(res) > 0:
|
if len(res) > 0:
|
||||||
@@ -535,7 +599,8 @@ class MainModel(ModelMT):
|
|||||||
os.unlink(os.path.join(self.internal_dirname, fn[0]))
|
os.unlink(os.path.join(self.internal_dirname, fn[0]))
|
||||||
|
|
||||||
# remove images
|
# remove images
|
||||||
sql = """select filename, thumbnail from images where file_id in (%s)""" % arg
|
sql = """SELECT filename, thumbnail FROM images
|
||||||
|
WHERE file_id IN %s""" % arg
|
||||||
db_cursor.execute(sql)
|
db_cursor.execute(sql)
|
||||||
res = db_cursor.fetchall()
|
res = db_cursor.fetchall()
|
||||||
if len(res) > 0:
|
if len(res) > 0:
|
||||||
@@ -545,32 +610,34 @@ class MainModel(ModelMT):
|
|||||||
os.unlink(os.path.join(self.internal_dirname, fn[1]))
|
os.unlink(os.path.join(self.internal_dirname, fn[1]))
|
||||||
|
|
||||||
# remove thumbs records
|
# remove thumbs records
|
||||||
sql = """delete from thumbnails where file_id = ?"""
|
sql = """DELETE FROM thumbnails WHERE file_id = ?"""
|
||||||
db_cursor.executemany(sql, generator())
|
db_cursor.executemany(sql, generator())
|
||||||
|
|
||||||
# remove images records
|
# remove images records
|
||||||
sql = """delete from images where file_id = ?"""
|
sql = """DELETE FROM images WHERE file_id = ?"""
|
||||||
db_cursor.executemany(sql, generator())
|
db_cursor.executemany(sql, generator())
|
||||||
|
|
||||||
# remove gthumb info
|
# remove gthumb info
|
||||||
sql = """delete from gthumb where file_id = ?"""
|
sql = """DELETE FROM gthumb WHERE file_id = ?"""
|
||||||
db_cursor.executemany(sql, generator())
|
db_cursor.executemany(sql, generator())
|
||||||
|
|
||||||
# correct parent direcotry sizes
|
# correct parent direcotry sizes
|
||||||
# get size and parent of deleting object
|
# get size and parent of deleting object
|
||||||
while parent_id:
|
while parent_id:
|
||||||
sql = """update files set size =
|
sql = """UPDATE files SET size =
|
||||||
(select case when
|
(SELECT CASE WHEN
|
||||||
sum(size) is null
|
SUM(size) IS null
|
||||||
then
|
THEN
|
||||||
0
|
0
|
||||||
else
|
ELSE
|
||||||
sum(size)
|
SUM(size)
|
||||||
end
|
END
|
||||||
from files where parent_id=?) where id=?"""
|
FROM files WHERE parent_id=?)
|
||||||
|
WHERE id=?"""
|
||||||
db_cursor.execute(sql, (parent_id, parent_id))
|
db_cursor.execute(sql, (parent_id, parent_id))
|
||||||
|
|
||||||
sql = """select parent_id from files where id = ? and parent_id!=id"""
|
sql = """SELECT parent_id FROM files
|
||||||
|
WHERE id = ? AND parent_id!=id"""
|
||||||
db_cursor.execute(sql, (parent_id,))
|
db_cursor.execute(sql, (parent_id,))
|
||||||
res = db_cursor.fetchone()
|
res = db_cursor.fetchone()
|
||||||
if res:
|
if res:
|
||||||
@@ -585,7 +652,7 @@ class MainModel(ModelMT):
|
|||||||
"""get statistic information"""
|
"""get statistic information"""
|
||||||
retval = {}
|
retval = {}
|
||||||
if selected_id:
|
if selected_id:
|
||||||
sql = """select id, type, parent_id from files where id=?"""
|
sql = """SELECT id, type, parent_id FROM files WHERE id=?"""
|
||||||
self.db_cursor.execute(sql, (selected_id,))
|
self.db_cursor.execute(sql, (selected_id,))
|
||||||
res = self.db_cursor.fetchone()
|
res = self.db_cursor.fetchone()
|
||||||
if not res:
|
if not res:
|
||||||
@@ -595,9 +662,11 @@ class MainModel(ModelMT):
|
|||||||
|
|
||||||
# collect all parent_id's
|
# collect all parent_id's
|
||||||
parents = []
|
parents = []
|
||||||
|
|
||||||
def _recurse(fid):
|
def _recurse(fid):
|
||||||
parents.append(fid)
|
parents.append(fid)
|
||||||
sql = """select id from files where type=? and parent_id=? and parent_id!=1"""
|
sql = """SELECT id FROM files
|
||||||
|
WHERE type=? AND parent_id=? AND parent_id!=1"""
|
||||||
self.db_cursor.execute(sql, (self.DIR, fid))
|
self.db_cursor.execute(sql, (self.DIR, fid))
|
||||||
res = self.db_cursor.fetchall()
|
res = self.db_cursor.fetchall()
|
||||||
if res:
|
if res:
|
||||||
@@ -615,37 +684,42 @@ class MainModel(ModelMT):
|
|||||||
files_count = 0
|
files_count = 0
|
||||||
|
|
||||||
for p in parents:
|
for p in parents:
|
||||||
sql = """select count(id) from files where type!=0 and type!=1 and parent_id=?"""
|
sql = """SELECT count(id) FROM files
|
||||||
|
WHERE type!=0 AND type!=1 AND parent_id=?"""
|
||||||
self.db_cursor.execute(sql, (p,))
|
self.db_cursor.execute(sql, (p,))
|
||||||
res = self.db_cursor.fetchone()
|
res = self.db_cursor.fetchone()
|
||||||
if res:
|
if res:
|
||||||
files_count+=res[0]
|
files_count+=res[0]
|
||||||
retval['files'] = files_count
|
retval['files'] = files_count
|
||||||
sql = """select size from files where id=?"""
|
sql = """SELECT size FROM files WHERE id=?"""
|
||||||
self.db_cursor.execute(sql, (selected_id,))
|
self.db_cursor.execute(sql, (selected_id,))
|
||||||
res = self.db_cursor.fetchone()
|
res = self.db_cursor.fetchone()
|
||||||
if res:
|
if res:
|
||||||
retval['size'] = self.__bytes_to_human(res[0])
|
retval['size'] = self.__bytes_to_human(res[0])
|
||||||
else:
|
else:
|
||||||
sql = """select count(id) from files where parent_id=1 and type=1"""
|
sql = """SELECT count(id) FROM files
|
||||||
|
WHERE parent_id=1 AND type=1"""
|
||||||
self.db_cursor.execute(sql)
|
self.db_cursor.execute(sql)
|
||||||
res = self.db_cursor.fetchone()
|
res = self.db_cursor.fetchone()
|
||||||
if res:
|
if res:
|
||||||
retval['discs'] = res[0]
|
retval['discs'] = res[0]
|
||||||
|
|
||||||
sql = """select count(id) from files where parent_id!=1 and type=1"""
|
sql = """SELECT count(id) FROM files
|
||||||
|
WHERE parent_id!=1 AND type=1"""
|
||||||
self.db_cursor.execute(sql)
|
self.db_cursor.execute(sql)
|
||||||
res = self.db_cursor.fetchone()
|
res = self.db_cursor.fetchone()
|
||||||
if res:
|
if res:
|
||||||
retval['dirs'] = res[0]
|
retval['dirs'] = res[0]
|
||||||
|
|
||||||
sql = """select count(id) from files where parent_id!=1 and type!=1"""
|
sql = """SELECT count(id) FROM files
|
||||||
|
WHERE parent_id!=1 AND type!=1"""
|
||||||
self.db_cursor.execute(sql)
|
self.db_cursor.execute(sql)
|
||||||
res = self.db_cursor.fetchone()
|
res = self.db_cursor.fetchone()
|
||||||
if res:
|
if res:
|
||||||
retval['files'] = res[0]
|
retval['files'] = res[0]
|
||||||
|
|
||||||
sql = """select sum(size) from files where parent_id=1 and type=1"""
|
sql = """SELECT sum(size) FROM files
|
||||||
|
WHERE parent_id=1 AND type=1"""
|
||||||
self.db_cursor.execute(sql)
|
self.db_cursor.execute(sql)
|
||||||
res = self.db_cursor.fetchone()
|
res = self.db_cursor.fetchone()
|
||||||
if res:
|
if res:
|
||||||
@@ -654,7 +728,7 @@ class MainModel(ModelMT):
|
|||||||
|
|
||||||
def get_image_path(self, img_id):
|
def get_image_path(self, img_id):
|
||||||
"""return image location"""
|
"""return image location"""
|
||||||
sql = """select filename from images where id=?"""
|
sql = """SELECT filename FROM images WHERE id=?"""
|
||||||
self.db_cursor.execute(sql, (img_id,))
|
self.db_cursor.execute(sql, (img_id,))
|
||||||
res = self.db_cursor.fetchone()
|
res = self.db_cursor.fetchone()
|
||||||
if res:
|
if res:
|
||||||
@@ -721,7 +795,8 @@ class MainModel(ModelMT):
|
|||||||
|
|
||||||
def __create_internal_dirname(self):
|
def __create_internal_dirname(self):
|
||||||
self.cleanup()
|
self.cleanup()
|
||||||
self.internal_dirname = "/tmp/pygtktalog%d" % datetime.now().microsecond
|
self.internal_dirname = "/tmp/pygtktalog%d" % \
|
||||||
|
datetime.now().microsecond
|
||||||
try:
|
try:
|
||||||
os.mkdir(self.internal_dirname)
|
os.mkdir(self.internal_dirname)
|
||||||
except IOError, (errno, strerror):
|
except IOError, (errno, strerror):
|
||||||
@@ -802,8 +877,11 @@ class MainModel(ModelMT):
|
|||||||
note TEXT,
|
note TEXT,
|
||||||
place TEXT,
|
place TEXT,
|
||||||
date datetime);""")
|
date datetime);""")
|
||||||
self.db_cursor.execute("insert into files values(1, 1, 'root', null, 0, 0, 0, 0, null, null);")
|
sql = """INSERT INTO files
|
||||||
self.db_cursor.execute("insert into groups values(1, 'default', 'black');")
|
VALUES(1, 1, 'root', null, 0, 0, 0, 0, null, null)"""
|
||||||
|
self.db_cursor.execute(sql)
|
||||||
|
sql = """INSERT INSERT groups VALUES(1, 'default', 'black')"""
|
||||||
|
self.db_cursor.execute(sql)
|
||||||
self.db_connection.commit()
|
self.db_connection.commit()
|
||||||
|
|
||||||
def __scan(self):
|
def __scan(self):
|
||||||
@@ -857,22 +935,21 @@ class MainModel(ModelMT):
|
|||||||
if parent_id == 1:
|
if parent_id == 1:
|
||||||
self.fresh_disk_iter = myit
|
self.fresh_disk_iter = myit
|
||||||
self.discs_tree.set_value(myit,2,gtk.STOCK_CDROM)
|
self.discs_tree.set_value(myit,2,gtk.STOCK_CDROM)
|
||||||
sql = """insert into
|
sql = """INSERT INTO
|
||||||
files(parent_id, filename, filepath, date, size, type, source)
|
files(parent_id, filename, filepath, date,
|
||||||
values(?,?,?,?,?,?,?)"""
|
size, type, source)
|
||||||
|
VALUES(?,?,?,?,?,?,?)"""
|
||||||
db_cursor.execute(sql, (parent_id, name, path, date, size,
|
db_cursor.execute(sql, (parent_id, name, path, date, size,
|
||||||
filetype, self.source))
|
filetype, self.source))
|
||||||
else:
|
else:
|
||||||
self.discs_tree.set_value(myit,2,gtk.STOCK_DIRECTORY)
|
self.discs_tree.set_value(myit,2,gtk.STOCK_DIRECTORY)
|
||||||
sql = """
|
sql = """INSERT INTO
|
||||||
insert into
|
|
||||||
files(parent_id, filename, filepath, date, size, type)
|
files(parent_id, filename, filepath, date, size, type)
|
||||||
values(?,?,?,?,?,?)
|
VALUES(?,?,?,?,?,?)"""
|
||||||
"""
|
db_cursor.execute(sql, (parent_id, name, path,
|
||||||
db_cursor.execute(sql,
|
date, size, filetype))
|
||||||
(parent_id, name, path, date, size, filetype))
|
|
||||||
|
|
||||||
sql = """select seq FROM sqlite_sequence WHERE name='files'"""
|
sql = """SELECT seq FROM sqlite_sequence WHERE name='files'"""
|
||||||
db_cursor.execute(sql)
|
db_cursor.execute(sql)
|
||||||
currentid=db_cursor.fetchone()[0]
|
currentid=db_cursor.fetchone()[0]
|
||||||
|
|
||||||
@@ -904,12 +981,11 @@ class MainModel(ModelMT):
|
|||||||
if os.path.islink(current_dir):
|
if os.path.islink(current_dir):
|
||||||
l = self.__decode_filename(os.readlink(current_dir))
|
l = self.__decode_filename(os.readlink(current_dir))
|
||||||
|
|
||||||
sql = """
|
sql = """INSERT INTO
|
||||||
insert into files(parent_id, filename, filepath, date, size, type)
|
files(parent_id, filename, filepath, date, size, type)
|
||||||
values(?,?,?,?,?,?)
|
VALUES(?,?,?,?,?,?)"""
|
||||||
"""
|
|
||||||
db_cursor.execute(sql, (currentid, j + " -> " + l,
|
db_cursor.execute(sql, (currentid, j + " -> " + l,
|
||||||
ocurrent_dir, st_mtime, 0,
|
current_dir, st_mtime, 0,
|
||||||
self.LIN))
|
self.LIN))
|
||||||
dirsize = 0
|
dirsize = 0
|
||||||
else:
|
else:
|
||||||
@@ -944,17 +1020,16 @@ class MainModel(ModelMT):
|
|||||||
# do NOT follow symbolic links
|
# do NOT follow symbolic links
|
||||||
if os.path.islink(current_file):
|
if os.path.islink(current_file):
|
||||||
l = self.__decode_filename(os.readlink(current_file))
|
l = self.__decode_filename(os.readlink(current_file))
|
||||||
sql = """insert into files(parent_id, filename, filepath,
|
sql = """INSERT INTO
|
||||||
date, size, type)
|
files(parent_id, filename, filepath, date, size, type)
|
||||||
values(?,?,?,?,?,?)"""
|
VALUES(?,?,?,?,?,?)"""
|
||||||
db_cursor.execute(sql, (currentid, j + " -> " + l,
|
db_cursor.execute(sql, (currentid, j + " -> " + l,
|
||||||
current_file, st_mtime, 0,
|
current_file, st_mtime, 0,
|
||||||
self.LIN))
|
self.LIN))
|
||||||
else:
|
else:
|
||||||
sql = """
|
sql = """INSERT INTO
|
||||||
insert into files(parent_id, filename, filepath, date, size, type)
|
files(parent_id, filename, filepath, date, size, type)
|
||||||
values(?,?,?,?,?,?)
|
VALUES(?,?,?,?,?,?)"""
|
||||||
"""
|
|
||||||
db_cursor.execute(sql, (currentid, j, current_file,
|
db_cursor.execute(sql, (currentid, j, current_file,
|
||||||
st_mtime, st_size, self.FIL))
|
st_mtime, st_size, self.FIL))
|
||||||
|
|
||||||
@@ -969,7 +1044,8 @@ class MainModel(ModelMT):
|
|||||||
update = True
|
update = True
|
||||||
exif = None
|
exif = None
|
||||||
|
|
||||||
sql = """select seq FROM sqlite_sequence WHERE name='files'"""
|
sql = """SELECT seq FROM sqlite_sequence
|
||||||
|
WHERE name='files'"""
|
||||||
db_cursor.execute(sql)
|
db_cursor.execute(sql)
|
||||||
fileid = db_cursor.fetchone()[0]
|
fileid = db_cursor.fetchone()[0]
|
||||||
|
|
||||||
@@ -977,14 +1053,19 @@ class MainModel(ModelMT):
|
|||||||
|
|
||||||
# Images - thumbnails and exif data
|
# Images - thumbnails and exif data
|
||||||
if self.config.confd['thumbs'] and ext in self.IMG:
|
if self.config.confd['thumbs'] and ext in self.IMG:
|
||||||
tpath, exif, ret_code = Thumbnail(current_file, base=self.internal_dirname).save(fileid)
|
t = Thumbnail(current_file,
|
||||||
|
base=self.internal_dirname)
|
||||||
|
tpath, exif, ret_code = t.save(fileid)
|
||||||
if ret_code != -1:
|
if ret_code != -1:
|
||||||
sql = """insert into thumbnails(file_id, filename) values (?, ?)"""
|
sql = """INSERT INTO
|
||||||
db_cursor.execute(sql, (fileid,
|
thumbnails(file_id, filename)
|
||||||
tpath.split(self.internal_dirname)[1][1:]))
|
VALUES(?, ?)"""
|
||||||
|
t = tpath.split(self.internal_dirname)[1][1:]
|
||||||
|
db_cursor.execute(sql, (fileid, t))
|
||||||
|
|
||||||
# exif - stroe data in exif table
|
# exif - store data in exif table
|
||||||
if self.config.confd['exif'] and ext in ['jpg', 'jpeg']:
|
jpg = ['jpg', 'jpeg']
|
||||||
|
if self.config.confd['exif'] and ext in jpg:
|
||||||
p = None
|
p = None
|
||||||
if self.config.confd['thumbs'] and exif:
|
if self.config.confd['thumbs'] and exif:
|
||||||
p = ParseExif(exif_dict=exif)
|
p = ParseExif(exif_dict=exif)
|
||||||
@@ -1026,13 +1107,11 @@ class MainModel(ModelMT):
|
|||||||
db_cursor.execute(sql, (fileid,
|
db_cursor.execute(sql, (fileid,
|
||||||
cmnts['note'],
|
cmnts['note'],
|
||||||
cmnts['place'],
|
cmnts['place'],
|
||||||
cmnts['date']
|
cmnts['date']))
|
||||||
))
|
if 'keywords' in cmnts:
|
||||||
if cmnts.has_key('keywords'):
|
# TODO: add gthumb keywords to tags
|
||||||
# TODO: add gthumb keywords to tags and group 'gthumb'
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
# Extensions - user defined actions
|
# Extensions - user defined actions
|
||||||
if ext in self.config.confd['extensions'].keys():
|
if ext in self.config.confd['extensions'].keys():
|
||||||
cmd = self.config.confd['extensions'][ext]
|
cmd = self.config.confd['extensions'][ext]
|
||||||
@@ -1041,19 +1120,17 @@ class MainModel(ModelMT):
|
|||||||
desc = ''
|
desc = ''
|
||||||
for line in output:
|
for line in output:
|
||||||
desc += line
|
desc += line
|
||||||
#desc = string.replace(desc, "\n", "\\n")
|
|
||||||
sql = """update files set description=? where id=?"""
|
sql = """UPDATE files SET description=?
|
||||||
|
WHERE id=?"""
|
||||||
db_cursor.execute(sql, (desc, fileid))
|
db_cursor.execute(sql, (desc, fileid))
|
||||||
|
|
||||||
#if i.split('.').[-1].lower() in mov_ext:
|
|
||||||
# # video only
|
|
||||||
# info = filetypeHelper.guess_video(os.path.join(root,i))
|
|
||||||
### end of scan
|
### end of scan
|
||||||
if update:
|
if update:
|
||||||
self.statusmsg = "Scannig: %s" % current_file
|
self.statusmsg = "Scannig: %s" % current_file
|
||||||
self.progress = step * self.count
|
self.progress = step * self.count
|
||||||
|
|
||||||
sql = """update files set size=? where id=?"""
|
sql = """UPDATE files SET size=? WHERE id=?"""
|
||||||
db_cursor.execute(sql,(_size, currentid))
|
db_cursor.execute(sql,(_size, currentid))
|
||||||
if self.abort:
|
if self.abort:
|
||||||
return -1
|
return -1
|
||||||
@@ -1098,23 +1175,20 @@ class MainModel(ModelMT):
|
|||||||
self.__clear_discs_tree()
|
self.__clear_discs_tree()
|
||||||
|
|
||||||
#connect
|
#connect
|
||||||
|
detect_types = sqlite.PARSE_DECLTYPES|sqlite.PARSE_COLNAMES
|
||||||
db_connection = sqlite.connect("%s" % \
|
db_connection = sqlite.connect("%s" % \
|
||||||
(self.internal_dirname + '/db.sqlite'),
|
(self.internal_dirname + '/db.sqlite'),
|
||||||
detect_types=sqlite.PARSE_DECLTYPES|sqlite.PARSE_COLNAMES)
|
detect_types = detect_types)
|
||||||
db_cursor = db_connection.cursor()
|
db_cursor = db_connection.cursor()
|
||||||
|
|
||||||
# fetch all the directories
|
# fetch all the directories
|
||||||
sql = """
|
sql = """SELECT id, parent_id, filename FROM files
|
||||||
SELECT id, parent_id, filename FROM files
|
WHERE type=1 ORDER BY parent_id, filename"""
|
||||||
WHERE type=1 ORDER BY parent_id, filename
|
|
||||||
"""
|
|
||||||
db_cursor.execute(sql)
|
db_cursor.execute(sql)
|
||||||
data = db_cursor.fetchall()
|
data = db_cursor.fetchall()
|
||||||
try:
|
try:
|
||||||
sql = """
|
sql = """SELECT id, parent_id, filename FROM files
|
||||||
SELECT id, parent_id, filename FROM files
|
WHERE type=1 ORDER BY parent_id, filename"""
|
||||||
WHERE type=1 ORDER BY parent_id, filename
|
|
||||||
"""
|
|
||||||
db_cursor.execute(sql)
|
db_cursor.execute(sql)
|
||||||
data = db_cursor.fetchall()
|
data = db_cursor.fetchall()
|
||||||
except:
|
except:
|
||||||
@@ -1148,19 +1222,22 @@ class MainModel(ModelMT):
|
|||||||
# launch scanning.
|
# launch scanning.
|
||||||
get_children()
|
get_children()
|
||||||
if __debug__:
|
if __debug__:
|
||||||
print "m_main.py: __fetch_db_into_treestore() tree generation time: ", (datetime.now() - start_date)
|
print "m_main.py: __fetch_db_into_treestore()",
|
||||||
|
print "tree generation time: ", (datetime.now() - start_date)
|
||||||
db_connection.close()
|
db_connection.close()
|
||||||
return
|
return
|
||||||
|
|
||||||
def __append_added_volume(self):
|
def __append_added_volume(self):
|
||||||
"""append branch from DB to existing tree model"""
|
"""append branch from DB to existing tree model"""
|
||||||
#connect
|
#connect
|
||||||
|
detect_types = sqlite.PARSE_DECLTYPES|sqlite.PARSE_COLNAMES
|
||||||
db_connection = sqlite.connect("%s" %
|
db_connection = sqlite.connect("%s" %
|
||||||
(self.internal_dirname + '/db.sqlite'),
|
(self.internal_dirname + '/db.sqlite'),
|
||||||
detect_types=sqlite.PARSE_DECLTYPES|sqlite.PARSE_COLNAMES)
|
detect_types = detect_types)
|
||||||
db_cursor = db_connection.cursor()
|
db_cursor = db_connection.cursor()
|
||||||
|
|
||||||
sql = """SELECT id, parent_id, filename FROM files WHERE type=1 ORDER BY parent_id, filename"""
|
sql = """SELECT id, parent_id, filename FROM files
|
||||||
|
WHERE type=1 ORDER BY parent_id, filename"""
|
||||||
db_cursor.execute(sql)
|
db_cursor.execute(sql)
|
||||||
data = db_cursor.fetchall()
|
data = db_cursor.fetchall()
|
||||||
|
|
||||||
@@ -1187,7 +1264,8 @@ class MainModel(ModelMT):
|
|||||||
# launch scanning.
|
# launch scanning.
|
||||||
get_children()
|
get_children()
|
||||||
if __debug__:
|
if __debug__:
|
||||||
print "m_main.py: __append_added_volume() tree generation time: ", (datetime.now() - start_date)
|
print "m_main.py: __append_added_volume() tree generation time: ",
|
||||||
|
print datetime.now() - start_date
|
||||||
db_connection.close()
|
db_connection.close()
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -1197,12 +1275,3 @@ class MainModel(ModelMT):
|
|||||||
else:
|
else:
|
||||||
return txt
|
return txt
|
||||||
|
|
||||||
def __list_to_string(self, array):
|
|
||||||
arg =''
|
|
||||||
for c in array:
|
|
||||||
if len(arg) > 0:
|
|
||||||
arg+=", %d" % c
|
|
||||||
else:
|
|
||||||
arg = "%d" % c
|
|
||||||
return arg
|
|
||||||
pass # end of class
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ class MainView(View):
|
|||||||
application. The widgets set is loaded from glade file"""
|
application. The widgets set is loaded from glade file"""
|
||||||
|
|
||||||
GLADE = os.path.join(utils.globals.GLADE_DIR, "main.glade")
|
GLADE = os.path.join(utils.globals.GLADE_DIR, "main.glade")
|
||||||
|
|
||||||
def __init__(self, ctrl):
|
def __init__(self, ctrl):
|
||||||
View.__init__(self, ctrl, self.GLADE)
|
View.__init__(self, ctrl, self.GLADE)
|
||||||
|
|
||||||
@@ -38,6 +39,7 @@ 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
|
||||||
|
|||||||
Reference in New Issue
Block a user