summaryrefslogtreecommitdiff
path: root/trunk/dimbola/taglist.py
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/dimbola/taglist.py')
-rw-r--r--trunk/dimbola/taglist.py161
1 files changed, 161 insertions, 0 deletions
diff --git a/trunk/dimbola/taglist.py b/trunk/dimbola/taglist.py
new file mode 100644
index 0000000..5d88921
--- /dev/null
+++ b/trunk/dimbola/taglist.py
@@ -0,0 +1,161 @@
+# Copyright (C) 2009 Lars Wirzenius <liw@liw.fi>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+import re
+
+import gtk
+
+import dimbola
+
+
+class Taglist(object):
+
+ '''User-editable list of tags.
+
+ This is used for the list of tags for a photo, or the tags that are
+ currently searched for. This is not the "tag tree" that is used to
+ hold the whole set of tags in the database.
+
+ '''
+
+ tagpat = re.compile(r'^taglist-photoid-(?P<photoid>\d+)-'
+ r'tagid-(?P<tagid>\d+)$')
+
+ def __init__(self, textview, popup, popup_prepare_cb):
+ self.textview = textview
+ self.popup = popup
+ self.popup_prepare = popup_prepare_cb
+ self.buf = self.textview.get_buffer()
+ self.textview.drag_dest_set(gtk.DEST_DEFAULT_ALL,
+ [(dimbola.TAGIDS_TYPE,
+ gtk.TARGET_SAME_APP, 0)],
+ gtk.gdk.ACTION_COPY)
+
+ def text_tag_name(self, photoid, tagid):
+ return 'taglist-photoid-%d-tagid-%d' % (photoid, tagid)
+
+ def parse_text_tag_name(self, name):
+ m = self.tagpat.match(name)
+ assert m
+ return int(m.group('photoid')), int(m.group('tagid'))
+
+ def is_our_text_tag_name(self, name):
+ return self.tagpat.match(name)
+
+ @property
+ def text_tag_names(self):
+ def save(text_tag, text_tag_names):
+ name = text_tag.get_property('name')
+ if self.is_our_text_tag_name(name):
+ text_tag_names.append(name)
+
+ names = []
+ tag_table = self.buf.get_tag_table()
+ tag_table.foreach(save, names)
+ return names
+
+ def clear(self):
+ '''Clear the buffer from all text and tags.'''
+ self.buf.set_text('')
+ tag_table = self.buf.get_tag_table()
+ for name in self.text_tag_names:
+ text_tag = tag_table.lookup(name)
+ tag_table.remove(text_tag)
+
+ def set_tags(self, photoid, tags):
+ '''Set tags in list.
+
+ tags is list of (tagid, tagname) pairs.
+
+ '''
+
+ self.clear()
+ taglist = [(tagname.lower(), tagid, tagname)
+ for tagid, tagname in tags]
+ taglist.sort()
+ tags = [(tagid, tagname) for sortkey, tagid, tagname in taglist]
+ for tagid, tagname in tags:
+ text_tag = self.buf.create_tag(self.text_tag_name(photoid, tagid))
+ it = self.buf.get_end_iter()
+ if self.buf.get_char_count() > 0:
+ self.buf.insert(it, '; ')
+ begin = self.buf.create_mark('begin-%s' % tagid, it, True)
+ self.buf.insert_with_tags(it, tagname, text_tag)
+ end = self.buf.create_mark('end-%d' % tagid, it, True)
+
+ def iter_to_ids(self, it):
+ '''Return the photoid, tagid that applies at an iterator.
+
+ If nothing is found, return None, None.
+
+ '''
+
+ for text_tag in it.get_tags():
+ text_tag_name = text_tag.get_property('name')
+ if self.is_our_text_tag_name(text_tag_name):
+ photoid, tagid = self.parse_text_tag_name(text_tag_name)
+ return photoid, tagid
+ return None, None
+
+ def select_tagname_at_iter(self, it):
+ '''Select the tagname at an iterator.'''
+
+ photoid, tagid = self.iter_to_ids(it)
+ if tagid is not None:
+ begin = self.buf.get_mark('begin-%d' % tagid)
+ end = self.buf.get_mark('end-%d' % tagid)
+ self.buf.select_range(self.buf.get_iter_at_mark(begin),
+ self.buf.get_iter_at_mark(end))
+
+ def button_press_event(self, widget, event): # pragma: no cover
+ shift = (event.state & gtk.gdk.SHIFT_MASK) == gtk.gdk.SHIFT_MASK
+ ctrl = (event.state & gtk.gdk.CONTROL_MASK) == gtk.gdk.CONTROL_MASK
+
+ if event.button == 1:
+ it = self.textview.get_iter_at_location(int(event.x), int(event.y))
+ self.select_tagname_at_iter(it)
+ return True
+ elif event.button == 3:
+ if not self.buf.get_has_selection():
+ it = self.textview.get_iter_at_location(int(event.x),
+ int(event.y))
+ if it:
+ self.select_tagname_at_iter(it)
+ if self.popup_prepare:
+ self.popup_prepare()
+ self.popup.popup(None, None, None, event.button, event.time)
+ return True
+
+ def selected_tag(self):
+ if self.buf.get_has_selection():
+ begin_mark = self.buf.get_insert()
+ it = self.buf.get_iter_at_mark(begin_mark)
+ return self.iter_to_ids(it)
+
+ def drag_motion(self, w, dc, x, y, timestamp): # pragma: no cover
+ self.textview.drag_highlight()
+ dc.drag_status(gtk.gdk.ACTION_COPY, timestamp)
+ return True
+
+ def drag_leave(self, w, dc, timestamp): # pragma: no cover
+ self.textview.drag_unhighlight()
+
+ def drag_data_received(self, *args): # pragma: no cover
+ w, dc, x, y, data, info, timestamp = args
+ tagids = dimbola.decode_dnd_tagids(data.data)
+ dc.finish(True, False, timestamp)
+ return tagids
+