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