GEPS 039: Genealogical symbols in gramps

Gramps should be able to use genealogy symbols everywhere.

Several kinds of use for the genealogical symbols :

    The graphical interface (GUI, editors, graphical views, ...)
    The Web reports (webcal, narrativeweb, ...)
    The Text reports (odf, rtf, pdf, ...)
    The graphical reports (odf, rtf, svg, ...)

Resolves #9098
This commit is contained in:
SNoiraud 2019-05-13 11:15:52 +01:00 committed by Nick Hall
parent 735e17a8cb
commit cd092897bc
22 changed files with 746 additions and 41 deletions

View File

@ -6,6 +6,7 @@
# Copyright (C) 2008-2009 Gary Burton
# Copyright (C) 2009-2012 Doug Blank <doug.blank@gmail.com>
# Copyright (C) 2011 Tim G L Lyons
# Copyright (C) 2015- 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
@ -310,6 +311,11 @@ register('researcher.researcher-state', '')
register('plugin.hiddenplugins', [])
register('plugin.addonplugins', [])
register('utf8.in-use', False)
register('utf8.available-fonts', [])
register('utf8.selected-font', "")
register('utf8.death-symbol', 13)
if __debug__: # enable a simple CLI test to see if the datestrings exist
register('test.january', _("localized lexeme inflections||January"))

View File

