# Copyright (C) 2009 Lars Wirzenius # # 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 . 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\d+)-' r'tagid-(?P\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