Make the tab columns resizable: PersistentTreeView

Implements #8767
This commit is contained in:
SNoiraud 2021-10-30 19:18:46 +02:00 committed by Nick Hall
parent 37395da262
commit c7b1a23441
65 changed files with 472 additions and 32 deletions

View File

@ -59,6 +59,7 @@ from .ddtargets import DdTargets
from .makefilter import make_filter
from .utils import is_right_click, no_match_primary_mask
from gramps.gen.const import GRAMPS_LOCALE as glocale
from gramps.gui.widgets.persistenttreeview import PersistentTreeView
_ = glocale.translation.sgettext
#-------------------------------------------------------------------------
@ -1412,6 +1413,7 @@ class ClipboardWindow(ManagedWindow):
self.db.connect('database-changed',
lambda x: ClipboardWindow.otree.clear())
mtv.restore_column_size()
self.show()
def build_menu_names(self, obj):
@ -1453,7 +1455,7 @@ class ClipboardWindow(ManagedWindow):
# MultiTreeView class
#
#-------------------------------------------------------------------------
class MultiTreeView(Gtk.TreeView):
class MultiTreeView(PersistentTreeView):
'''
TreeView that captures mouse events to make drag and drop work properly
'''
@ -1461,7 +1463,7 @@ class MultiTreeView(Gtk.TreeView):
self.dbstate = dbstate
self.uistate = uistate
self.title = title if title else _("Clipboard")
Gtk.TreeView.__init__(self)
PersistentTreeView.__init__(self, self.uistate, "clipboard")
self.connect('button_press_event', self.on_button_press)
self.connect('button_release_event', self.on_button_release)
self.connect('drag-end', self.on_drag_end)

View File

@ -63,7 +63,8 @@ class ColumnOrder(Gtk.Box):
Column ordering selection widget
"""
def __init__(self, config, column_names, widths, on_apply, tree=False):
def __init__(self, config, column_names, widths, on_apply, tree=False,
resizable=None):
"""
Create the Column Ordering widget based on config
@ -80,6 +81,7 @@ class ColumnOrder(Gtk.Box):
self.colnames = column_names
self.config = config
self.on_apply = on_apply
self.resizable = resizable
self.pack_start(Gtk.Label(label=' '), False, False, 0)
@ -137,7 +139,23 @@ class ColumnOrder(Gtk.Box):
#obtain the columns from config file
self.oldorder = self.config.get('columns.rank')
self.oldsize = self.config.get('columns.size')
oldsize = self.config.get('columns.size')
# try to use the current columns size.
if resizable:
self.oldsize = []
newsize = self.resizable.get_columns_size()
nbc = 0
for size in oldsize:
if nbc < len(newsize):
if newsize[nbc] == 0:
self.oldsize.append(size)
else:
self.oldsize.append(newsize[nbc])
else:
self.oldsize.append(size)
nbc += 1
else:
self.oldsize = oldsize
self.oldvis = self.config.get('columns.visible')
colord = []
index = 0
@ -185,6 +203,7 @@ class ColumnOrder(Gtk.Box):
self.config.set('columns.visible', newvis)
self.config.save()
self.on_apply()
self.resizable.save_column_info()
def toggled(cell, path, model):
"""

View File

@ -69,6 +69,7 @@ from .listmodel import ListModel
from gramps.gen.constfunc import win
from gramps.gen.plug import BasePluginManager
from gramps.gen.const import GRAMPS_LOCALE as glocale
from gramps.gui.widgets.persistenttreeview import PersistentTreeView
_ = glocale.translation.gettext
#-------------------------------------------------------------------------
@ -170,7 +171,7 @@ class DbManager(CLIDbManager, ManagedWindow):
self.viewmanager = viewmanager
for attr in ['connect_btn', 'cancel_btn', 'new_btn', 'remove_btn',
'info_btn', 'dblist', 'rename_btn', 'convert_btn',
'info_btn', 'rename_btn', 'convert_btn',
'repair_btn', 'rcs_btn', 'msg', 'close_btn']:
setattr(self, attr, self.glade.get_object(attr))
@ -179,6 +180,11 @@ class DbManager(CLIDbManager, ManagedWindow):
self.lock_file = None
self.data_to_delete = None
objectlist = self.glade.get_object('dblist')
self.dblist = PersistentTreeView(uistate, "dbman")
scrolledwindow = self.glade.get_object('scrolledwindow88')
scrolledwindow.remove(objectlist)
scrolledwindow.add(self.dblist)
self.selection = self.dblist.get_selection()
# For already loaded database:
@ -410,6 +416,7 @@ class DbManager(CLIDbManager, ManagedWindow):
column = Gtk.TreeViewColumn(_('Last accessed'), render, text=DATE_COL)
column.set_sort_column_id(DSORT_COL)
self.dblist.append_column(column)
self.dblist.restore_column_size()
def __populate(self):
"""

View File