@ -41,6 +41,7 @@ from ..display.name import displayer as name_displayer
from ..display.place import displayer as place_displayer
from .db import (get_birth_or_fallback, get_death_or_fallback,
get_marriage_or_fallback)
from gramps.gen.utils.symbols import Symbols
#-------------------------------------------------------------------------
#
@ -51,10 +52,29 @@ class FormattingHelper:
"""Format of commonly used expressions, making use of a cache to not
recompute
"""
def __init__(self, dbstate):
def __init__(self, dbstate, uistate=None):
self.dbstate = dbstate
self.uistate = uistate
self._text_cache = {}
self._markup_cache = {}
self.symbols = Symbols()
self.reload_symbols()
def reload_symbols(self):
self.clear_cache()
if self.uistate and self.uistate.symbols:
death_idx = self.uistate.death_symbol
self.male = self.symbols.get_symbol_for_string(self.symbols.SYMBOL_MALE)
self.female = self.symbols.get_symbol_for_string(self.symbols.SYMBOL_FEMALE)
self.bth = self.symbols.get_symbol_for_string(self.symbols.SYMBOL_BIRTH)
self.marr = self.symbols.get_symbol_for_string(self.symbols.SYMBOL_MARRIAGE)
self.dth = self.symbols.get_death_symbol_for_char(death_idx)
else:
death_idx = self.symbols.DEATH_SYMBOL_LATIN_CROSS
self.male = self.symbols.get_symbol_fallback(self.symbols.SYMBOL_MALE)
self.female = self.symbols.get_symbol_fallback(self.symbols.SYMBOL_FEMALE)
self.marr = self.symbols.get_symbol_fallback(self.symbols.SYMBOL_MARRIAGE)
self.dth = self.symbols.get_death_symbol_fallback(death_idx)
def format_relation(self, family, line_count, use_markup=False):
""" Format a relation between parents of a family
@ -73,18 +93,15 @@ class FormattingHelper:
text = ""
marriage = get_marriage_or_fallback(self.dbstate.db, family)
if marriage and use_markup and marriage.get_type() != EventType.MARRIAGE:
mdate = "<i>%s %s</i>" % (marriage.get_type().get_abbreviation(),
escape(get_date(marriage)))
mdate = "<i>%s %s</i>" % (self.marr, escape(get_date(marriage)))
mplace = "<i>%s</i>" % escape(self.get_place_name(marriage.get_place_handle()))
name = "<i>%s</i>" % str(marriage.get_type())
elif marriage and use_markup:
mdate = "%s %s" % (marriage.get_type().get_abbreviation(),
escape(get_date(marriage)))
mdate = "%s %s" % (self.marr, escape(get_date(marriage)))
mplace = escape(self.get_place_name(marriage.get_place_handle()))
name = str(marriage.get_type())
elif marriage:
mdate = "%s %s" % (marriage.get_type().get_abbreviation(),
get_date(marriage))
mdate = "%s %s" % (self.marr, get_date(marriage))
mplace = self.get_place_name(marriage.get_place_handle())
name = str(marriage.get_type())
else:
@ -178,10 +195,12 @@ class FormattingHelper:
dplace = ""
if line_count < 5:
text = "%s\n* %s\n+ %s" % (name, bdate, ddate)
text = "%s\n%s %s\n%s %s" % (name, self.bth, bdate,
self.dth, ddate)
else:
text = "%s\n* %s\n %s\n+ %s\n %s" % (name, bdate, bplace,
ddate, dplace)
text = "%s\n%s %s\n %s\n%s %s\n %s" % (name, self.bth, bdate,
bplace, self.dth,
ddate, dplace)
if use_markup:
if not person.handle in self._markup_cache:
self._markup_cache[person.handle] = {}

189
gramps/gen/utils/symbols.py Normal file
View File

@ -0,0 +1,189 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2015- 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.
#
# https://en.wikipedia.org/wiki/Miscellaneous_Symbols
# http://www.w3schools.com/charsets/ref_utf_symbols.asp
#
#-------------------------------------------------------------------------
#
# Standard python modules
#
#-------------------------------------------------------------------------
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.sgettext
# pylint: disable=superfluous-parens
# pylint: disable=anomalous-unicode-escape-in-string
class Symbols(object):
# genealogical symbols
SYMBOL_FEMALE = 0
SYMBOL_MALE = 1
SYMBOL_ASEXUAL_SEXLESS = 2 # Unknown
SYMBOL_LESBIAN = 3
SYMBOL_MALE_HOMOSEXUAL = 4
SYMBOL_HETEROSEXUAL = 5
SYMBOL_HERMAPHRODITE = 6
SYMBOL_TRANSGENDER = 7
SYMBOL_NEUTER = 8
SYMBOL_ILLEGITIM = 9
SYMBOL_BIRTH = 10
SYMBOL_BAPTISM = 11 # CHRISTENING
SYMBOL_ENGAGED = 12
SYMBOL_MARRIAGE = 13
SYMBOL_DIVORCE = 14
SYMBOL_UNMARRIED_PARTNERSHIP = 15
SYMBOL_BURIED = 16
SYMBOL_CREMATED = 17 # Funeral urn
SYMBOL_KILLED_IN_ACTION = 18
SYMBOL_EXTINCT = 19
all_symbols = [
# Name UNICODE SUBSTITUTION
(_("Female"), '\u2640', ""),
(_("Male"), '\u2642', ""),
(_("Asexuality, sexless, genderless"), '\u26aa', ""),
(_("Lesbianism"), '\u26a2', "oo"),
(_("Male homosexuality"), '\u26a3', "oo"),
(_("Heterosexuality"), '\u26a4', "oo"),
(_("Transgender, hermaphrodite (in entomology)"), '\u26a5', ""),
(_("Transgender"), '\u26a6', ""),
(_("Neuter"), '\u26b2', ""),
(_("Illegitimate"), '\u229b', ""),
(_("Birth"), '\u002a', "*"),
(_("Baptism/Christening"), '\u007e', "~"),
(_("Engaged"), '\u26ac', "o"),
(_("Marriage"), '\u26ad', "oo"),
(_("Divorce"), '\u26ae', "o|o"),
(_("Unmarried partnership"), '\u26af', "o-o"),
(_("Buried"), '\u26b0', "d"),
(_("Cremated/Funeral urn"), '\u26b1', "d"),
(_("Killed in action"), '\u2694', "d"),
(_("Extinct"), '\u2021', ""),
]
# genealogical death symbols
DEATH_SYMBOL_NONE = 0
DEATH_SYMBOL_X = 1
DEATH_SYMBOL_SKULL = 2
DEATH_SYMBOL_ANKH = 3
DEATH_SYMBOL_ORTHODOX_CROSS = 4
DEATH_SYMBOL_CHI_RHO = 5
DEATH_SYMBOL_LORRAINE_CROSS = 6
DEATH_SYMBOL_JERUSALEM_CROSS = 7
DEATH_SYMBOL_STAR_CRESCENT = 8
DEATH_SYMBOL_WEST_SYRIAC_CROSS = 9
DEATH_SYMBOL_EAST_SYRIAC_CROSS = 10
DEATH_SYMBOL_HEAVY_GREEK_CROSS = 11
DEATH_SYMBOL_LATIN_CROSS = 12
DEATH_SYMBOL_SHADOWED_LATIN_CROSS = 13
DEATH_SYMBOL_MALTESE_CROSS = 14
DEATH_SYMBOL_STAR_OF_DAVID = 15
DEATH_SYMBOL_DEAD = 16
# The following is used in the global preferences in the display tab.
# Name
# UNICODE SUBSTITUTION
death_symbols = [(_("Nothing"), "", ""),
("x", "x", "x"),
(_("Skull and crossbones"), "\u2620", "+"),
(_("Ankh"), "\u2625", "+"),
(_("Orthodox cross"), "\u2626", "+"),
(_("Chi rho"), "\u2627", "+"),
(_("Cross of Lorraine"), "\u2628", "+"),
(_("Cross of Jerusalem"), "\u2629", "+"),
(_("Star and crescent"), "\u262a", "+"),
(_("West Syriac cross"), "\u2670", "+"),
(_("East Syriac cross"), "\u2671", "+"),
(_("Heavy Greek cross"), "\u271a", "+"),
(_("Latin cross"), "\u271d", "+"),
(_("Shadowed White Latin cross"), "\u271e", "+"),
(_("Maltese cross"), "\u2720", "+"),
(_("Star of David"), "\u2721", "+"),
(_("Dead"), _("Dead"), _("Dead"))
]
def __init__(self):
self.symbols = None
#
# functions for general symbols
#
def get_symbol_for_html(self, symbol):
""" return the html string like '&#9898;' """
return '&#%d;' % ord(self.all_symbols[symbol][1])
def get_symbol_name(self, symbol):
"""
Return the name of the symbol.
"""
return self.all_symbols[symbol][0]
def get_symbol_for_string(self, symbol):
""" return the utf-8 character like '\u2670' """
return self.all_symbols[symbol][1]
def get_symbol_fallback(self, symbol):
"""
Return the replacement string.
This is used if the utf-8 symbol in not present within a font.
"""
return self.all_symbols[symbol][2]
#
# functions for death symbols
#
def get_death_symbols(self):
"""
Return the list of death symbols.
This is used in the global preference to choose which symbol we'll use.
"""
return self.death_symbols
def get_death_symbol_name(self, symbol):
"""
Return the name of the symbol.
"""
return self.death_symbols[symbol][0]
def get_death_symbol_for_html(self, symbol):
"""
return the html string like '&#9898;'.
"""
return '&#%d;' % ord(self.death_symbols[symbol][1])
def get_death_symbol_for_char(self, symbol):
"""
Return the utf-8 character for the symbol.
"""
return self.death_symbols[symbol][1]
def get_death_symbol_fallback(self, symbol):
"""
Return the string replacement for the symbol.
"""
return self.death_symbols[symbol][2]
#
# functions for all symbols
#
def get_how_many_symbols(self):
return len(self.death_symbols) + len(self.all_symbols) - 4

View File

@ -6,6 +6,7 @@
# Copyright (C) 2010 Benny Malengier
# Copyright (C) 2010 Nick Hall
# Copyright (C) 2012 Doug Blank <doug.blank@gmail.com>
# Copyright (C) 2015- 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
@ -72,6 +73,8 @@ from .plug import PluginWindows
from .spell import HAVE_GTKSPELL
from gramps.gen.constfunc import win
_ = glocale.translation.gettext
from gramps.gen.utils.symbols import Symbols
from gramps.gen.constfunc import get_env_var
#-------------------------------------------------------------------------
#
@ -363,6 +366,14 @@ class ConfigureDialog(ManagedWindow):
grid.attach(text, start, index, stop - start, 1)
return text
def add_button(self, grid, label, index, constant, extra_callback=None, config=None):
if not config:
config = self.__config
button = Gtk.Button(label=label)
button.connect('clicked', extra_callback)
grid.attach(button, 1, index, 1, 1)
return button
def add_path_box(self, grid, label, index, entry, path, callback_label,
callback_sel, config=None):
"""
@ -555,7 +566,8 @@ class GrampsPreferences(ConfigureDialog):
self.add_date_panel,
self.add_researcher_panel,
self.add_advanced_panel,
self.add_color_panel
self.add_color_panel,
self.add_symbols_panel
)
ConfigureDialog.__init__(self, uistate, dbstate, page_funcs,
GrampsPreferences, config,
@ -1932,3 +1944,264 @@ class GrampsPreferences(ConfigureDialog):
def build_menu_names(self, obj):
return (_('Preferences'), _('Preferences'))
def add_symbols_panel(self, configdialog):
self.grid = Gtk.Grid()
self.grid.set_border_width(12)
self.grid.set_column_spacing(6)
self.grid.set_row_spacing(6)
message = _('This tab gives you the possibility to use one font'
' which is able to show all genealogical symbols\n\n'
'If you select the "use symbols" checkbox, '
'Gramps will use the selected font if it exists.'
)
message += '\n'
message += _('This can be useful if you want to add phonetic in '
'a note to show how to pronounce a name or if you mix'
' multiple languages like greek and russian.'
)
self.add_text(self.grid, message,
0, line_wrap=True)
self.add_checkbox(self.grid,
_('Use symbols'),
1,
'utf8.in-use',
extra_callback=self.activate_change_font
)
message = _('Be careful, if you click on the "Try to find" button, it can '
'take a while before you can continue (10 minutes or more). '
'\nIf you cancel the process, nothing will be changed.'
)
self.add_text(self.grid, message,
2, line_wrap=True)
available_fonts = config.get('utf8.available-fonts')
self.all_avail_fonts = list(enumerate(available_fonts))
if len(available_fonts) > 0:
self.add_text(self.grid,
_('You have already run the tool to search for genealogy fonts.'
'\nRun it again only if you added fonts on your system.'
),
3, line_wrap=True)
self.add_button(self.grid,
_('Try to find'),
4,
'utf8.in-use',
extra_callback=self.can_we_use_genealogical_fonts)
sel_font = config.get('utf8.selected-font')
if len(available_fonts) > 0:
try:
active_val = available_fonts.index(sel_font)
except:
active_val = 0
self.add_combo(self.grid,
_('Choose font'),
5, 'utf8.selected-font',
self.all_avail_fonts,
callback=self.utf8_update_font,
valueactive=True, setactive=active_val)
symbols = Symbols()
all_sbls = symbols.get_death_symbols()
all_symbols = []
for symbol in all_sbls:
all_symbols.append(symbol[1] + " " + symbol[0])
self.all_death_symbols = list(enumerate(all_symbols))
pos = config.get('utf8.death-symbol')
combo = self.add_combo(self.grid,
_('Select default death symbol'),
6, 'utf8.death-symbol',
self.all_death_symbols,
callback=self.utf8_update_death_symbol,
valueactive=True, setactive='')
combo.set_active(pos)
if config.get('utf8.selected-font') != "":
self.utf8_show_example()
return _('Genealogical Symbols'), self.grid
def can_we_use_genealogical_fonts(self, obj):
try:
import fontconfig
from gramps.gui.utils import ProgressMeter
from collections import defaultdict
except:
from gramps.gui.dialog import WarningDialog
WarningDialog(_("Cannot look for genealogical fonts"),
_("I am not able to select genealogical fonts. "
"Please, install the module fontconfig for python 3."),
parent=self.uistate.window)
return False
try:
# remove the old messages with old font
self.grid.remove_row(8)
self.grid.remove_row(7)
self.grid.remove_row(6)
self.grid.remove_row(5)
except:
pass
fonts = fontconfig.query()
all_fonts = defaultdict(set)
symbols = Symbols()
nb_symbols = symbols.get_how_many_symbols()
self.in_progress = True
self.progress = ProgressMeter(_('Checking available genealogical fonts'),
can_cancel=True,
cancel_callback=self.stop_looking_for_font,
parent=self.uistate.window)
self.progress.set_pass(_('Looking for all fonts with genealogical symbols.'), nb_symbols*len(fonts))
for path in fonts:
if not self.in_progress:
return # We clicked on Cancel
font = fontconfig.FcFont(path)
local = get_env_var('LANGUAGE', 'en')
if isinstance(font.family, list):
fontname = None
for lang,fam in font.family:
if lang == local:
fontname = fam
if not fontname:
fontname = font.family[0][1]
else:
if local in font.family:
fontname = font.family[local]
else:
for lang, name in font.family: # version 0.6.0 use dict
fontname = name
break
for rand in range(symbols.SYMBOL_MALE, symbols.SYMBOL_EXTINCT+1):
string = symbols.get_symbol_for_html(rand)
value = symbols.get_symbol_for_string(rand)
if font.has_char(value):
all_fonts[fontname].add(value)
self.progress.step()
for rand in range(symbols.DEATH_SYMBOL_SKULL,
symbols.DEATH_SYMBOL_DEAD):
value = symbols.get_death_symbol_for_char(rand)
if font.has_char(value):
all_fonts[fontname].add(value)
self.progress.step()
self.progress.close()
available_fonts = []
for font, font_usage in all_fonts.items():
if not font_usage:
continue
if len(font_usage) == nb_symbols: # If the font use all symbols
available_fonts.append(font)
config.set('utf8.available-fonts', available_fonts)
sel_font = config.get('utf8.selected-font')
try:
active_val = available_fonts.index(sel_font)
except:
active_val = 0
if len(available_fonts) > 0:
self.all_avail_fonts = list(enumerate(available_fonts))
choosefont = self.add_combo(self.grid,
_('Choose font'),
5, 'utf8.selected-font',
self.all_avail_fonts, callback=self.utf8_update_font,
valueactive=True, setactive=active_val)
if len(available_fonts) == 1:
single_font = self.all_avail_fonts[choosefont.get_active()][1]
config.set('utf8.selected-font',
self.all_avail_fonts[single_font])
self.utf8_show_example()
symbols = Symbols()
all_sbls = symbols.get_death_symbols()
all_symbols = []
for symbol in all_sbls:
all_symbols.append(symbol[1] + " " + symbol[0])
self.all_death_symbols = list(enumerate(all_symbols))
pos = config.get('utf8.death-symbol')
combo = self.add_combo(self.grid,
_('Select default death symbol'),
6, 'utf8.death-symbol',
self.all_death_symbols,
callback=self.utf8_update_death_symbol,
valueactive=True, setactive='')
combo.set_active(pos)
else:
self.add_text(self.grid,
_('You have no font with genealogical symbols on your '
'system. Gramps will not be able to use symbols.'
),
6, line_wrap=True)
config.set('utf8.selected-font',"")
self.grid.show_all()
self.in_progress = False
def utf8_update_font(self, obj, constant):
entry = obj.get_active()
config.set(constant, self.all_avail_fonts[entry][1])
self.utf8_show_example()
def activate_change_font(self, obj=None):
if obj:
if not obj.get_active():
# reset to the system default
self.uistate.viewmanager.reset_font()
font = config.get('utf8.selected-font')
if not self.uistate.viewmanager.change_font(font):
# We can't change the font, so reset the checkbox.
if obj:
obj.set_active(False)
self.uistate.reload_symbols()
self.uistate.emit('font-changed')
def utf8_show_example(self):
from gi.repository import Pango
from gramps.gen.utils.grampslocale import _LOCALE_NAMES as X
from string import ascii_letters
try:
# remove the old messages with old font
self.grid.remove_row(8)
self.grid.remove_row(7)
except:
pass
font = config.get('utf8.selected-font')
symbols = Symbols()
my_characters = _("What you will see") + " :\n"
my_characters += ascii_letters
my_characters += " àäâçùéèiïîêëiÉÀÈïÏËÄœŒÅåØøìòô ...\n"
for k,v in sorted(X.items()):
lang = Pango.Language.from_string(k)
my_characters += v[2] + ":\t" + lang.get_sample_string() + "\n"
scrollw = Gtk.ScrolledWindow()
scrollw.set_size_request(600, 100)
text = Gtk.Label()
text.set_line_wrap(True)
font_description = Pango.font_description_from_string(font)
text.modify_font(font_description)
self.activate_change_font()
text.set_halign(Gtk.Align.START)
text.set_text(my_characters)
scrollw.add(text)
scrollw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
self.grid.attach(scrollw, 1, 7, 8, 1)
my_characters = ""
for idx in range(symbols.SYMBOL_FEMALE, symbols.SYMBOL_EXTINCT+1):
my_characters += symbols.get_symbol_for_string(idx) + " "
death_symbl = config.get('utf8.death-symbol')
my_characters += symbols.get_death_symbol_for_char(death_symbl)
text = Gtk.Label()
text.set_line_wrap(True)
font_description = Pango.font_description_from_string(font)
text.modify_font(font_description)
text.set_halign(Gtk.Align.START)
text.set_markup("<big><big><big><big>" +
my_characters +
"</big></big></big></big>")
self.grid.attach(text, 1, 8, 8, 1)
self.grid.show_all()
def stop_looking_for_font(self, *args, **kwargs):
self.progress.close()
self.in_progress = False
def utf8_update_death_symbol(self, obj, constant):
entry = obj.get_active()
config.set(constant, entry)
self.utf8_show_example()

View File

@ -400,6 +400,7 @@ class DisplayState(Callback):
'grampletbar-close-changed' : None,
'update-available' : (list, ),
'autobackup' : None,
'font-changed' : None,
}
#nav_type to message
@ -439,6 +440,8 @@ class DisplayState(Callback):
self.set_relationship_class()
self.export = False
self.backup_timer = None
self.symbols = config.get('utf8.in-use')
self.death_symbol = config.get('utf8.death-symbol')
formatter = logging.Formatter('%(levelname)s %(name)s: %(message)s')
warnbtn = status.get_warning_button()
@ -669,3 +672,7 @@ class DisplayState(Callback):
self.status.pop(self.status_id)
self.status.push(self.status_id, text)
process_pending_events()
def reload_symbols(self):
self.symbols = config.get('utf8.in-use')
self.death_symbol = config.get('utf8.death-symbol')

