1# 2# Gramps - a GTK+/GNOME based genealogy program 3# 4# Copyright (C) 2014-2017 Nick Hall 5# 6# This program is free software; you can redistribute it and/or modify 7# it under the terms of the GNU General Public License as published by 8# the Free Software Foundation; either version 2 of the License, or 9# (at your option) any later version. 10# 11# This program is distributed in the hope that it will be useful, 12# but WITHOUT ANY WARRANTY; without even the implied warranty of 13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14# GNU General Public License for more details. 15# 16# You should have received a copy of the GNU General Public License 17# along with this program; if not, write to the Free Software 18# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19# 20 21""" 22Class handling displaying of places. 23""" 24 25#--------------------------------------------------------------- 26# 27# Python imports 28# 29#--------------------------------------------------------------- 30import os 31import xml.dom.minidom 32 33#------------------------------------------------------------------------- 34# 35# Gramps modules 36# 37#------------------------------------------------------------------------- 38from ..const import PLACE_FORMATS, GRAMPS_LOCALE as glocale 39_ = glocale.translation.gettext 40from ..config import config 41from ..utils.location import get_location_list 42from ..lib import PlaceType 43 44#------------------------------------------------------------------------- 45# 46# PlaceFormat class 47# 48#------------------------------------------------------------------------- 49class PlaceFormat: 50 def __init__(self, name, levels, language, street, reverse): 51 self.name = name 52 self.levels = levels 53 self.language = language 54 self.street = street 55 self.reverse = reverse 56 57 58#------------------------------------------------------------------------- 59# 60# PlaceDisplay class 61# 62#------------------------------------------------------------------------- 63class PlaceDisplay: 64 65 def __init__(self): 66 self.place_formats = [] 67 self.default_format = config.get('preferences.place-format') 68 if os.path.exists(PLACE_FORMATS): 69 try: 70 self.load_formats() 71 return 72 except BaseException: 73 print(_("Error in '%s' file: cannot load.") % PLACE_FORMATS) 74 pf = PlaceFormat(_('Full'), ':', '', 0, False) 75 self.place_formats.append(pf) 76 77 def display_event(self, db, event, fmt=-1): 78 if not event: 79 return "" 80 place_handle = event.get_place_handle() 81 if place_handle: 82 place = db.get_place_from_handle(place_handle) 83 return self.display(db, place, event.get_date_object(), fmt) 84 else: 85 return "" 86 87 def display(self, db, place, date=None, fmt=-1): 88 if not place: 89 return "" 90 if not config.get('preferences.place-auto'): 91 return place.title 92 else: 93 if fmt == -1: 94 fmt = config.get('preferences.place-format') 95 pf = self.place_formats[fmt] 96 lang = pf.language 97 all_places = get_location_list(db, place, date, lang) 98 99 # Apply format string to place list 100 index = _find_populated_place(all_places) 101 places = [] 102 for slice in pf.levels.split(','): 103 parts = slice.split(':') 104 if len(parts) == 1: 105 offset = _get_offset(parts[0], index) 106 if offset is not None: 107 try: 108 places.append(all_places[offset]) 109 except IndexError: 110 pass 111 elif len(parts) == 2: 112 start = _get_offset(parts[0], index) 113 end = _get_offset(parts[1], index) 114 if start is None: 115 places.extend(all_places[:end]) 116 elif end is None: 117 places.extend(all_places[start:]) 118 else: 119 places.extend(all_places[start:end]) 120 121 if pf.street: 122 types = [item[1] for item in places] 123 try: 124 idx = types.index(PlaceType.NUMBER) 125 except ValueError: 126 idx = None 127 if idx is not None and len(places) > idx+1: 128 if pf.street == 1: 129 combined = (places[idx][0] + ' ' + places[idx+1][0], 130 places[idx+1][1]) 131 else: 132 combined = (places[idx+1][0] + ' ' + places[idx][0], 133 places[idx+1][1]) 134 places = places[:idx] + [combined] + places[idx+2:] 135 136 names = [item[0] for item in places] 137 if pf.reverse: 138 names.reverse() 139 140 # TODO for Arabic, should the next line's comma be translated? 141 return ", ".join(names) 142 143 def get_formats(self): 144 return self.place_formats 145 146 def set_formats(self, formats): 147 self.place_formats = formats 148 149 def load_formats(self): 150 dom = xml.dom.minidom.parse(PLACE_FORMATS) 151 top = dom.getElementsByTagName('place_formats') 152 153 for fmt in top[0].getElementsByTagName('format'): 154 name = fmt.attributes['name'].value 155 levels = fmt.attributes['levels'].value 156 language = fmt.attributes['language'].value 157 street = int(fmt.attributes['street'].value) 158 reverse = fmt.attributes['reverse'].value == 'True' 159 pf = PlaceFormat(name, levels, language, street, reverse) 160 self.place_formats.append(pf) 161 162 dom.unlink() 163 164 def save_formats(self): 165 doc = xml.dom.minidom.Document() 166 place_formats = doc.createElement('place_formats') 167 doc.appendChild(place_formats) 168 for fmt in self.place_formats: 169 node = doc.createElement('format') 170 place_formats.appendChild(node) 171 node.setAttribute('name', fmt.name) 172 node.setAttribute('levels', fmt.levels) 173 node.setAttribute('language', fmt.language) 174 node.setAttribute('street', str(fmt.street)) 175 node.setAttribute('reverse', str(fmt.reverse)) 176 with open(PLACE_FORMATS, 'w', encoding='utf-8') as f_d: 177 doc.writexml(f_d, addindent=' ', newl='\n', encoding='utf-8') 178 179 180def _get_offset(value, index): 181 if index is not None and value.startswith('p'): 182 try: 183 offset = int(value[1:]) 184 except ValueError: 185 offset = 0 186 offset += index 187 else: 188 try: 189 offset = int(value) 190 except ValueError: 191 offset = None 192 return offset 193 194def _find_populated_place(places): 195 populated_place = None 196 for index, item in enumerate(places): 197 if int(item[1]) in [PlaceType.HAMLET, PlaceType.VILLAGE, 198 PlaceType.TOWN, PlaceType.CITY]: 199 populated_place = index 200 return populated_place 201 202displayer = PlaceDisplay() 203