diff options
Diffstat (limited to 'trunk/dimbola/taglist.py')
-rw-r--r-- | trunk/dimbola/taglist.py | 161 |
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 + |