~ubuntu-branches/ubuntu/trusty/gramps/trusty-proposed

« back to all changes in this revision

Viewing changes to gramps/gui/grampsgui.py

  • Committer: Package Import Robot
  • Author(s): Ross Gammon
  • Date: 2014-02-03 17:28:04 UTC
  • mfrom: (39.1.7 sid)
  • Revision ID: package-import@ubuntu.com-20140203172804-76y7nwxiw92zhlnj
Tags: 4.0.3+dfsg-1
* New upstream release (Closes: #720858)
* To-do notes improved and made persistent (Closes: #680692)
* Applied patch to setup.py to fix resource path problem
* Applied patch to disable the optional HTML View & prevent a crash
* Remove sourceless javascript files (Closes: #736436)
* Gramps uses Bat Mitzva internally (Closes: #502532)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#
 
2
# Gramps - a GTK+/GNOME based genealogy program
 
3
#
 
4
# Copyright (C) 2000-2006  Donald N. Allingham
 
5
# Copyright (C) 2009 Benny Malengier
 
6
#
 
7
# This program is free software; you can redistribute it and/or modify
 
8
# it under the terms of the GNU General Public License as published by
 
9
# the Free Software Foundation; either version 2 of the License, or
 
10
# (at your option) any later version.
 
11
#
 
12
# This program is distributed in the hope that it will be useful,
 
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
# GNU General Public License for more details.
 
16
#
 
17
# You should have received a copy of the GNU General Public License
 
18
# along with this program; if not, write to the Free Software
 
19
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
20
#
 
21
 
 
22
# $Id$
 
23
 
 
24
#-------------------------------------------------------------------------
 
25
#
 
26
# Python modules
 
27
#
 
28
#-------------------------------------------------------------------------
 
29
from __future__ import print_function
 
30
 
 
31
import sys
 
32
import os
 
33
import logging
 
34
LOG = logging.getLogger(".grampsgui")
 
35
 
 
36
#-------------------------------------------------------------------------
 
37
#
 
38
# GRAMPS Modules
 
39
#
 
40
#-------------------------------------------------------------------------
 
41
from gramps.gen.config import config
 
42
from gramps.gen.const import DATA_DIR, IMAGE_DIR, GTK_GETTEXT_DOMAIN
 
43
from gramps.gen.constfunc import has_display, win, lin
 
44
from gramps.gen.const import GRAMPS_LOCALE as glocale
 
45
_ = glocale.translation.gettext
 
46
 
 
47
#-------------------------------------------------------------------------
 
48
#
 
49
# Miscellaneous initialization
 
50
#
 
51
#-------------------------------------------------------------------------
 
52
 
 
53
MIN_PYGOBJECT_VERSION = (3, 3, 2)
 
54
PYGOBJ_ERR = False
 
55
 
 
56
try:
 
57
    #import gnome introspection, part of pygobject
 
58
    import gi
 
59
    giversion = gi.require_version
 
60
except:
 
61
    print(_("Your version of gi (gnome-instrospection) seems to be too old. "
 
62
            "You need a version which has the function 'require_version' "
 
63
            "to start Gramps"))
 
64
    sys.exit(0)
 
65
            
 
66
if not PYGOBJ_ERR:
 
67
    try:
 
68
        from gi.repository import GObject
 
69
        if not GObject.pygobject_version >= MIN_PYGOBJECT_VERSION :
 
70
            PYGOBJ_ERR = True
 
71
    except:
 
72
        PYGOBJ_ERR = True
 
73
 
 
74
if PYGOBJ_ERR:
 
75
    print((_("Your pygobject version does not meet the requirements.\n"
 
76
             "At least pygobject %(major)d.%(feature)d.%(minor)d "
 
77
             "is needed to start Gramps with a GUI.\n\n"
 
78
             "Gramps will terminate now.") % 
 
79
            {'major':MIN_PYGOBJECT_VERSION[0], 
 
80
            'feature':MIN_PYGOBJECT_VERSION[1],
 
81
            'minor':MIN_PYGOBJECT_VERSION[2]}))
 
82
    sys.exit(0)
 
83
 
 
84
try:
 
85
    gi.require_version('Gtk', '3.0')
 
86
    #It is important to import Pango before Gtk, or some things start to go
 
87
    #wrong in GTK3 !
 
88
    from gi.repository import Pango
 
89
    from gi.repository import Gtk, Gdk
 
90
except (ImportError, ValueError):
 
91
    print((_("Gdk, Gtk or Pango typelib not installed.\n"
 
92
             "Install Gnome Introspection, and "
 
93
             "pygobject version 3.3.2 or later.\n"
 
94
             "Install then instrospection data for Gdk, Gtk and Pango\n\n"
 
95
             "Gramps will terminate now.")))
 
96
    sys.exit(0)
 
97
 
 
98
try:
 
99
    import cairo
 
100
except ImportError:
 
101
    print((_("\ncairo python support not installed. Install cairo for your "
 
102
             "version of python\n\n"
 
103
             "Gramps will terminate now.")))
 
104
    sys.exit(0)
 
105
 
 
106
#-------------------------------------------------------------------------
 
107
#
 
108
# Functions
 
109
#
 
110
#-------------------------------------------------------------------------
 
111
 
 
112
def register_stock_icons ():
 
113
    """
 
114
    Add the gramps names for its icons (eg gramps-person) to the GTK icon
 
115
    factory. This allows all gramps modules to call up the icons by their name
 
116
    """
 
117
    from .pluginmanager import base_reg_stock_icons
 
118
 
 
119
    #iconpath to the base image. The front of the list has highest priority
 
120
    if win():
 
121
        iconpaths = [
 
122
                    (os.path.join(IMAGE_DIR, '48x48'), '.png'),
 
123
                    (IMAGE_DIR, '.png'),
 
124
                    ]
 
125
    else :
 
126
        iconpaths = [
 
127
                    (os.path.join(IMAGE_DIR, 'scalable'), '.svg'),
 
128
                    (IMAGE_DIR, '.svg'), (IMAGE_DIR, '.png'),
 
129
                    ]
 
130
 
 
131
    #sizes: menu=16, small_toolbar=18, large_toolbar=24,
 
132
    #       button=20, dnd=32, dialog=48
 
133
    #add to the back of this list to overrule images set at beginning of list
 
134
    extraiconsize = [
 
135
                    (os.path.join(IMAGE_DIR, '22x22'),
 
136
                            Gtk.IconSize.LARGE_TOOLBAR),
 
137
                    (os.path.join(IMAGE_DIR, '16x16'),
 
138
                            Gtk.IconSize.MENU),
 
139
                    (os.path.join(IMAGE_DIR, '22x22'),
 
140
                            Gtk.IconSize.BUTTON),
 
141
                    ]
 
142
 
 
143
    items = [
 
144
        ('gramps-db', _('Family Trees'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
145
        ('gramps-address', _('Address'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
146
        ('gramps-attribute', _('Attribute'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
147
        #('gramps-bookmark', _('Bookmarks'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
148
        #('gramps-bookmark-delete', _('Delete bookmark'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
149
        ('gramps-bookmark-new', _('_Add bookmark'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
150
        ('gramps-bookmark-edit', _('Organize Bookmarks'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
151
        ('gramps-config', _('Configure'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
152
        ('gramps-date', _('Date'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
153
        ('gramps-date-edit', _('Edit Date'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
154
        ('gramps-event', _('Events'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
155
        ('gramps-family', _('Family'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
156
        ('gramps-fanchart', _('Fan Chart'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
157
        ('gramps-fanchartdesc', _('Descendant Fan Chart'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
158
        ('gramps-font', _('Font'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
159
        ('gramps-font-color', _('Font Color'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
160
        ('gramps-font-bgcolor', _('Font Background Color'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
161
        ('gramps-gramplet', _('Gramplets'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
162
        ('gramps-geo', _('Geography'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
163
        ('gramps-geo-mainmap', _('Geography'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
164
        ('gramps-geo-altmap', _('Geography'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
165
        ('geo-show-person', _('GeoPerson'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
166
        ('geo-show-family', _('GeoFamily'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
167
        ('geo-show-event', _('GeoEvents'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
168
        ('geo-show-place', _('GeoPlaces'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
169
        ('gramps-lock', _('Public'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
170
        ('gramps-media', _('Media'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
171
        ('gramps-merge', _('Merge'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
172
        ('gramps-notes', _('Notes'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
173
        ('gramps-parents', _('Parents'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
174
        ('gramps-parents-add', _('Add Parents'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
175
        ('gramps-parents-open', _('Select Parents'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
176
        ('gramps-pedigree', _('Pedigree'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
177
        ('gramps-person', _('Person'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
178
        ('gramps-place', _('Places'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
179
        ('gramps-relation', _('Relationships'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
180
        ('gramps-reports', _('Reports'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
181
        ('gramps-repository', _('Repositories'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
182
        ('gramps-source', _('Sources'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
183
        ('gramps-spouse', _('Add Spouse'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
184
        ('gramps-tag', _('Tag'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
185
        ('gramps-tag-new', _('New Tag'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
186
        ('gramps-tools', _('Tools'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
187
        ('gramps-tree-group', _('Grouped List'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
188
        ('gramps-tree-list', _('List'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
189
        ('gramps-tree-select', _('Select'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
190
        ('gramps-unlock', _('Private'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
191
        ('gramps-view', _('View'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
192
        ('gramps-viewmedia', _('View'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
193
        ('gramps-zoom-in', _('Zoom In'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
194
        ('gramps-zoom-out', _('Zoom Out'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
195
        ('gramps-zoom-fit-width', _('Fit Width'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
196
        ('gramps-zoom-best-fit', _('Fit Page'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
197
        ('gramps-citation', _('Citations'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
198
        ]
 
199
    # the following icons are not yet in new directory structure
 
200
    # they should be ported in the near future
 
201
    items_legacy = [
 
202
         ('gramps-export', _('Export'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
203
         ('gramps-import', _('Import'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
204
         ('gramps-undo-history', _('Undo History'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
205
         ('gramps-url', _('URL'), Gdk.ModifierType.CONTROL_MASK, 0, ''),
 
206
        ]
 
207
 
 
208
    base_reg_stock_icons(iconpaths, extraiconsize, items+items_legacy)
 
209
 
 
210
def _display_welcome_message():
 
211
    """
 
212
    Display a welcome message to the user.
 
213
    """
 
214
    if not config.get('behavior.betawarn'):
 
215
        from .dialog import WarningDialog
 
216
        WarningDialog(
 
217
            _('Danger: This is unstable code!'),
 
218
            _("This Gramps 3.x-trunk is a development release. "
 
219
              "This version is not meant for normal usage. Use "
 
220
              "at your own risk.\n"
 
221
              "\n"
 
222
              "This version may:\n"
 
223
              "1) Work differently than you expect.\n"
 
224
              "2) Fail to run at all.\n"
 
225
              "3) Crash often.\n"
 
226
              "4) Corrupt your data.\n"
 
227
              "5) Save data in a format that is incompatible with the "
 
228
              "official release.\n"
 
229
              "\n"
 
230
              "<b>BACKUP</b> your existing databases before opening "
 
231
              "them with this version, and make sure to export your "
 
232
              "data to XML every now and then."))
 
233
        config.set('behavior.autoload', False)
 
234
#        config.set('behavior.betawarn', True)
 
235
        config.set('behavior.betawarn', config.get('behavior.betawarn'))
 
236
 
 
237
#-------------------------------------------------------------------------
 
238
#
 
239
# Main Gramps class
 
240
#
 
241
#-------------------------------------------------------------------------
 
242
class Gramps(object):
 
243
    """
 
244
    Main class corresponding to a running gramps process.
 
245
 
 
246
    There can be only one instance of this class per gramps application
 
247
    process. It may spawn several windows and control several databases.
 
248
    """
 
249
 
 
250
    def __init__(self, argparser):
 
251
        from gramps.gen.dbstate import DbState
 
252
        from . import viewmanager
 
253
        from .viewmanager import ViewManager
 
254
        from gramps.cli.arghandler import ArgHandler
 
255
        from .tipofday import TipOfDay
 
256
        from .dialog import WarningDialog
 
257
        import gettext
 
258
 
 
259
        register_stock_icons()
 
260
 
 
261
        if lin() and glocale.lang != 'C' and not gettext.find(GTK_GETTEXT_DOMAIN):
 
262
            LOG.warn("GTK translations missing, GUI will be broken, especially for RTL languages!")
 
263
            # Note: the warning dialog below will likely have wrong stock icons!
 
264
            # Translators: the current language will be the one you translate into.
 
265
            WarningDialog(
 
266
               _("Gramps detected an incomplete GTK installation"),
 
267
               _("""GTK translations for the current language (%s) are missing.
 
268
<b>Gramps</b> will proceed nevertheless.
 
269
The GUI will likely be broken as a result, especially for RTL languages!
 
270
 
 
271
See the Gramps README documentation for installation prerequisites,
 
272
typically located in /usr/share/doc/gramps.""") % glocale.lang)
 
273
 
 
274
        dbstate = DbState()
 
275
        self.vm = ViewManager(dbstate, config.get("interface.view-categories"))
 
276
        self.vm.init_interface()
 
277
 
 
278
        #act based on the given arguments
 
279
        ah = ArgHandler(dbstate, argparser, self.vm, self.argerrorfunc,
 
280
                        gui=True)
 
281
        ah.handle_args_gui()
 
282
        if ah.open or ah.imp_db_path:
 
283
            # if we opened or imported something, only show the interface
 
284
            self.vm.post_init_interface(show_manager=False)
 
285
        elif config.get('paths.recent-file') and config.get('behavior.autoload'):
 
286
            # if we need to autoload last seen file, do so
 
287
            filename = config.get('paths.recent-file')
 
288
            if os.path.isdir(filename) and \
 
289
                    os.path.isfile(os.path.join(filename, "name.txt")) and \
 
290
                    ah.check_db(filename):
 
291
                self.vm.post_init_interface(show_manager=False)
 
292
                self.vm.open_activate(filename)
 
293
            else:
 
294
                self.vm.post_init_interface()
 
295
        else:
 
296
            # open without fam tree loaded
 
297
            self.vm.post_init_interface()
 
298
 
 
299
        if config.get('behavior.use-tips'):
 
300
            TipOfDay(self.vm.uistate)
 
301
 
 
302
    def argerrorfunc(self, string):
 
303
        from .dialog import ErrorDialog
 
304
        """ Show basic errors in argument handling in GUI fashion"""
 
305
        ErrorDialog(_("Error parsing arguments"), string)
 
306
 
 
307
#-------------------------------------------------------------------------
 
308
#
 
309
# Main startup functions
 
310
#
 
311
#-------------------------------------------------------------------------
 
312
 
 
313
def __startgramps(errors, argparser):
 
314
    """
 
315
    Main startup function started via GObject.timeout_add
 
316
    First action inside the gtk loop
 
317
    """
 
318
    from .dialog import ErrorDialog
 
319
    #handle first existing errors in GUI fashion
 
320
    if errors:
 
321
        for error in errors:
 
322
            ErrorDialog(error[0], error[1])
 
323
        Gtk.main_quit()
 
324
        sys.exit(1)
 
325
 
 
326
    if argparser.errors:
 
327
        for error in argparser.errors:
 
328
            ErrorDialog(error[0], error[1])
 
329
        Gtk.main_quit()
 
330
        sys.exit(1)
 
331
 
 
332
    # add gui logger
 
333
    from .logger import RotateHandler, GtkHandler
 
334
    form = logging.Formatter(fmt="%(relativeCreated)d: %(levelname)s: "
 
335
                                "%(filename)s: line %(lineno)d: %(message)s")
 
336
    # Create the log handlers
 
337
    rh = RotateHandler(capacity=20)
 
338
    rh.setFormatter(form)
 
339
    # Only error and critical log records should
 
340
    # trigger the GUI handler.
 
341
    gtkh = GtkHandler(rotate_handler=rh)
 
342
    gtkh.setFormatter(form)
 
343
    gtkh.setLevel(logging.ERROR)
 
344
    l = logging.getLogger()
 
345
    l.addHandler(rh)
 
346
    l.addHandler(gtkh)
 
347
 
 
348
    # start GRAMPS, errors stop the gtk loop
 
349
    try:
 
350
        quit_now = False
 
351
        exit_code = 0
 
352
        if has_display():
 
353
            Gramps(argparser)
 
354
        else:
 
355
            print("Gramps terminated because of no DISPLAY")
 
356
            sys.exit(exit_code)
 
357
 
 
358
    except SystemExit as e:
 
359
        quit_now = True
 
360
        if e.code:
 
361
            exit_code = e.code
 
362
            LOG.error("Gramps terminated with exit code: %d." \
 
363
                      % e.code, exc_info=True)
 
364
    except OSError as e:
 
365
        quit_now = True
 
366
        exit_code = e[0] or 1
 
367
        try:
 
368
            fn = e.filename
 
369
        except AttributeError:
 
370
            fn = ""
 
371
        LOG.error("Gramps terminated because of OS Error\n" +
 
372
            "Error details: %s %s" % (repr(e), fn), exc_info=True)
 
373
 
 
374
    except:
 
375
        quit_now = True
 
376
        exit_code = 1
 
377
        LOG.error(_(
 
378
    "\nGramps failed to start. Please report a bug about this.\n"
 
379
    "This could be because of an error in a (third party) View on startup.\n"
 
380
    "To use another view, don't load a Family Tree, change view, and then load"
 
381
    " your Family Tree.\n"
 
382
    "You can also change manually the startup view in the gramps.ini file \n"
 
383
    "by changing the last-view parameter.\n"
 
384
                   ), exc_info=True)
 
385
 
 
386
    if quit_now:
 
387
        #stop gtk loop and quit
 
388
        Gtk.main_quit()
 
389
        sys.exit(exit_code)
 
390
 
 
391
    #function finished, return False to stop the timeout_add function calls
 
392
    return False
 
393
 
 
394
def startgtkloop(errors, argparser):
 
395
    """ We start the gtk loop and run the function to start up GRAMPS
 
396
    """
 
397
    GObject.threads_init()
 
398
 
 
399
    GObject.timeout_add(100, __startgramps, errors, argparser, priority=100)
 
400
    if os.path.exists(os.path.join(DATA_DIR, "gramps.accel")):
 
401
        Gtk.AccelMap.load(os.path.join(DATA_DIR, "gramps.accel"))
 
402
    Gtk.main()