@ -148,3 +148,6 @@ class AddrEmbedList(EmbeddedList):
Called to update the screen when the address changes
"""
self.rebuild()
def get_config_name(self):
return __name__

View File

@ -123,3 +123,6 @@ class AttrEmbedList(EmbeddedList):
def edit_callback(self, name):
self.changed = True
self.rebuild()
def get_config_name(self):
return __name__

View File

@ -143,3 +143,6 @@ class BackRefList(EmbeddedList):
def edit_button_clicked(self, obj):
(reftype, ref) = self.find_node()
edit_object(self.dbstate, self.uistate, reftype, ref)
def get_config_name(self):
return __name__

View File

@ -283,3 +283,6 @@ class CitationEmbedList(EmbeddedList, DbGUIElement):
parent=self.uistate.window)
else:
raise ValueError("selection must be either source or citation")
def get_config_name(self):
return __name__

View File

@ -43,6 +43,7 @@ from gi.repository import Pango
#
#-------------------------------------------------------------------------
from ...widgets.cellrenderertextedit import CellRendererTextEdit
from ...widgets.persistenttreeview import PersistentTreeView
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
from ...utils import is_right_click
@ -388,7 +389,8 @@ class EmbeddedList(ButtonTab):
# create the tree, turn on rule hinting and connect the
# button press to the double click function.
self.tree = Gtk.TreeView()
_name = self.get_config_name()
self.tree = PersistentTreeView(self.uistate, _name)
self.tree.set_reorderable(True)
self.tree.connect('button_press_event', self.double_click)
self.tree.connect('key_press_event', self.key_pressed)
@ -533,6 +535,12 @@ class EmbeddedList(ButtonTab):
self.columns.append(column)
self.tree.append_column(column)
self.track_ref_for_deletion("columns")
self.tree.restore_column_size()
def get_config_name(self):
""" used to associate the selector config name to the PersistentTreeView
"""
assert False, "Must be defined in the subclass"
def icon_func(self, column, renderer, model, iter_, col_num):
'''

View File

@ -41,3 +41,6 @@ class EventAttrEmbedList(AttrEmbedList):
def get_user_values(self):
return self.dbstate.db.get_event_attribute_types()
def get_config_name(self):
return __name__

View File

@ -419,3 +419,6 @@ class EventEmbedList(DbGUIElement, GroupEmbeddedList):
self.tree.expand_all()
if prebuildpath is not None:
self.selection.select_path(prebuildpath)
def get_config_name(self):
return __name__

View File

@ -41,3 +41,6 @@ class FamilyAttrEmbedList(AttrEmbedList):
def get_user_values(self):
return self.dbstate.db.get_family_attribute_types()
def get_config_name(self):
return __name__

View File

@ -68,3 +68,6 @@ class FamilyLdsEmbedList(LdsEmbedList):
lds = LdsOrd()
lds.set_type(LdsOrd.SEAL_TO_SPOUSE)
return lds
def get_config_name(self):
return __name__

View File

@ -400,3 +400,6 @@ class GroupEmbeddedList(EmbeddedList):
self._move_down(pos, ref[1])
elif ref and ref[1] is None:
self._move_down_group(ref[0])
def get_config_name(self):
assert False, "Must be defined in the subclass"

View File

@ -109,3 +109,6 @@ class LdsEmbedList(EmbeddedList):
def edit_callback(self, name):
self.rebuild()
def get_config_name(self):
return __name__

View File

@ -98,3 +98,6 @@ class LocationEmbedList(EmbeddedList):
def edit_callback(self, name):
self.rebuild()
def get_config_name(self):
return __name__

View File

@ -41,3 +41,6 @@ class MediaAttrEmbedList(AttrEmbedList):
def get_user_values(self):
return self.dbstate.db.get_media_attribute_types()
def get_config_name(self):
return __name__

View File

@ -221,3 +221,6 @@ class NameEmbedList(GroupEmbeddedList):
self.tree.expand_all()
if prebuildpath is not None:
self.selection.select_path(prebuildpath)
def get_config_name(self):
return __name__

View File

@ -208,3 +208,6 @@ class NoteTab(EmbeddedList, DbGUIElement):
if handle in self.data:
self.rebuild()
break
def get_config_name(self):
return __name__

View File

@ -190,3 +190,6 @@ class PersonEventEmbedList(EventEmbedList):
path = (index + 2,)
self.tree.get_selection().select_path(path)
GLib.idle_add(self.tree.scroll_to_cell, path)
def get_config_name(self):
return __name__

View File

@ -197,3 +197,6 @@ class PersonRefEmbedList(DbGUIElement, EmbeddedList):
ref, self.add_callback)
except WindowActiveError:
pass
def get_config_name(self):
return __name__

View File

@ -116,3 +116,6 @@ class PlaceNameEmbedList(EmbeddedList):
Called to update the screen when the place name changes.
"""
self.rebuild()
def get_config_name(self):
return __name__

View File

@ -199,3 +199,6 @@ class PlaceRefEmbedList(DbGUIElement, EmbeddedList):
place, placeref, self.add_callback)
except WindowActiveError:
pass
def get_config_name(self):
return __name__

View File

@ -214,3 +214,6 @@ class RepoEmbedList(EmbeddedList, DbGUIElement):
if handle in ref_handles:
self.rebuild()
break
def get_config_name(self):
return __name__

View File

@ -159,3 +159,6 @@ class SrcAttrEmbedList(EmbeddedList):
def edit_callback(self, name):
self.changed = True
self.rebuild()
def get_config_name(self):
return __name__

