Add tag list view, enable tag drag and drop
This commit is contained in:
parent
47f392ef70
commit
0b88f0cbbe
gramps
gen/utils
gui
plugins/view
@ -369,6 +369,10 @@ def navigation_label(db, nav_type, handle):
|
||||
label = " ".join(label.split())
|
||||
if len(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:
|
||||
label = '[%s] %s' % (obj.get_gramps_id(), label)
|
||||
|
@ -94,7 +94,8 @@ for (name, icon) in (("media", "gramps-media"),
|
||||
('source', 'gramps-source'),
|
||||
('citation', 'gramps-citation'),
|
||||
('text', 'gramps-font'),
|
||||
('url', 'gramps-geo')):
|
||||
('url', 'gramps-geo'),
|
||||
('tag', 'gramps-tag')):
|
||||
ICONS[name] = theme.load_icon(icon, 16, 0)
|
||||
|
||||
|
||||
@ -118,6 +119,7 @@ def map2class(target):
|
||||
'place-link': ClipPlace,
|
||||
'placeref': ClipPlaceRef,
|
||||
'note-link': ClipNote,
|
||||
'tag': ClipTag,
|
||||
'TEXT': ClipText}
|
||||
return _d_[target] if target in _d_ else None
|
||||
|
||||
@ -131,7 +133,8 @@ def obj2class(target):
|
||||
'Event': ClipEvent,
|
||||
'Media': ClipMediaObj,
|
||||
'Place': ClipPlace,
|
||||
'Note': ClipNote}
|
||||
'Note': ClipNote,
|
||||
'Tag': ClipTag}
|
||||
return _d_[target] if target in _d_ else None
|
||||
|
||||
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),
|
||||
'Media': Gdk.atom_intern('media', 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):
|
||||
@ -280,7 +284,7 @@ class ClipObjWrapper(ClipWrapper):
|
||||
return False
|
||||
|
||||
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):
|
||||
return False
|
||||
|
||||
@ -424,6 +428,26 @@ class ClipUrl(ClipObjWrapper):
|
||||
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):
|
||||
|
||||
DROP_TARGETS = [DdTargets.ATTRIBUTE]
|
||||
@ -993,7 +1017,9 @@ class ClipboardListView:
|
||||
'event-rebuild',
|
||||
'repository-update',
|
||||
'repository-rebuild',
|
||||
'note-rebuild')
|
||||
'note-rebuild',
|
||||
'tag-update',
|
||||
'tag-rebuild')
|
||||
|
||||
for signal in db_signals:
|
||||
clipdb.connect(signal, self.refresh_objects)
|
||||
@ -1022,6 +1048,8 @@ class ClipboardListView:
|
||||
gen_del_obj(self.delete_object, 'place-link'))
|
||||
clipdb.connect('note-delete',
|
||||
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!
|
||||
|
||||
self.refresh_objects()
|
||||
@ -1087,6 +1115,7 @@ class ClipboardListView:
|
||||
self.register_wrapper_class(ClipChildRef)
|
||||
self.register_wrapper_class(ClipText)
|
||||
self.register_wrapper_class(ClipNote)
|
||||
self.register_wrapper_class(ClipTag)
|
||||
|
||||
def register_wrapper_class(self, wrapper_class):
|
||||
for drop_target in wrapper_class.DROP_TARGETS:
|
||||
@ -1593,6 +1622,7 @@ class MultiTreeView(Gtk.TreeView):
|
||||
from .editors import (EditPerson, EditEvent, EditFamily, EditSource,
|
||||
EditPlace, EditRepository, EditNote, EditMedia,
|
||||
EditCitation)
|
||||
from .views.tags import EditTag
|
||||
if obj2class(objclass): # make sure it is an editable object
|
||||
if self.dbstate.db.method('has_%s_handle', objclass)(handle):
|
||||
g_object = self.dbstate.db.method(
|
||||
|
@ -154,6 +154,7 @@ class _DdTargets:
|
||||
self.URL = _DdType(self, 'url')
|
||||
self.SURNAME = _DdType(self, 'surname')
|
||||
self.CITATION_LINK = _DdType(self, 'citation-link')
|
||||
self.TAG_LINK = _DdType(self, 'tag')
|
||||
|
||||
# List of all types that are used between
|
||||
# gramps widgets but should not be exported
|
||||
@ -185,7 +186,8 @@ class _DdTargets:
|
||||
self.SRCATTRIBUTE,
|
||||
self.URL,
|
||||
self.SURNAME,
|
||||
self.CITATION_LINK
|
||||
self.CITATION_LINK,
|
||||
self.TAG_LINK,
|
||||
]
|
||||
|
||||
self.CHILD = _DdType(self, 'child')
|
||||
|
@ -68,6 +68,7 @@ CATEGORY_ICON = {
|
||||
'Media': 'gramps-media',
|
||||
'Notes': 'gramps-notes',
|
||||
'Citations': 'gramps-citation',
|
||||
'Tags': 'gramps-tag'
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
@ -781,7 +781,8 @@ class ListView(NavigationView):
|
||||
#force rebuild of the model on build of tree
|
||||
self.dirty = True
|
||||
self.build_tree()
|
||||
self.bookmarks.redraw()
|
||||
if self.bookmarks:
|
||||
self.bookmarks.redraw()
|
||||
else:
|
||||
self.dirty = True
|
||||
|
||||
@ -894,7 +895,8 @@ class ListView(NavigationView):
|
||||
if self.active:
|
||||
# Save the currently selected handles, if any:
|
||||
selected_ids = self.selected_handles()
|
||||
self.bookmarks.redraw()
|
||||
if self.bookmarks:
|
||||
self.bookmarks.redraw()
|
||||
self.build_tree()
|
||||
# Reselect one, if it still exists after rebuild:
|
||||
nav_type = self.navigation_type()
|
||||
|
@ -75,7 +75,12 @@ class NavigationView(PageView):
|
||||
|
||||
def __init__(self, title, pdata, state, uistate, bm_type, nav_group):
|
||||
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.back_action = None
|
||||
@ -103,7 +108,8 @@ class NavigationView(PageView):
|
||||
Define menu actions.
|
||||
"""
|
||||
PageView.define_actions(self)
|
||||
self.bookmark_actions()
|
||||
if self.bookmarks:
|
||||
self.bookmark_actions()
|
||||
self.navigation_actions()
|
||||
|
||||
def disable_action_group(self):
|
||||
@ -151,7 +157,8 @@ class NavigationView(PageView):
|
||||
Called when the page becomes active (displayed).
|
||||
"""
|
||||
PageView.set_active(self)
|
||||
self.bookmarks.display()
|
||||
if self.bookmarks:
|
||||
self.bookmarks.display()
|
||||
|
||||
hobj = self.get_history()
|
||||
self.active_signal = hobj.connect('active-changed', self.goto_active)
|
||||
@ -166,7 +173,8 @@ class NavigationView(PageView):
|
||||
"""
|
||||
if self.active:
|
||||
PageView.set_inactive(self)
|
||||
self.bookmarks.undisplay()
|
||||
if self.bookmarks:
|
||||
self.bookmarks.undisplay()
|
||||
hobj = self.get_history()
|
||||
hobj.disconnect(self.active_signal)
|
||||
hobj.disconnect(self.mru_signal)
|
||||
|
@ -30,6 +30,7 @@ __all__ = ["MonitoredCheckbox", "MonitoredEntry",
|
||||
# Standard python modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
import pickle
|
||||
import logging
|
||||
_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.lib.date import Date, NextYear
|
||||
from gramps.gen.errors import ValidationError
|
||||
from gramps.gui.ddtargets import DdTargets
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# constants
|
||||
#
|
||||
#------------------------------------------------------------------------
|
||||
|
||||
_RETURN = Gdk.keyval_from_name("Return")
|
||||
_KP_ENTER = Gdk.keyval_from_name("KP_Enter")
|
||||
|
||||
@ -813,6 +814,8 @@ class MonitoredTagList:
|
||||
self.label = label
|
||||
self.label.set_halign(Gtk.Align.START)
|
||||
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.set_from_icon_name('gramps-tag', Gtk.IconSize.MENU)
|
||||
button.set_image (image)
|
||||
@ -856,3 +859,19 @@ class MonitoredTagList:
|
||||
self.set_list([item[0] for item in self.tag_list])
|
||||
return True
|
||||
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',
|
||||
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,
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user