View File

@ -114,6 +114,11 @@ WIKI_HELP_PAGE_FAQ = '%s_-_FAQ' % URL_MANUAL_PAGE
WIKI_HELP_PAGE_KEY = '%s_-_Keybindings' % URL_MANUAL_PAGE
WIKI_HELP_PAGE_MAN = '%s' % URL_MANUAL_PAGE
CSS_FONT = """
#view {
font-family: %s;
}
"""
#-------------------------------------------------------------------------
#
# ViewManager
@ -247,12 +252,17 @@ class ViewManager(CLIManager):
height = config.get('interface.main-window-height')
horiz_position = config.get('interface.main-window-horiz-position')
vert_position = config.get('interface.main-window-vert-position')
font = config.get('utf8.selected-font')
self.window = Gtk.ApplicationWindow(application=self.app)
self.app.window = self.window
self.window.set_icon_from_file(ICON)
self.window.set_default_size(width, height)
self.window.move(horiz_position, vert_position)
self.provider = Gtk.CssProvider()
self.change_font(font)
#Set the mnemonic modifier on Macs to alt-ctrl so that it
#doesn't interfere with the extended keyboard, see
#https://gramps-project.org/bugs/view.php?id=6943
@ -661,6 +671,33 @@ class ViewManager(CLIManager):
except WindowActiveError:
return
def reset_font(self):
"""
Reset to the default application font.
"""
Gtk.StyleContext.remove_provider_for_screen(self.window.get_screen(),
self.provider)
def change_font(self, font):
"""
Change the default application font.
Only in the case we use symbols.
"""
if config.get('utf8.in-use') and font != "":
css_font = CSS_FONT % font
try:
self.provider.load_from_data(css_font.encode('UTF-8'))
Gtk.StyleContext.add_provider_for_screen(
self.window.get_screen(), self.provider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
return True
except:
# Force gramps to use the standard font.
print("I can't set the new font :", font)
config.set('utf8.in-use', False)
config.set('utf8.selected-font', "")
return False
def tip_of_day_activate(self, *obj):
"""
Display Tip of the day

View File

@ -137,6 +137,7 @@ class PageView(DbGUIElement, metaclass=ABCMeta):
self.top = None
self.sidebar = None
self.bottombar = None
self.widget = None
DbGUIElement.__init__(self, dbstate.db)
@ -159,9 +160,10 @@ class PageView(DbGUIElement, metaclass=ABCMeta):
hpane.show()
vpane.show()
widget = self.build_widget()
widget.show_all()
vpane.pack1(widget, resize=True, shrink=False)
self.widget = self.build_widget()
self.widget.show_all()
self.widget.set_name('view')
vpane.pack1(self.widget, resize=True, shrink=False)
vpane.pack2(self.bottombar, resize=False, shrink=True)
self._setup_slider_config(vpane, 'vpane.slider-position')

View File

@ -93,6 +93,7 @@ from gramps.gen.const import (
TYPE_BOX_FAMILY)
_ = glocale.translation.gettext
from ..utilscairo import warpPath
from gramps.gen.utils.symbols import Symbols
# following are used in name_displayer format def
# (must not conflict with standard defs)
@ -166,6 +167,7 @@ class FanChartBaseWidget(Gtk.DrawingArea):
[DdTargets.PERSON_LINK.target()],
Gdk.DragAction.COPY)
self.connect('drag_data_received', self.on_drag_data_received)
self.uistate.connect('font-changed', self.reload_symbols)
self._mouse_click = False
self.rotate_value = 90 # degrees, initially, 1st gen male on right half
@ -176,6 +178,17 @@ class FanChartBaseWidget(Gtk.DrawingArea):
#(re)compute everything
self.reset()
self.set_size_request(120, 120)
self.symbols = Symbols()
self.reload_symbols()
def reload_symbols(self):
dth_idx = self.uistate.death_symbol
if self.uistate.symbols:
self.bth = self.symbols.get_symbol_for_string(self.symbols.SYMBOL_BIRTH)
self.dth = self.symbols.get_death_symbol_for_char(dth_idx)
else:
self.bth = self.symbols.get_symbol_fallback(self.symbols.SYMBOL_BIRTH)
self.dth = self.symbols.get_death_symbol_fallback(dth_idx)
def reset(self):
"""
@ -582,8 +595,14 @@ class FanChartBaseWidget(Gtk.DrawingArea):
radial=False, fontcolor=(0, 0, 0), bold=False, can_flip = True):
if not person: return
draw_radial = radial and self.radialtext
try:
alive = probably_alive(person, self.dbstate.db)
except RuntimeError:
alive = False
if not self.twolinename:
name=name_displayer.display(person)
if self.uistate.symbols and not alive:
name = self.dth + ' ' + name
self.draw_text(cr, name, radiusin, radiusout, start, stop, draw_radial,
fontcolor, bold)
else:
@ -656,7 +675,7 @@ class FanChartBaseWidget(Gtk.DrawingArea):
Display text at a particular radius, between start_rad and stop_rad
radians.
"""
font = Pango.FontDescription(self.fontdescr)
font = Pango.FontDescription("")
fontsize = self.fontsize
font.set_size(fontsize * Pango.SCALE)
if bold:
@ -767,7 +786,7 @@ class FanChartBaseWidget(Gtk.DrawingArea):
cr.translate(-self.center_xy[0], -self.center_xy[1])
font = Pango.FontDescription(self.fontdescr)
font = Pango.FontDescription("")
fontsize = self.fontsize
font.set_size(fontsize * Pango.SCALE)
for color, text in zip(self.gradcol, self.gradval):
@ -1505,7 +1524,11 @@ class FanChartGrampsGUI:
"""
self.fan = None
self.on_childmenu_changed = on_childmenu_changed
self.format_helper = FormattingHelper(self.dbstate)
self.format_helper = FormattingHelper(self.dbstate, self.uistate)
self.uistate.connect('font-changed', self.reload_symbols)
def reload_symbols(self):
self.format_helper.reload_symbols()
def set_fan(self, fan):
"""

View File

@ -48,6 +48,7 @@ import cairo
# Gramps Modules
#
#-------------------------------------------------------------------------
from gramps.gen.config import config
#-------------------------------------------------------------------------
#
@ -77,7 +78,7 @@ class DateLayer(GObject.GObject, osmgpsmap.MapLayer):
self.first = " "
self.last = " "
self.color = "black"
self.font = "Arial"
self.font = config.get('utf8.selected-font')
self.size = 36
def clear_dates(self):
@ -87,7 +88,7 @@ class DateLayer(GObject.GObject, osmgpsmap.MapLayer):
self.first = " "
self.last = " "
self.color = "black"
self.font = "Arial"
self.font = config.get('utf8.selected-font')
self.size = 36
def set_font_attributes(self, font, size, color):
@ -95,7 +96,10 @@ class DateLayer(GObject.GObject, osmgpsmap.MapLayer):
Set the font color, size and name
"""
self.color = color
self.font = font
if font:
self.font = font
else:
self.font = config.get('utf8.selected-font')
self.size = size
def add_date(self, date):

