Add tag list view, enable tag drag and drop
This commit is contained in:
@@ -369,6 +369,10 @@ def navigation_label(db, nav_type, handle):
|
|||||||
label = " ".join(label.split())
|
label = " ".join(label.split())
|
||||||
if len(label) > 40:
|
if len(label) > 40:
|
||||||
label = label[:40] + "..."
|
label = label[:40] + "..."
|
||||||
|
elif nav_type == 'Tag':
|
||||||
|
obj = db.get_tag_from_handle(handle)
|
||||||
|
if obj:
|
||||||
|
return ('[%s] %s' % (_('Tag'), obj.get_name()), obj)
|
||||||
|
|
||||||
if label and obj:
|
if label and obj:
|
||||||
label = '[%s] %s' % (obj.get_gramps_id(), label)
|
label = '[%s] %s' % (obj.get_gramps_id(), label)
|
||||||
|
@@ -94,7 +94,8 @@ for (name, icon) in (("media", "gramps-media"),
|
|||||||
('source', 'gramps-source'),
|
('source', 'gramps-source'),
|
||||||
('citation', 'gramps-citation'),
|
('citation', 'gramps-citation'),
|
||||||
('text', 'gramps-font'),
|
('text', 'gramps-font'),
|
||||||
('url', 'gramps-geo')):
|
('url', 'gramps-geo'),
|
||||||
|
('tag', 'gramps-tag')):
|
||||||
ICONS[name] = theme.load_icon(icon, 16, 0)
|
ICONS[name] = theme.load_icon(icon, 16, 0)
|
||||||
|
|
||||||
|
|
||||||
@@ -118,6 +119,7 @@ def map2class(target):
|
|||||||
'place-link': ClipPlace,
|
'place-link': ClipPlace,
|
||||||
'placeref': ClipPlaceRef,
|
'placeref': ClipPlaceRef,
|
||||||
'note-link': ClipNote,
|
'note-link': ClipNote,
|
||||||
|
'tag': ClipTag,
|
||||||
'TEXT': ClipText}
|
'TEXT': ClipText}
|
||||||
return _d_[target] if target in _d_ else None
|
return _d_[target] if target in _d_ else None
|
||||||
|
|
||||||
@@ -131,7 +133,8 @@ def obj2class(target):
|
|||||||
'Event': ClipEvent,
|
'Event': ClipEvent,
|
||||||
'Media': ClipMediaObj,
|
'Media': ClipMediaObj,
|
||||||
'Place': ClipPlace,
|
'Place': ClipPlace,
|
||||||
'Note': ClipNote}
|
'Note': ClipNote,
|
||||||
|
'Tag': ClipTag}
|
||||||
return _d_[target] if target in _d_ else None
|
return _d_[target] if target in _d_ else None
|
||||||
|
|
||||||
OBJ2TARGET = {"Person": Gdk.atom_intern('person-link', False),
|
OBJ2TARGET = {"Person": Gdk.atom_intern('person-link', False),
|
||||||
@@ -142,7 +145,8 @@ OBJ2TARGET = {"Person": Gdk.atom_intern('person-link', False),
|
|||||||
'Event': Gdk.atom_intern('pevent', False),
|
'Event': Gdk.atom_intern('pevent', False),
|
||||||
'Media': Gdk.atom_intern('media', False),
|
'Media': Gdk.atom_intern('media', False),
|
||||||
'Place': Gdk.atom_intern('place-link', False),
|
'Place': Gdk.atom_intern('place-link', False),
|
||||||
'Note': Gdk.atom_intern('note-link', False)}
|
'Note': Gdk.atom_intern('note-link', False),
|
||||||
|
"Tag": Gdk.atom_intern('tag', False)}
|
||||||
|
|
||||||
|
|
||||||
def obj2target(target):
|
def obj2target(target):
|
||||||
@@ -280,7 +284,7 @@ class ClipObjWrapper(ClipWrapper):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
for (clname, handle) in self._obj.get_referenced_handles_recursively():
|
for (clname, handle) in self._obj.get_referenced_handles_recursively():
|
||||||
if obj2class(clname): # a class we care about (not tag)
|
if obj2class(clname): # a class we care about
|
||||||
if not clipdb.method("has_%s_handle", clname)(handle):
|
if not clipdb.method("has_%s_handle", clname)(handle):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -424,6 +428,26 @@ class ClipUrl(ClipObjWrapper):
|
|||||||
self._value = self._obj.get_description()
|
self._value = self._obj.get_description()
|
||||||
|
|
||||||
|
|
||||||
|
class ClipTag(ClipHandleWrapper):
|
||||||
|
|
||||||
|
DROP_TARGETS = [DdTargets.TAG_LINK]
|
||||||
|
DRAG_TARGET = DdTargets.TAG_LINK
|
||||||
|
ICON = ICONS['tag']
|
||||||
|
|
||||||
|
def __init__(self, obj):
|
||||||
|
super(ClipTag, self).__init__(obj)
|
||||||
|
self._type = _("Tag")
|
||||||
|
self._objclass = "Tag"
|
||||||
|
self.refresh()
|
||||||
|
|
||||||
|
def refresh(self):
|
||||||
|
if self._handle:
|
||||||
|
value = clipdb.get_tag_from_handle(self._handle)
|
||||||
|
if value:
|
||||||
|
self._title = value.get_name()
|
||||||
|
self._value = value.get_color()
|
||||||
|
|
||||||
|
|
||||||
class ClipAttribute(ClipObjWrapper):
|
class ClipAttribute(ClipObjWrapper):
|
||||||
|
|
||||||
DROP_TARGETS = [DdTargets.ATTRIBUTE]
|
DROP_TARGETS = [DdTargets.ATTRIBUTE]
|
||||||
@@ -993,7 +1017,9 @@ class ClipboardListView:
|
|||||||
'event-rebuild',
|
'event-rebuild',
|
||||||
'repository-update',
|
'repository-update',
|
||||||
'repository-rebuild',
|
'repository-rebuild',
|
||||||
'note-rebuild')
|
'note-rebuild',
|
||||||
|
'tag-update',
|
||||||
|
'tag-rebuild')
|
||||||
|
|
||||||
for signal in db_signals:
|
for signal in db_signals:
|
||||||
clipdb.connect(signal, self.refresh_objects)
|
clipdb.connect(signal, self.refresh_objects)
|
||||||
@@ -1022,6 +1048,8 @@ class ClipboardListView:
|
|||||||
gen_del_obj(self.delete_object, 'place-link'))
|
gen_del_obj(self.delete_object, 'place-link'))
|
||||||
clipdb.connect('note-delete',
|
clipdb.connect('note-delete',
|
||||||
gen_del_obj(self.delete_object, 'note-link'))
|
gen_del_obj(self.delete_object, 'note-link'))
|
||||||
|
clipdb.connect('tag-delete',
|
||||||
|
gen_del_obj(self.delete_object, 'tag'))
|
||||||
# family-delete not needed, cannot be dragged!
|
# family-delete not needed, cannot be dragged!
|
||||||
|
|
||||||
self.refresh_objects()
|
self.refresh_objects()
|
||||||
@@ -1087,6 +1115,7 @@ class ClipboardListView:
|
|||||||
self.register_wrapper_class(ClipChildRef)
|
self.register_wrapper_class(ClipChildRef)
|
||||||
self.register_wrapper_class(ClipText)
|
self.register_wrapper_class(ClipText)
|
||||||
self.register_wrapper_class(ClipNote)
|
self.register_wrapper_class(ClipNote)
|
||||||
|
self.register_wrapper_class(ClipTag)
|
||||||
|
|
||||||
def register_wrapper_class(self, wrapper_class):
|
def register_wrapper_class(self, wrapper_class):
|
||||||
for drop_target in wrapper_class.DROP_TARGETS:
|
for drop_target in wrapper_class.DROP_TARGETS:
|
||||||
@@ -1593,6 +1622,7 @@ class MultiTreeView(Gtk.TreeView):
|
|||||||
from .editors import (EditPerson, EditEvent, EditFamily, EditSource,
|
from .editors import (EditPerson, EditEvent, EditFamily, EditSource,
|
||||||
EditPlace, EditRepository, EditNote, EditMedia,
|
EditPlace, EditRepository, EditNote, EditMedia,
|
||||||
EditCitation)
|
EditCitation)
|
||||||
|
from .views.tags import EditTag
|
||||||
if obj2class(objclass): # make sure it is an editable object
|
if obj2class(objclass): # make sure it is an editable object
|
||||||
if self.dbstate.db.method('has_%s_handle', objclass)(handle):
|
if self.dbstate.db.method('has_%s_handle', objclass)(handle):
|
||||||
g_object = self.dbstate.db.method(
|
g_object = self.dbstate.db.method(
|
||||||
|
@@ -154,6 +154,7 @@ class _DdTargets:
|
|||||||
self.URL = _DdType(self, 'url')
|
self.URL = _DdType(self, 'url')
|
||||||
self.SURNAME = _DdType(self, 'surname')
|
self.SURNAME = _DdType(self, 'surname')
|
||||||
self.CITATION_LINK = _DdType(self, 'citation-link')
|
self.CITATION_LINK = _DdType(self, 'citation-link')
|
||||||
|
self.TAG_LINK = _DdType(self, 'tag')
|
||||||
|
|
||||||
# List of all types that are used between
|
# List of all types that are used between
|
||||||
# gramps widgets but should not be exported
|
# gramps widgets but should not be exported
|
||||||
@@ -185,7 +186,8 @@ class _DdTargets:
|
|||||||
self.SRCATTRIBUTE,
|
self.SRCATTRIBUTE,
|
||||||
self.URL,
|
self.URL,
|
||||||
self.SURNAME,
|
self.SURNAME,
|
||||||
self.CITATION_LINK
|
self.CITATION_LINK,
|
||||||
|
self.TAG_LINK,
|
||||||
]
|
]
|
||||||
|
|
||||||
self.CHILD = _DdType(self, 'child')
|
self.CHILD = _DdType(self, 'child')
|
||||||
|
@@ -68,6 +68,7 @@ CATEGORY_ICON = {
|
|||||||
'Media': 'gramps-media',
|
'Media': 'gramps-media',
|
||||||
'Notes': 'gramps-notes',
|
'Notes': 'gramps-notes',
|
||||||
'Citations': 'gramps-citation',
|
'Citations': 'gramps-citation',
|
||||||
|
'Tags': 'gramps-tag'
|
||||||
}
|
}
|
||||||
|
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
|
@@ -781,6 +781,7 @@ class ListView(NavigationView):
|
|||||||
#force rebuild of the model on build of tree
|
#force rebuild of the model on build of tree
|
||||||
self.dirty = True
|
self.dirty = True
|
||||||
self.build_tree()
|
self.build_tree()
|
||||||
|
if self.bookmarks:
|
||||||
self.bookmarks.redraw()
|
self.bookmarks.redraw()
|
||||||
else:
|
else:
|
||||||
self.dirty = True
|
self.dirty = True
|
||||||
@@ -894,6 +895,7 @@ class ListView(NavigationView):
|
|||||||
if self.active:
|
if self.active:
|
||||||
# Save the currently selected handles, if any:
|
# Save the currently selected handles, if any:
|
||||||
selected_ids = self.selected_handles()
|
selected_ids = self.selected_handles()
|
||||||
|
if self.bookmarks:
|
||||||
self.bookmarks.redraw()
|
self.bookmarks.redraw()
|
||||||
self.build_tree()
|
self.build_tree()
|
||||||
# Reselect one, if it still exists after rebuild:
|
# Reselect one, if it still exists after rebuild:
|
||||||
|
@@ -75,7 +75,12 @@ class NavigationView(PageView):
|
|||||||
|
|
||||||
def __init__(self, title, pdata, state, uistate, bm_type, nav_group):
|
def __init__(self, title, pdata, state, uistate, bm_type, nav_group):
|
||||||
PageView.__init__(self, title, pdata, state, uistate)
|
PageView.__init__(self, title, pdata, state, uistate)
|
||||||
self.bookmarks = bm_type(self.dbstate, self.uistate, self.change_active)
|
if bm_type:
|
||||||
|
self.bookmarks = bm_type(
|
||||||
|
self.dbstate, self.uistate, self.change_active
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.bookmarks = None
|
||||||
|
|
||||||
self.fwd_action = None
|
self.fwd_action = None
|
||||||
self.back_action = None
|
self.back_action = None
|
||||||
@@ -103,6 +108,7 @@ class NavigationView(PageView):
|
|||||||
Define menu actions.
|
Define menu actions.
|
||||||
"""
|
"""
|
||||||
PageView.define_actions(self)
|
PageView.define_actions(self)
|
||||||
|
if self.bookmarks:
|
||||||
self.bookmark_actions()
|
self.bookmark_actions()
|
||||||
self.navigation_actions()
|
self.navigation_actions()
|
||||||
|
|
||||||
@@ -151,6 +157,7 @@ class NavigationView(PageView):
|
|||||||
Called when the page becomes active (displayed).
|
Called when the page becomes active (displayed).
|
||||||
"""
|
"""
|
||||||
PageView.set_active(self)
|
PageView.set_active(self)
|
||||||
|
if self.bookmarks:
|
||||||
self.bookmarks.display()
|
self.bookmarks.display()
|
||||||
|
|
||||||
hobj = self.get_history()
|
hobj = self.get_history()
|
||||||
@@ -166,6 +173,7 @@ class NavigationView(PageView):
|
|||||||
"""
|
"""
|
||||||
if self.active:
|
if self.active:
|
||||||
PageView.set_inactive(self)
|
PageView.set_inactive(self)
|
||||||
|
if self.bookmarks:
|
||||||
self.bookmarks.undisplay()
|
self.bookmarks.undisplay()
|
||||||
hobj = self.get_history()
|
hobj = self.get_history()
|
||||||
hobj.disconnect(self.active_signal)
|
hobj.disconnect(self.active_signal)
|
||||||
|
@@ -30,6 +30,7 @@ __all__ = ["MonitoredCheckbox", "MonitoredEntry",
|
|||||||
# Standard python modules
|
# Standard python modules
|
||||||
#
|
#
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
|
import pickle
|
||||||
import logging
|
import logging
|
||||||
_LOG = logging.getLogger(".widgets.monitoredwidgets")
|
_LOG = logging.getLogger(".widgets.monitoredwidgets")
|
||||||
|
|
||||||
@@ -54,13 +55,13 @@ from ..autocomp import StandardCustomSelector, fill_entry
|
|||||||
from gramps.gen.datehandler import displayer, parser
|
from gramps.gen.datehandler import displayer, parser
|
||||||
from gramps.gen.lib.date import Date, NextYear
|
from gramps.gen.lib.date import Date, NextYear
|
||||||
from gramps.gen.errors import ValidationError
|
from gramps.gen.errors import ValidationError
|
||||||
|
from gramps.gui.ddtargets import DdTargets
|
||||||
|
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# constants
|
# constants
|
||||||
#
|
#
|
||||||
#------------------------------------------------------------------------
|
#------------------------------------------------------------------------
|
||||||
|
|
||||||
_RETURN = Gdk.keyval_from_name("Return")
|
_RETURN = Gdk.keyval_from_name("Return")
|
||||||
_KP_ENTER = Gdk.keyval_from_name("KP_Enter")
|
_KP_ENTER = Gdk.keyval_from_name("KP_Enter")
|
||||||
|
|
||||||
@@ -813,6 +814,8 @@ class MonitoredTagList:
|
|||||||
self.label = label
|
self.label = label
|
||||||
self.label.set_halign(Gtk.Align.START)
|
self.label.set_halign(Gtk.Align.START)
|
||||||
self.label.set_ellipsize(Pango.EllipsizeMode.END)
|
self.label.set_ellipsize(Pango.EllipsizeMode.END)
|
||||||
|
self.label.drag_dest_set(Gtk.DestDefaults.ALL, [DdTargets.TAG_LINK.target()], Gdk.DragAction.COPY)
|
||||||
|
self.label.connect('drag_data_received', self.tag_dropped)
|
||||||
image = Gtk.Image()
|
image = Gtk.Image()
|
||||||
image.set_from_icon_name('gramps-tag', Gtk.IconSize.MENU)
|
image.set_from_icon_name('gramps-tag', Gtk.IconSize.MENU)
|
||||||
button.set_image (image)
|
button.set_image (image)
|
||||||
@@ -856,3 +859,19 @@ class MonitoredTagList:
|
|||||||
self.set_list([item[0] for item in self.tag_list])
|
self.set_list([item[0] for item in self.tag_list])
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def tag_dropped(self, _dummy_widget, _dummy_context, _dummy_x,
|
||||||
|
_dummy_y, data, _dummy_info, _dummy_time):
|
||||||
|
"""
|
||||||
|
Add dropped tag if not in list.
|
||||||
|
"""
|
||||||
|
if data and data.get_data():
|
||||||
|
(dnd_type, obj_id, handle, val) = pickle.loads(data.get_data())
|
||||||
|
for item in self.tag_list:
|
||||||
|
if item[0] == handle:
|
||||||
|
return True
|
||||||
|
tag = self.db.get_tag_from_handle(handle)
|
||||||
|
self.tag_list.append((handle, tag.get_name()))
|
||||||
|
self._display()
|
||||||
|
self.set_list([item[0] for item in self.tag_list])
|
||||||
|
return True
|
||||||
|
583
gramps/plugins/view/tagview.py
Normal file
583
gramps/plugins/view/tagview.py
Normal file
@@ -0,0 +1,583 @@
|
|||||||
|
# Gramps - a GTK+/GNOME based genealogy program
|
||||||
|
#
|
||||||
|
# Copyright (C) 2001-2006 Donald N. Allingham
|
||||||
|
# Copyright (C) 2008 Gary Burton
|
||||||
|
# Copyright (C) 2010 Nick Hall
|
||||||
|
# Copyright (C) 2022 Christopher Horn
|
||||||
|
#
|
||||||
|
# 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 2 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, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
Tag View.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# GTK/Gnome Modules
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
from gi.repository import Gtk
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Gramps Modules
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||||
|
from gramps.gen.datehandler import format_time
|
||||||
|
from gramps.gen.db import DbTxn
|
||||||
|
from gramps.gen.errors import WindowActiveError
|
||||||
|
from gramps.gen.lib import Tag
|
||||||
|
from gramps.gui.ddtargets import DdTargets
|
||||||
|
from gramps.gui.dialog import QuestionDialog2
|
||||||
|
from gramps.gui.views.listview import ListView, TEXT
|
||||||
|
from gramps.gui.views.tags import EditTag, OrganizeTagsDialog
|
||||||
|
from gramps.gui.views.treemodels.flatbasemodel import FlatBaseModel
|
||||||
|
import gramps.gui.widgets.progressdialog as progressdlg
|
||||||
|
|
||||||
|
_ = glocale.translation.sgettext
|
||||||
|
|
||||||
|
|
||||||
|
(POS_HANDLE, POS_NAME, POS_COLOR, POS_PRIORITY, POS_CHANGE) = list(range(5))
|
||||||
|
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# TagModel
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
class TagModel(FlatBaseModel):
|
||||||
|
"""
|
||||||
|
Basic model for a Tag list
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
db,
|
||||||
|
uistate,
|
||||||
|
scol=0,
|
||||||
|
order=Gtk.SortType.ASCENDING,
|
||||||
|
search=None,
|
||||||
|
skip=None,
|
||||||
|
sort_map=None,
|
||||||
|
):
|
||||||
|
"""Setup initial values for instance variables."""
|
||||||
|
skip = skip or set()
|
||||||
|
self.gen_cursor = db.get_tag_cursor
|
||||||
|
self.map = db.get_raw_tag_data
|
||||||
|
self.fmap = [
|
||||||
|
self.column_name,
|
||||||
|
self.column_color,
|
||||||
|
self.column_priority,
|
||||||
|
self.column_change,
|
||||||
|
self.column_count,
|
||||||
|
]
|
||||||
|
self.smap = [
|
||||||
|
self.column_name,
|
||||||
|
self.column_color,
|
||||||
|
self.column_priority,
|
||||||
|
self.sort_change,
|
||||||
|
self.sort_count,
|
||||||
|
]
|
||||||
|
FlatBaseModel.__init__(
|
||||||
|
self,
|
||||||
|
db,
|
||||||
|
uistate,
|
||||||
|
scol,
|
||||||
|
order,
|
||||||
|
search=search,
|
||||||
|
skip=skip,
|
||||||
|
sort_map=sort_map,
|
||||||
|
)
|
||||||
|
|
||||||
|
def destroy(self):
|
||||||
|
"""
|
||||||
|
Unset all elements that can prevent garbage collection
|
||||||
|
"""
|
||||||
|
self.db = None
|
||||||
|
self.gen_cursor = None
|
||||||
|
self.map = None
|
||||||
|
self.fmap = None
|
||||||
|
self.smap = None
|
||||||
|
FlatBaseModel.destroy(self)
|
||||||
|
|
||||||
|
def color_column(self):
|
||||||
|
"""
|
||||||
|
Return the color column.
|
||||||
|
"""
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def on_get_n_columns(self):
|
||||||
|
"""
|
||||||
|
Return the column number of the Tag tab.
|
||||||
|
"""
|
||||||
|
return len(self.fmap) + 1
|
||||||
|
|
||||||
|
def column_handle(self, data):
|
||||||
|
"""
|
||||||
|
Return the handle of the Tag.
|
||||||
|
"""
|
||||||
|
return data[POS_HANDLE]
|
||||||
|
|
||||||
|
def column_name(self, data):
|
||||||
|
"""
|
||||||
|
Return the name of the Tag in readable format.
|
||||||
|
"""
|
||||||
|
return data[POS_NAME]
|
||||||
|
|
||||||
|
def column_priority(self, data):
|
||||||
|
"""
|
||||||
|
Return the priority of the Tag.
|
||||||
|
"""
|
||||||
|
return "%03d" % data[POS_PRIORITY]
|
||||||
|
|
||||||
|
def column_color(self, data):
|
||||||
|
"""
|
||||||
|
Return the color.
|
||||||
|
"""
|
||||||
|
return data[POS_COLOR]
|
||||||
|
|
||||||
|
def sort_change(self, data):
|
||||||
|
"""
|
||||||
|
Return sort value for change.
|
||||||
|
"""
|
||||||
|
return "%012x" % data[POS_CHANGE]
|
||||||
|
|
||||||
|
def column_change(self, data):
|
||||||
|
"""
|
||||||
|
Return formatted change time.
|
||||||
|
"""
|
||||||
|
return format_time(data[POS_CHANGE])
|
||||||
|
|
||||||
|
def sort_count(self, data):
|
||||||
|
"""
|
||||||
|
Return sort value for count of tagged items.
|
||||||
|
"""
|
||||||
|
return "%012d" % len(
|
||||||
|
list(self.db.find_backlink_handles(data[POS_HANDLE]))
|
||||||
|
)
|
||||||
|
|
||||||
|
def column_count(self, data):
|
||||||
|
"""
|
||||||
|
Return count of tagged items.
|
||||||
|
"""
|
||||||
|
return int(len(list(self.db.find_backlink_handles(data[POS_HANDLE]))))
|
||||||
|
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# TagView
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
class TagView(ListView):
|
||||||
|
"""
|
||||||
|
TagView, a normal flat listview for the tags
|
||||||
|
"""
|
||||||
|
|
||||||
|
COL_NAME = 0
|
||||||
|
COL_COLO = 1
|
||||||
|
COL_PRIO = 2
|
||||||
|
COL_CHAN = 3
|
||||||
|
COL_COUNT = 4
|
||||||
|
|
||||||
|
# column definitions
|
||||||
|
COLUMNS = [
|
||||||
|
(_("Name"), TEXT, None),
|
||||||
|
(_("Color"), TEXT, None),
|
||||||
|
(_("Priority"), TEXT, None),
|
||||||
|
(_("Last Changed"), TEXT, None),
|
||||||
|
(_("Tagged Items"), TEXT, None),
|
||||||
|
]
|
||||||
|
# default setting with visible columns, order of the col, and their size
|
||||||
|
CONFIGSETTINGS = (
|
||||||
|
(
|
||||||
|
"columns.visible",
|
||||||
|
[COL_NAME, COL_COLO, COL_PRIO, COL_CHAN, COL_COUNT],
|
||||||
|
),
|
||||||
|
("columns.rank", [COL_NAME, COL_COLO, COL_PRIO, COL_CHAN, COL_COUNT]),
|
||||||
|
("columns.size", [330, 150, 70, 200, 50]),
|
||||||
|
)
|
||||||
|
|
||||||
|
ADD_MSG = _("Add a new tag")
|
||||||
|
EDIT_MSG = _("Edit the selected tag")
|
||||||
|
DEL_MSG = _("Delete the selected tag")
|
||||||
|
ORGANIZE_MSG = _("Organize tags")
|
||||||
|
|
||||||
|
FILTER_TYPE = "Tag"
|
||||||
|
QR_CATEGORY = -1
|
||||||
|
|
||||||
|
def __init__(self, pdata, dbstate, uistate, nav_group=0):
|
||||||
|
signal_map = {
|
||||||
|
"tag-add": self.row_add,
|
||||||
|
"tag-update": self.row_update,
|
||||||
|
"tag-delete": self.row_delete,
|
||||||
|
"tag-rebuild": self.object_build,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Work around for modify_statusbar issues
|
||||||
|
if "Tag" not in uistate.NAV2MES:
|
||||||
|
uistate.NAV2MES["Tag"] = ""
|
||||||
|
|
||||||
|
ListView.__init__(
|
||||||
|
self,
|
||||||
|
_("Tags"),
|
||||||
|
pdata,
|
||||||
|
dbstate,
|
||||||
|
uistate,
|
||||||
|
TagModel,
|
||||||
|
signal_map,
|
||||||
|
None,
|
||||||
|
nav_group,
|
||||||
|
filter_class=None,
|
||||||
|
multiple=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.additional_uis.append(self.additional_ui)
|
||||||
|
|
||||||
|
def navigation_type(self):
|
||||||
|
"""
|
||||||
|
Return the navigation type.
|
||||||
|
"""
|
||||||
|
return "Tag"
|
||||||
|
|
||||||
|
def drag_info(self):
|
||||||
|
"""
|
||||||
|
Return a drag type of TAG_LINK
|
||||||
|
"""
|
||||||
|
return DdTargets.TAG_LINK
|
||||||
|
|
||||||
|
def get_stock(self):
|
||||||
|
"""
|
||||||
|
Return the gramps-tag stock icon
|
||||||
|
"""
|
||||||
|
return "gramps-tag"
|
||||||
|
|
||||||
|
additional_ui = [ # Defines the UI string for UIManager
|
||||||
|
"""
|
||||||
|
<placeholder id="LocalExport">
|
||||||
|
<item>
|
||||||
|
<attribute name="action">win.ExportTab</attribute>
|
||||||
|
<attribute name="label" translatable="yes">Export View...</attribute>
|
||||||
|
</item>
|
||||||
|
</placeholder>
|
||||||
|
""",
|
||||||
|
"""
|
||||||
|
<placeholder id="CommonGo">
|
||||||
|
<section>
|
||||||
|
<item>
|
||||||
|
<attribute name="action">win.Back</attribute>
|
||||||
|
<attribute name="label" translatable="yes">_Back</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="action">win.Forward</attribute>
|
||||||
|
<attribute name="label" translatable="yes">_Forward</attribute>
|
||||||
|
</item>
|
||||||
|
</section>
|
||||||
|
</placeholder>
|
||||||
|
""",
|
||||||
|
"""
|
||||||
|
<section id='CommonEdit' groups='RW'>
|
||||||
|
<item>
|
||||||
|
<attribute name="action">win.Add</attribute>
|
||||||
|
<attribute name="label" translatable="yes">_Add...</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="action">win.Edit</attribute>
|
||||||
|
<attribute name="label">%s</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="action">win.Remove</attribute>
|
||||||
|
<attribute name="label" translatable="yes">_Delete</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="action">win.Organize</attribute>
|
||||||
|
<attribute name="label" translatable="yes">_Organize...</attribute>
|
||||||
|
</item>
|
||||||
|
</section>
|
||||||
|
"""
|
||||||
|
% _(
|
||||||
|
"_Edit...", "action"
|
||||||
|
), # to use sgettext() # Following are the Toolbar items
|
||||||
|
"""
|
||||||
|
<placeholder id='CommonNavigation'>
|
||||||
|
<child groups='RO'>
|
||||||
|
<object class="GtkToolButton">
|
||||||
|
<property name="icon-name">go-previous</property>
|
||||||
|
<property name="action-name">win.Back</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">"""
|
||||||
|
"""Go to the previous object in the history</property>
|
||||||
|
<property name="label" translatable="yes">_Back</property>
|
||||||
|
<property name="use-underline">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="homogeneous">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child groups='RO'>
|
||||||
|
<object class="GtkToolButton">
|
||||||
|
<property name="icon-name">go-next</property>
|
||||||
|
<property name="action-name">win.Forward</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">"""
|
||||||
|
"""Go to the next object in the history</property>
|
||||||
|
<property name="label" translatable="yes">_Forward</property>
|
||||||
|
<property name="use-underline">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="homogeneous">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</placeholder>
|
||||||
|
""",
|
||||||
|
"""
|
||||||
|
<placeholder id='BarCommonEdit'>
|
||||||
|
<child groups='RW'>
|
||||||
|
<object class="GtkToolButton">
|
||||||
|
<property name="icon-name">list-add</property>
|
||||||
|
<property name="action-name">win.Add</property>
|
||||||
|
<property name="tooltip_text">%s</property>
|
||||||
|
<property name="label" translatable="yes">_Add...</property>
|
||||||
|
<property name="use-underline">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="homogeneous">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child groups='RW'>
|
||||||
|
<object class="GtkToolButton">
|
||||||
|
<property name="icon-name">gtk-edit</property>
|
||||||
|
<property name="action-name">win.Edit</property>
|
||||||
|
<property name="tooltip_text">%s</property>
|
||||||
|
<property name="label" translatable="yes">Edit...</property>
|
||||||
|
<property name="use-underline">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="homogeneous">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child groups='RW'>
|
||||||
|
<object class="GtkToolButton">
|
||||||
|
<property name="icon-name">list-remove</property>
|
||||||
|
<property name="action-name">win.Remove</property>
|
||||||
|
<property name="tooltip_text">%s</property>
|
||||||
|
<property name="label" translatable="yes">_Delete</property>
|
||||||
|
<property name="use-underline">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="homogeneous">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child groups='RW'>
|
||||||
|
<object class="GtkToolButton">
|
||||||
|
<property name="icon-name">view-sort-descending</property>
|
||||||
|
<property name="action-name">win.Organize</property>
|
||||||
|
<property name="tooltip_text">%s</property>
|
||||||
|
<property name="label" translatable="yes">_Organize</property>
|
||||||
|
<property name="use-underline">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="homogeneous">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</placeholder>
|
||||||
|
"""
|
||||||
|
% (ADD_MSG, EDIT_MSG, DEL_MSG, ORGANIZE_MSG),
|
||||||
|
"""
|
||||||
|
<menu id="Popup">
|
||||||
|
<section>
|
||||||
|
<item>
|
||||||
|
<attribute name="action">win.Back</attribute>
|
||||||
|
<attribute name="label" translatable="yes">_Back</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="action">win.Forward</attribute>
|
||||||
|
<attribute name="label" translatable="yes">Forward</attribute>
|
||||||
|
</item>
|
||||||
|
</section>
|
||||||
|
<section id="PopUpTree">
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<item>
|
||||||
|
<attribute name="action">win.Add</attribute>
|
||||||
|
<attribute name="label" translatable="yes">_Add...</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="action">win.Edit</attribute>
|
||||||
|
<attribute name="label">%s</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="action">win.Remove</attribute>
|
||||||
|
<attribute name="label" translatable="yes">_Delete</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="action">win.Organize</attribute>
|
||||||
|
<attribute name="label" translatable="yes">_Organize...</attribute>
|
||||||
|
</item>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<placeholder id='QuickReport'>
|
||||||
|
</placeholder>
|
||||||
|
</section>
|
||||||
|
</menu>
|
||||||
|
"""
|
||||||
|
% _("_Edit...", "action"), # to use sgettext()
|
||||||
|
]
|
||||||
|
# Leave QuickReport as placeholder
|
||||||
|
|
||||||
|
def define_actions(self):
|
||||||
|
"""
|
||||||
|
Define actions for the view.
|
||||||
|
"""
|
||||||
|
ListView.define_actions(self)
|
||||||
|
self.edit_action.add_actions(
|
||||||
|
[("Organize", self.organize, "<PRIMARY>Home")]
|
||||||
|
)
|
||||||
|
|
||||||
|
def set_active(self):
|
||||||
|
"""
|
||||||
|
Set view active.
|
||||||
|
"""
|
||||||
|
ListView.set_active(self)
|
||||||
|
self.uistate.viewmanager.tags.tag_disable()
|
||||||
|
|
||||||
|
def set_inactive(self):
|
||||||
|
"""
|
||||||
|
Set view inactive.
|
||||||
|
"""
|
||||||
|
ListView.set_inactive(self)
|
||||||
|
self.uistate.viewmanager.tags.tag_enable(update_menu=False)
|
||||||
|
|
||||||
|
def get_handle_from_gramps_id(self, gid):
|
||||||
|
"""
|
||||||
|
Not applicable.
|
||||||
|
"""
|
||||||
|
return None
|
||||||
|
|
||||||
|
def add(self, *obj):
|
||||||
|
"""
|
||||||
|
Add new tag.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
EditTag(self.dbstate.db, self.uistate, [], Tag())
|
||||||
|
except WindowActiveError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def remove(self, *obj):
|
||||||
|
"""
|
||||||
|
Remove selected tag.
|
||||||
|
"""
|
||||||
|
handles = self.selected_handles()
|
||||||
|
if handles:
|
||||||
|
tag = self.dbstate.db.get_tag_from_handle(handles[0])
|
||||||
|
delete_tag(self.uistate.window, self.dbstate.db, tag)
|
||||||
|
|
||||||
|
def edit(self, *obj):
|
||||||
|
"""
|
||||||
|
Edit selected tag.
|
||||||
|
"""
|
||||||
|
for handle in self.selected_handles():
|
||||||
|
tag = self.dbstate.db.get_tag_from_handle(handle)
|
||||||
|
try:
|
||||||
|
EditTag(self.dbstate.db, self.uistate, [], tag)
|
||||||
|
except WindowActiveError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def organize(self, *_dummy_obj):
|
||||||
|
"""
|
||||||
|
Launch organize tool.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
OrganizeTagsDialog(self.dbstate.db, self.uistate, [])
|
||||||
|
except WindowActiveError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def merge(self, *obj):
|
||||||
|
"""
|
||||||
|
Not supported for now.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def tag_updated(self, handle_list):
|
||||||
|
"""
|
||||||
|
Not applicable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def get_default_gramplets(self):
|
||||||
|
"""
|
||||||
|
Define the default gramplets for the sidebar and bottombar.
|
||||||
|
"""
|
||||||
|
return ((), ())
|
||||||
|
|
||||||
|
def remove_object_from_handle(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Not applicable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def delete_tag(window, db, tag):
|
||||||
|
"""
|
||||||
|
Handle tag deletion, extracted from OrganizeTagsDialog.
|
||||||
|
"""
|
||||||
|
yes_no = QuestionDialog2(
|
||||||
|
_("Remove tag '%s'?") % tag.name,
|
||||||
|
_(
|
||||||
|
"The tag definition will be removed. The tag will be also "
|
||||||
|
"removed from all objects in the database."
|
||||||
|
),
|
||||||
|
_("Yes"),
|
||||||
|
_("No"),
|
||||||
|
parent=window,
|
||||||
|
)
|
||||||
|
prompt = yes_no.run()
|
||||||
|
if prompt:
|
||||||
|
fnc = {
|
||||||
|
"Person": (db.get_person_from_handle, db.commit_person),
|
||||||
|
"Family": (db.get_family_from_handle, db.commit_family),
|
||||||
|
"Event": (db.get_event_from_handle, db.commit_event),
|
||||||
|
"Place": (db.get_place_from_handle, db.commit_place),
|
||||||
|
"Source": (db.get_source_from_handle, db.commit_source),
|
||||||
|
"Citation": (db.get_citation_from_handle, db.commit_citation),
|
||||||
|
"Repository": (
|
||||||
|
db.get_repository_from_handle,
|
||||||
|
db.commit_repository,
|
||||||
|
),
|
||||||
|
"Media": (db.get_media_from_handle, db.commit_media),
|
||||||
|
"Note": (db.get_note_from_handle, db.commit_note),
|
||||||
|
}
|
||||||
|
|
||||||
|
links = list(db.find_backlink_handles(tag.handle))
|
||||||
|
# Make the dialog modal so that the user can't start another
|
||||||
|
# database transaction while the one removing tags is still running.
|
||||||
|
pmon = progressdlg.ProgressMonitor(
|
||||||
|
progressdlg.GtkProgressDialog,
|
||||||
|
("", window, Gtk.DialogFlags.MODAL),
|
||||||
|
popup_time=2,
|
||||||
|
)
|
||||||
|
status = progressdlg.LongOpStatus(
|
||||||
|
msg=_("Removing Tags"),
|
||||||
|
total_steps=len(links),
|
||||||
|
interval=len(links) // 20,
|
||||||
|
)
|
||||||
|
pmon.add_op(status)
|
||||||
|
|
||||||
|
msg = _("Delete Tag (%s)") % tag.name
|
||||||
|
with DbTxn(msg, db) as trans:
|
||||||
|
for classname, handle in links:
|
||||||
|
status.heartbeat()
|
||||||
|
obj = fnc[classname][0](handle) # get from handle
|
||||||
|
obj.remove_tag(tag.handle)
|
||||||
|
fnc[classname][1](obj, trans) # commit
|
||||||
|
|
||||||
|
db.remove_tag(tag.handle, trans)
|
||||||
|
status.end()
|
@@ -307,3 +307,20 @@ category = ("Sources", _("Sources")),
|
|||||||
viewclass = 'CitationTreeView',
|
viewclass = 'CitationTreeView',
|
||||||
stock_icon = 'gramps-tree-select',
|
stock_icon = 'gramps-tree-select',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
register(
|
||||||
|
VIEW,
|
||||||
|
id="tagview",
|
||||||
|
name=_("Tags"),
|
||||||
|
description=_("The view showing all the tags"),
|
||||||
|
version="1.0",
|
||||||
|
gramps_target_version=MODULE_VERSION,
|
||||||
|
status=STABLE,
|
||||||
|
fname="tagview.py",
|
||||||
|
authors=["The Gramps project"],
|
||||||
|
authors_email=["http://gramps-project.org"],
|
||||||
|
category=("Tags", _("Tags")),
|
||||||
|
stock_icon="gramps-tag",
|
||||||
|
viewclass="TagView",
|
||||||
|
order=START,
|
||||||
|
)
|
||||||
|
Reference in New Issue
Block a user