1# Gramps - a GTK+/GNOME based genealogy program
2#
3# Copyright (C) 2001-2006  Donald N. Allingham
4# Copyright (C) 2008       Gary Burton
5# Copyright (C) 2011       Tim G L Lyons
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20#
21
22"""
23Source View
24"""
25
26#-------------------------------------------------------------------------
27#
28# GTK/Gnome modules
29#
30#-------------------------------------------------------------------------
31from gi.repository import Gtk
32import logging
33LOG = logging.getLogger(".citation")
34
35#-------------------------------------------------------------------------
36#
37# gramps modules
38#
39#-------------------------------------------------------------------------
40from gramps.gen.lib import Source
41from gramps.gen.config import config
42from gramps.gui.views.listview import ListView, TEXT, MARKUP, ICON
43from gramps.gui.views.treemodels import SourceModel
44from gramps.gen.utils.db import get_source_and_citation_referents
45from gramps.gui.views.bookmarks import SourceBookmarks
46from gramps.gen.errors import WindowActiveError
47from gramps.gui.ddtargets import DdTargets
48from gramps.gui.dialog import ErrorDialog
49from gramps.gui.editors import EditSource, DeleteSrcQuery
50from gramps.gui.filters.sidebar import SourceSidebarFilter
51from gramps.gui.merge import MergeSource
52from gramps.gen.plug import CATEGORY_QR_SOURCE
53
54#-------------------------------------------------------------------------
55#
56# internationalization
57#
58#-------------------------------------------------------------------------
59from gramps.gen.const import GRAMPS_LOCALE as glocale
60_ = glocale.translation.sgettext
61
62
63#-------------------------------------------------------------------------
64#
65# SourceView
66#
67#-------------------------------------------------------------------------
68class SourceView(ListView):
69    """ sources listview class
70    """
71    COL_TITLE = 0
72    COL_ID = 1
73    COL_AUTH = 2
74    COL_ABBR = 3
75    COL_PINFO = 4
76    COL_PRIV = 5
77    COL_TAGS = 6
78    COL_CHAN = 7
79
80    # column definitions
81    COLUMNS = [
82        (_('Title'), TEXT, None),
83        (_('ID'), TEXT, None),
84        (_('Author'), TEXT, None),
85        (_('Abbreviation'), TEXT, None),
86        (_('Publication Information'), TEXT, None),
87        (_('Private'), ICON, 'gramps-lock'),
88        (_('Tags'), TEXT, None),
89        (_('Last Changed'), TEXT, None),
90        ]
91    # default setting with visible columns, order of the col, and their size
92    CONFIGSETTINGS = (
93        ('columns.visible', [COL_TITLE, COL_ID, COL_AUTH, COL_PINFO]),
94        ('columns.rank', [COL_TITLE, COL_ID, COL_AUTH, COL_ABBR, COL_PINFO,
95                          COL_PRIV, COL_TAGS, COL_CHAN]),
96        ('columns.size', [200, 75, 150, 100, 150, 40, 100, 100])
97        )
98    ADD_MSG = _("Add a new source")
99    EDIT_MSG = _("Edit the selected source")
100    DEL_MSG = _("Delete the selected source")
101    MERGE_MSG = _("Merge the selected sources")
102    FILTER_TYPE = "Source"
103    QR_CATEGORY = CATEGORY_QR_SOURCE
104
105    def __init__(self, pdata, dbstate, uistate, nav_group=0):
106
107        signal_map = {
108            'source-add'     : self.row_add,
109            'source-update'  : self.row_update,
110            'source-delete'  : self.row_delete,
111            'source-rebuild' : self.object_build,
112            }
113
114        ListView.__init__(
115            self, _('Sources'), pdata, dbstate, uistate,
116            SourceModel, signal_map,
117            SourceBookmarks, nav_group,
118            multiple=True,
119            filter_class=SourceSidebarFilter)
120
121        self.additional_uis.append(self.additional_ui)
122
123    def navigation_type(self):
124        return 'Source'
125
126    def drag_info(self):
127        return DdTargets.SOURCE_LINK
128
129    def get_stock(self):
130        return 'gramps-source'
131
132    additional_ui = [  # Defines the UI string for UIManager
133        '''
134      <placeholder id="LocalExport">
135        <item>
136          <attribute name="action">win.ExportTab</attribute>
137          <attribute name="label" translatable="yes">Export View...</attribute>
138        </item>
139      </placeholder>
140''',
141        '''
142      <section id="AddEditBook">
143        <item>
144          <attribute name="action">win.AddBook</attribute>
145          <attribute name="label" translatable="yes">_Add Bookmark</attribute>
146        </item>
147        <item>
148          <attribute name="action">win.EditBook</attribute>
149          <attribute name="label" translatable="no">%s...</attribute>
150        </item>
151      </section>
152''' % _('Organize Bookmarks'),
153        '''
154      <placeholder id="CommonGo">
155      <section>
156        <item>
157          <attribute name="action">win.Back</attribute>
158          <attribute name="label" translatable="yes">_Back</attribute>
159        </item>
160        <item>
161          <attribute name="action">win.Forward</attribute>
162          <attribute name="label" translatable="yes">_Forward</attribute>
163        </item>
164      </section>
165      </placeholder>
166''',
167        '''
168      <section id='CommonEdit' groups='RW'>
169        <item>
170          <attribute name="action">win.Add</attribute>
171          <attribute name="label" translatable="yes">_Add...</attribute>
172        </item>
173        <item>
174          <attribute name="action">win.Edit</attribute>
175          <attribute name="label">%s</attribute>
176        </item>
177        <item>
178          <attribute name="action">win.Remove</attribute>
179          <attribute name="label" translatable="yes">_Delete</attribute>
180        </item>
181        <item>
182          <attribute name="action">win.Merge</attribute>
183          <attribute name="label" translatable="yes">_Merge...</attribute>
184        </item>
185      </section>
186''' % _("action|_Edit..."),  # to use sgettext()
187        '''
188        <placeholder id='otheredit'>
189        <item>
190          <attribute name="action">win.FilterEdit</attribute>
191          <attribute name="label" translatable="yes">'''
192        '''Source Filter Editor</attribute>
193        </item>
194        </placeholder>
195''',  # Following are the Toolbar items
196        '''
197    <placeholder id='CommonNavigation'>
198    <child groups='RO'>
199      <object class="GtkToolButton">
200        <property name="icon-name">go-previous</property>
201        <property name="action-name">win.Back</property>
202        <property name="tooltip_text" translatable="yes">'''
203        '''Go to the previous object in the history</property>
204        <property name="label" translatable="yes">_Back</property>
205        <property name="use-underline">True</property>
206      </object>
207      <packing>
208        <property name="homogeneous">False</property>
209      </packing>
210    </child>
211    <child groups='RO'>
212      <object class="GtkToolButton">
213        <property name="icon-name">go-next</property>
214        <property name="action-name">win.Forward</property>
215        <property name="tooltip_text" translatable="yes">'''
216        '''Go to the next object in the history</property>
217        <property name="label" translatable="yes">_Forward</property>
218        <property name="use-underline">True</property>
219      </object>
220      <packing>
221        <property name="homogeneous">False</property>
222      </packing>
223    </child>
224    </placeholder>
225''',
226        '''
227    <placeholder id='BarCommonEdit'>
228    <child groups='RW'>
229      <object class="GtkToolButton">
230        <property name="icon-name">list-add</property>
231        <property name="action-name">win.Add</property>
232        <property name="tooltip_text">%s</property>
233        <property name="label" translatable="yes">_Add...</property>
234        <property name="use-underline">True</property>
235      </object>
236      <packing>
237        <property name="homogeneous">False</property>
238      </packing>
239    </child>
240    <child groups='RW'>
241      <object class="GtkToolButton">
242        <property name="icon-name">gtk-edit</property>
243        <property name="action-name">win.Edit</property>
244        <property name="tooltip_text">%s</property>
245        <property name="label" translatable="yes">Edit...</property>
246        <property name="use-underline">True</property>
247      </object>
248      <packing>
249        <property name="homogeneous">False</property>
250      </packing>
251    </child>
252    <child groups='RW'>
253      <object class="GtkToolButton">
254        <property name="icon-name">list-remove</property>
255        <property name="action-name">win.Remove</property>
256        <property name="tooltip_text">%s</property>
257        <property name="label" translatable="yes">_Delete</property>
258        <property name="use-underline">True</property>
259      </object>
260      <packing>
261        <property name="homogeneous">False</property>
262      </packing>
263    </child>
264    <child groups='RW'>
265      <object class="GtkToolButton">
266        <property name="icon-name">gramps-merge</property>
267        <property name="action-name">win.Merge</property>
268        <property name="tooltip_text">%s</property>
269        <property name="label" translatable="yes">_Merge...</property>
270        <property name="use-underline">True</property>
271      </object>
272      <packing>
273        <property name="homogeneous">False</property>
274      </packing>
275    </child>
276    </placeholder>
277''' % (ADD_MSG, EDIT_MSG, DEL_MSG, MERGE_MSG),
278        '''
279    <menu id="Popup">
280      <section>
281        <item>
282          <attribute name="action">win.Back</attribute>
283          <attribute name="label" translatable="yes">_Back</attribute>
284        </item>
285        <item>
286          <attribute name="action">win.Forward</attribute>
287          <attribute name="label" translatable="yes">Forward</attribute>
288        </item>
289      </section>
290      <section id="PopUpTree">
291      </section>
292      <section>
293        <item>
294          <attribute name="action">win.Add</attribute>
295          <attribute name="label" translatable="yes">_Add...</attribute>
296        </item>
297        <item>
298          <attribute name="action">win.Edit</attribute>
299          <attribute name="label">%s</attribute>
300        </item>
301        <item>
302          <attribute name="action">win.Remove</attribute>
303          <attribute name="label" translatable="yes">_Delete</attribute>
304        </item>
305        <item>
306          <attribute name="action">win.Merge</attribute>
307          <attribute name="label" translatable="yes">_Merge...</attribute>
308        </item>
309      </section>
310      <section>
311        <placeholder id='QuickReport'>
312        </placeholder>
313      </section>
314    </menu>
315    ''' % _('action|_Edit...')  # to use sgettext()
316    ]
317
318    def add(self, *obj):
319        EditSource(self.dbstate, self.uistate, [], Source())
320
321    def remove(self, *obj):
322        self.remove_selected_objects()
323
324    def remove_object_from_handle(self, handle):
325        the_lists = get_source_and_citation_referents(handle, self.dbstate.db)
326        LOG.debug('the_lists %s' % [the_lists])
327
328        object = self.dbstate.db.get_source_from_handle(handle)
329        query = DeleteSrcQuery(self.dbstate, self.uistate, object, the_lists)
330        is_used = any(the_lists)
331        return (query, is_used, object)
332
333    def edit(self, *obj):
334        for handle in self.selected_handles():
335            source = self.dbstate.db.get_source_from_handle(handle)
336            try:
337                EditSource(self.dbstate, self.uistate, [], source)
338            except WindowActiveError:
339                pass
340
341    def merge(self, *obj):
342        """
343        Merge the selected sources.
344        """
345        mlist = self.selected_handles()
346
347        if len(mlist) != 2:
348            msg = _("Cannot merge sources.")
349            msg2 = _("Exactly two sources must be selected to perform a merge. "
350                     "A second source can be selected by holding down the "
351                     "control key while clicking on the desired source.")
352            ErrorDialog(msg, msg2, parent=self.uistate.window)
353        else:
354            MergeSource(self.dbstate, self.uistate, [], mlist[0], mlist[1])
355
356    def get_handle_from_gramps_id(self, gid):
357        obj = self.dbstate.db.get_source_from_gramps_id(gid)
358        if obj:
359            return obj.get_handle()
360        else:
361            return None
362
363    def tag_updated(self, handle_list):
364        """
365        Update tagged rows when a tag color changes.
366        """
367        all_links = set([])
368        for tag_handle in handle_list:
369            links = set([link[1] for link in
370                         self.dbstate.db.find_backlink_handles(tag_handle,
371                                                include_classes='Source')])
372            all_links = all_links.union(links)
373        self.row_update(list(all_links))
374
375    def add_tag(self, transaction, source_handle, tag_handle):
376        """
377        Add the given tag to the given source.
378        """
379        source = self.dbstate.db.get_source_from_handle(source_handle)
380        source.add_tag(tag_handle)
381        self.dbstate.db.commit_source(source, transaction)
382
383    def get_default_gramplets(self):
384        """
385        Define the default gramplets for the sidebar and bottombar.
386        """
387        return (("Source Filter",),
388                ("Source Gallery",
389                 "Source Notes",
390                 "Source Backlinks"))
391