View File

@ -144,7 +144,10 @@ class GeoGraphyView(OsmGps, NavigationView):
if config.get('geography.path') == "":
config.set('geography.path', GEOGRAPHY_PATH)
self.format_helper = FormattingHelper(self.dbstate)
self.uistate = uistate
self.uistate.connect('font-changed', self.font_changed)
self.uistate.connect('nameformat-changed', self.build_tree)
self.format_helper = FormattingHelper(self.dbstate, self.uistate)
self.centerlat = self.centerlon = 0.0
self.cross_map = None
self.current_map = None
@ -184,6 +187,12 @@ class GeoGraphyView(OsmGps, NavigationView):
self.nbmarkers = 0
self.place_without_coordinates = []
def font_changed(self):
"""
The font or the death symbol changed.
"""
self.build_tree()
def add_bookmark(self, menu):
"""
Add the place to the bookmark

View File

@ -48,6 +48,7 @@ from gi.repository import Pango, PangoCairo
# Gramps Modules
#
#-------------------------------------------------------------------------
from gramps.gen.config import config
from gramps.gen.constfunc import is_quartz
#-------------------------------------------------------------------------
@ -78,7 +79,7 @@ class MessageLayer(GObject.GObject, osmgpsmap.MapLayer):
GObject.GObject.__init__(self)
self.message = ""
self.color = "black"
self.font = "Sans"
self.font = config.get('utf8.selected-font')
self.size = 13
def clear_messages(self):
@ -92,7 +93,7 @@ class MessageLayer(GObject.GObject, osmgpsmap.MapLayer):
reset the font attributes.
"""
self.color = "black"
self.font = "Sans"
self.font = config.get('utf8.selected-font')
self.size = 13
def set_font_attributes(self, font, size, color):
@ -103,6 +104,8 @@ class MessageLayer(GObject.GObject, osmgpsmap.MapLayer):
self.color = color
if font is not None:
self.font = font
else:
self.font = config.get('utf8.selected-font')
if size is not None:
self.size = size

