GEPS 019: Improved sidebar which allows sidebar plugins
svn: r15029
This commit is contained in:
parent
5726d688ca
commit
6ae2f1c81e
@ -140,6 +140,7 @@ src/plugins/lib/Makefile
|
||||
src/plugins/mapservices/Makefile
|
||||
src/plugins/quickview/Makefile
|
||||
src/plugins/rel/Makefile
|
||||
src/plugins/sidebar/Makefile
|
||||
src/plugins/textreport/Makefile
|
||||
src/plugins/tool/Makefile
|
||||
src/plugins/view/Makefile
|
||||
|
@ -288,6 +288,11 @@ class BasePluginManager(object):
|
||||
"""
|
||||
return self.__pgr.gramplet_plugins()
|
||||
|
||||
def get_reg_sidebars(self):
|
||||
""" Return list of registered sidebars.
|
||||
"""
|
||||
return self.__pgr.sidebar_plugins()
|
||||
|
||||
def get_external_opt_dict(self):
|
||||
""" Return the dictionary of external options. """
|
||||
return self.__external_opt_dict
|
||||
|
@ -68,8 +68,9 @@ MAPSERVICE = 7
|
||||
VIEW = 8
|
||||
RELCALC = 9
|
||||
GRAMPLET = 10
|
||||
PTYPE = [ REPORT , QUICKREPORT, TOOL, IMPORT,
|
||||
EXPORT, DOCGEN, GENERAL, MAPSERVICE, VIEW, RELCALC, GRAMPLET]
|
||||
SIDEBAR = 11
|
||||
PTYPE = [REPORT , QUICKREPORT, TOOL, IMPORT, EXPORT, DOCGEN, GENERAL,
|
||||
MAPSERVICE, VIEW, RELCALC, GRAMPLET, SIDEBAR]
|
||||
PTYPE_STR = {
|
||||
REPORT: _('Report') ,
|
||||
QUICKREPORT: _('Quickreport'),
|
||||
@ -82,6 +83,7 @@ PTYPE_STR = {
|
||||
VIEW: _('Gramps View'),
|
||||
RELCALC: _('Relationships'),
|
||||
GRAMPLET: _('Gramplet'),
|
||||
SIDEBAR: _('Sidebar'),
|
||||
}
|
||||
|
||||
#possible report categories
|
||||
@ -301,6 +303,14 @@ class PluginData(object):
|
||||
the view is appended to the list of views. If START, then the view is
|
||||
prepended. Only set START if you want a view to be the first in the
|
||||
order of views
|
||||
.. attribute:: stock_icon
|
||||
The icon in the toolbar or sidebar used to select the view
|
||||
|
||||
Attributes for SIDEBAR plugins
|
||||
.. attribute:: sidebarclass
|
||||
The class that defines the sidebar.
|
||||
.. attribute:: menu_label
|
||||
A label to use on the seltion menu.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
@ -364,6 +374,10 @@ class PluginData(object):
|
||||
#VIEW attr
|
||||
self._viewclass = None
|
||||
self._order = END
|
||||
self._stock_icon = None
|
||||
#SIDEBAR attr
|
||||
self._sidebarclass = None
|
||||
self._menu_label = ''
|
||||
|
||||
def _set_id(self, id):
|
||||
self._id = id
|
||||
@ -811,10 +825,38 @@ class PluginData(object):
|
||||
def _get_order(self):
|
||||
return self._order
|
||||
|
||||
viewclass = property(_get_viewclass, _set_viewclass)
|
||||
order = property(_get_order, _set_order)
|
||||
|
||||
def _set_stock_icon(self, stock_icon):
|
||||
if not self._ptype == VIEW:
|
||||
raise ValueError, 'stock_icon may only be set for VIEW plugins'
|
||||
self._stock_icon = stock_icon
|
||||
|
||||
def _get_stock_icon(self):
|
||||
return self._stock_icon
|
||||
|
||||
viewclass = property(_get_viewclass, _set_viewclass)
|
||||
order = property(_get_order, _set_order)
|
||||
stock_icon = property(_get_stock_icon, _set_stock_icon)
|
||||
|
||||
#SIDEBAR attributes
|
||||
def _set_sidebarclass(self, sidebarclass):
|
||||
if not self._ptype == SIDEBAR:
|
||||
raise ValueError, 'sidebarclass may only be set for SIDEBAR plugins'
|
||||
self._sidebarclass = sidebarclass
|
||||
|
||||
def _get_sidebarclass(self):
|
||||
return self._sidebarclass
|
||||
|
||||
def _set_menu_label(self, menu_label):
|
||||
if not self._ptype == SIDEBAR:
|
||||
raise ValueError, 'menu_label may only be set for SIDEBAR plugins'
|
||||
self._menu_label = menu_label
|
||||
|
||||
def _get_menu_label(self):
|
||||
return self._menu_label
|
||||
|
||||
sidebarclass = property(_get_sidebarclass, _set_sidebarclass)
|
||||
menu_label = property(_get_menu_label, _set_menu_label)
|
||||
|
||||
def newplugin():
|
||||
"""
|
||||
Function to create a new plugindata object, add it to list of
|
||||
@ -865,6 +907,7 @@ def make_environment(**kwargs):
|
||||
'VIEW': VIEW,
|
||||
'RELCALC': RELCALC,
|
||||
'GRAMPLET': GRAMPLET,
|
||||
'SIDEBAR': SIDEBAR,
|
||||
'CATEGORY_TEXT': CATEGORY_TEXT,
|
||||
'CATEGORY_DRAW': CATEGORY_DRAW,
|
||||
'CATEGORY_CODE': CATEGORY_CODE,
|
||||
@ -1106,6 +1149,11 @@ class PluginRegister(object):
|
||||
"""Return a list of PluginData that are of type GRAMPLET
|
||||
"""
|
||||
return self.type_plugins(GRAMPLET)
|
||||
|
||||
def sidebar_plugins(self):
|
||||
"""Return a list of PluginData that are of type SIDEBAR
|
||||
"""
|
||||
return self.type_plugins(SIDEBAR)
|
||||
|
||||
def filter_load_on_reg(self):
|
||||
"""Return a list of PluginData that have load_on_reg == True
|
||||
|
@ -21,6 +21,7 @@ pkgdata_PYTHON = \
|
||||
filtereditor.py \
|
||||
grampsgui.py \
|
||||
pluginmanager.py \
|
||||
sidebar.py \
|
||||
utils.py \
|
||||
viewmanager.py
|
||||
|
||||
|
@ -62,8 +62,7 @@ from QuestionDialog import ErrorDialog
|
||||
import config
|
||||
import Utils
|
||||
from constfunc import win
|
||||
from gui.pluginmanager import GuiPluginManager, base_reg_stock_icons
|
||||
from gen.plug import (START, END)
|
||||
from gui.pluginmanager import base_reg_stock_icons
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@ -71,7 +70,6 @@ from gen.plug import (START, END)
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
|
||||
def register_stock_icons ():
|
||||
"""
|
||||
Add the gramps names for its icons (eg gramps-person) to the GTK icon
|
||||
@ -185,53 +183,6 @@ def _display_welcome_message():
|
||||
config.set('behavior.autoload', False)
|
||||
# config.set('behavior.betawarn', True)
|
||||
config.set('behavior.betawarn', config.get('behavior.betawarn'))
|
||||
|
||||
def construct_view_order():
|
||||
"""
|
||||
Query the views and determine what views to show and in which order
|
||||
|
||||
:Returns: a list of lists containing tuples (view_id, viewclass)
|
||||
"""
|
||||
pmgr = GuiPluginManager.get_instance()
|
||||
view_list = pmgr.get_reg_views()
|
||||
viewstoshow = {}
|
||||
for pdata in view_list:
|
||||
mod = pmgr.load_plugin(pdata)
|
||||
if not mod or not hasattr(mod, pdata.viewclass):
|
||||
#import of plugin failed
|
||||
ErrorDialog(
|
||||
_('Failed Loading View'),
|
||||
_('The view %(name)s did not load. See Help Menu, Plugin Manager'
|
||||
' for more info.\nUse http://bugs.gramps-project.org to'
|
||||
' submit bugs of official views, contact the view '
|
||||
'author (%(firstauthoremail)s) otherwise. ') % {
|
||||
'name': pdata.name,
|
||||
'firstauthoremail': pdata.authors_email[0] if
|
||||
pdata.authors_email else '...'})
|
||||
continue
|
||||
viewclass = getattr(mod, pdata.viewclass)
|
||||
# pdata.category is (string, trans-string):
|
||||
if pdata.category[0] in viewstoshow:
|
||||
if pdata.order == START:
|
||||
viewstoshow[pdata.category[0]].insert(0, ((pdata, viewclass)))
|
||||
else:
|
||||
viewstoshow[pdata.category[0]].append((pdata, viewclass))
|
||||
else:
|
||||
viewstoshow[pdata.category[0]] = [(pdata, viewclass)]
|
||||
|
||||
resultorder = []
|
||||
# First, get those in order defined, if exists:
|
||||
for item in config.get("interface.view-categories"):
|
||||
if item in viewstoshow:
|
||||
resultorder.append(viewstoshow[item])
|
||||
# Next, get the rest in some order:
|
||||
viewstoshow_names = viewstoshow.keys()
|
||||
viewstoshow_names.sort()
|
||||
for item in viewstoshow_names:
|
||||
if viewstoshow[item] in resultorder:
|
||||
continue
|
||||
resultorder.append(viewstoshow[item])
|
||||
return resultorder
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@ -256,11 +207,7 @@ class Gramps(object):
|
||||
|
||||
dbstate = DbState.DbState()
|
||||
self.vm = ViewManager(dbstate, config.get("interface.view-categories"))
|
||||
|
||||
#now we determine which views are present, which to show, and we
|
||||
#instruct the viewmanager to show them
|
||||
vieworder = construct_view_order()
|
||||
self.vm.init_interface(vieworder)
|
||||
self.vm.init_interface()
|
||||
|
||||
#act based on the given arguments
|
||||
ah = ArgHandler(dbstate, argparser, self.vm, self.argerrorfunc,
|
||||
@ -289,7 +236,6 @@ class Gramps(object):
|
||||
def argerrorfunc(self, string):
|
||||
""" Show basic errors in argument handling in GUI fashion"""
|
||||
ErrorDialog(_("Error parsing arguments"), string)
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
|
@ -289,6 +289,12 @@ class GuiPluginManager(gen.utils.Callback):
|
||||
return [plg for plg in self.basemgr.get_reg_gramplets()
|
||||
if plg.id not in self.__hidden_plugins]
|
||||
|
||||
def get_reg_sidebars(self):
|
||||
""" Return list of non hidden registered sidebars
|
||||
"""
|
||||
return [plg for plg in self.basemgr.get_reg_sidebars()
|
||||
if plg.id not in self.__hidden_plugins]
|
||||
|
||||
def get_reg_importers(self):
|
||||
""" Return list of registered importers
|
||||
"""
|
||||
|
173
src/gui/sidebar.py
Normal file
173
src/gui/sidebar.py
Normal file
@ -0,0 +1,173 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2010 Nick Hall
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# $Id$
|
||||
|
||||
"""
|
||||
A module that provides pluggable sidebars. These provide an interface to
|
||||
manage pages in the main Gramps window.
|
||||
"""
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# GNOME modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
import gtk
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Sidebar class
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class Sidebar(object):
|
||||
"""
|
||||
A class which defines the graphical representation of the Gramps sidebar.
|
||||
"""
|
||||
def __init__(self, viewmanager):
|
||||
|
||||
self.viewmanager = viewmanager
|
||||
self.pages = []
|
||||
self.top = gtk.VBox()
|
||||
|
||||
frame = gtk.Frame()
|
||||
hbox = gtk.HBox()
|
||||
frame.add(hbox)
|
||||
|
||||
select_button = gtk.ToggleButton()
|
||||
select_button.set_relief(gtk.RELIEF_NONE)
|
||||
select_hbox = gtk.HBox()
|
||||
self.title_label = gtk.Label('Category')
|
||||
arrow = gtk.Arrow(gtk.ARROW_DOWN, gtk.SHADOW_NONE)
|
||||
select_hbox.pack_start(self.title_label, False)
|
||||
select_hbox.pack_end(arrow, False)
|
||||
select_button.add(select_hbox)
|
||||
|
||||
select_button.connect('button_press_event', self.__menu_button_pressed)
|
||||
|
||||
close_button = gtk.Button()
|
||||
img = gtk.image_new_from_stock(gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU)
|
||||
close_button.set_image(img)
|
||||
close_button.set_relief(gtk.RELIEF_NONE)
|
||||
close_button.connect('clicked', self.cb_close_clicked)
|
||||
hbox.pack_start(select_button, False)
|
||||
hbox.pack_end(close_button, False)
|
||||
|
||||
self.top.pack_start(frame, False)
|
||||
|
||||
self.menu = gtk.Menu()
|
||||
self.menu.show()
|
||||
self.menu.connect('deactivate', cb_menu_deactivate, select_button)
|
||||
|
||||
self.notebook = gtk.Notebook()
|
||||
self.notebook.show()
|
||||
self.notebook.set_show_tabs(False)
|
||||
self.notebook.set_show_border(False)
|
||||
self.notebook.connect('switch_page', self.cb_switch_page)
|
||||
self.top.pack_start(self.notebook, True)
|
||||
|
||||
def get_top(self):
|
||||
"""
|
||||
Return the top container widget for the GUI.
|
||||
"""
|
||||
return self.top
|
||||
|
||||
def add(self, title, sidebar):
|
||||
"""
|
||||
Add a page to the sidebar for a plugin.
|
||||
"""
|
||||
index = self.notebook.append_page(sidebar.get_top(), gtk.Label(title))
|
||||
self.pages.append((title, sidebar))
|
||||
|
||||
menu_item = gtk.MenuItem(title)
|
||||
menu_item.connect('activate', self.cb_menu_activate, index)
|
||||
menu_item.show()
|
||||
self.menu.append(menu_item)
|
||||
|
||||
def view_changed(self, page_num):
|
||||
"""
|
||||
Called when a Gramps view is changed.
|
||||
"""
|
||||
for page in self.pages:
|
||||
page[1].view_changed(page_num)
|
||||
|
||||
def handlers_block(self):
|
||||
"""
|
||||
Block signals to the buttons to prevent spurious events.
|
||||
"""
|
||||
for page in self.pages:
|
||||
page[1].handlers_block()
|
||||
|
||||
def handlers_unblock(self):
|
||||
"""
|
||||
Unblock signals to the buttons.
|
||||
"""
|
||||
for page in self.pages:
|
||||
page[1].handlers_unblock()
|
||||
|
||||
def __menu_button_pressed(self, button, event):
|
||||
"""
|
||||
Called when the button to select a sidebar page is pressed.
|
||||
"""
|
||||
if event.button == 1 and event.type == gtk.gdk.BUTTON_PRESS:
|
||||
button.grab_focus()
|
||||
button.set_active(True)
|
||||
|
||||
self.menu.popup(None, None, cb_menu_position, event.button,
|
||||
event.time, button)
|
||||
|
||||
def cb_menu_activate(self, menu, index):
|
||||
"""
|
||||
Called when an item in the popup menu is selected.
|
||||
"""
|
||||
self.notebook.set_current_page(index)
|
||||
|
||||
def cb_switch_page(self, notebook, unused, index):
|
||||
"""
|
||||
Called when the user has switched to a new sidebar plugin page.
|
||||
"""
|
||||
if self.pages:
|
||||
self.title_label.set_text(self.pages[index][0])
|
||||
|
||||
def cb_close_clicked(self, button):
|
||||
"""
|
||||
Called when the sidebar is closed.
|
||||
"""
|
||||
uimanager = self.viewmanager.uimanager
|
||||
uimanager.get_action('/MenuBar/ViewMenu/Sidebar').activate()
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Functions
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
def cb_menu_position(menu, button):
|
||||
"""
|
||||
Determine the position of the popup menu.
|
||||
"""
|
||||
x_pos, y_pos = button.window.get_origin()
|
||||
x_pos += button.allocation.x
|
||||
y_pos += button.allocation.y + button.allocation.height
|
||||
|
||||
return (x_pos, y_pos, False)
|
||||
|
||||
def cb_menu_deactivate(menu, button):
|
||||
"""
|
||||
Called when the popup menu disappears.
|
||||
"""
|
||||
button.set_active(False)
|
@ -4,6 +4,7 @@
|
||||
# Copyright (C) 2005-2007 Donald N. Allingham
|
||||
# Copyright (C) 2008 Brian G. Matherly
|
||||
# Copyright (C) 2009 Benny Malengier
|
||||
# Copyright (C) 2010 Nick Hall
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@ -61,6 +62,7 @@ from PluginUtils import Tool, PluginWindows, \
|
||||
ReportPluginDialog, ToolPluginDialog, gui_tool
|
||||
from gen.plug import REPORT
|
||||
from gui.pluginmanager import GuiPluginManager
|
||||
from gen.plug import (START, END)
|
||||
import Relationship
|
||||
import ReportBase
|
||||
import DisplayState
|
||||
@ -78,6 +80,7 @@ from gui.configure import GrampsPreferences
|
||||
from gen.db.backup import backup
|
||||
from gen.db.exceptions import DbException
|
||||
from GrampsAboutDialog import GrampsAboutDialog
|
||||
from gui.sidebar import Sidebar
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@ -183,20 +186,6 @@ UIDEFAULT = '''<ui>
|
||||
</ui>
|
||||
'''
|
||||
|
||||
UICATEGORY = '''<ui>
|
||||
<menubar name="MenuBar">
|
||||
<menu action="ViewMenu">
|
||||
<placeholder name="ViewsInCategory">%s
|
||||
</placeholder>
|
||||
</menu>
|
||||
</menubar>
|
||||
<toolbar name="ToolBar">
|
||||
<placeholder name="ViewsInCategory">%s
|
||||
</placeholder>
|
||||
</toolbar>
|
||||
</ui>
|
||||
'''
|
||||
|
||||
WIKI_HELP_PAGE_FAQ = '%s_-_FAQ' % const.URL_MANUAL_PAGE
|
||||
WIKI_HELP_PAGE_KEY = '%s_-_Keybindings' % const.URL_MANUAL_PAGE
|
||||
WIKI_HELP_PAGE_MAN = '%s' % const.URL_MANUAL_PAGE
|
||||
@ -234,13 +223,14 @@ class ViewManager(CLIManager):
|
||||
|
||||
The View Manager does not have to know the number of views, the type of
|
||||
views, or any other details about the views. It simply provides the
|
||||
method of containing each view, and switching between the views.
|
||||
method of containing each view, and has methods for creating, deleting and
|
||||
switching between the views.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, dbstate, view_category_order):
|
||||
"""
|
||||
The viewmanager is initialiste with a dbstate on which GRAMPS is
|
||||
The viewmanager is initialised with a dbstate on which GRAMPS is
|
||||
working, and a fixed view_category_order, which is the order in which
|
||||
the view categories are accessible in the sidebar.
|
||||
"""
|
||||
@ -248,12 +238,8 @@ class ViewManager(CLIManager):
|
||||
self.view_category_order = view_category_order
|
||||
#set pluginmanager to GUI one
|
||||
self._pmgr = GuiPluginManager.get_instance()
|
||||
self.page_is_changing = False
|
||||
self.active_page = None
|
||||
self.views = []
|
||||
self.pages = []
|
||||
self.button_handlers = []
|
||||
self.buttons = []
|
||||
self.merge_ids = []
|
||||
self.toolactions = None
|
||||
self.tool_menu_ui_id = None
|
||||
@ -294,12 +280,13 @@ class ViewManager(CLIManager):
|
||||
|
||||
vbox = gtk.VBox()
|
||||
self.window.add(vbox)
|
||||
hbox = gtk.HBox()
|
||||
hpane = gtk.HPaned()
|
||||
self.ebox = gtk.EventBox()
|
||||
self.bbox = gtk.VBox()
|
||||
self.ebox.add(self.bbox)
|
||||
hbox.pack_start(self.ebox, False)
|
||||
hbox.show_all()
|
||||
|
||||
self.sidebar = Sidebar(self)
|
||||
self.ebox.add(self.sidebar.get_top())
|
||||
hpane.add1(self.ebox)
|
||||
hpane.show_all()
|
||||
|
||||
self.notebook = gtk.Notebook()
|
||||
self.notebook.set_scrollable(True)
|
||||
@ -309,12 +296,12 @@ class ViewManager(CLIManager):
|
||||
self.__init_lists()
|
||||
self.__build_ui_manager()
|
||||
|
||||
hbox.pack_start(self.notebook, True)
|
||||
hpane.add2(self.notebook)
|
||||
self.menubar = self.uimanager.get_widget('/MenuBar')
|
||||
self.toolbar = self.uimanager.get_widget('/ToolBar')
|
||||
vbox.pack_start(self.menubar, False)
|
||||
vbox.pack_start(self.toolbar, False)
|
||||
vbox.add(hbox)
|
||||
vbox.add(hpane)
|
||||
vbox.pack_end(self.__setup_statusbar(), False)
|
||||
vbox.show()
|
||||
|
||||
@ -358,6 +345,21 @@ class ViewManager(CLIManager):
|
||||
# But we need to realize it here to have gtk.gdk.window handy
|
||||
self.window.realize()
|
||||
|
||||
def __load_sidebar_plugins(self):
|
||||
"""
|
||||
Load the sidebar plugins.
|
||||
"""
|
||||
for pdata in self._pmgr.get_reg_sidebars():
|
||||
module = self._pmgr.load_plugin(pdata)
|
||||
if not module:
|
||||
print "Error loading sidebar '%s': skipping content" \
|
||||
% pdata.name
|
||||
continue
|
||||
|
||||
sidebar_class = getattr(module, pdata.sidebarclass)
|
||||
sidebar_page = sidebar_class(self.dbstate, self.uistate)
|
||||
self.sidebar.add(pdata.menu_label, sidebar_page)
|
||||
|
||||
def __setup_statusbar(self):
|
||||
"""
|
||||
Create the statusbar that sits at the bottom of the window
|
||||
@ -387,10 +389,8 @@ class ViewManager(CLIManager):
|
||||
"""
|
||||
if self.show_sidebar:
|
||||
self.ebox.show()
|
||||
self.notebook.set_show_tabs(False)
|
||||
else:
|
||||
self.ebox.hide()
|
||||
self.notebook.set_show_tabs(True)
|
||||
|
||||
def __build_open_button(self):
|
||||
"""
|
||||
@ -406,10 +406,17 @@ class ViewManager(CLIManager):
|
||||
|
||||
def __connect_signals(self):
|
||||
"""
|
||||
connects the signals needed
|
||||
Connects the signals needed
|
||||
"""
|
||||
self.window.connect('delete-event', self.quit)
|
||||
self.notebook.connect('switch-page', self.change_category)
|
||||
self.notebook.connect('switch-page', self.view_changed)
|
||||
|
||||
def view_changed(self, notebook, page, page_num):
|
||||
"""
|
||||
Called when the notebook page is changed.
|
||||
"""
|
||||
self.sidebar.view_changed(page_num)
|
||||
self.__change_page(page_num)
|
||||
|
||||
def __init_lists(self):
|
||||
"""
|
||||
@ -547,11 +554,9 @@ class ViewManager(CLIManager):
|
||||
new_page = 0
|
||||
else:
|
||||
new_page = current_page + 1
|
||||
if self.show_sidebar:
|
||||
#cause a click signal
|
||||
self.buttons[new_page].set_active(True)
|
||||
else:
|
||||
self.notebook.set_current_page(new_page)
|
||||
self.sidebar.handlers_block()
|
||||
self.notebook.set_current_page(new_page)
|
||||
self.sidebar.handlers_unblock()
|
||||
|
||||
def __prev_view(self, action):
|
||||
"""
|
||||
@ -564,19 +569,16 @@ class ViewManager(CLIManager):
|
||||
new_page = len(self.pages)-1
|
||||
else:
|
||||
new_page = current_page - 1
|
||||
if self.show_sidebar:
|
||||
#cause a click signal
|
||||
self.buttons[new_page].set_active(True)
|
||||
else:
|
||||
self.notebook.set_current_page(new_page)
|
||||
self.sidebar.handlers_block()
|
||||
self.notebook.set_current_page(new_page)
|
||||
self.sidebar.handlers_unblock()
|
||||
|
||||
def init_interface(self, vieworder):
|
||||
def init_interface(self):
|
||||
"""
|
||||
Initialize the interface, creating the pages as given in vieworder
|
||||
"""
|
||||
self.views = vieworder
|
||||
self.__init_lists()
|
||||
self.__create_pages()
|
||||
self.__load_sidebar_plugins()
|
||||
|
||||
if not self.file_loaded:
|
||||
self.actiongroup.set_visible(False)
|
||||
@ -787,12 +789,10 @@ class ViewManager(CLIManager):
|
||||
"""
|
||||
if obj.get_active():
|
||||
self.ebox.show()
|
||||
self.notebook.set_show_tabs(False)
|
||||
config.set('interface.view', True)
|
||||
self.show_sidebar = True
|
||||
else:
|
||||
self.ebox.hide()
|
||||
self.notebook.set_show_tabs(True)
|
||||
config.set('interface.view', False)
|
||||
self.show_sidebar = False
|
||||
config.save()
|
||||
@ -823,267 +823,71 @@ class ViewManager(CLIManager):
|
||||
config.set('interface.fullscreen', False)
|
||||
config.save()
|
||||
|
||||
def view_toggle(self, radioaction, current, category_page):
|
||||
"""
|
||||
Go to the views in category_page, with in category: view_page
|
||||
The view has id id_page
|
||||
This is the only method that can call change of views in a category
|
||||
"""
|
||||
self.__vb_handlers_block()
|
||||
if self.notebook.get_current_page() != category_page:
|
||||
raise Error, 'Error changing view, category is not active'
|
||||
cat_notebook = self.notebook_cat[category_page]
|
||||
view_page = radioaction.get_current_value()
|
||||
if self.notebook_cat[category_page].get_current_page() != view_page:
|
||||
self.notebook_cat[category_page].set_current_page(view_page)
|
||||
self.__change_view(category_page, view_page)
|
||||
self.__vb_handlers_unblock()
|
||||
def create_page(self, pdata, page_def):
|
||||
try:
|
||||
page = page_def(self.dbstate, self.uistate)
|
||||
except:
|
||||
import traceback
|
||||
LOG.warn("View '%s' failed to load." % pdata.id)
|
||||
traceback.print_exc()
|
||||
return
|
||||
# Category is (string, trans):
|
||||
page.set_category(pdata.category)
|
||||
page.set_ident(page.get_category() + '_' + pdata.id)
|
||||
page_title = page.get_title()
|
||||
page_category = page.get_category()
|
||||
page_translated_category = page.get_translated_category()
|
||||
page_stock = page.get_stock()
|
||||
|
||||
page.define_actions()
|
||||
try:
|
||||
page_display = page.get_display()
|
||||
except:
|
||||
import traceback
|
||||
print "ERROR: '%s' failed to create view" % pdata.name
|
||||
traceback.print_exc()
|
||||
return
|
||||
page_display.show_all()
|
||||
page.post()
|
||||
self.pages.append(page)
|
||||
|
||||
# create icon/label for workspace notebook
|
||||
hbox = gtk.HBox()
|
||||
image = gtk.Image()
|
||||
image.set_from_stock(page_stock, gtk.ICON_SIZE_MENU)
|
||||
hbox.pack_start(image, False)
|
||||
hbox.add(gtk.Label(pdata.name))
|
||||
hbox.show_all()
|
||||
|
||||
def __switch_page_on_dnd(self, widget, context, xpos, ypos, time, page_no):
|
||||
"""
|
||||
Switches the page based on drag and drop
|
||||
"""
|
||||
self.__vb_handlers_block()
|
||||
if self.notebook.get_current_page() != page_no:
|
||||
self.notebook.set_current_page(page_no)
|
||||
self.__vb_handlers_unblock()
|
||||
page_num = self.notebook.append_page(page_display, hbox)
|
||||
return page_num
|
||||
|
||||
def goto_page(self, page_num):
|
||||
self.sidebar.handlers_block()
|
||||
self.notebook.set_current_page(page_num)
|
||||
self.sidebar.handlers_unblock()
|
||||
|
||||
self.__change_page(page_num)
|
||||
|
||||
def __change_page(self, page_num):
|
||||
self.__disconnect_previous_page()
|
||||
|
||||
self.active_page = self.pages[page_num]
|
||||
self.active_page.set_active()
|
||||
self.__connect_active_page(page_num)
|
||||
|
||||
self.uimanager.ensure_update()
|
||||
while gtk.events_pending():
|
||||
gtk.main_iteration()
|
||||
|
||||
self.active_page.change_page()
|
||||
|
||||
def __delete_pages(self):
|
||||
"""
|
||||
Calls on_delete() for each view
|
||||
"""
|
||||
for pages in self.pages:
|
||||
for page in pages:
|
||||
page.on_delete()
|
||||
|
||||
def __create_pages(self):
|
||||
"""
|
||||
Create the Views
|
||||
"""
|
||||
self.pages = []
|
||||
self.ui_category = {}
|
||||
self.view_toggle_actions = {}
|
||||
self.cat_view_group = None
|
||||
|
||||
use_text = config.get('interface.sidebar-text')
|
||||
#obtain which views should be the active ones
|
||||
current_cat, current_cat_view, default_cat_views = \
|
||||
self.__views_to_show(config.get('preferences.use-last-view'))
|
||||
|
||||
for indexcat, cat_views in enumerate(self.views):
|
||||
#for every category, we create a button in the sidebar and a main
|
||||
#workspace in which to show the view
|
||||
nr_views = len(cat_views)
|
||||
uimenuitems = ''
|
||||
uitoolitems = ''
|
||||
self.view_toggle_actions[indexcat] = []
|
||||
self.pages.append([])
|
||||
nrpage = 0
|
||||
for pdata, page_def in cat_views:
|
||||
try:
|
||||
page = page_def(self.dbstate, self.uistate)
|
||||
except:
|
||||
import traceback
|
||||
LOG.warn("View '%s' failed to load." % pdata.id)
|
||||
traceback.print_exc()
|
||||
continue
|
||||
# Category is (string, trans):
|
||||
page.set_category(pdata.category)
|
||||
page.set_ident(page.get_category() + '_' + pdata.id)
|
||||
page_title = page.get_title()
|
||||
page_category = page.get_category()
|
||||
page_translated_category = page.get_translated_category()
|
||||
page_stock = page.get_stock()
|
||||
|
||||
if nrpage == 0:
|
||||
#the first page of this category, used to obtain
|
||||
#category workspace notebook
|
||||
notebook = gtk.Notebook()
|
||||
notebook.set_scrollable(False)
|
||||
notebook.set_show_tabs(False)
|
||||
notebook.show()
|
||||
self.notebook_cat.append(notebook)
|
||||
# create icon/label for workspace notebook
|
||||
hbox = gtk.HBox()
|
||||
image = gtk.Image()
|
||||
image.set_from_stock(page_stock, gtk.ICON_SIZE_MENU)
|
||||
hbox.pack_start(image, False)
|
||||
hbox.add(gtk.Label(page_translated_category))
|
||||
hbox.show_all()
|
||||
page_cat = self.notebook.append_page(notebook, hbox)
|
||||
# Enable view switching during DnD
|
||||
hbox.drag_dest_set(0, [], 0)
|
||||
hbox.connect('drag_motion', self.__switch_page_on_dnd,
|
||||
page_cat)
|
||||
|
||||
# create the button and add it to the sidebar
|
||||
button = self.__make_sidebar_button(use_text, indexcat,
|
||||
page_translated_category,
|
||||
page_stock)
|
||||
|
||||
self.bbox.pack_start(button, False)
|
||||
self.buttons.append(button)
|
||||
|
||||
# Enable view switching during DnD
|
||||
button.drag_dest_set(0, [], 0)
|
||||
button.connect('drag_motion', self.__switch_page_on_dnd,
|
||||
page_cat)
|
||||
|
||||
# create view page and add to category notebook
|
||||
page.define_actions()
|
||||
try:
|
||||
page_display = page.get_display()
|
||||
except:
|
||||
import traceback
|
||||
print "ERROR: '%s' failed to create view" % pdata.name
|
||||
traceback.print_exc()
|
||||
continue
|
||||
page_display.show_all()
|
||||
page.post()
|
||||
page_no = self.notebook_cat[-1].append_page(page_display,
|
||||
gtk.Label(page_title))
|
||||
self.pages[-1].append(page)
|
||||
pageid = (pdata.id + '_%i' % nrpage)
|
||||
uimenuitems += '\n<menuitem action="%s"/>' % pageid
|
||||
uitoolitems += '\n<toolitem action="%s"/>' % pageid
|
||||
# id, stock, button text, UI, tooltip, page
|
||||
if nrpage < 9:
|
||||
modifier = "<CONTROL>%d" % ((nrpage % 9) + 1)
|
||||
else:
|
||||
modifier = ""
|
||||
self.view_toggle_actions[indexcat].append((pageid,
|
||||
page.get_viewtype_stock(),
|
||||
pdata.name, modifier, page_title, nrpage))
|
||||
|
||||
nrpage += 1
|
||||
if nr_views > 1:
|
||||
#allow for switching views in a category
|
||||
self.ui_category[indexcat] = UICATEGORY % (uimenuitems,
|
||||
uitoolitems)
|
||||
#set view cat to last used in this category
|
||||
self.notebook_cat[-1].set_current_page(default_cat_views[indexcat])
|
||||
|
||||
if self.views:
|
||||
self.active_page = self.pages[current_cat][current_cat_view]
|
||||
self.buttons[current_cat].set_active(True)
|
||||
self.active_page.set_active()
|
||||
self.notebook.set_current_page(current_cat)
|
||||
self.notebook_cat[current_cat].set_current_page(current_cat_view)
|
||||
else:
|
||||
#not one single view loaded
|
||||
WarningDialog(
|
||||
_("No views loaded"),
|
||||
_("No view plugins are loaded. Go to Help->Plugin "
|
||||
"Manager, and make sure some plugins of type 'View' are "
|
||||
"enabled. Then restart Gramps"))
|
||||
|
||||
|
||||
def __views_to_show(self, use_last = True):
|
||||
"""
|
||||
Determine based on preference setting which views should be shown
|
||||
"""
|
||||
current_cat = 0
|
||||
current_cat_view = 0
|
||||
default_cat_views = [0] * len(self.views)
|
||||
if use_last:
|
||||
current_page_id = config.get('preferences.last-view')
|
||||
default_page_ids = config.get('preferences.last-views')
|
||||
found = False
|
||||
for indexcat, cat_views in enumerate(self.views):
|
||||
cat_view = 0
|
||||
for pdata, page_def in cat_views:
|
||||
if not found:
|
||||
if pdata.id == current_page_id:
|
||||
current_cat = indexcat
|
||||
current_cat_view = cat_view
|
||||
default_cat_views[indexcat] = cat_view
|
||||
found = True
|
||||
break
|
||||
if pdata.id in default_page_ids:
|
||||
default_cat_views[indexcat] = cat_view
|
||||
cat_view += 1
|
||||
if not found:
|
||||
current_cat = 0
|
||||
current_cat_view = 0
|
||||
return current_cat, current_cat_view, default_cat_views
|
||||
|
||||
def __make_sidebar_button(self, use_text, index, page_title, page_stock):
|
||||
"""
|
||||
Create the sidebar button. The page_title is the text associated with
|
||||
the button.
|
||||
"""
|
||||
|
||||
# create the button
|
||||
button = gtk.ToggleButton()
|
||||
button.set_relief(gtk.RELIEF_NONE)
|
||||
button.set_alignment(0, 0.5)
|
||||
|
||||
# add the tooltip
|
||||
button.set_tooltip_text(page_title)
|
||||
#self.tips.set_tip(button, page_title)
|
||||
|
||||
# connect the signal, along with the index as user data
|
||||
handler_id = button.connect('clicked', self.__vb_clicked, index)
|
||||
self.button_handlers.append(handler_id)
|
||||
button.show()
|
||||
|
||||
# add the image. If we are using text, use the BUTTON (larger) size.
|
||||
# otherwise, use the smaller size
|
||||
hbox = gtk.HBox()
|
||||
hbox.show()
|
||||
image = gtk.Image()
|
||||
if use_text:
|
||||
image.set_from_stock(page_stock, gtk.ICON_SIZE_BUTTON)
|
||||
else:
|
||||
image.set_from_stock(page_stock, gtk.ICON_SIZE_DND)
|
||||
image.show()
|
||||
hbox.pack_start(image, False, False)
|
||||
hbox.set_spacing(4)
|
||||
|
||||
# add text if requested
|
||||
if use_text:
|
||||
label = gtk.Label(page_title)
|
||||
label.show()
|
||||
hbox.pack_start(label, False, True)
|
||||
|
||||
button.add(hbox)
|
||||
return button
|
||||
|
||||
def __vb_clicked(self, button, index):
|
||||
"""
|
||||
Called when the button causes a page change
|
||||
"""
|
||||
if config.get('interface.view'):
|
||||
self.__vb_handlers_block()
|
||||
self.notebook.set_current_page(index)
|
||||
|
||||
# If the click is on the same view we're in,
|
||||
# restore the button state to active
|
||||
if not button.get_active():
|
||||
button.set_active(True)
|
||||
self.__vb_handlers_unblock()
|
||||
|
||||
def __vb_handlers_block(self):
|
||||
"""
|
||||
Block signals to the buttons to prevent spurious events
|
||||
"""
|
||||
for idx in range(len(self.buttons)):
|
||||
self.buttons[idx].handler_block(self.button_handlers[idx])
|
||||
|
||||
def __vb_handlers_unblock(self):
|
||||
"""
|
||||
Unblock signals to the buttons
|
||||
"""
|
||||
for idx in range(len(self.buttons)):
|
||||
self.buttons[idx].handler_unblock(self.button_handlers[idx])
|
||||
|
||||
def __set_active_button(self, num):
|
||||
"""
|
||||
Set the corresponding button active, while setting the others
|
||||
inactive
|
||||
"""
|
||||
for idx in range(len(self.buttons)):
|
||||
self.buttons[idx].set_active(idx==num)
|
||||
for page in self.pages:
|
||||
page.on_delete()
|
||||
|
||||
def __disconnect_previous_page(self):
|
||||
"""
|
||||
@ -1098,11 +902,8 @@ class ViewManager(CLIManager):
|
||||
for grp in groups:
|
||||
if grp in self.uimanager.get_action_groups():
|
||||
self.uimanager.remove_action_group(grp)
|
||||
if self.cat_view_group:
|
||||
if self.cat_view_group in self.uimanager.get_action_groups():
|
||||
self.uimanager.remove_action_group(self.cat_view_group)
|
||||
|
||||
def __connect_active_page(self, category_page, view_page):
|
||||
def __connect_active_page(self, page_num):
|
||||
"""
|
||||
Inserts the action groups associated with the current page
|
||||
into the UIManager
|
||||
@ -1117,90 +918,12 @@ class ViewManager(CLIManager):
|
||||
mergeid = self.uimanager.add_ui_from_string(uidef)
|
||||
self.merge_ids.append(mergeid)
|
||||
|
||||
if category_page in self.ui_category:
|
||||
#add entries for the different views in the category
|
||||
self.cat_view_group = gtk.ActionGroup('categoryviews')
|
||||
self.cat_view_group.add_radio_actions(
|
||||
self.view_toggle_actions[category_page], value=view_page,
|
||||
on_change=self.view_toggle, user_data=category_page)
|
||||
self.cat_view_group.set_sensitive(True)
|
||||
self.uimanager.insert_action_group(self.cat_view_group, 1)
|
||||
mergeid = self.uimanager.add_ui_from_string(self.ui_category[
|
||||
category_page])
|
||||
self.merge_ids.append(mergeid)
|
||||
|
||||
configaction = self.actiongroup.get_action('ConfigView')
|
||||
if self.active_page.can_configure():
|
||||
configaction.set_sensitive(True)
|
||||
else:
|
||||
configaction.set_sensitive(False)
|
||||
|
||||
def change_category(self, obj, page, num=-1):
|
||||
"""
|
||||
Wrapper for the __do_change_category, to prevent entering into the
|
||||
routine while already in it.
|
||||
"""
|
||||
if not self.page_is_changing:
|
||||
self.page_is_changing = True
|
||||
self.__do_change_category(num)
|
||||
self.page_is_changing = False
|
||||
|
||||
def __do_change_category(self, num):
|
||||
"""
|
||||
Change the category to the new category
|
||||
"""
|
||||
if num == -1:
|
||||
num = self.notebook.get_current_page()
|
||||
|
||||
# set button of current page active
|
||||
self.__set_active_button(num)
|
||||
# now do view specific change
|
||||
self.__change_view(num)
|
||||
|
||||
def __change_view(self, category_page, view_page=-1):
|
||||
"""
|
||||
Change a view in a category.
|
||||
|
||||
:Param category_page: the category number the view is in
|
||||
:Type category_page: integer >= 0
|
||||
|
||||
:Param view_page: the view page number to switch to. If -1 is passed
|
||||
the currently already active view in the category is switched to.
|
||||
Use this when a category changes.
|
||||
:Type view_page: integer >=0 to switch to a specific page, or -1 to
|
||||
switch to the active view in the category
|
||||
"""
|
||||
if self.notebook_cat:
|
||||
if view_page == -1:
|
||||
#just show active one
|
||||
view_page = self.notebook_cat[category_page].get_current_page()
|
||||
if self.dbstate.open:
|
||||
self.__disconnect_previous_page()
|
||||
if len(self.pages) > 0:
|
||||
self.active_page = self.pages[category_page][view_page]
|
||||
self.active_page.set_active()
|
||||
newcurpageid = self.views[category_page][view_page][0].id
|
||||
config.set('preferences.last-view', newcurpageid)
|
||||
olddefaults = config.get('preferences.last-views')
|
||||
if len(olddefaults) != len(self.pages):
|
||||
#number views changed, we cannot trust the old
|
||||
olddefaults = [''] * len(self.pages)
|
||||
olddefaults[category_page] = newcurpageid
|
||||
config.set('preferences.last-views', olddefaults)
|
||||
config.save()
|
||||
|
||||
self.__connect_active_page(category_page, view_page)
|
||||
|
||||
self.uimanager.ensure_update()
|
||||
|
||||
while gtk.events_pending():
|
||||
gtk.main_iteration()
|
||||
|
||||
self.active_page.change_page()
|
||||
else:
|
||||
#no views loaded
|
||||
pass
|
||||
|
||||
def import_data(self, obj):
|
||||
"""
|
||||
Imports a file
|
||||
@ -1267,7 +990,6 @@ class ViewManager(CLIManager):
|
||||
self.uistate.window.set_title(msg)
|
||||
self.actiongroup.set_sensitive(True)
|
||||
|
||||
self.change_category(None, None)
|
||||
self.actiongroup.set_visible(True)
|
||||
self.readonlygroup.set_visible(True)
|
||||
|
||||
@ -1597,3 +1319,79 @@ def make_plugin_callback(pdata, dbstate, uistate):
|
||||
Makes a callback for a report/tool menu item
|
||||
"""
|
||||
return lambda x: run_plugin(pdata, dbstate, uistate)
|
||||
|
||||
def get_available_views():
|
||||
"""
|
||||
Query the views and determine what views to show and in which order
|
||||
|
||||
:Returns: a list of lists containing tuples (view_id, viewclass)
|
||||
"""
|
||||
pmgr = GuiPluginManager.get_instance()
|
||||
view_list = pmgr.get_reg_views()
|
||||
viewstoshow = {}
|
||||
for pdata in view_list:
|
||||
mod = pmgr.load_plugin(pdata)
|
||||
if not mod or not hasattr(mod, pdata.viewclass):
|
||||
#import of plugin failed
|
||||
ErrorDialog(
|
||||
_('Failed Loading View'),
|
||||
_('The view %(name)s did not load. See Help Menu, Plugin Manager'
|
||||
' for more info.\nUse http://bugs.gramps-project.org to'
|
||||
' submit bugs of official views, contact the view '
|
||||
'author (%(firstauthoremail)s) otherwise. ') % {
|
||||
'name': pdata.name,
|
||||
'firstauthoremail': pdata.authors_email[0] if
|
||||
pdata.authors_email else '...'})
|
||||
continue
|
||||
viewclass = getattr(mod, pdata.viewclass)
|
||||
# pdata.category is (string, trans-string):
|
||||
if pdata.category[0] in viewstoshow:
|
||||
if pdata.order == START:
|
||||
viewstoshow[pdata.category[0]].insert(0, ((pdata, viewclass)))
|
||||
else:
|
||||
viewstoshow[pdata.category[0]].append((pdata, viewclass))
|
||||
else:
|
||||
viewstoshow[pdata.category[0]] = [(pdata, viewclass)]
|
||||
|
||||
resultorder = []
|
||||
# First, get those in order defined, if exists:
|
||||
for item in config.get("interface.view-categories"):
|
||||
if item in viewstoshow:
|
||||
resultorder.append(viewstoshow[item])
|
||||
# Next, get the rest in some order:
|
||||
viewstoshow_names = viewstoshow.keys()
|
||||
viewstoshow_names.sort()
|
||||
for item in viewstoshow_names:
|
||||
if viewstoshow[item] in resultorder:
|
||||
continue
|
||||
resultorder.append(viewstoshow[item])
|
||||
return resultorder
|
||||
|
||||
def views_to_show(views, use_last=True):
|
||||
"""
|
||||
Determine based on preference setting which views should be shown
|
||||
"""
|
||||
current_cat = 0
|
||||
current_cat_view = 0
|
||||
default_cat_views = [0] * len(views)
|
||||
if use_last:
|
||||
current_page_id = config.get('preferences.last-view')
|
||||
default_page_ids = config.get('preferences.last-views')
|
||||
found = False
|
||||
for indexcat, cat_views in enumerate(views):
|
||||
cat_view = 0
|
||||
for pdata, page_def in cat_views:
|
||||
if not found:
|
||||
if pdata.id == current_page_id:
|
||||
current_cat = indexcat
|
||||
current_cat_view = cat_view
|
||||
default_cat_views[indexcat] = cat_view
|
||||
found = True
|
||||
break
|
||||
if pdata.id in default_page_ids:
|
||||
default_cat_views[indexcat] = cat_view
|
||||
cat_view += 1
|
||||
if not found:
|
||||
current_cat = 0
|
||||
current_cat_view = 0
|
||||
return current_cat, current_cat_view, default_cat_views
|
||||
|
22
src/plugins/sidebar/Makefile.am
Normal file
22
src/plugins/sidebar/Makefile.am
Normal file
@ -0,0 +1,22 @@
|
||||
# This is the src/plugins/sidebar level Makefile for Gramps
|
||||
# We could use GNU make's ':=' syntax for nice wildcard use,
|
||||
# but that is not necessarily portable.
|
||||
# If not using GNU make, then list all .py files individually
|
||||
|
||||
pkgdatadir = $(datadir)/@PACKAGE@/plugins/sidebar
|
||||
|
||||
pkgdata_PYTHON = \
|
||||
categorysidebar.py\
|
||||
sidebar.gpr.py
|
||||
|
||||
pkgpyexecdir = @pkgpyexecdir@/plugins/sidebar
|
||||
pkgpythondir = @pkgpythondir@/plugins/sidebar
|
||||
|
||||
# Clean up all the byte-compiled files
|
||||
MOSTLYCLEANFILES = *pyc *pyo
|
||||
|
||||
GRAMPS_PY_MODPATH = "../../"
|
||||
|
||||
pycheck:
|
||||
(export PYTHONPATH=$(GRAMPS_PY_MODPATH); \
|
||||
pychecker $(pkgdata_PYTHON));
|
301
src/plugins/sidebar/categorysidebar.py
Normal file
301
src/plugins/sidebar/categorysidebar.py
Normal file
@ -0,0 +1,301 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2005-2007 Donald N. Allingham
|
||||
# Copyright (C) 2008 Brian G. Matherly
|
||||
# Copyright (C) 2009 Benny Malengier
|
||||
# Copyright (C) 2010 Nick Hall
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# $Id$
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# GNOME modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
import gtk
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# GRAMPS modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
import config
|
||||
from gui.viewmanager import get_available_views, views_to_show
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Constants
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
UICATEGORY = '''<ui>
|
||||
<menubar name="MenuBar">
|
||||
<menu action="ViewMenu">
|
||||
<placeholder name="ViewsInCategory">%s
|
||||
</placeholder>
|
||||
</menu>
|
||||
</menubar>
|
||||
<toolbar name="ToolBar">
|
||||
<placeholder name="ViewsInCategory">%s
|
||||
</placeholder>
|
||||
</toolbar>
|
||||
</ui>
|
||||
'''
|
||||
|
||||
CATEGORY_ICON = {
|
||||
'Gramplets': 'gramps-gramplet',
|
||||
'People': 'gramps-person',
|
||||
'Relationships': 'gramps-relation',
|
||||
'Families': 'gramps-family',
|
||||
'Events': 'gramps-event',
|
||||
'Ancestry': 'gramps-pedigree',
|
||||
'Places': 'gramps-place',
|
||||
'Geography': 'gramps-geo',
|
||||
'Sources': 'gramps-source',
|
||||
'Repositories': 'gramps-repository',
|
||||
'Media': 'gramps-media',
|
||||
'Notes': 'gramps-notes'}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# CategorySidebar class
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class CategorySidebar(object):
|
||||
"""
|
||||
A sidebar displaying a column of toggle buttons that allows the user to
|
||||
change the current view.
|
||||
"""
|
||||
def __init__(self, dbstate, uistate):
|
||||
|
||||
self.viewmanager = uistate.viewmanager
|
||||
|
||||
self.buttons = []
|
||||
self.button_handlers = []
|
||||
|
||||
self.vbox = gtk.VBox()
|
||||
|
||||
self.pages = {}
|
||||
self.page_defs = {}
|
||||
|
||||
self.ui_category = {}
|
||||
self.view_toggle_actions = {}
|
||||
self.cat_view_group = None
|
||||
self.merge_ids = []
|
||||
|
||||
self.views = get_available_views()
|
||||
defaults = views_to_show(self.views,
|
||||
config.get('preferences.use-last-view'))
|
||||
self.current_views = defaults[2]
|
||||
|
||||
use_text = config.get('interface.sidebar-text')
|
||||
for cat_num, cat_views in enumerate(self.views):
|
||||
uimenuitems = ''
|
||||
uitoolitems = ''
|
||||
self.view_toggle_actions[cat_num] = []
|
||||
for view_num, page in enumerate(cat_views):
|
||||
|
||||
if view_num == 0:
|
||||
category = page[0].category[1]
|
||||
cat_icon = CATEGORY_ICON.get(page[0].category[0])
|
||||
if cat_icon is None:
|
||||
cat_icon = 'gramps-view'
|
||||
|
||||
# create the button and add it to the sidebar
|
||||
button = self.__make_sidebar_button(use_text, cat_num,
|
||||
category, cat_icon)
|
||||
self.vbox.pack_start(button, False)
|
||||
|
||||
# Enable view switching during DnD
|
||||
button.drag_dest_set(0, [], 0)
|
||||
button.connect('drag_motion', self.cb_switch_page_on_dnd,
|
||||
cat_num)
|
||||
self.vbox.show_all()
|
||||
|
||||
self.page_defs[(cat_num, view_num)] = page
|
||||
|
||||
pageid = (page[0].id + '_%i' % view_num)
|
||||
uimenuitems += '\n<menuitem action="%s"/>' % pageid
|
||||
uitoolitems += '\n<toolitem action="%s"/>' % pageid
|
||||
# id, stock, button text, UI, tooltip, page
|
||||
if view_num < 9:
|
||||
modifier = "<CONTROL>%d" % ((view_num % 9) + 1)
|
||||
else:
|
||||
modifier = ""
|
||||
|
||||
stock_icon = page[0].stock_icon
|
||||
if stock_icon is None:
|
||||
stock_icon = cat_icon
|
||||
self.view_toggle_actions[cat_num].append((pageid,
|
||||
stock_icon,
|
||||
page[0].name, modifier, page[0].name, view_num))
|
||||
|
||||
if len(cat_views) > 1:
|
||||
#allow for switching views in a category
|
||||
self.ui_category[cat_num] = UICATEGORY % (uimenuitems,
|
||||
uitoolitems)
|
||||
# Open the default view
|
||||
self.__category_clicked(self.buttons[defaults[0]], defaults[0])
|
||||
|
||||
def get_top(self):
|
||||
"""
|
||||
Return the top container widget for the GUI.
|
||||
"""
|
||||
return self.vbox
|
||||
|
||||
def view_changed(self, page_num):
|
||||
"""
|
||||
Called when the active view is changed.
|
||||
"""
|
||||
cat_num = view_num = None
|
||||
for key in self.pages:
|
||||
if self.pages[key] == page_num:
|
||||
cat_num, view_num = key
|
||||
break
|
||||
|
||||
# Save last view in configuration
|
||||
view_id = self.views[cat_num][view_num][0].id
|
||||
config.set('preferences.last-view', view_id)
|
||||
last_views = config.get('preferences.last-views')
|
||||
last_views[cat_num] = view_id
|
||||
config.set('preferences.last-views', last_views)
|
||||
config.save()
|
||||
|
||||
# Add buttons to the toolbar for the different view in the category
|
||||
uimanager = self.viewmanager.uimanager
|
||||
if self.cat_view_group:
|
||||
if self.cat_view_group in uimanager.get_action_groups():
|
||||
uimanager.remove_action_group(self.cat_view_group)
|
||||
|
||||
map(uimanager.remove_ui, self.merge_ids)
|
||||
|
||||
if cat_num in self.ui_category:
|
||||
self.cat_view_group = gtk.ActionGroup('categoryviews')
|
||||
self.cat_view_group.add_radio_actions(
|
||||
self.view_toggle_actions[cat_num], value=view_num,
|
||||
on_change=self.cb_view_clicked, user_data=cat_num)
|
||||
self.cat_view_group.set_sensitive(True)
|
||||
uimanager.insert_action_group(self.cat_view_group, 1)
|
||||
mergeid = uimanager.add_ui_from_string(self.ui_category[cat_num])
|
||||
self.merge_ids.append(mergeid)
|
||||
|
||||
# Set new button as selected
|
||||
self.handlers_block()
|
||||
for index, button in enumerate(self.buttons):
|
||||
if index == cat_num:
|
||||
button.set_active(True)
|
||||
else:
|
||||
button.set_active(False)
|
||||
self.handlers_unblock()
|
||||
|
||||
def handlers_block(self):
|
||||
"""
|
||||
Block signals to the buttons to prevent spurious events.
|
||||
"""
|
||||
for idx in range(len(self.buttons)):
|
||||
self.buttons[idx].handler_block(self.button_handlers[idx])
|
||||
|
||||
def handlers_unblock(self):
|
||||
"""
|
||||
Unblock signals to the buttons.
|
||||
"""
|
||||
for idx in range(len(self.buttons)):
|
||||
self.buttons[idx].handler_unblock(self.button_handlers[idx])
|
||||
|
||||
def cb_view_clicked(self, radioaction, current, cat_num):
|
||||
"""
|
||||
Called when a button causes a view change.
|
||||
"""
|
||||
view_num = radioaction.get_current_value()
|
||||
self.__goto_page(cat_num, view_num)
|
||||
|
||||
def __category_clicked(self, button, cat_num):
|
||||
"""
|
||||
Called when a button causes a category change.
|
||||
"""
|
||||
view_num = self.current_views[cat_num]
|
||||
self.__goto_page(cat_num, view_num)
|
||||
|
||||
# If the click is on the same view we're in,
|
||||
# restore the button state to active
|
||||
if not button.get_active():
|
||||
button.set_active(True)
|
||||
|
||||
def __goto_page(self, cat_num, view_num):
|
||||
"""
|
||||
Create the page if it doesn't exist and make it the current page.
|
||||
"""
|
||||
self.current_views[cat_num] = view_num
|
||||
|
||||
page_num = self.pages.get((cat_num, view_num))
|
||||
if page_num is None:
|
||||
page = self.page_defs[(cat_num, view_num)]
|
||||
page_num = self.viewmanager.create_page(page[0], page[1])
|
||||
self.pages[(cat_num, view_num)] = page_num
|
||||
|
||||
self.current_views[cat_num] = view_num
|
||||
self.viewmanager.goto_page(page_num)
|
||||
|
||||
def __make_sidebar_button(self, use_text, index, page_title, page_stock):
|
||||
"""
|
||||
Create the sidebar button. The page_title is the text associated with
|
||||
the button.
|
||||
"""
|
||||
# create the button
|
||||
button = gtk.ToggleButton()
|
||||
button.set_relief(gtk.RELIEF_NONE)
|
||||
button.set_alignment(0, 0.5)
|
||||
self.buttons.append(button)
|
||||
|
||||
# add the tooltip
|
||||
button.set_tooltip_text(page_title)
|
||||
|
||||
# connect the signal, along with the index as user data
|
||||
handler_id = button.connect('clicked', self.__category_clicked, index)
|
||||
self.button_handlers.append(handler_id)
|
||||
button.show()
|
||||
|
||||
# add the image. If we are using text, use the BUTTON (larger) size.
|
||||
# otherwise, use the smaller size
|
||||
hbox = gtk.HBox()
|
||||
hbox.show()
|
||||
image = gtk.Image()
|
||||
if use_text:
|
||||
image.set_from_stock(page_stock, gtk.ICON_SIZE_BUTTON)
|
||||
else:
|
||||
image.set_from_stock(page_stock, gtk.ICON_SIZE_DND)
|
||||
image.show()
|
||||
hbox.pack_start(image, False, False)
|
||||
hbox.set_spacing(4)
|
||||
|
||||
# add text if requested
|
||||
if use_text:
|
||||
label = gtk.Label(page_title)
|
||||
label.show()
|
||||
hbox.pack_start(label, False, True)
|
||||
|
||||
button.add(hbox)
|
||||
return button
|
||||
|
||||
def cb_switch_page_on_dnd(self, widget, context, xpos, ypos, time, page_no):
|
||||
"""
|
||||
Switches the page based on drag and drop.
|
||||
"""
|
||||
self.handlers_block()
|
||||
if self.viewmanager.notebook.get_current_page() != page_no:
|
||||
self.viewmanager.notebook.set_current_page(page_no)
|
||||
self.handlers_unblock()
|
40
src/plugins/sidebar/sidebar.gpr.py
Normal file
40
src/plugins/sidebar/sidebar.gpr.py
Normal file
@ -0,0 +1,40 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2010 Nick Hall
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# $Id$
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
# Register default sidebars
|
||||
#
|
||||
#------------------------------------------------------------------------
|
||||
|
||||
register(SIDEBAR,
|
||||
id = 'categorysidebar',
|
||||
name = _("Category Sidebar"),
|
||||
description = _("A sidebar to allow the selection of view categories"),
|
||||
version = '1.0',
|
||||
gramps_target_version = '3.3',
|
||||
status = STABLE,
|
||||
fname = 'categorysidebar.py',
|
||||
authors = [u"Nick Hall"],
|
||||
authors_email = ["nick__hall@hotmail.com"],
|
||||
sidebarclass = 'CategorySidebar',
|
||||
menu_label = 'Category'
|
||||
)
|
@ -10,4 +10,5 @@ register(VIEW,
|
||||
authors = [u"Douglas S. Blank"],
|
||||
authors_email = ["doug.blank@gmail.com"],
|
||||
viewclass = 'FanChartView',
|
||||
stock_icon = 'gramps-fanchart',
|
||||
)
|
||||
|
@ -10,4 +10,5 @@ register(VIEW,
|
||||
authors_email = [""],
|
||||
category = ("Places", _("Places")),
|
||||
viewclass = 'PlaceTreeView',
|
||||
stock_icon = 'gramps-tree-group',
|
||||
)
|
||||
|
@ -130,6 +130,7 @@ authors_email = ["http://gramps-project.org"],
|
||||
category = ("Ancestry", _("Ancestry")),
|
||||
viewclass = 'PedigreeView',
|
||||
order = START,
|
||||
stock_icon = 'gramps-pedigree',
|
||||
)
|
||||
|
||||
register(VIEW,
|
||||
@ -145,6 +146,7 @@ authors_email = ["http://gramps-project.org"],
|
||||
category = ("People", _("People")),
|
||||
viewclass = 'PersonTreeView',
|
||||
order = START,
|
||||
stock_icon = 'gramps-tree-group',
|
||||
)
|
||||
|
||||
register(VIEW,
|
||||
@ -161,7 +163,9 @@ authors_email = ["http://gramps-project.org"],
|
||||
category = ("People", _("People")),
|
||||
viewclass = 'PersonListView',
|
||||
order = START,
|
||||
stock_icon = 'gramps-tree-list',
|
||||
)
|
||||
|
||||
register(VIEW,
|
||||
id = 'placelistview',
|
||||
name = _("Place View"),
|
||||
@ -175,6 +179,7 @@ authors_email = ["http://gramps-project.org"],
|
||||
category = ("Places", _("Places")),
|
||||
viewclass = 'PlaceListView',
|
||||
order = START,
|
||||
stock_icon = 'gramps-tree-list',
|
||||
)
|
||||
|
||||
register(VIEW,
|
||||
|
Loading…
x
Reference in New Issue
Block a user