In .:
2006-05-04 Alex Roitman <shura@gramps-project.org> * src/DisplayTabs.py: remove file. * src/DisplayTabs: Add package. In po: 2006-05-04 Alex Roitman <shura@gramps-project.org> * POTFILES.in: Add new files. svn: r6546
This commit is contained in:
353
src/DisplayTabs/_EmbeddedList.py
Normal file
353
src/DisplayTabs/_EmbeddedList.py
Normal file
@ -0,0 +1,353 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2000-2006 Donald N. Allingham
|
||||
#
|
||||
# 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$
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# python
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from gettext import gettext as _
|
||||
import pickle
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# GTK libraries
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
import gtk
|
||||
import pango
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# GRAMPS classes
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from _ButtonTab import ButtonTab
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Classes
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class EmbeddedList(ButtonTab):
|
||||
"""
|
||||
This class provides the base class for all the list tabs. It
|
||||
maintains a gtk.TreeView, including the selection and button
|
||||
sensitivity.
|
||||
"""
|
||||
|
||||
_HANDLE_COL = -1
|
||||
_DND_TYPE = None
|
||||
_DND_EXTRA = None
|
||||
|
||||
def __init__(self, dbstate, uistate, track, name, build_model,
|
||||
share=False):
|
||||
"""
|
||||
Creates a new list, using the passed build_model to
|
||||
populate the list.
|
||||
"""
|
||||
ButtonTab.__init__(self, dbstate, uistate, track, name, share)
|
||||
|
||||
|
||||
self.changed = False
|
||||
self.build_model = build_model
|
||||
|
||||
# handle the selection
|
||||
self.selection = self.tree.get_selection()
|
||||
self.selection.connect('changed', self._selection_changed)
|
||||
|
||||
# build the columns
|
||||
self.columns = []
|
||||
self.build_columns()
|
||||
|
||||
if self._DND_TYPE:
|
||||
self._set_dnd()
|
||||
|
||||
# set up right click option
|
||||
self.tree.connect('button-press-event', self._on_button_press)
|
||||
|
||||
# build the initial data
|
||||
self.rebuild()
|
||||
self.show_all()
|
||||
|
||||
def _on_button_press(self, obj, event):
|
||||
if event.type == gtk.gdk.BUTTON_PRESS and event.button == 3:
|
||||
ref = self.get_selected()
|
||||
if ref:
|
||||
self.right_click(obj, event)
|
||||
|
||||
def right_click(self, obj, event):
|
||||
|
||||
if self.share_btn:
|
||||
itemlist = [
|
||||
(True, gtk.STOCK_ADD, self.add_button_clicked),
|
||||
(False, _('Share'), self.edit_button_clicked),
|
||||
(True, gtk.STOCK_EDIT, self.edit_button_clicked),
|
||||
(True, gtk.STOCK_REMOVE, self.del_button_clicked),
|
||||
]
|
||||
else:
|
||||
itemlist = [
|
||||
(True, gtk.STOCK_ADD, self.add_button_clicked),
|
||||
(True, gtk.STOCK_EDIT, self.edit_button_clicked),
|
||||
(True, gtk.STOCK_REMOVE, self.del_button_clicked),
|
||||
]
|
||||
|
||||
menu = gtk.Menu()
|
||||
for (image, title, func) in itemlist:
|
||||
if image:
|
||||
item = gtk.ImageMenuItem(stock_id=title)
|
||||
else:
|
||||
item = gtk.MenuItem(title)
|
||||
item.connect('activate', func)
|
||||
item.show()
|
||||
menu.append(item)
|
||||
menu.popup(None, None, None, event.button, event.time)
|
||||
|
||||
def find_index(self, obj):
|
||||
"""
|
||||
returns the index of the object within the associated data
|
||||
"""
|
||||
return self.get_data().index(obj)
|
||||
|
||||
def _set_dnd(self):
|
||||
"""
|
||||
Sets up drag-n-drop. The source and destionation are set by calling .target()
|
||||
on the _DND_TYPE. Obviously, this means that there must be a _DND_TYPE
|
||||
variable defined that points to an entry in DdTargets.
|
||||
"""
|
||||
|
||||
if self._DND_EXTRA:
|
||||
dnd_types = [ self._DND_TYPE.target(), self._DND_EXTRA.target() ]
|
||||
else:
|
||||
dnd_types = [ self._DND_TYPE.target() ]
|
||||
|
||||
self.tree.drag_dest_set(gtk.DEST_DEFAULT_ALL, dnd_types,
|
||||
gtk.gdk.ACTION_COPY)
|
||||
self.tree.drag_source_set(gtk.gdk.BUTTON1_MASK,
|
||||
[self._DND_TYPE.target()],
|
||||
gtk.gdk.ACTION_COPY)
|
||||
self.tree.connect('drag_data_get', self.drag_data_get)
|
||||
self.tree.connect('drag_data_received', self.drag_data_received)
|
||||
|
||||
def drag_data_get(self, widget, context, sel_data, info, time):
|
||||
"""
|
||||
Provide the drag_data_get function, which passes a tuple consisting of:
|
||||
|
||||
1) Drag type defined by the .drag_type field specfied by the value
|
||||
assigned to _DND_TYPE
|
||||
2) The id value of this object, used for the purpose of determining
|
||||
the source of the object. If the source of the object is the same
|
||||
as the object, we are doing a reorder instead of a normal drag
|
||||
and drop
|
||||
3) Pickled data. The pickled version of the selected object
|
||||
4) Source row. Used for a reorder to determine the original position
|
||||
of the object
|
||||
"""
|
||||
|
||||
# get the selected object, returning if not is defined
|
||||
obj = self.get_selected()
|
||||
if not obj:
|
||||
return
|
||||
|
||||
# pickle the data, and build the tuple to be passed
|
||||
value = (self._DND_TYPE.drag_type, id(self), obj,self.find_index(obj))
|
||||
data = pickle.dumps(value)
|
||||
|
||||
# pass as a string (8 bits)
|
||||
sel_data.set(sel_data.target, 8, data)
|
||||
|
||||
def drag_data_received(self, widget, context, x, y, sel_data, info, time):
|
||||
"""
|
||||
Handle the standard gtk interface for drag_data_received.
|
||||
|
||||
If the selection data is define, extract the value from sel_data.data,
|
||||
and decide if this is a move or a reorder.
|
||||
"""
|
||||
if sel_data and sel_data.data:
|
||||
(mytype, selfid, obj, row_from) = pickle.loads(sel_data.data)
|
||||
|
||||
# make sure this is the correct DND type for this object
|
||||
if mytype == self._DND_TYPE.drag_type:
|
||||
|
||||
# determine the destination row
|
||||
row = self._find_row(x, y)
|
||||
|
||||
# if the is same object, we have a move, otherwise,
|
||||
# it is a standard drag-n-drop
|
||||
|
||||
if id(self) == selfid:
|
||||
self._move(row_from, row, obj)
|
||||
else:
|
||||
self._handle_drag(row, obj)
|
||||
self.rebuild()
|
||||
elif self._DND_EXTRA and mytype == self._DND_EXTRA.drag_type:
|
||||
self.handle_extra_type(mytype, obj)
|
||||
|
||||
def handle_extra_type(self, objtype, obj):
|
||||
pass
|
||||
|
||||
def _find_row(self, x, y):
|
||||
row = self.tree.get_path_at_pos(x, y)
|
||||
if row == None:
|
||||
return len(self.get_data())
|
||||
else:
|
||||
return row[0][0]
|
||||
|
||||
def _handle_drag(self, row, obj):
|
||||
self.get_data().insert(row, obj)
|
||||
self.changed = True
|
||||
self.rebuild()
|
||||
|
||||
def _move(self, row_from, row_to, obj):
|
||||
dlist = self.get_data()
|
||||
if row_from < row_to:
|
||||
dlist.insert(row_to, obj)
|
||||
del dlist[row_from]
|
||||
else:
|
||||
del dlist[row_from]
|
||||
dlist.insert(row_to-1, obj)
|
||||
self.changed = True
|
||||
self.rebuild()
|
||||
|
||||
def get_icon_name(self):
|
||||
"""
|
||||
Specifies the basic icon used for a generic list. Typically,
|
||||
a derived class will override this. The icon chose is the
|
||||
STOCK_JUSTIFY_FILL icon, which in the default GTK style
|
||||
looks kind of like a list.
|
||||
"""
|
||||
return gtk.STOCK_JUSTIFY_FILL
|
||||
|
||||
def del_button_clicked(self, obj):
|
||||
ref = self.get_selected()
|
||||
if ref:
|
||||
ref_list = self.get_data()
|
||||
ref_list.remove(ref)
|
||||
self.changed = True
|
||||
self.rebuild()
|
||||
|
||||
def build_interface(self):
|
||||
"""
|
||||
Builds the interface, instantiating a gtk.TreeView in a
|
||||
gtk.ScrolledWindow.
|
||||
"""
|
||||
|
||||
# create the tree, turn on rule hinting and connect the
|
||||
# button press to the double click function.
|
||||
|
||||
self.tree = gtk.TreeView()
|
||||
self.tree.set_reorderable(True)
|
||||
self.tree.set_rules_hint(True)
|
||||
self.tree.connect('button_press_event', self.double_click)
|
||||
|
||||
# create the scrolled window, and attach the treeview
|
||||
scroll = gtk.ScrolledWindow()
|
||||
scroll.set_shadow_type(gtk.SHADOW_IN)
|
||||
scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
|
||||
scroll.add(self.tree)
|
||||
self.pack_start(scroll, True)
|
||||
|
||||
def get_selected(self):
|
||||
"""
|
||||
returns the value associated with selected row in the model,
|
||||
based of the _HANDLE_COL value. Each model must define this
|
||||
to indicate what the returned value should be. If no selection
|
||||
has been made, None is returned.
|
||||
"""
|
||||
(model, node) = self.selection.get_selected()
|
||||
if node:
|
||||
return model.get_value(node, self._HANDLE_COL)
|
||||
return None
|
||||
|
||||
def is_empty(self):
|
||||
"""
|
||||
Returns True if the get_data returns a length greater than
|
||||
0. Typically, get_data returns the list of associated data.
|
||||
"""
|
||||
return len(self.get_data()) == 0
|
||||
|
||||
def get_data(self):
|
||||
"""
|
||||
Returns the data associated with the list. This is typically
|
||||
a list of objects.
|
||||
|
||||
This should be overridden in the derrived classes.
|
||||
"""
|
||||
return []
|
||||
|
||||
def column_order(self):
|
||||
"""
|
||||
Specifies the column order for the columns. This should be
|
||||
in the format of a list of tuples, in the format of (int,int),
|
||||
where the first in indicates if the column is visible, and the
|
||||
second column indicates the index into the model.
|
||||
|
||||
This should be overridden in the derrived classes.
|
||||
"""
|
||||
return []
|
||||
|
||||
def build_columns(self):
|
||||
"""
|
||||
Builds the columns and inserts them into the TreeView. Any
|
||||
previous columns exist, they will be in the self.columns array,
|
||||
and removed.
|
||||
"""
|
||||
|
||||
# remove any existing columns, which would be stored in
|
||||
# self.columns
|
||||
|
||||
for column in self.columns:
|
||||
self.tree.remove_column(column)
|
||||
self.columns = []
|
||||
|
||||
# loop through the values returned by column_order
|
||||
|
||||
for pair in self.column_order():
|
||||
|
||||
# if the first value isn't 1, then we skip the values
|
||||
if not pair[0]:
|
||||
continue
|
||||
|
||||
# extract the name from the _column_names variable, and
|
||||
# assign it to the column name. The text value is extracted
|
||||
# from the model column specified in pair[1]
|
||||
name = self._column_names[pair[1]][0]
|
||||
renderer = gtk.CellRendererText()
|
||||
renderer.set_property('ellipsize', pango.ELLIPSIZE_END)
|
||||
column = gtk.TreeViewColumn(name, renderer, text=pair[1])
|
||||
|
||||
# insert the colum into the tree
|
||||
column.set_resizable(True)
|
||||
column.set_min_width(self._column_names[pair[1]][2])
|
||||
column.set_sort_column_id(self._column_names[pair[1]][1])
|
||||
self.columns.append(column)
|
||||
self.tree.append_column(column)
|
||||
|
||||
def rebuild(self):
|
||||
"""
|
||||
Rebuilds the data in the database by creating a new model,
|
||||
using the build_model function passed at creation time.
|
||||
"""
|
||||
self.model = self.build_model(self.get_data(), self.dbstate.db)
|
||||
self.tree.set_model(self.model)
|
||||
self._set_label()
|
||||
self._selection_changed()
|
Reference in New Issue
Block a user