View File

@ -105,6 +105,15 @@ class FanChart2WayView(fanchart2way.FanChart2WayGrampsGUI, NavigationView):
self.additional_uis.append(FanChartView.additional_ui)
self.allfonts = [x for x in enumerate(SystemFonts().get_system_fonts())]
self.func_list.update({
'<PRIMARY>J' : self.jump,
})
self.uistate.connect('font-changed', self.font_changed)
def font_changed(self):
self.format_helper.reload_symbols()
self.update()
def navigation_type(self):
return 'Person'

View File

@ -100,6 +100,15 @@ class FanChartDescView(fanchartdesc.FanChartDescGrampsGUI, NavigationView):
self.additional_uis.append(FanChartView.additional_ui)
self.allfonts = [x for x in enumerate(SystemFonts().get_system_fonts())]
self.func_list.update({
'<PRIMARY>J' : self.jump,
})
self.uistate.connect('font-changed', self.font_changed)
def font_changed(self):
self.format_helper.reload_symbols()
self.update()
def navigation_type(self):
return 'Person'

View File

@ -96,6 +96,15 @@ class FanChartView(fanchart.FanChartGrampsGUI, NavigationView):
self.additional_uis.append(self.additional_ui)
self.allfonts = [x for x in enumerate(SystemFonts().get_system_fonts())]
self.func_list.update({
'<PRIMARY>J' : self.jump,
})
self.uistate.connect('font-changed', self.font_changed)
def font_changed(self):
self.format_helper.reload_symbols()
self.update()
def navigation_type(self):
return 'Person'