View File

@ -410,3 +410,6 @@ class SurnameTab(EmbeddedList):
self.curr_celle.editing_done()
return
return True
def get_config_name(self):
return __name__

View File

@ -124,3 +124,6 @@ class WebEmbedList(EmbeddedList):
url = self.get_selected()
if url.get_path():
display_url(url.get_path())
def get_config_name(self):
return __name__

View File

@ -367,6 +367,9 @@ class ChildEmbedList(DbGUIElement, EmbeddedList):
else:
return name
def get_config_name(self):
return __name__
class FastMaleFilter:
def __init__(self, db):
@ -1275,6 +1278,9 @@ class EditFamily(EditPrimary):
return name
return name
def get_config_name(self):
return __name__
def button_activated(event, mouse_button):
if (event.type == Gdk.EventType.BUTTON_PRESS and
event.button == mouse_button) or \

View File

@ -74,6 +74,7 @@ from gramps.gen.utils.string import conf_strings
from ..widgets import DateEntry
from gramps.gen.datehandler import displayer
from gramps.gen.config import config
from gramps.gui.widgets.persistenttreeview import PersistentTreeView
#-------------------------------------------------------------------------
#
@ -456,10 +457,15 @@ class EditRule(ManagedWindow):
self.window.hide()
self.valuebox = self.get_widget('valuebox')
self.rname_filter = self.get_widget('ruletreefilter')
self.rname = self.get_widget('ruletree')
self.rule_name = self.get_widget('rulename')
self.description = self.get_widget('description')
objectlist = self.get_widget('ruletree')
self.rname = PersistentTreeView(uistate, "filt_edit")
scrolledwindow = self.get_widget('box2')
scrolledwindow.remove(objectlist)
scrolledwindow.add(self.rname)
self.notebook = Gtk.Notebook()
self.notebook.set_show_tabs(0)
self.notebook.set_show_border(0)
@ -697,6 +703,7 @@ class EditRule(ManagedWindow):
config.register('interface.edit-rule-pane', 205)
panepos = config.get('interface.edit-rule-pane')
self.get_widget('hpaned1').set_position(panepos)
self.rname.restore_column_size()
self.show()
def select_iter(self, data):
@ -831,8 +838,14 @@ class EditFilter(ManagedWindow):
_('Define filter'))
self.setup_configs('interface.edit-filter', 500, 420)
objectlist = self.get_widget('rule_list')
self.rule_list = PersistentTreeView(uistate, "filt_rule")
scrolledwindow = self.get_widget('scrolledwindow1')
scrolledwindow.remove(objectlist)
scrolledwindow.add(self.rule_list)
self.rlist = ListModel(
self.get_widget('rule_list'),
self.rule_list,
[(_('Name'),-1,150),(_('Values'),-1,150)],
self.select_row,
self.on_edit_clicked)
@ -867,6 +880,7 @@ class EditFilter(ManagedWindow):
self.draw_rules()
self._set_size()
self.rule_list.restore_column_size()
self.show()
def on_help_clicked(self, obj):
@ -1089,7 +1103,6 @@ class FilterEditor(ManagedWindow):
self.namespace = namespace
self.define_glade('filter_list', RULE_GLADE)
self.filter_list = self.get_widget('filters')
self.edit = self.get_widget('filter_list_edit')
self.clone = self.get_widget('filter_list_clone')
self.delete = self.get_widget('filter_list_delete')
@ -1100,6 +1113,12 @@ class FilterEditor(ManagedWindow):
self.delete.set_sensitive(False)
self.test.set_sensitive(False)
objectlist = self.get_widget('filters')
self.filter_list = PersistentTreeView(self.uistate, "filt_list")
scrolledwindow = self.get_widget('scrolledwindow2')
scrolledwindow.remove(objectlist)
scrolledwindow.add(self.filter_list)
self.set_window(self.get_widget('filter_list'),
self.get_widget('filter_list_title'),
_TITLES[self.namespace])
@ -1123,6 +1142,7 @@ class FilterEditor(ManagedWindow):
self.edit_filter)
self.draw_filters()
self._set_size()
self.filter_list.restore_column_size()
self.show()
def build_menu_names(self, obj):

View File

@ -2,6 +2,7 @@
<!-- Generated with glade 3.18.3 -->
<interface>
<requires lib="gtk+" version="3.10"/>
<requires lib="grampswidgets" version="0.0"/>
<object class="GtkDialog" id="baseselector">
<property name="can_focus">False</property>
<property name="modal">True</property>
@ -100,7 +101,7 @@
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkTreeView" id="plist">
<object class="PersistentTreeView" id="plist">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="fixed_height_mode">True</property>

View File

@ -33,3 +33,6 @@ class StyledTextEditor(Gtk.TextView):
class UndoableBuffer(Gtk.TextBuffer):
__gtype_name__ = 'UndoableBuffer'
class PersistentTreeView(Gtk.TreeView):
__gtype_name__ = 'PersistentTreeView'

View File

