1# 2# Gramps - a GTK+/GNOME based genealogy program 3# 4# Copyright (C) 2000-2006 Donald N. Allingham 5# 2009 Gary Burton 6# Copyright (C) 2014 Paul Franklin 7# 8# This program is free software; you can redistribute it and/or modify 9# it under the terms of the GNU General Public License as published by 10# the Free Software Foundation; either version 2 of the License, or 11# (at your option) any later version. 12# 13# This program is distributed in the hope that it will be useful, 14# but WITHOUT ANY WARRANTY; without even the implied warranty of 15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16# GNU General Public License for more details. 17# 18# You should have received a copy of the GNU General Public License 19# along with this program; if not, write to the Free Software 20# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21# 22 23#------------------------------------------------------------------------- 24# 25# GTK/Gnome modules 26# 27#------------------------------------------------------------------------- 28from gi.repository import Gtk 29 30#------------------------------------------------------------------------- 31# 32# Gramps modules 33# 34#------------------------------------------------------------------------- 35from gramps.gen.const import GRAMPS_LOCALE as glocale 36_ = glocale.translation.gettext 37from ..dialog import ErrorDialog 38from ..managedwindow import ManagedWindow 39from .displaytabs import GrampsTab 40from gramps.gen.config import config 41from ..dbguielement import DbGUIElement 42 43#------------------------------------------------------------------------- 44# 45# Classes 46# 47#------------------------------------------------------------------------- 48 49class RefTab(GrampsTab): 50 """ 51 This class provides a simple tabpage for use on EditReference 52 """ 53 54 def __init__(self, dbstate, uistate, track, name, widget): 55 """ 56 @param dbstate: The database state. Contains a reference to 57 the database, along with other state information. The GrampsTab 58 uses this to access the database and to pass to and created 59 child windows (such as edit dialogs). 60 @type dbstate: DbState 61 @param uistate: The UI state. Used primarily to pass to any created 62 subwindows. 63 @type uistate: DisplayState 64 @param track: The window tracking mechanism used to manage windows. 65 This is only used to pass to generted child windows. 66 @type track: list 67 @param name: Notebook label name 68 @type name: str/unicode 69 @param widget: widget to be shown in the tab 70 @type widge: gtk widget 71 """ 72 GrampsTab.__init__(self, dbstate, uistate, track, name) 73 eventbox = Gtk.EventBox() 74 eventbox.add(widget) 75 self.pack_start(eventbox, True, True, 0) 76 self._set_label(show_image=False) 77 widget.connect('key_press_event', self.key_pressed) 78 self.show_all() 79 80 def is_empty(self): 81 """ 82 Override base class 83 """ 84 return False 85 86#------------------------------------------------------------------------- 87# 88# EditReference class 89# 90#------------------------------------------------------------------------- 91class EditReference(ManagedWindow, DbGUIElement): 92 93 def __init__(self, state, uistate, track, source, source_ref, update): 94 self.db = state.db 95 self.dbstate = state 96 self.uistate = uistate 97 self.source_ref = source_ref 98 self.source = source 99 self.source_added = False 100 self.update = update 101 self.warn_box = None 102 self.__tabs = [] 103 104 ManagedWindow.__init__(self, uistate, track, source_ref) 105 DbGUIElement.__init__(self, self.db) 106 107 self._local_init() 108 self._set_size() 109 self._create_tabbed_pages() 110 self._setup_fields() 111 self._connect_signals() 112 self.show() 113 self._post_init() 114 115 def _local_init(self): 116 """ 117 Derived class should do any pre-window initalization in this task. 118 """ 119 pass 120 121 def define_warn_box(self,box): 122 self.warn_box = box 123 124 def enable_warnbox(self): 125 self.warn_box.show() 126 127 def define_expander(self,expander): 128 expander.set_expanded(True) 129 130 def _post_init(self): 131 """ 132 Derived class should do any post-window initalization in this task. 133 """ 134 pass 135 136 def _setup_notebook_tabs(self, notebook): 137 for child in notebook.get_children(): 138 label = notebook.get_tab_label(child) 139 page_no = notebook.page_num(child) 140 label.drag_dest_set(0, [], 0) 141 label.connect('drag_motion', 142 self._switch_page_on_dnd, 143 notebook, 144 page_no) 145 child.set_parent_notebook(notebook) 146 notebook.connect('key-press-event', self.key_pressed, notebook) 147 148 def key_pressed(self, obj, event, notebook): 149 """ 150 Handles the key being pressed on the notebook, pass to key press of 151 current page. 152 """ 153 pag = notebook.get_current_page() 154 if not pag == -1: 155 notebook.get_nth_page(pag).key_pressed(obj, event) 156 157 def _switch_page_on_dnd(self, widget, context, x, y, time, notebook, page_no): 158 if notebook.get_current_page() != page_no: 159 notebook.set_current_page(page_no) 160 161 def _add_tab(self, notebook,page): 162 self.__tabs.append(page) 163 notebook.insert_page(page, page.get_tab_widget(), -1) 164 page.label.set_use_underline(True) 165 return page 166 167 def _connect_signals(self): 168 pass 169 170 def _setup_fields(self): 171 pass 172 173 def _create_tabbed_pages(self): 174 pass 175 176 def build_window_key(self,sourceref): 177 #the window key for managedwindow identification. No need to return None 178 if self.source and self.source.get_handle(): 179 return self.source.get_handle() 180 else: 181 return id(self) 182 183 def define_ok_button(self, button, function): 184 button.connect('clicked',function) 185 button.set_sensitive(not self.db.readonly) 186 187 def define_cancel_button(self, button): 188 button.connect('clicked',self.close_and_cancel) 189 190 def close_and_cancel(self, obj): 191 self.close(obj) 192 193 def check_for_close(self, handles): 194 """ 195 Callback method for delete signals. 196 If there is a delete signal of the primary object we are editing, the 197 editor (and all child windows spawned) should be closed 198 """ 199 if self.source.get_handle() in handles: 200 self.close() 201 202 def define_help_button(self, button, webpage='', section=''): 203 from ..display import display_help 204 button.connect('clicked', lambda x: display_help(webpage, 205 section)) 206 button.set_sensitive(True) 207 208 def _cleanup_on_exit(self): 209 """Unset all things that can block garbage collection. 210 Finalize rest 211 """ 212 for tab in self.__tabs: 213 if hasattr(tab, '_cleanup_on_exit'): 214 tab._cleanup_on_exit() 215 self.__tabs = None 216 self.dbstate = None 217 self.uistate = None 218 self.source_ref = None 219 self.source = None 220 self.update = None 221 self.warn_box = None 222 self.db = None 223 self.callman.database = None 224 self.callman = None 225 226 def close(self,*obj): 227 self._cleanup_db_connects() 228 self._cleanup_connects() 229 ManagedWindow.close(self) 230 self._cleanup_on_exit() 231 232 def _cleanup_db_connects(self): 233 """ 234 All connects that happened to signals of the db must be removed on 235 closed. This implies two things: 236 1. The connects on the main view must be disconnected 237 2. Connects done in subelements must be disconnected 238 """ 239 #cleanup callbackmanager of this editor 240 self._cleanup_callbacks() 241 for tab in [tab for tab in self.__tabs if hasattr(tab, 'callman')]: 242 tab._cleanup_callbacks() 243 244 def _cleanup_connects(self): 245 """ 246 Connects to interface elements to things outside the element should be 247 removed before destroying the interface 248 """ 249 self._cleanup_local_connects() 250 for tab in [tab for tab in self.__tabs if hasattr(tab, '_cleanup_local_connects')]: 251 tab._cleanup_local_connects() 252 253 def _cleanup_local_connects(self): 254 """ 255 Connects to interface elements to things outside the element should be 256 removed before destroying the interface. This methods cleans connects 257 of the main interface, not of the displaytabs. 258 """ 259 pass 260 261 def check_for_duplicate_id(self, type): 262 """ 263 check to see if the gramps ID (if any) already exists 264 265 type : the gramps primary object type, a string 266 returns : True if the gramps ID already exists, else False 267 268 N.B. the various strings, string variables, and titles existed already 269 """ 270 new_id = self.source.get_gramps_id() 271 if new_id: 272 id_func = getattr(self.db, 'get_%s_from_gramps_id' % type.lower()) 273 old_primary = id_func(new_id) 274 if old_primary: 275 description = None 276 if type == 'Event': 277 msg1 = _("Cannot save event. ID already exists.") 278 description = old_primary.get_description() 279 elif type == 'Media': 280 msg1 = _("Cannot save media object. ID already exists.") 281 description = old_primary.get_description() 282 elif type == 'Repository': 283 msg1 = _("Cannot save repository. ID already exists.") 284 description = old_primary.get_name() 285 else: 286 msg1 = _("Cannot save item. ID already exists.") 287 if description: 288 msg2 = _("You have attempted to use the existing Gramps " 289 "ID with value %(id)s. This value is already " 290 "used by '%(prim_object)s'. Please enter a " 291 "different ID or leave blank to get the next " 292 "available ID value.") % { 293 'id' : new_id, 'prim_object' : description } 294 else: 295 msg2 = _("You have attempted to use the existing Gramps " 296 "ID with value %(id)s. This value is already " 297 "used. Please enter a " 298 "different ID or leave blank to get the next " 299 "available ID value.") % { 300 'id' : new_id} 301 ErrorDialog(msg1, msg2, parent=self.window) 302 return True 303 return False 304