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""" 23Citation List View 24""" 25 26#------------------------------------------------------------------------- 27# 28# python modules 29# 30#------------------------------------------------------------------------- 31import logging 32LOG = logging.getLogger(".citation") 33 34#------------------------------------------------------------------------- 35# 36# GTK/Gnome modules 37# 38#------------------------------------------------------------------------- 39from gi.repository import Gtk 40 41#------------------------------------------------------------------------- 42# 43# gramps modules 44# 45#------------------------------------------------------------------------- 46from gramps.gui.views.treemodels.citationlistmodel import CitationListModel 47from gramps.gen.plug import CATEGORY_QR_CITATION 48from gramps.gen.lib import Citation, Source 49from gramps.gui.views.listview import ListView, TEXT, MARKUP, ICON 50from gramps.gen.utils.db import get_citation_referents 51from gramps.gui.views.bookmarks import CitationBookmarks 52from gramps.gen.errors import WindowActiveError 53from gramps.gui.ddtargets import DdTargets 54from gramps.gui.dialog import ErrorDialog 55from gramps.gui.editors import EditCitation, DeleteCitationQuery 56from gramps.gui.filters.sidebar import CitationSidebarFilter 57from gramps.gui.merge import MergeCitation 58 59#------------------------------------------------------------------------- 60# 61# internationalization 62# 63#------------------------------------------------------------------------- 64from gramps.gen.const import GRAMPS_LOCALE as glocale 65_ = glocale.translation.sgettext 66 67 68#------------------------------------------------------------------------- 69# 70# CitationView 71# 72#------------------------------------------------------------------------- 73class CitationListView(ListView): 74 """ 75 A list view of citations. 76 77 The citation list view only shows the citations (it does not show 78 sources as separate list entries). 79 """ 80 # The data items here have to correspond, in order, to the items in 81 # src/giu/views/treemodels/citationlismodel.py 82 COL_TITLE_PAGE = 0 83 COL_ID = 1 84 COL_DATE = 2 85 COL_CONFIDENCE = 3 86 COL_PRIV = 4 87 COL_TAGS = 5 88 COL_CHAN = 6 89 COL_SRC_TITLE = 7 90 COL_SRC_ID = 8 91 COL_SRC_AUTH = 9 92 COL_SRC_ABBR = 10 93 COL_SRC_PINFO = 11 94 COL_SRC_PRIV = 12 95 COL_SRC_CHAN = 13 96 # column definitions 97 COLUMNS = [ 98 (_('Volume/Page'), TEXT, None), 99 (_('ID'), TEXT, None), 100 (_('Date'), MARKUP, None), 101 (_('Confidence'), TEXT, None), 102 (_('Private'), ICON, 'gramps-lock'), 103 (_('Tags'), TEXT, None), 104 (_('Last Changed'), TEXT, None), 105 (_('Source: Title'), TEXT, None), 106 (_('Source: ID'), TEXT, None), 107 (_('Source: Author'), TEXT, None), 108 (_('Source: Abbreviation'), TEXT, None), 109 (_('Source: Publication Information'), TEXT, None), 110 (_('Source: Private'), ICON, 'gramps-lock'), 111 (_('Source: Last Changed'), TEXT, None), 112 ] 113 # default setting with visible columns, order of the col, and their size 114 CONFIGSETTINGS = ( 115 ('columns.visible', [COL_TITLE_PAGE, COL_ID, COL_DATE, 116 COL_CONFIDENCE]), 117 ('columns.rank', [COL_TITLE_PAGE, COL_ID, COL_DATE, COL_CONFIDENCE, 118 COL_PRIV, COL_TAGS, COL_CHAN, COL_SRC_TITLE, 119 COL_SRC_ID, COL_SRC_AUTH, COL_SRC_ABBR, COL_SRC_PINFO, 120 COL_SRC_PRIV, COL_SRC_CHAN]), 121 ('columns.size', [200, 75, 100, 100, 40, 100, 100, 200, 75, 75, 100, 122 150, 40, 100]) 123 ) 124 ADD_MSG = _("Add a new citation and a new source") 125 ADD_SOURCE_MSG = _("Add a new source") 126 ADD_CITATION_MSG = _("Add a new citation to an existing source") 127 EDIT_MSG = _("Edit the selected citation") 128 DEL_MSG = _("Delete the selected citation") 129 MERGE_MSG = _("Merge the selected citations") 130 FILTER_TYPE = "Citation" 131 QR_CATEGORY = CATEGORY_QR_CITATION 132 133 def __init__(self, pdata, dbstate, uistate, nav_group=0): 134 135 signal_map = { 136 'citation-add' : self.row_add, 137 'citation-update' : self.row_update, 138 'citation-delete' : self.row_delete, 139 'citation-rebuild' : self.object_build, 140 } 141 142 ListView.__init__( 143 self, _('Citation View'), pdata, dbstate, uistate, 144 CitationListModel, signal_map, 145 CitationBookmarks, nav_group, 146 multiple=True, 147 filter_class=CitationSidebarFilter) 148 149 self.additional_uis.append(self.additional_ui) 150 151 def navigation_type(self): 152 return 'Citation' 153 154 def drag_info(self): 155 return DdTargets.CITATION_LINK 156 157 def get_stock(self): 158 return 'gramps-citation' 159 160 additional_ui = [ 161 ''' 162 <placeholder id="LocalExport"> 163 <item> 164 <attribute name="action">win.ExportTab</attribute> 165 <attribute name="label" translatable="yes">Export View...</attribute> 166 </item> 167 </placeholder> 168''', 169 ''' 170 <section id="AddEditBook"> 171 <item> 172 <attribute name="action">win.AddBook</attribute> 173 <attribute name="label" translatable="yes">_Add Bookmark</attribute> 174 </item> 175 <item> 176 <attribute name="action">win.EditBook</attribute> 177 <attribute name="label" translatable="no">%s...</attribute> 178 </item> 179 </section> 180''' % _('Organize Bookmarks'), 181 ''' 182 <placeholder id="CommonGo"> 183 <section> 184 <item> 185 <attribute name="action">win.Back</attribute> 186 <attribute name="label" translatable="yes">_Back</attribute> 187 </item> 188 <item> 189 <attribute name="action">win.Forward</attribute> 190 <attribute name="label" translatable="yes">_Forward</attribute> 191 </item> 192 </section> 193 </placeholder> 194''', 195 ''' 196 <section id='CommonEdit' groups='RW'> 197 <item> 198 <attribute name="action">win.Add</attribute> 199 <attribute name="label" translatable="yes">_Add...</attribute> 200 </item> 201 <item> 202 <attribute name="action">win.Edit</attribute> 203 <attribute name="label">%s</attribute> 204 </item> 205 <item> 206 <attribute name="action">win.Remove</attribute> 207 <attribute name="label" translatable="yes">_Delete</attribute> 208 </item> 209 <item> 210 <attribute name="action">win.Merge</attribute> 211 <attribute name="label" translatable="yes">_Merge...</attribute> 212 </item> 213 </section> 214''' % _("action|_Edit..."), # to use sgettext() 215 ''' 216 <placeholder id='otheredit'> 217 <item> 218 <attribute name="action">win.FilterEdit</attribute> 219 <attribute name="label" translatable="yes">''' 220 '''Citation Filter Editor</attribute> 221 </item> 222 </placeholder> 223''', # Following are the Toolbar items 224 ''' 225 <placeholder id='CommonNavigation'> 226 <child groups='RO'> 227 <object class="GtkToolButton"> 228 <property name="icon-name">go-previous</property> 229 <property name="action-name">win.Back</property> 230 <property name="tooltip_text" translatable="yes">''' 231 '''Go to the previous object in the history</property> 232 <property name="label" translatable="yes">_Back</property> 233 <property name="use-underline">True</property> 234 </object> 235 <packing> 236 <property name="homogeneous">False</property> 237 </packing> 238 </child> 239 <child groups='RO'> 240 <object class="GtkToolButton"> 241 <property name="icon-name">go-next</property> 242 <property name="action-name">win.Forward</property> 243 <property name="tooltip_text" translatable="yes">''' 244 '''Go to the next object in the history</property> 245 <property name="label" translatable="yes">_Forward</property> 246 <property name="use-underline">True</property> 247 </object> 248 <packing> 249 <property name="homogeneous">False</property> 250 </packing> 251 </child> 252 </placeholder> 253''', 254 ''' 255 <placeholder id='BarCommonEdit'> 256 <child groups='RW'> 257 <object class="GtkToolButton"> 258 <property name="icon-name">list-add</property> 259 <property name="action-name">win.Add</property> 260 <property name="tooltip_text">%s</property> 261 <property name="label" translatable="yes">_Add...</property> 262 <property name="use-underline">True</property> 263 </object> 264 <packing> 265 <property name="homogeneous">False</property> 266 </packing> 267 </child> 268 <child groups='RW'> 269 <object class="GtkToolButton"> 270 <property name="icon-name">gtk-edit</property> 271 <property name="action-name">win.Edit</property> 272 <property name="tooltip_text">%s</property> 273 <property name="label" translatable="yes">Edit...</property> 274 <property name="use-underline">True</property> 275 </object> 276 <packing> 277 <property name="homogeneous">False</property> 278 </packing> 279 </child> 280 <child groups='RW'> 281 <object class="GtkToolButton"> 282 <property name="icon-name">list-remove</property> 283 <property name="action-name">win.Remove</property> 284 <property name="tooltip_text">%s</property> 285 <property name="label" translatable="yes">_Delete</property> 286 <property name="use-underline">True</property> 287 </object> 288 <packing> 289 <property name="homogeneous">False</property> 290 </packing> 291 </child> 292 <child groups='RW'> 293 <object class="GtkToolButton"> 294 <property name="icon-name">gramps-merge</property> 295 <property name="action-name">win.Merge</property> 296 <property name="tooltip_text" >%s</property> 297 <property name="label" translatable="yes">_Merge...</property> 298 <property name="use-underline">True</property> 299 </object> 300 <packing> 301 <property name="homogeneous">False</property> 302 </packing> 303 </child> 304 </placeholder> 305''' % (ADD_MSG, EDIT_MSG, DEL_MSG, MERGE_MSG), 306 ''' 307 <menu id="Popup"> 308 <section> 309 <item> 310 <attribute name="action">win.Back</attribute> 311 <attribute name="label" translatable="yes">_Back</attribute> 312 </item> 313 <item> 314 <attribute name="action">win.Forward</attribute> 315 <attribute name="label" translatable="yes">Forward</attribute> 316 </item> 317 </section> 318 <section id="PopUpTree"> 319 </section> 320 <section> 321 <item> 322 <attribute name="action">win.Add</attribute> 323 <attribute name="label" translatable="yes">_Add...</attribute> 324 </item> 325 <item> 326 <attribute name="action">win.Edit</attribute> 327 <attribute name="label">%s</attribute> 328 </item> 329 <item> 330 <attribute name="action">win.Remove</attribute> 331 <attribute name="label" translatable="yes">_Delete</attribute> 332 </item> 333 <item> 334 <attribute name="action">win.Merge</attribute> 335 <attribute name="label" translatable="yes">_Merge...</attribute> 336 </item> 337 </section> 338 <section> 339 <placeholder id='QuickReport'> 340 </placeholder> 341 </section> 342 </menu> 343''' % _('action|_Edit...') # to use sgettext() 344] 345 346 def add(self, *obj): 347 """ 348 add: Add a new citation and a new source (this can also be done 349 by source view add a source, then citation view add a new 350 citation to an existing source) 351 352 Create a new Source instance and Citation instance and call the 353 EditCitation editor with the new source and new citation. 354 355 Called when the Add button is clicked. 356 If the window already exists (WindowActiveError), we ignore it. 357 This prevents the dialog from coming up twice on the same object. 358 359 However, since the window is identified by the Source object, and 360 we have just created a new one, it seems to be impossible for the 361 window to already exist, so this is just an extra safety measure. 362 """ 363 try: 364 EditCitation(self.dbstate, self.uistate, [], Citation(), 365 Source()) 366 except WindowActiveError: 367 pass 368 369 def remove(self, *obj): 370 self.remove_selected_objects() 371 372 def remove_object_from_handle(self, handle): 373 the_lists = get_citation_referents(handle, self.dbstate.db) 374 object = self.dbstate.db.get_citation_from_handle(handle) 375 query = DeleteCitationQuery(self.dbstate, self.uistate, object, 376 the_lists) 377 is_used = any(the_lists) 378 return (query, is_used, object) 379 380 def edit(self, *obj): 381 """ 382 Edit a Citation 383 """ 384 for handle in self.selected_handles(): 385 citation = self.dbstate.db.get_citation_from_handle(handle) 386 try: 387 EditCitation(self.dbstate, self.uistate, [], citation) 388 except WindowActiveError: 389 pass 390 391 def __blocked_text(self): 392 """ 393 Return the common text used when citation cannot be edited 394 """ 395 return _("This citation cannot be edited at this time. " 396 "Either the associated citation is already being " 397 "edited or another object that is associated with " 398 "the same citation is being edited.\n\nTo edit this " 399 "citation, you need to close the object.") 400 401 def merge(self, *obj): 402 """ 403 Merge the selected citations. 404 """ 405 mlist = self.selected_handles() 406 407 if len(mlist) != 2: 408 msg = _("Cannot merge citations.") 409 msg2 = _("Exactly two citations must be selected to perform a " 410 "merge. A second citation can be selected by holding " 411 "down the control key while clicking on the desired " 412 "citation.") 413 ErrorDialog(msg, msg2, parent=self.uistate.window) 414 else: 415 citation1 = self.dbstate.db.get_citation_from_handle(mlist[0]) 416 citation2 = self.dbstate.db.get_citation_from_handle(mlist[1]) 417 if not citation1.get_reference_handle() == \ 418 citation2.get_reference_handle(): 419 msg = _("Cannot merge citations.") 420 msg2 = _("The two selected citations must have the same " 421 "source to perform a merge. If you want to merge " 422 "these two citations, then you must merge the " 423 "sources first.") 424 ErrorDialog(msg, msg2, parent=self.uistate.window) 425 else: 426 MergeCitation(self.dbstate, self.uistate, [], mlist[0], 427 mlist[1]) 428 429 def get_handle_from_gramps_id(self, gid): 430 obj = self.dbstate.db.get_citation_from_gramps_id(gid) 431 if obj: 432 return obj.get_handle() 433 else: 434 return None 435 436 def tag_updated(self, handle_list): 437 """ 438 Update tagged rows when a tag color changes. 439 """ 440 all_links = set([]) 441 for tag_handle in handle_list: 442 links = set([link[1] for link in 443 self.dbstate.db.find_backlink_handles(tag_handle, 444 include_classes='Citation')]) 445 all_links = all_links.union(links) 446 self.row_update(list(all_links)) 447 448 def add_tag(self, transaction, citation_handle, tag_handle): 449 """ 450 Add the given tag to the given citation. 451 """ 452 citation = self.dbstate.db.get_citation_from_handle(citation_handle) 453 citation.add_tag(tag_handle) 454 self.dbstate.db.commit_citation(citation, transaction) 455 456 def get_default_gramplets(self): 457 """ 458 Define the default gramplets for the sidebar and bottombar. 459 This is overridden for the tree view to give 'Source Filter' 460 """ 461 return (("Citation Filter",), 462 ("Citation Gallery", 463 "Citation Notes", 464 "Citation Backlinks")) 465