View File

@ -296,6 +296,7 @@ class GeoClose(GeoGraphyView):
self.remove_all_markers()
self.lifeway_layer.clear_ways()
self.message_layer.clear_messages()
self.message_layer.set_font_attributes(None, None, None)
active = self.get_active()
if active:
indiv1 = self.dbstate.db.get_person_from_handle(active)

View File

@ -331,6 +331,8 @@ class GeoEvents(GeoGraphyView):
self.nbplaces = 0
self.without = 0
self.cal = config.get('preferences.calendar-format-report')
self.message_layer.clear_messages()
self.message_layer.clear_font_attributes()
self.no_show_places_in_status_bar = False
if self.show_all:
self.show_all = False
@ -358,9 +360,15 @@ class GeoEvents(GeoGraphyView):
self._createmap_for_one_event(event)
progress.step()
progress.close()
elif obj:
event = dbstate.db.get_event_from_handle(obj)
self._createmap_for_one_event(event)
else:
if obj:
event = dbstate.db.get_event_from_handle(obj)
self._createmap_for_one_event(event)
self.message_layer.add_message(
_("Right click on the map and select 'show all events'"
" to show all known events with coordinates. "
"You can use the history to navigate on the map. "
"You can use filtering."))
self.sort = sorted(self.place_list,
key=operator.itemgetter(3, 4, 6)
)

View File

@ -329,6 +329,7 @@ class GeoFamClose(GeoGraphyView):
self.remove_all_markers()
self.lifeway_layer.clear_ways()
self.message_layer.clear_messages()
self.message_layer.set_font_attributes(None, None, None)
active = self.get_active()
family = None
if active:

View File