@ -15,10 +15,15 @@
name="StyledTextEditor"
title="Styled Text Editor"
generic-name="styled_text"/>
<glade-widget-class
name="PersistentTreeView"
title="Persistent TreeView"
generic-name="resizable_treeview"/>
</glade-widget-classes>
<glade-widget-group name="GrampsWidgets" title="Gramps Widgets">
<glade-widget-class-ref name="ValidatableMaskedEntry"/>
<glade-widget-class-ref name="UndoableEntry"/>
<glade-widget-class-ref name="StyledTextEditor"/>
<glade-widget-class-ref name="PersistentTreeView"/>
</glade-widget-group>
</glade-catalog>

View File

@ -97,7 +97,7 @@
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkTreeView" id="objectlist">
<object class="PersistentTreeView" id="objectlist">
<property name="visible">True</property>
<property name="can_focus">True</property>
<child internal-child="selection">

View File

@ -185,7 +185,7 @@
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkTreeView" id="dblist">
<object class="PersistentTreeView" id="dblist">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>

View File

@ -91,7 +91,7 @@
<property name="can_focus">False</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkTreeView" id="filters">
<object class="PersistentTreeView" id="filters">
<property name="visible">True</property>
<property name="can_focus">True</property>
<child internal-child="selection">
@ -581,7 +581,7 @@
<property name="margin_left">6</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkTreeView" id="rule_list">
<object class="PersistentTreeView" id="rule_list">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
@ -855,7 +855,7 @@
</packing>
</child>
<child>
<object class="GtkTreeView" id="ruletree">
<object class="PersistentTreeView" id="ruletree">
<property name="width_request">250</property>
<property name="visible">True</property>
<property name="can_focus">True</property>

View File

@ -219,7 +219,8 @@ class ListModel:
column.set_sort_column_id(name[1])
column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
column.set_fixed_width(name[2])
# removed since we have a PersistentTreeView
# column.set_fixed_width(name[2])
cnum += 1
self.cids.append(name[1])

View File

@ -38,6 +38,7 @@ from ..glade import Glade
from ..widgets.interactivesearchbox import InteractiveSearchBox
from ..display import display_help
from gramps.gen.const import URL_MANUAL_PAGE
from gramps.gui.widgets.persistenttreeview import PersistentTreeView
#-------------------------------------------------------------------------
#
@ -83,7 +84,12 @@ class BaseSelector(ManagedWindow):
self.showall = self.glade.get_object('showall')
title_label = self.glade.get_object('title')
vbox = self.glade.get_object('select_person_vbox')
self.tree = self.glade.get_object('plist')
objectlist = self.glade.get_object('plist')
_name = self.get_config_name()
self.tree = PersistentTreeView(self.uistate, _name)
scrolledwindow = self.glade.get_object('scrolledwindow33')
scrolledwindow.remove(objectlist)
scrolledwindow.add(self.tree)
self.tree.set_headers_visible(True)
self.tree.set_headers_clickable(True)
self.tree.connect('row-activated', self._on_row_activated)
@ -298,6 +304,7 @@ class BaseSelector(ManagedWindow):
search=filter_info)
self.tree.set_model(self.model)
self.tree.restore_column_size()
#sorting arrow in column header (not on start, only on click)
if not self.setupcols :
@ -314,6 +321,9 @@ class BaseSelector(ManagedWindow):
if sel:
self.goto_handle(sel)
def get_config_name(self):
assert False, "Must be defined in the subclass"
def column_clicked(self, obj, data):
if self.sort_col != data:
self.sortorder = Gtk.SortType.ASCENDING
@ -336,6 +346,7 @@ class BaseSelector(ManagedWindow):
sort_map=self.column_order(), skip=self.skip_list,
search=filter_info)
self.tree.set_model(self.model)
self.tree.restore_column_size()
self.tree.grab_focus()
def clear_model(self):

View File

@ -83,5 +83,8 @@ class SelectCitation(BaseSelector):
else:
return self.db.get_citation_from_handle(handle)
def get_config_name(self):
return __name__
WIKI_HELP_PAGE = URL_MANUAL_SECT2
WIKI_HELP_SEC = _('Select_Source_or_Citation_selector', 'manual')

View File

@ -75,5 +75,8 @@ class SelectEvent(BaseSelector):
def get_from_handle_func(self):
return self.db.get_event_from_handle
def get_config_name(self):
return __name__
WIKI_HELP_PAGE = URL_MANUAL_SECT1
WIKI_HELP_SEC = _('Select_Event_selector', 'manual')

View File

@ -72,5 +72,8 @@ class SelectFamily(BaseSelector):
def get_from_handle_func(self):
return self.db.get_family_from_handle
def get_config_name(self):
return __name__
WIKI_HELP_PAGE = '%s_-_Categories' % URL_MANUAL_PAGE
WIKI_HELP_SEC = _('Select_Family_selector', 'manual')

View File

@ -78,5 +78,8 @@ class SelectNote(BaseSelector):
def get_from_handle_func(self):
return self.db.get_note_from_handle
def get_config_name(self):
return __name__
WIKI_HELP_PAGE = URL_MANUAL_SECT1
WIKI_HELP_SEC = _('Select_Note_selector', 'manual')

View File

@ -104,5 +104,8 @@ class SelectObject(BaseSelector):
self.preview.set_from_pixbuf(pix)
gc.collect()
def get_config_name(self):
return __name__
WIKI_HELP_PAGE = URL_MANUAL_SECT1
WIKI_HELP_SEC = _('Select_Media_Object_selector', 'manual')

