diff --git a/ChangeLog b/ChangeLog index 9c5c5d579..8428b7cee 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2006-08-10 Don Allingham + * src/DisplayTabs/_EmbeddedList.py: use cPickle instead of pickle + * src/DisplayTabs/_GalleryTab.py: add dnd functionality + * src/DdTargets.py: add URI_LIST and APP_ROOT + 2006-08-10 Alex Roitman * src/Filters/SideBar/_PersonSidebarFilter.py (get_filter): Typo. diff --git a/src/DdTargets.py b/src/DdTargets.py index 4793c8790..91f5c31d8 100644 --- a/src/DdTargets.py +++ b/src/DdTargets.py @@ -153,6 +153,8 @@ class _DdTargets(object): self.STRING = _DdType(self,'STRING', 0, 2) self.COMPOUND_TEXT = _DdType(self,'COMPOUND_TEXT', 0, 3) self.UTF8_STRING = _DdType(self,'UTF8_STRING', 0, 4) + self.URI_LIST = _DdType(self,'text/uri-list', 0, 5) + self.APP_ROOT = _DdType(self,'application/x-rootwin-drop', 0, 6) # List of all the test types. These are types # that can be interpreted as text. diff --git a/src/DisplayTabs/_EmbeddedList.py b/src/DisplayTabs/_EmbeddedList.py index ad384e090..792740d2b 100644 --- a/src/DisplayTabs/_EmbeddedList.py +++ b/src/DisplayTabs/_EmbeddedList.py @@ -26,7 +26,7 @@ # #------------------------------------------------------------------------- from gettext import gettext as _ -import pickle +import cPickle as pickle #------------------------------------------------------------------------- # diff --git a/src/DisplayTabs/_GalleryTab.py b/src/DisplayTabs/_GalleryTab.py index 74cedccd8..4baced688 100644 --- a/src/DisplayTabs/_GalleryTab.py +++ b/src/DisplayTabs/_GalleryTab.py @@ -26,6 +26,8 @@ # #------------------------------------------------------------------------- from gettext import gettext as _ +import cPickle as pickle +import urlparse #------------------------------------------------------------------------- # @@ -33,6 +35,7 @@ from gettext import gettext as _ # #------------------------------------------------------------------------- import gtk +import os #------------------------------------------------------------------------- # @@ -44,6 +47,8 @@ import Utils import ImgManip import Mime import Errors +import Mime +from DdTargets import DdTargets from _ButtonTab import ButtonTab #------------------------------------------------------------------------- @@ -61,10 +66,16 @@ def make_launcher(prog, path): #------------------------------------------------------------------------- class GalleryTab(ButtonTab): + _DND_TYPE = DdTargets.MEDIAOBJ + _DND_EXTRA = DdTargets.URI_LIST + def __init__(self, dbstate, uistate, track, media_list, update=None): ButtonTab.__init__(self, dbstate, uistate, track, _('Gallery'), True) self.media_list = media_list self.update = update + + self._set_dnd() + self.rebuild() self.show_all() @@ -257,3 +268,163 @@ class GalleryTab(ButtonTab): def edit_callback(self, media_ref, ref): self.changed = True self.rebuild() + + def _set_dnd(self): + """ + Sets up drag-n-drop. The source and destionation are set by calling .target() + on the _DND_TYPE. Obviously, this means that there must be a _DND_TYPE + variable defined that points to an entry in DdTargets. + """ + + if self._DND_EXTRA: + dnd_types = [ self._DND_TYPE.target(), self._DND_EXTRA.target() ] + else: + dnd_types = [ self._DND_TYPE.target() ] + + self.iconlist.drag_dest_set(gtk.DEST_DEFAULT_ALL, dnd_types, + gtk.gdk.ACTION_COPY) + self.iconlist.drag_source_set(gtk.gdk.BUTTON1_MASK, + [self._DND_TYPE.target()], + gtk.gdk.ACTION_COPY) + self.iconlist.connect('drag_data_get', self.drag_data_get) + self.iconlist.connect('drag_data_received', self.drag_data_received) + + def drag_data_get(self, widget, context, sel_data, info, time): + """ + Provide the drag_data_get function, which passes a tuple consisting of: + + 1) Drag type defined by the .drag_type field specfied by the value + assigned to _DND_TYPE + 2) The id value of this object, used for the purpose of determining + the source of the object. If the source of the object is the same + as the object, we are doing a reorder instead of a normal drag + and drop + 3) Pickled data. The pickled version of the selected object + 4) Source row. Used for a reorder to determine the original position + of the object + """ + + # get the selected object, returning if not is defined + obj = self.get_selected() + if not obj: + return + + # pickle the data, and build the tuple to be passed + value = (self._DND_TYPE.drag_type, id(self), obj, self.find_index(obj)) + data = pickle.dumps(value) + + # pass as a string (8 bits) + sel_data.set(sel_data.target, 8, data) + + def drag_data_received(self, widget, context, x, y, sel_data, info, time): + """ + Handle the standard gtk interface for drag_data_received. + + If the selection data is define, extract the value from sel_data.data, + and decide if this is a move or a reorder. + """ + + if sel_data and sel_data.data: + try: + (mytype, selfid, obj, row_from) = pickle.loads(sel_data.data) + + # make sure this is the correct DND type for this object + if mytype == self._DND_TYPE.drag_type: + + # determine the destination row + data = self.iconlist.get_dest_item_at_pos(x,y) + if data: + (path, pos) = data + row = path[0] + + if pos == gtk.ICON_VIEW_DROP_LEFT: + row = max(row, 0) + elif pos == gtk.ICON_VIEW_DROP_RIGHT: + row = min(row, len(self.get_data())) + elif pos == gtk.ICON_VIEW_DROP_INTO: + row = min(row+1, len(self.get_data())) + else: + row = len(self.get_data()) + + # if the is same object, we have a move, otherwise, + # it is a standard drag-n-drop + + if id(self) == selfid: + self._move(row_from, row, obj) + else: + self._handle_drag(row, obj) + self.rebuild() + elif self._DND_EXTRA and mytype == self._DND_EXTRA.drag_type: + self.handle_extra_type(mytype, obj) + except pickle.UnpicklingError: + d = Utils.fix_encoding(sel_data.data.replace('\0',' ').strip()) + protocol,site,mfile,j,k,l = urlparse.urlparse(d) + if protocol == "file": + name = Utils.fix_encoding(mfile) + mime = Mime.get_type(name) + if not Mime.is_valid_type(mime): + return + photo = RelLib.MediaObject() + photo.set_path(name) + photo.set_mime_type(mime) + basename = os.path.basename(name) + (root,ext) = os.path.splitext(basename) + photo.set_description(root) + trans = self.dbstate.db.transaction_begin() + self.dbstate.db.add_object(photo, trans) + oref = RelLib.MediaRef() + oref.set_reference_handle(photo.get_handle()) + self.get_data().append(oref) + self.changed = True +# self.dataobj.add_media_reference(oref) + self.dbstate.db.transaction_commit(trans, + _("Drag Media Object")) + self.rebuild() +# elif protocol != "": +# import urllib +# u = urllib.URLopener() +# try: +# tfile,headers = u.retrieve(d) +# except (IOError,OSError), msg: +# t = _("Could not import %s") % d +# ErrorDialog(t,str(msg)) +# return +# tfile = Utils.fix_encoding(tfile) +# mime = GrampsMime.get_type(tfile) +# photo = RelLib.MediaObject() +# photo.set_mime_type(mime) +# photo.set_description(d) +# photo.set_path(tfile) +# trans = self.db.transaction_begin() +# self.db.add_object(photo,trans) +# self.db.transaction_commit(trans,_("Drag Media Object")) +# oref = RelLib.MediaRef() +# oref.set_reference_handle(photo.get_handle()) +# self.dataobj.add_media_reference(oref) +# self.add_thumbnail(oref) + + def handle_extra_type(self, objtype, obj): + pass + + def _handle_drag(self, row, obj): + self.get_data().insert(row, obj) + self.changed = True + self.rebuild() + + def _move(self, row_from, row_to, obj): + dlist = self.get_data() + if row_from < row_to: + dlist.insert(row_to, obj) + del dlist[row_from] + else: + del dlist[row_from] + dlist.insert(row_to-1, obj) + self.changed = True + self.rebuild() + + def find_index(self, obj): + """ + returns the index of the object within the associated data + """ + return self.get_data().index(obj) +