@ -445,6 +445,7 @@ class GeoFamily(GeoGraphyView):
self.minyear = 9999
self.maxyear = 0
self.message_layer.clear_messages()
self.message_layer.set_font_attributes(None, None, None)
if self.dbstate.db.has_family_handle(handle):
family = self.dbstate.db.get_family_from_handle(handle)
self._createmap_for_one_family(family)

View File

@ -299,6 +299,7 @@ class GeoMoves(GeoGraphyView):
self.lifeway_layer.clear_ways()
self.date_layer.clear_dates()
self.message_layer.clear_messages()
self.message_layer.set_font_attributes(None, None, None)
def draw(self, menu, marks, color):
"""

View File

@ -368,6 +368,7 @@ class GeoPerson(GeoGraphyView):
self.nbplaces = 0
self.nbmarkers = 0
self.message_layer.clear_messages()
self.message_layer.set_font_attributes(None, None, None)
self.kml_layer.clear()
person_handle = self.uistate.get_active('Person')
person = None

View File

@ -68,6 +68,7 @@ from gramps.gui.utils import color_graph_box, hex_to_rgb_float, is_right_click
from gramps.gen.constfunc import lin
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.sgettext
from gramps.gen.utils.symbols import Symbols
#-------------------------------------------------------------------------
#
@ -530,11 +531,13 @@ class PedigreeView(NavigationView):
PersonBookmarks, nav_group)
self.dbstate = dbstate
self.uistate = uistate
self.dbstate.connect('database-changed', self.change_db)
uistate.connect('nameformat-changed', self.person_rebuild)
uistate.connect('placeformat-changed', self.person_rebuild)
uistate.connect('font-changed', self.person_rebuild)
self.format_helper = FormattingHelper(self.dbstate)
self.format_helper = FormattingHelper(self.dbstate, self.uistate)
# Depth of tree.
self._depth = 1
@ -568,6 +571,23 @@ class PedigreeView(NavigationView):
self.show_unknown_people = self._config.get(
'interface.pedview-show-unknown-people')
self.func_list.update({
'<PRIMARY>J' : self.jump,
})
# use symbols
self.symbols = Symbols()
self.uistate.connect('font-changed', self.reload_symbols)
def reload_symbols(self):
dth_idx = self.uistate.death_symbol
if self.uistate.symbols:
self.bth = self.symbols.get_symbol_for_string(self.symbols.SYMBOL_BIRTH)
self.dth = self.symbols.get_death_symbol_for_char(dth_idx)
else:
self.bth = self.symbols.get_symbol_fallback(self.symbols.SYMBOL_BIRTH)
self.dth = self.symbols.get_death_symbol_fallback(dth_idx)
def get_handle_from_gramps_id(self, gid):
"""
returns the handle of the specified object
@ -833,6 +853,7 @@ class PedigreeView(NavigationView):
def person_rebuild(self, dummy=None):
"""Callback function for signals of change database."""
self.format_helper.clear_cache()
self.format_helper.reload_symbols()
self.dirty = True
if self.active:
self.rebuild_trees(self.get_active())

View File