View File

@ -122,3 +122,6 @@ class SelectPerson(BaseSelector):
self.tree.expand_row(paths[0], 0)
return True
return False
def get_config_name(self):
return __name__

View File

@ -86,5 +86,8 @@ class SelectPlace(BaseSelector):
]
self.search_bar.setup_filter(cols)
def get_config_name(self):
return __name__
WIKI_HELP_PAGE = URL_MANUAL_SECT2
WIKI_HELP_SEC = _('Select_Place_selector', 'manual')

View File

@ -71,5 +71,8 @@ class SelectRepository(BaseSelector):
def get_from_handle_func(self):
return self.db.get_repository_from_handle
def get_config_name(self):
return __name__
WIKI_HELP_PAGE = URL_MANUAL_SECT2
WIKI_HELP_SEC = _('Select_Repository_selector', 'manual')

View File

@ -73,5 +73,8 @@ class SelectSource(BaseSelector):
def get_from_handle_func(self):
return self.db.get_source_from_handle
def get_config_name(self):
return __name__
WIKI_HELP_PAGE = URL_MANUAL_SECT2
WIKI_HELP_SEC = _('Select_Source_selector', 'manual')

View File

@ -53,6 +53,7 @@ from ..display import display_help
from ..listmodel import ListModel
from ..managedwindow import ManagedWindow
from ..uimanager import ActionGroup
from gramps.gui.widgets.persistenttreeview import PersistentTreeView
from gramps.gen.utils.db import navigation_label
from gramps.gen.const import URL_MANUAL_PAGE
from gramps.gen.const import GRAMPS_LOCALE as glocale
@ -98,6 +99,7 @@ class Bookmarks(metaclass=ABCMeta):
self.connect_signals()
self.dbstate.connect('database-changed', self.db_changed)
self.dbstate.connect("no-database", self.undisplay)
self.name = None
def db_changed(self, data):
"""
@ -236,7 +238,14 @@ class Bookmarks(metaclass=ABCMeta):
selected row is attached to the name list. This is either 0 if the
list is not empty, or -1 if it is.
"""
BookmarksDialog(self)
self.bkm_dialog = BookmarksDialog(self)
self.bkm_dialog.namelist.set_config_name(self.name)
def set_config_name(self, name):
"""
Set the config name used to have persistent column size.
"""
self.name = name
class BookmarksDialog(ManagedWindow):
"""
@ -249,7 +258,7 @@ class BookmarksDialog(ManagedWindow):
self.bookmarks = bm_class.bookmarks
self.dbstate = bm_class.dbstate
self.make_label = bm_class.make_label
uistate = bm_class.uistate
self.uistate = bm_class.uistate
self.namemodel = None
self.top = None
@ -257,13 +266,17 @@ class BookmarksDialog(ManagedWindow):
self.response = None
self.namelist = None
ManagedWindow.__init__(self, uistate, [], self.__class__, modal=True)
ManagedWindow.__init__(self, self.uistate, [], self.__class__, modal=True)
# the self.top.run() below makes Gtk make it modal, so any change to
# the previous line's "modal" would require that line to be changed
self.draw_window()
self.set_window(self.top, None, _("Organize Bookmarks"))
self.setup_configs('interface.bookmarksdialog', 400, 350)
# Remove the "bkm_" prefix to the config class name and add it to the
# bookmarksdialog config name.
# This allow to have different widget size depending on the class.
config_name = 'interface.bookmarksdialog-' + bm_class.name[4:]
self.setup_configs(config_name, 400, 350)
self.show()
self.edit()
@ -279,7 +292,7 @@ class BookmarksDialog(ManagedWindow):
self.top.vbox.pack_start(box, 1, 1, 5)
name_titles = [(_('Name'), -1, 200), (_('ID'), -1, 50), ('', -1, 0)]
self.namelist = Gtk.TreeView()
self.namelist = PersistentTreeView(self.uistate, self.bm_class.name)
self.namemodel = ListModel(self.namelist, name_titles)
slist = Gtk.ScrolledWindow()
@ -301,6 +314,7 @@ class BookmarksDialog(ManagedWindow):
bbox.add(down)
bbox.add(delete)
box.pack_start(bbox, 0, 0, 5)
self.namelist.restore_column_size()
def edit(self):
"""
@ -377,6 +391,7 @@ class ListBookmarks(Bookmarks):
def __init__(self, dbstate, uistate, change_active):
self.change_active = change_active
Bookmarks.__init__(self, dbstate, uistate)
self.set_config_name(self.get_config_name())
def callback(self, handle):
return make_callback(handle, self.do_callback)
@ -400,6 +415,9 @@ class PersonBookmarks(ListBookmarks):
def get_bookmarks(self):
return self.dbstate.db.get_bookmarks()
def get_config_name(self):
return __name__
class FamilyBookmarks(ListBookmarks):
"Handle the bookmarks interface for Gramps."
@ -415,6 +433,9 @@ class FamilyBookmarks(ListBookmarks):
def get_bookmarks(self):
return self.dbstate.db.get_family_bookmarks()
def get_config_name(self):
return __name__
class EventBookmarks(ListBookmarks):
"Handle the bookmarks interface for Gramps."
@ -430,6 +451,9 @@ class EventBookmarks(ListBookmarks):
def get_bookmarks(self):
return self.dbstate.db.get_event_bookmarks()
def get_config_name(self):
return __name__
class SourceBookmarks(ListBookmarks):
"Handle the bookmarks interface for Gramps."
@ -445,6 +469,9 @@ class SourceBookmarks(ListBookmarks):
def get_bookmarks(self):
return self.dbstate.db.get_source_bookmarks()
def get_config_name(self):
return __name__
class CitationBookmarks(ListBookmarks):
"Handle the bookmarks interface for Gramps."
@ -486,6 +513,9 @@ class CitationBookmarks(ListBookmarks):
def get_bookmarks(self):
return self.dbstate.db.get_citation_bookmarks()
def get_config_name(self):
return __name__
class MediaBookmarks(ListBookmarks):
"Handle the bookmarks interface for Gramps."
@ -501,6 +531,9 @@ class MediaBookmarks(ListBookmarks):
def get_bookmarks(self):
return self.dbstate.db.get_media_bookmarks()
def get_config_name(self):
return __name__
class RepoBookmarks(ListBookmarks):
"Handle the bookmarks interface for Gramps."
@ -516,6 +549,9 @@ class RepoBookmarks(ListBookmarks):
def get_bookmarks(self):
return self.dbstate.db.get_repo_bookmarks()
def get_config_name(self):
return __name__
class PlaceBookmarks(ListBookmarks):
"Handle the bookmarks interface for Gramps."
@ -531,6 +567,9 @@ class PlaceBookmarks(ListBookmarks):
def get_bookmarks(self):
return self.dbstate.db.get_place_bookmarks()
def get_config_name(self):
return __name__
class NoteBookmarks(ListBookmarks):
"Handle the bookmarks interface for Gramps."
@ -546,6 +585,9 @@ class NoteBookmarks(ListBookmarks):
def get_bookmarks(self):
return self.dbstate.db.get_note_bookmarks()
def get_config_name(self):
return __name__
def make_callback(handle, function):
"""
Build a unique call to the function with the associated handle.

View File

@ -75,6 +75,7 @@ from ..ddtargets import DdTargets
from ..plug.quick import create_quickreport_menu, create_web_connect_menu
from ..utils import is_right_click
from ..widgets.interactivesearchbox import InteractiveSearchBox
from ..widgets.persistenttreeview import PersistentTreeView
#----------------------------------------------------------------
#
@ -156,7 +157,7 @@ class ListView(NavigationView):
self.search_build_tree)
filter_box = self.search_bar.build()
self.list = Gtk.TreeView()
self.list = PersistentTreeView(self.uistate, self.get_config_name())
self.list.set_headers_visible(True)
self.list.set_headers_clickable(True)
self.list.set_fixed_height_mode(True)
@ -196,6 +197,7 @@ class ListView(NavigationView):
self.selection.connect('changed', self.row_changed)
self.setup_filter()
self.list.restore_column_size()
return self.vbox
def define_actions(self):
@ -227,8 +229,9 @@ class ListView(NavigationView):
build the columns
"""
# Preserve the column widths if rebuilding the view.
if self.columns and preserve_col:
self.save_column_info()
# removed since we have a PersistentTreeView
#if self.columns and preserve_col:
# self.list.save_column_info(self.list)
list(map(self.list.remove_column, self.columns))
self.columns = []
@ -263,11 +266,13 @@ class ListView(NavigationView):
column.set_resizable(True)
column.set_clickable(True)
column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
column.set_fixed_width(pair[2])
# removed since we have a PersistentTreeView
# column.set_fixed_width(pair[2])
self.columns.append(column)
self.list.append_column(column)
index += 1
return self.vbox
def icon(self, column, renderer, model, iter_, col_num):
'''
@ -336,6 +341,7 @@ class ListView(NavigationView):
cput1 = perf_counter()
self.build_columns(preserve_col)
self.list.restore_column_size()
cput2 = perf_counter()
self.list.set_model(self.model)
cput3 = perf_counter()
@ -1115,12 +1121,21 @@ class ListView(NavigationView):
"""
Save the column widths when the view is shutdown.
"""
self.list.save_column_info()
# The following is used when we change the columns order.
self.save_column_info()
PageView.on_delete(self)
def get_config_name(self):
"""
Set the associated config name string for the treeview
"""
assert False, "Must be defined in the subclass"
def save_column_info(self):
"""
Save the column widths, order, and view settings
This is used only when we change the columns order
"""
widths = self.get_column_widths()
order = self._config.get('columns.rank')
@ -1343,7 +1358,8 @@ class ListView(NavigationView):
return _('Columns'), ColumnOrder(self._config, column_names,
self.get_column_widths(),
self.set_column_order,
tree=not flat)
tree=not flat,
resizable=self.list)
def csvdialect(configdialog):
return _('CSV Dialect'), CsvDialect()
return [columnpage, csvdialect]

View File

@ -44,3 +44,4 @@ from .undoablestyledbuffer import *
from .validatedcomboentry import *
from .validatedmaskedentry import *
from .placewithin import *
from .persistenttreeview import *

View File

@ -0,0 +1,145 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2021 Serge Noiraud
#
# 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.
#
"""
An override to allow resizable columns
"""
import logging
from gi.repository import Gtk
from gramps.gen.config import config
_LOG = logging.getLogger(".persistent")
# -------------------------------------------------------------------------
#
# PersistentTreeView class
#
# -------------------------------------------------------------------------
__all__ = ["PersistentTreeView"]
class PersistentTreeView(Gtk.TreeView):
'''
TreeView that has resizable columns
'''
__gtype_name__ = 'PersistentTreeView'
def __init__(self, uistate=None, config_name=None):
"""
Create a TreeView widgets with column size saving
"""
Gtk.TreeView.__init__(self)
self.config_name = "undefined"
self.connect("destroy", self.save_column_info)
self.uistate = None
if uistate:
self.set_uistate(uistate)
if config_name:
self.set_config_name(config_name)
def set_uistate(self, uistate):
"""
parameter: uistate: The associated uistate
This parameter is used to connect some signals from this class.
"""
if not self.uistate and uistate:
_LOG.debug("connect signal font-changed")
uistate.connect('font-changed', self.restore_column_size)
self.uistate = uistate
def set_config_name(self, name):
"""
parameter: name: The associated config name string for the treeview
This parameter must be unique as it allow the tab columns size saving
"""
# Here, we can have:
# name = gramps.gui.editors.displaytabs.personeventembedlist
# The following line return only the last part of the string.
# personeventembedlist in this case
last = name.split('.')[-1]
_LOG.debug("set persistent name : %s" % last)
self.config_name = "spacing.%s" % last
if not config.is_set(self.config_name):
_LOG.debug("registering %s" % self.config_name)
config.register(self.config_name, [])
def save_column_info(self, tree=None):
"""
Save the columns width
"""
if self.config_name == "undefined":
return
newsize = self.get_columns_size()
if 0 not in newsize:
# Don't save the values if one column size is null.
config.set(self.config_name, newsize)
_LOG.debug("save persistent : %s = %s" % (self.config_name,
newsize))
return
def get_columns_size(self):
"""
Get all the columns size
"""
columns = self.get_columns()
newsize = []
nbc = 0
context = Gtk.Label().get_pango_context()
font_desc = context.get_font_description()
char_width = 1.2 * (font_desc.get_size() / 1000)
for column in columns:
if nbc < len(columns) - 1 or len(columns) == 1:
# Don't save the last column size, it's wrong
# except if we have only one column (i.e. EditRule)
child = column.get_widget()
if isinstance(child, Gtk.Image):
# Don't resize the icons
size = 2
else:
size = int(column.get_width() / char_width) + 1
size = 2 if size < 2 else size
newsize.append(size)
nbc += 1
return newsize
def restore_column_size(self):
"""
restore the columns width
"""
if self.config_name == "undefined":
return
size = config.get(self.config_name)
if len(size) == 0:
_LOG.debug("restore for the first time : %s = %s" %
(self.config_name, size))
return
_LOG.debug("restore persistent : %s = %s" % (self.config_name, size))
context = Gtk.Label().get_pango_context()
font_desc = context.get_font_description()
char_width = 1.2 * (font_desc.get_size() / 1000)
nbc = 0
columns = self.get_columns()
for column in columns:
if nbc < len(size):
column.set_fixed_width(size[nbc] * char_width)
nbc += 1

View File

@ -39,8 +39,11 @@ from gramps.gen.datehandler import get_date
from gramps.gen.display.place import displayer as place_displayer
from gramps.gen.errors import WindowActiveError
from gramps.gen.const import GRAMPS_LOCALE as glocale
from gramps.gui.widgets.persistenttreeview import PersistentTreeView
_ = glocale.translation.gettext
class Children(Gramplet):
"""
Displays the children of a person or family.
@ -51,6 +54,10 @@ class Children(Gramplet):
self.gui.get_container_widget().add(self.gui.WIDGET)
self.gui.WIDGET.show()
self.uistate.connect('nameformat-changed', self.update)
self.gui.WIDGET.restore_column_size()
def on_save(self):
self.gui.WIDGET.save_column_info()
def get_date_place(self, event):
"""
@ -88,7 +95,7 @@ class PersonChildren(Children):
"""
tip = _('Double-click on a row to edit the selected child.')
self.set_tooltip(tip)
top = Gtk.TreeView()
top = PersistentTreeView(self.uistate, __name__)
titles = [('', NOSORT, 50,),
(_('Child'), 1, 250),
(_('Birth Date'), 3, 100),
@ -187,7 +194,7 @@ class FamilyChildren(Children):
"""
tip = _('Double-click on a row to edit the selected child.')
self.set_tooltip(tip)
top = Gtk.TreeView()
top = PersistentTreeView(self.uistate, __name__)
titles = [('', NOSORT, 50,),
(_('Child'), 1, 250),
(_('Birth Date'), 3, 100),

View File

@ -38,6 +38,8 @@ from gramps.gen.datehandler._dateutils import get_date
from gramps.gui.dbguielement import DbGUIElement
from gramps.gen.errors import WindowActiveError
from gramps.gen.const import GRAMPS_LOCALE as glocale
from gramps.gui.widgets.persistenttreeview import PersistentTreeView
_ = glocale.translation.gettext
class Citations(Gramplet, DbGUIElement):
@ -55,6 +57,10 @@ class Citations(Gramplet, DbGUIElement):
self.gui.get_container_widget().remove(self.gui.textview)
self.gui.get_container_widget().add(self.gui.WIDGET)
self.gui.WIDGET.show()
self.gui.WIDGET.restore_column_size()
def on_save(self):
self.gui.WIDGET.save_column_info()
def _connect_db_signals(self):
"""
@ -81,7 +87,7 @@ class Citations(Gramplet, DbGUIElement):
"""
tip = _('Double-click on a row to edit the selected source/citation.')
self.set_tooltip(tip)
top = Gtk.TreeView()
top = PersistentTreeView(self.uistate, __name__)
titles = [('', NOSORT, 50,),
(_('Source/Date'), 1, 350),
(_('Volume/Page'), 2, 150),

View File

@ -44,6 +44,7 @@ from gramps.gen.errors import WindowActiveError
from gramps.gen.config import config
from gramps.gen.proxy.cache import CacheProxyDb
from gramps.gen.const import GRAMPS_LOCALE as glocale
from gramps.gui.widgets.persistenttreeview import PersistentTreeView
_ = glocale.translation.gettext
age_precision = config.get('preferences.age-display-precision')
@ -63,6 +64,10 @@ class Events(Gramplet, DbGUIElement):
self.gui.get_container_widget().remove(self.gui.textview)
self.gui.get_container_widget().add(self.gui.WIDGET)
self.gui.WIDGET.show()
self.gui.WIDGET.restore_column_size()
def on_save(self):
self.gui.WIDGET.save_column_info()
def _connect_db_signals(self):
"""
@ -83,7 +88,7 @@ class Events(Gramplet, DbGUIElement):
"""
tip = _('Double-click on a row to edit the selected event.')
self.set_tooltip(tip)
top = Gtk.TreeView()
top = PersistentTreeView(self.uistate, __name__)
titles = [('', NOSORT, 50,),
(_('Type'), 1, 100),
(_('Description'), 2, 150),

View File

@ -538,3 +538,9 @@ class BasePersonView(ListView):
"Person Notes",
"Person Attributes",
"Person Backlinks"))
def get_config_name(self):
"""
return the config name for this view
"""
assert False, "Must be defined in the subclass"

View File

@ -463,3 +463,6 @@ class CitationListView(ListView):
("Citation Gallery",
"Citation Notes",
"Citation Backlinks"))
def get_config_name(self):
return __name__

View File

@ -743,3 +743,6 @@ class CitationTreeView(LibSourceView, ListView):
("Citation Gallery",
"Citation Notes",
"Citation Backlinks"))
def get_config_name(self):
return __name__

View File

@ -445,3 +445,6 @@ class EventView(ListView):
"Event Notes",
"Event Attributes",
"Event Backlinks"))
def get_config_name(self):
return __name__

View File

@ -124,6 +124,9 @@ class FamilyView(ListView):
self.additional_uis.append(self.additional_ui)
def get_config_name(self):
return __name__
def navigation_type(self):
return 'Family'

View File

@ -538,3 +538,6 @@ class MediaView(ListView):
"Media Attributes",
"Metadata Viewer",
"Media Backlinks"))
def get_config_name(self):
return __name__

View File

@ -390,3 +390,6 @@ class NoteView(ListView):
"""
return (("Note Filter",),
("Note Backlinks",))
def get_config_name(self):
return __name__

View File

@ -42,7 +42,7 @@ _ = glocale.translation.gettext
#-------------------------------------------------------------------------
#
# PlaceTreeView
# PersonListView
#
#-------------------------------------------------------------------------
class PersonListView(BasePersonView):
@ -53,3 +53,6 @@ class PersonListView(BasePersonView):
BasePersonView.__init__(self, pdata, dbstate, uistate,
_('Person View'), PersonListModel,
nav_group=nav_group)
def get_config_name(self):
return __name__

View File

@ -124,3 +124,6 @@ class PersonTreeView(BasePersonView):
EditPerson(self.dbstate, self.uistate, [], person)
except WindowActiveError:
pass
def get_config_name(self):
return __name__

View File

@ -50,3 +50,6 @@ class PlaceListView(PlaceBaseView):
PlaceBaseView.__init__(self, pdata, dbstate, uistate,
_('Place View'), PlaceListModel,
nav_group=0)
def get_config_name(self):
return __name__

View File

@ -153,3 +153,6 @@ class PlaceTreeView(PlaceBaseView):
old_handle = self.model.get_handle_from_iter(parent_iter)
children = self.model.get_node_from_iter(iter_).children
return new_handle != old_handle or children
def get_config_name(self):
return __name__

View File

@ -407,3 +407,6 @@ class RepositoryView(ListView):
("Repository Details",
"Repository Notes",
"Repository Backlinks"))
def get_config_name(self):
return __name__

View File

@ -384,3 +384,6 @@ class SourceView(LibSourceView, ListView):
("Source Gallery",
"Source Notes",
"Source Backlinks"))
def get_config_name(self):
return __name__