Allow the user to directly input a Gramps ID in selectors
When a selector is focused, presso Ctrl-O to trigger an input dialog and insert a Gramps ID directly. The Gramps ID, if valid, will be used to populate the reference, bypassing the usual tree model loading mechanism of selection dialogs. This is a proof-of-concept commit. At a minimum, the feature should be made available as a button on selector dialogs, as currently the only way to access it is to know the "magic shortcut" Ctrl-O.
This commit is contained in:
parent
d04174cb99
commit
80cf4f0cb2
@ -25,6 +25,7 @@
|
|||||||
#
|
#
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
from gi.repository import Gtk
|
from gi.repository import Gtk
|
||||||
|
from gi.repository import Gdk
|
||||||
from gi.repository import Pango
|
from gi.repository import Pango
|
||||||
|
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
@ -32,12 +33,19 @@ from gi.repository import Pango
|
|||||||
# gramps modules
|
# gramps modules
|
||||||
#
|
#
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
|
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||||
|
_ = glocale.translation.gettext
|
||||||
from ..managedwindow import ManagedWindow
|
from ..managedwindow import ManagedWindow
|
||||||
from ..filters import SearchBar
|
from ..filters import SearchBar
|
||||||
from ..glade import Glade
|
from ..glade import Glade
|
||||||
from ..widgets.interactivesearchbox import InteractiveSearchBox
|
from ..widgets.interactivesearchbox import InteractiveSearchBox
|
||||||
from ..display import display_help
|
from ..display import display_help
|
||||||
from gramps.gen.const import URL_MANUAL_PAGE
|
from gramps.gen.const import URL_MANUAL_PAGE
|
||||||
|
from ..utils import match_primary_mask
|
||||||
|
from ..dialog import InputDialog
|
||||||
|
|
||||||
|
# keyboard shortcut for direct ID input (see BaseSelector._direct_id_input)
|
||||||
|
_OPEN = Gdk.keyval_from_name("o")
|
||||||
|
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
@ -55,11 +63,13 @@ class BaseSelector(ManagedWindow):
|
|||||||
IMAGE = 2
|
IMAGE = 2
|
||||||
|
|
||||||
def __init__(self, dbstate, uistate, track=[], filter=None, skip=set(),
|
def __init__(self, dbstate, uistate, track=[], filter=None, skip=set(),
|
||||||
show_search_bar = True, default=None):
|
show_search_bar=True, default=None, direct_id_input=True):
|
||||||
"""Set up the dialog with the dbstate and uistate, track of parent
|
"""Set up the dialog with the dbstate and uistate, track of parent
|
||||||
windows for ManagedWindow, initial filter for the model, skip with
|
windows for ManagedWindow, initial filter for the model, skip with
|
||||||
set of handles to skip in the view, and search_bar to show the
|
set of handles to skip in the view, and search_bar to show the
|
||||||
SearchBar at the top or not.
|
SearchBar at the top or not. direct_id_input enables a keyboard
|
||||||
|
shortcut to allow the user to directly input an object ID for this
|
||||||
|
selection.
|
||||||
"""
|
"""
|
||||||
self.filter = (2, filter, False)
|
self.filter = (2, filter, False)
|
||||||
|
|
||||||
@ -92,9 +102,9 @@ class BaseSelector(ManagedWindow):
|
|||||||
self.glade.get_object('help'), self.WIKI_HELP_PAGE,
|
self.glade.get_object('help'), self.WIKI_HELP_PAGE,
|
||||||
self.WIKI_HELP_SEC)
|
self.WIKI_HELP_SEC)
|
||||||
|
|
||||||
# connect to signal for custom interactive-search
|
# connect to signal for shortcuts and custom interactive-search
|
||||||
self.searchbox = InteractiveSearchBox(self.tree)
|
self.searchbox = InteractiveSearchBox(self.tree)
|
||||||
self.tree.connect('key-press-event', self.searchbox.treeview_keypress)
|
self.tree.connect('key-press-event', self._key_pressed)
|
||||||
|
|
||||||
#add the search bar
|
#add the search bar
|
||||||
self.search_bar = SearchBar(dbstate, uistate, self.build_tree, apply_clear=self.apply_clear)
|
self.search_bar = SearchBar(dbstate, uistate, self.build_tree, apply_clear=self.apply_clear)
|
||||||
@ -136,6 +146,9 @@ class BaseSelector(ManagedWindow):
|
|||||||
if default:
|
if default:
|
||||||
self.goto_handle(default)
|
self.goto_handle(default)
|
||||||
|
|
||||||
|
self.can_direct_id_input = direct_id_input
|
||||||
|
self.direct_id_selection = None
|
||||||
|
|
||||||
def goto_handle(self, handle):
|
def goto_handle(self, handle):
|
||||||
"""
|
"""
|
||||||
Goto the correct row.
|
Goto the correct row.
|
||||||
@ -209,6 +222,12 @@ class BaseSelector(ManagedWindow):
|
|||||||
if id_list and id_list[0]:
|
if id_list and id_list[0]:
|
||||||
result = self.get_from_handle_func()(id_list[0])
|
result = self.get_from_handle_func()(id_list[0])
|
||||||
self.close()
|
self.close()
|
||||||
|
elif val == Gtk.ResponseType.APPLY:
|
||||||
|
# user has invoked the direct selection dialog and typed a valid id,
|
||||||
|
# direct_id_input() has populated self.direct_id_selection and
|
||||||
|
# emitted an APPLY response to signal we're done.
|
||||||
|
result = self.direct_id_selection
|
||||||
|
self.close()
|
||||||
elif val != Gtk.ResponseType.DELETE_EVENT:
|
elif val != Gtk.ResponseType.DELETE_EVENT:
|
||||||
self.close()
|
self.close()
|
||||||
return result
|
return result
|
||||||
@ -240,6 +259,9 @@ class BaseSelector(ManagedWindow):
|
|||||||
def get_from_handle_func(self):
|
def get_from_handle_func(self):
|
||||||
assert False, "Must be defined in the subclass"
|
assert False, "Must be defined in the subclass"
|
||||||
|
|
||||||
|
def get_from_gramps_id_func(self):
|
||||||
|
assert False, "Must be defined in the subclass"
|
||||||
|
|
||||||
def set_show_search_bar(self, value):
|
def set_show_search_bar(self, value):
|
||||||
"""make the search bar at the top shown
|
"""make the search bar at the top shown
|
||||||
"""
|
"""
|
||||||
@ -338,6 +360,29 @@ class BaseSelector(ManagedWindow):
|
|||||||
self.tree.set_model(self.model)
|
self.tree.set_model(self.model)
|
||||||
self.tree.grab_focus()
|
self.tree.grab_focus()
|
||||||
|
|
||||||
|
def direct_id_input(self):
|
||||||
|
"""Handles "direct ID input" - in other words, directly accepts an
|
||||||
|
object ID from the user and selects it in the list.
|
||||||
|
"""
|
||||||
|
id_input = InputDialog(_('Enter the object ID'), None,
|
||||||
|
self.window).run()
|
||||||
|
if id_input is not None:
|
||||||
|
obj = self.get_from_gramps_id_func()(id_input)
|
||||||
|
if obj is not None:
|
||||||
|
# instead of selecting the object in the tree, we bypass the
|
||||||
|
# tree altogether, to avoid an expensive rebuild in case the
|
||||||
|
# selected object is not loaded
|
||||||
|
self.direct_id_selection = obj
|
||||||
|
self.window.response(Gtk.ResponseType.APPLY)
|
||||||
|
|
||||||
|
def _key_pressed(self, obj, event):
|
||||||
|
if self.can_direct_id_input and event.keyval in (_OPEN,) and \
|
||||||
|
match_primary_mask(event.get_state()):
|
||||||
|
self.direct_id_input()
|
||||||
|
else:
|
||||||
|
# fallback to "interactive search" if the event is not more specific
|
||||||
|
self.searchbox.treeview_keypress(obj, event)
|
||||||
|
|
||||||
def clear_model(self):
|
def clear_model(self):
|
||||||
if self.model:
|
if self.model:
|
||||||
self.tree.set_model(None)
|
self.tree.set_model(None)
|
||||||
|
@ -74,10 +74,20 @@ class SelectCitation(BaseSelector):
|
|||||||
(_('Last Change'), 150, BaseSelector.TEXT, 6),
|
(_('Last Change'), 150, BaseSelector.TEXT, 6),
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_from_handle_func(self):
|
def get_from_gramps_id_func(self):
|
||||||
return self.get_source_or_citation
|
return self._get_source_or_citation_by_gramps_id
|
||||||
|
|
||||||
def get_source_or_citation(self, handle):
|
def get_from_handle_func(self):
|
||||||
|
return self._get_source_or_citation_by_handle
|
||||||
|
|
||||||
|
def _get_source_or_citation_by_gramps_id(self, gramps_id):
|
||||||
|
source = self.db.get_source_from_gramps_id(gramps_id)
|
||||||
|
if source is not None:
|
||||||
|
return source
|
||||||
|
else:
|
||||||
|
return self.db.get_citation_from_gramps_id(gramps_id)
|
||||||
|
|
||||||
|
def _get_source_or_citation_by_handle(self, handle):
|
||||||
if self.db.has_source_handle(handle):
|
if self.db.has_source_handle(handle):
|
||||||
return self.db.get_source_from_handle(handle)
|
return self.db.get_source_from_handle(handle)
|
||||||
else:
|
else:
|
||||||
|
@ -72,6 +72,9 @@ class SelectEvent(BaseSelector):
|
|||||||
(_('Last Change'), 150, BaseSelector.TEXT, 7)
|
(_('Last Change'), 150, BaseSelector.TEXT, 7)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def get_from_gramps_id_func(self):
|
||||||
|
return self.db.get_event_from_gramps_id
|
||||||
|
|
||||||
def get_from_handle_func(self):
|
def get_from_handle_func(self):
|
||||||
return self.db.get_event_from_handle
|
return self.db.get_event_from_handle
|
||||||
|
|
||||||
|
@ -69,6 +69,9 @@ class SelectFamily(BaseSelector):
|
|||||||
(_('Last Change'), 150, BaseSelector.TEXT, 7),
|
(_('Last Change'), 150, BaseSelector.TEXT, 7),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def get_from_gramps_id_func(self):
|
||||||
|
return self.db.get_family_from_gramps_id
|
||||||
|
|
||||||
def get_from_handle_func(self):
|
def get_from_handle_func(self):
|
||||||
return self.db.get_family_from_handle
|
return self.db.get_family_from_handle
|
||||||
|
|
||||||
|
@ -75,6 +75,9 @@ class SelectNote(BaseSelector):
|
|||||||
(_('Last Change'), 150, BaseSelector.TEXT, 5),
|
(_('Last Change'), 150, BaseSelector.TEXT, 5),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def get_from_gramps_id_func(self):
|
||||||
|
return self.db.get_note_from_gramps_id
|
||||||
|
|
||||||
def get_from_handle_func(self):
|
def get_from_handle_func(self):
|
||||||
return self.db.get_note_from_handle
|
return self.db.get_note_from_handle
|
||||||
|
|
||||||
|
@ -69,6 +69,9 @@ class SelectObject(BaseSelector):
|
|||||||
def get_model_class(self):
|
def get_model_class(self):
|
||||||
return MediaModel
|
return MediaModel
|
||||||
|
|
||||||
|
def get_from_gramps_id_func(self):
|
||||||
|
return self.db.get_media_from_gramps_id
|
||||||
|
|
||||||
def get_from_handle_func(self):
|
def get_from_handle_func(self):
|
||||||
return self.db.get_media_from_handle
|
return self.db.get_media_from_handle
|
||||||
|
|
||||||
|
@ -98,6 +98,9 @@ class SelectPerson(BaseSelector):
|
|||||||
(_('Last Change'), 150, BaseSelector.TEXT, 14)
|
(_('Last Change'), 150, BaseSelector.TEXT, 14)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def get_from_gramps_id_func(self):
|
||||||
|
return self.db.get_person_from_gramps_id
|
||||||
|
|
||||||
def get_from_handle_func(self):
|
def get_from_handle_func(self):
|
||||||
return self.db.get_person_from_handle
|
return self.db.get_person_from_handle
|
||||||
|
|
||||||
|
@ -71,6 +71,9 @@ class SelectPlace(BaseSelector):
|
|||||||
(_('Last Change'), 150, BaseSelector.TEXT, 9),
|
(_('Last Change'), 150, BaseSelector.TEXT, 9),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def get_from_gramps_id_func(self):
|
||||||
|
return self.db.get_place_from_gramps_id
|
||||||
|
|
||||||
def get_from_handle_func(self):
|
def get_from_handle_func(self):
|
||||||
return self.db.get_place_from_handle
|
return self.db.get_place_from_handle
|
||||||
|
|
||||||
|
@ -68,6 +68,9 @@ class SelectRepository(BaseSelector):
|
|||||||
(_('Last Change'), 150, BaseSelector.TEXT, 14),
|
(_('Last Change'), 150, BaseSelector.TEXT, 14),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def get_from_gramps_id_func(self):
|
||||||
|
return self.db.get_repository_from_gramps_id
|
||||||
|
|
||||||
def get_from_handle_func(self):
|
def get_from_handle_func(self):
|
||||||
return self.db.get_repository_from_handle
|
return self.db.get_repository_from_handle
|
||||||
|
|
||||||
|
@ -70,6 +70,9 @@ class SelectSource(BaseSelector):
|
|||||||
(_('Last Change'), 150, BaseSelector.TEXT, 7),
|
(_('Last Change'), 150, BaseSelector.TEXT, 7),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def get_from_gramps_id_func(self):
|
||||||
|
return self.db.get_source_from_gramps_id
|
||||||
|
|
||||||
def get_from_handle_func(self):
|
def get_from_handle_func(self):
|
||||||
return self.db.get_source_from_handle
|
return self.db.get_source_from_handle
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user