@ -80,12 +80,7 @@ from gramps.gen.const import CUSTOM_FILTERS
from gramps.gen.utils.db import (get_birth_or_fallback, get_death_or_fallback,
preset_name)
from gramps.gui.ddtargets import DdTargets
_GenderCode = {
Person.MALE : '\u2642',
Person.FEMALE : '\u2640',
Person.UNKNOWN : '\u2650',
}
from gramps.gen.utils.symbols import Symbols
_NAME_START = 0
_LABEL_START = 0
@ -141,6 +136,7 @@ class RelationshipView(NavigationView):
dbstate.connect('database-changed', self.change_db)
uistate.connect('nameformat-changed', self.build_tree)
uistate.connect('placeformat-changed', self.build_tree)
uistate.connect('font-changed', self.font_changed)
self.redrawing = False
self.child = None
@ -157,7 +153,8 @@ class RelationshipView(NavigationView):
self.theme = self._config.get('preferences.relation-display-theme')
self.toolbar_visible = config.get('interface.toolbar-on')
self.age_precision = config.get('preferences.age-display-precision')
self.symbols = Symbols()
self.reload_symbols()
def get_handle_from_gramps_id(self, gid):
"""
@ -186,6 +183,36 @@ class RelationshipView(NavigationView):
self.callman.add_db_signal('person-delete', self.redraw)
def reload_symbols(self):
if self.uistate and self.uistate.symbols:
gsfs = self.symbols.get_symbol_for_string
self.male = gsfs(self.symbols.SYMBOL_MALE)
self.female = gsfs(self.symbols.SYMBOL_FEMALE)
self.bth = gsfs(self.symbols.SYMBOL_BIRTH)
self.marr = gsfs(self.symbols.SYMBOL_HETEROSEXUAL)
self.homom = gsfs(self.symbols.SYMBOL_MALE_HOMOSEXUAL)
self.homof = gsfs(self.symbols.SYMBOL_LESBIAN)
self.divorce = gsfs(self.symbols.SYMBOL_DIVORCE)
self.unmarr = gsfs(self.symbols.SYMBOL_UNMARRIED_PARTNERSHIP)
death_idx = self.uistate.death_symbol
self.dth = self.symbols.get_death_symbol_for_char(death_idx)
else:
gsf = self.symbols.get_symbol_fallback
self.male = gsf(self.symbols.SYMBOL_MALE)
self.female = gsf(self.symbols.SYMBOL_FEMALE)
self.bth = gsf(self.symbols.SYMBOL_BIRTH)
self.marr = gsf(self.symbols.SYMBOL_HETEROSEXUAL)
self.homom = gsf(self.symbols.SYMBOL_MALE_HOMOSEXUAL)
self.homof = gsf(self.symbols.SYMBOL_LESBIAN)
self.divorce = gsf(self.symbols.SYMBOL_DIVORCE)
self.unmarr = gsf(self.symbols.SYMBOL_UNMARRIED_PARTNERSHIP)
death_idx = self.symbols.DEATH_SYMBOL_LATIN_CROSS
self.dth = self.symbols.get_death_symbol_fallback(death_idx)
def font_changed(self):
self.reload_symbols()
self.build_tree()
def navigation_type(self):
return 'Person'
@ -554,7 +581,7 @@ class RelationshipView(NavigationView):
person = self.dbstate.db.get_person_from_handle(handle)
name = name_displayer.display(person)
if use_gender:
gender = _GenderCode[person.gender]
gender = self.symbols.get_symbol_for_string(person.gender)
else:
gender = ""
return (name, gender)
@ -667,7 +694,8 @@ class RelationshipView(NavigationView):
name = name_displayer.display(person)
fmt = '<span size="larger" weight="bold">%s</span>'
text = fmt % escape(name)
label = widgets.DualMarkupLabel(text, _GenderCode[person.gender],
gender_code = self.symbols.get_symbol_for_string(person.gender)
label = widgets.DualMarkupLabel(text, gender_code,
halign=Gtk.Align.END)
if self._config.get('preferences.releditbtn'):
button = widgets.IconButton(self.edit_button_press,
@ -847,6 +875,29 @@ class RelationshipView(NavigationView):
_ADATA_STOP-_ADATA_START, 1)
self.row += 1
def marriage_symbol(self, family, markup=True):
if family:
father = mother = None
hdl1 = family.get_father_handle()
if hdl1:
father = self.dbstate.db.get_person_from_handle(hdl1).gender
hdl2 = family.get_mother_handle()
if hdl2:
mother = self.dbstate.db.get_person_from_handle(hdl2).gender
if father != mother:
symbol = self.marr
elif father == Person.MALE:
symbol = self.homom
else:
symbol = self.homof
if markup:
msg = '<span size="24000" >%s</span>' % symbol
else:
msg = symbol
else:
msg = ""
return msg
def write_label(self, title, family, is_parent, person = None):
"""
Write a Family header row
@ -897,6 +948,10 @@ class RelationshipView(NavigationView):
hbox = Gtk.Box()
hbox.set_spacing(12)
hbox.set_hexpand(True)
if self.uistate and self.uistate.symbols:
msg = self.marriage_symbol(family)
marriage = widgets.MarkupLabel(msg)
hbox.pack_start(marriage, False, True, 0)
if is_parent:
call_fcn = self.add_parent_family
del_fcn = self.delete_parent_family
@ -984,6 +1039,7 @@ class RelationshipView(NavigationView):
childmsg = _(" (1 sibling)")
else :
childmsg = _(" (only child)")
self.family = family
box = self.get_people_box(family.get_father_handle(),
family.get_mother_handle(),
post_msg=childmsg)
@ -1038,6 +1094,7 @@ class RelationshipView(NavigationView):
childmsg = _(" (1 sibling)")
else :
childmsg = _(" (only child)")
self.family = None
box = self.get_people_box(post_msg=childmsg)
eventbox = widgets.ShadeBox(self.use_shade)
eventbox.add(box)
@ -1077,6 +1134,10 @@ class RelationshipView(NavigationView):
def get_people_box(self, *handles, **kwargs):
hbox = Gtk.Box()
initial_name = True
if self.uistate and self.uistate.symbols:
msg = self.marriage_symbol(self.family) + " "
marriage = widgets.MarkupLabel(msg)
hbox.pack_start(marriage, False, True, 0)
for handle in handles:
if not initial_name:
link_label = Gtk.Label(label=" %s " % _('and'))
@ -1317,15 +1378,15 @@ class RelationshipView(NavigationView):
if bdate and ddate:
value = _("%(birthabbrev)s %(birthdate)s, %(deathabbrev)s %(deathdate)s") % {
'birthabbrev': birth.type.get_abbreviation(),
'deathabbrev': death.type.get_abbreviation(),
'birthabbrev': self.bth,
'deathabbrev': self.dth,
'birthdate' : bdate,
'deathdate' : ddate
}
elif bdate:
value = _("%(event)s %(date)s") % {'event': birth.type.get_abbreviation(), 'date': bdate}
value = _("%(event)s %(date)s") % {'event': self.bth, 'date': bdate}
elif ddate:
value = _("%(event)s %(date)s") % {'event': death.type.get_abbreviation(), 'date': ddate}
value = _("%(event)s %(date)s") % {'event': self.dth, 'date': ddate}
else:
value = ""
return value
@ -1395,7 +1456,16 @@ class RelationshipView(NavigationView):
if (event and event.get_type().is_relationship_event() and
(event_ref.get_role() == EventRoleType.FAMILY or
event_ref.get_role() == EventRoleType.PRIMARY)):
self.write_event_ref(vbox, event.get_type().string, event)
if event.get_type() == EventType.MARRIAGE:
msg = self.marriage_symbol(family, markup=False)
etype = msg
elif event.get_type() == EventType.DIVORCE:
etype = self.divorce
elif event.get_type() == EventType.DIVORCE:
etype = self.divorce
else:
etype = event.get_type().string
self.write_event_ref(vbox, etype, event)
value = True
return value
@ -1470,6 +1540,7 @@ class RelationshipView(NavigationView):
).format(number_of=count)
else :
childmsg = _(" (no children)")
self.family = family
box = self.get_people_box(handle, post_msg=childmsg)
eventbox = widgets.ShadeBox(self.use_shade)
eventbox.add(box)
@ -1515,6 +1586,7 @@ class RelationshipView(NavigationView):
).format(number_of=count)
else :
childmsg = _(" (no children)")
self.family = None
box = self.get_people_box(post_msg=childmsg)
eventbox = widgets.ShadeBox(self.use_shade)
eventbox.add(box)