2006-02-03 01:13:42 +05:30
|
|
|
#
|
|
|
|
# Gramps - a GTK+/GNOME based genealogy program
|
|
|
|
#
|
2006-02-04 03:33:53 +05:30
|
|
|
# Copyright (C) 2000-2006 Donald N. Allingham
|
2009-04-01 02:15:20 +05:30
|
|
|
# Copyright (C) 2009 Gary Burton
|
2006-02-03 01:13:42 +05:30
|
|
|
#
|
|
|
|
# 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$
|
|
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
|
|
#
|
|
|
|
# Standard python modules
|
|
|
|
#
|
|
|
|
#-------------------------------------------------------------------------
|
2010-01-18 10:12:17 +05:30
|
|
|
from gen.ggettext import gettext as _
|
2006-02-03 01:13:42 +05:30
|
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
|
|
#
|
|
|
|
# GTK/Gnome modules
|
|
|
|
#
|
|
|
|
#-------------------------------------------------------------------------
|
|
|
|
import gtk
|
|
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
|
|
#
|
|
|
|
# gramps modules
|
|
|
|
#
|
|
|
|
#-------------------------------------------------------------------------
|
2009-06-24 02:19:18 +05:30
|
|
|
from gui.utils import open_file_with_default_application
|
2007-10-08 22:11:39 +05:30
|
|
|
import gen.lib
|
2010-01-25 01:47:14 +05:30
|
|
|
import gen.mime
|
2007-09-11 03:44:33 +05:30
|
|
|
import ThumbNails
|
2007-11-16 22:15:45 +05:30
|
|
|
import Utils
|
2009-12-15 11:26:12 +05:30
|
|
|
from editprimary import EditPrimary
|
2009-12-14 08:50:19 +05:30
|
|
|
from gui.widgets import MonitoredDate, MonitoredEntry, PrivacyButton
|
2009-12-16 11:41:06 +05:30
|
|
|
from displaytabs import (SourceEmbedList, AttrEmbedList, NoteTab,
|
2008-02-08 20:59:28 +05:30
|
|
|
MediaBackRefList)
|
2009-12-15 11:26:12 +05:30
|
|
|
from addmedia import AddMediaObject
|
2008-02-09 06:37:23 +05:30
|
|
|
from QuestionDialog import ErrorDialog
|
2009-05-15 01:45:59 +05:30
|
|
|
from glade import Glade
|
2009-05-08 01:38:27 +05:30
|
|
|
|
2006-02-03 01:13:42 +05:30
|
|
|
#-------------------------------------------------------------------------
|
|
|
|
#
|
|
|
|
# EditMedia
|
|
|
|
#
|
|
|
|
#-------------------------------------------------------------------------
|
2006-03-04 12:04:48 +05:30
|
|
|
class EditMedia(EditPrimary):
|
2006-02-03 01:13:42 +05:30
|
|
|
|
2008-02-09 06:37:23 +05:30
|
|
|
def __init__(self, dbstate, uistate, track, obj, callback=None):
|
2006-02-03 01:13:42 +05:30
|
|
|
|
2008-02-09 06:37:23 +05:30
|
|
|
EditPrimary.__init__(self, dbstate, uistate, track, obj,
|
|
|
|
dbstate.db.get_object_from_handle,
|
|
|
|
dbstate.db.get_object_from_gramps_id, callback)
|
2008-01-06 16:21:20 +05:30
|
|
|
if not self.obj.get_handle():
|
|
|
|
#show the addmedia dialog immediately, with track of parent.
|
2008-02-09 06:37:23 +05:30
|
|
|
AddMediaObject(dbstate, self.uistate, self.track, self.obj,
|
2008-01-06 16:21:20 +05:30
|
|
|
self._update_addmedia)
|
2006-03-01 10:38:11 +05:30
|
|
|
|
2006-04-01 01:16:41 +05:30
|
|
|
def empty_object(self):
|
2007-10-08 22:11:39 +05:30
|
|
|
return gen.lib.MediaObject()
|
2006-04-01 01:16:41 +05:30
|
|
|
|
2006-11-27 02:48:30 +05:30
|
|
|
def get_menu_title(self):
|
|
|
|
if self.obj.get_handle():
|
2006-12-12 10:27:36 +05:30
|
|
|
name = self.obj.get_description()
|
|
|
|
if not name:
|
|
|
|
name = self.obj.get_path()
|
|
|
|
if not name:
|
|
|
|
name = self.obj.get_mime_type()
|
|
|
|
if not name:
|
|
|
|
name = _('Note')
|
|
|
|
dialog_title = _('Media: %s') % name
|
2006-11-27 02:48:30 +05:30
|
|
|
else:
|
|
|
|
dialog_title = _('New Media')
|
|
|
|
return dialog_title
|
|
|
|
|
2006-03-01 10:38:11 +05:30
|
|
|
def _local_init(self):
|
|
|
|
assert(self.obj)
|
2009-10-08 06:42:51 +05:30
|
|
|
self.width_key = 'interface.media-width'
|
|
|
|
self.height_key = 'interface.media-height'
|
2009-05-08 01:38:27 +05:30
|
|
|
|
2009-05-15 01:45:59 +05:30
|
|
|
self.glade = Glade()
|
|
|
|
self.set_window(self.glade.toplevel,
|
2006-12-12 10:27:36 +05:30
|
|
|
None, self.get_menu_title())
|
2006-03-01 10:38:11 +05:30
|
|
|
|
|
|
|
def _connect_signals(self):
|
2009-05-08 01:38:27 +05:30
|
|
|
self.define_cancel_button(self.glade.get_object('button91'))
|
|
|
|
self.define_ok_button(self.glade.get_object('ok'), self.save)
|
|
|
|
self.define_help_button(self.glade.get_object('button102'))
|
2006-03-01 10:38:11 +05:30
|
|
|
|
2009-08-05 16:02:05 +05:30
|
|
|
def _connect_db_signals(self):
|
|
|
|
"""
|
|
|
|
Connect any signals that need to be connected.
|
|
|
|
Called by the init routine of the base class (_EditPrimary).
|
|
|
|
"""
|
|
|
|
self._add_db_signal('media-rebuild', self._do_close)
|
|
|
|
self._add_db_signal('media-delete', self.check_for_close)
|
|
|
|
|
2006-03-01 10:38:11 +05:30
|
|
|
def _setup_fields(self):
|
2009-05-08 01:38:27 +05:30
|
|
|
self.date_field = MonitoredDate(self.glade.get_object("date_entry"),
|
|
|
|
self.glade.get_object("date_edit"),
|
2008-02-09 06:37:23 +05:30
|
|
|
self.obj.get_date_object(),
|
|
|
|
self.uistate, self.track,
|
|
|
|
self.db.readonly)
|
|
|
|
|
2009-05-08 01:38:27 +05:30
|
|
|
self.descr_window = MonitoredEntry(self.glade.get_object("description"),
|
2008-02-09 06:37:23 +05:30
|
|
|
self.obj.set_description,
|
|
|
|
self.obj.get_description,
|
|
|
|
self.db.readonly)
|
2006-02-03 01:13:42 +05:30
|
|
|
|
2009-05-08 01:38:27 +05:30
|
|
|
self.gid = MonitoredEntry(self.glade.get_object("gid"),
|
2008-02-09 06:37:23 +05:30
|
|
|
self.obj.set_gramps_id,
|
|
|
|
self.obj.get_gramps_id, self.db.readonly)
|
2006-02-03 01:13:42 +05:30
|
|
|
|
2009-05-08 01:38:27 +05:30
|
|
|
self.privacy = PrivacyButton(self.glade.get_object("private"),
|
2008-02-09 06:37:23 +05:30
|
|
|
self.obj, self.db.readonly)
|
2006-08-30 05:15:07 +05:30
|
|
|
|
2009-05-08 01:38:27 +05:30
|
|
|
self.pixmap = self.glade.get_object("pixmap")
|
|
|
|
ebox = self.glade.get_object('eventbox')
|
2007-11-28 01:21:45 +05:30
|
|
|
ebox.connect('button-press-event', self.button_press_event)
|
2006-02-03 01:13:42 +05:30
|
|
|
|
2009-05-08 01:38:27 +05:30
|
|
|
self.mimetext = self.glade.get_object("type")
|
2008-01-06 16:21:20 +05:30
|
|
|
self.setup_filepath()
|
2008-02-12 03:57:24 +05:30
|
|
|
self.determine_mime()
|
|
|
|
self.draw_preview()
|
|
|
|
|
|
|
|
def determine_mime(self):
|
2010-01-25 01:47:14 +05:30
|
|
|
descr = gen.mime.get_description(self.obj.get_mime_type())
|
2008-02-12 03:57:24 +05:30
|
|
|
if descr:
|
|
|
|
self.mimetext.set_text(descr)
|
|
|
|
|
|
|
|
path = self.file_path.get_text()
|
|
|
|
path_full = Utils.media_path_full(self.db, path)
|
|
|
|
if path != self.obj.get_path() and path_full != self.obj.get_path():
|
|
|
|
#redetermine mime
|
2010-01-25 01:47:14 +05:30
|
|
|
mime = gen.mime.get_type(Utils.find_file(path_full))
|
2008-02-12 03:57:24 +05:30
|
|
|
self.obj.set_mime_type(mime)
|
2010-01-25 01:47:14 +05:30
|
|
|
descr = gen.mime.get_description(mime)
|
2008-02-12 03:57:24 +05:30
|
|
|
if descr:
|
|
|
|
self.mimetext.set_text(descr)
|
|
|
|
else:
|
|
|
|
self.mimetext.set_text(_('Unknown'))
|
|
|
|
#if mime type not set, is note
|
|
|
|
if not self.obj.get_mime_type():
|
|
|
|
self.mimetext.set_text(_('Note'))
|
2008-01-06 16:21:20 +05:30
|
|
|
|
|
|
|
def draw_preview(self):
|
2006-02-03 01:13:42 +05:30
|
|
|
mtype = self.obj.get_mime_type()
|
|
|
|
if mtype:
|
2009-05-08 01:38:27 +05:30
|
|
|
pb = ThumbNails.get_thumbnail_image(
|
|
|
|
Utils.media_path_full(self.db, self.obj.get_path()),
|
2008-02-12 03:57:24 +05:30
|
|
|
mtype)
|
2008-01-06 16:21:20 +05:30
|
|
|
self.pixmap.set_from_pixbuf(pb)
|
2006-02-03 01:13:42 +05:30
|
|
|
else:
|
2010-01-24 10:22:22 +05:30
|
|
|
pb = ThumbNails.find_mime_type_pixbuf('text/plain')
|
2008-01-06 16:21:20 +05:30
|
|
|
self.pixmap.set_from_pixbuf(pb)
|
|
|
|
|
|
|
|
def setup_filepath(self):
|
2009-05-08 01:38:27 +05:30
|
|
|
self.select = self.glade.get_object('file_select')
|
|
|
|
self.file_path = self.glade.get_object("path")
|
2008-01-06 16:21:20 +05:30
|
|
|
|
|
|
|
fname = self.obj.get_path()
|
|
|
|
self.file_path.set_text(fname)
|
|
|
|
self.select.connect('clicked', self.select_file)
|
2006-02-03 01:13:42 +05:30
|
|
|
|
2006-03-01 10:38:11 +05:30
|
|
|
def _create_tabbed_pages(self):
|
|
|
|
notebook = gtk.Notebook()
|
2006-02-03 01:13:42 +05:30
|
|
|
|
2009-04-01 02:15:20 +05:30
|
|
|
self.src_tab = SourceEmbedList(self.dbstate,
|
|
|
|
self.uistate,
|
|
|
|
self.track,
|
|
|
|
self.obj)
|
|
|
|
self._add_tab(notebook, self.src_tab)
|
|
|
|
self.track_ref_for_deletion("src_tab")
|
2007-11-28 01:21:45 +05:30
|
|
|
|
2009-04-01 02:15:20 +05:30
|
|
|
self.attr_tab = AttrEmbedList(self.dbstate,
|
|
|
|
self.uistate,
|
|
|
|
self.track,
|
|
|
|
self.obj.get_attribute_list())
|
|
|
|
self._add_tab(notebook, self.attr_tab)
|
|
|
|
self.track_ref_for_deletion("attr_tab")
|
2007-11-28 01:21:45 +05:30
|
|
|
|
2009-04-01 02:15:20 +05:30
|
|
|
self.note_tab = NoteTab(self.dbstate,
|
|
|
|
self.uistate,
|
|
|
|
self.track,
|
|
|
|
self.obj.get_note_list(),
|
|
|
|
notetype=gen.lib.NoteType.MEDIA)
|
|
|
|
self._add_tab(notebook, self.note_tab)
|
|
|
|
self.track_ref_for_deletion("note_tab")
|
|
|
|
|
|
|
|
self.backref_tab = MediaBackRefList(self.dbstate,
|
|
|
|
self.uistate,
|
|
|
|
self.track,
|
|
|
|
self.db.find_backlink_handles(self.obj.handle))
|
|
|
|
self.backref_list = self._add_tab(notebook, self.backref_tab)
|
|
|
|
self.track_ref_for_deletion("backref_tab")
|
|
|
|
self.track_ref_for_deletion("backref_list")
|
2006-02-25 03:30:20 +05:30
|
|
|
|
2007-01-16 12:42:10 +05:30
|
|
|
self._setup_notebook_tabs( notebook)
|
2006-03-01 10:38:11 +05:30
|
|
|
notebook.show_all()
|
2009-05-08 01:38:27 +05:30
|
|
|
self.glade.get_object('vbox').pack_start(notebook, True)
|
2006-02-25 03:30:20 +05:30
|
|
|
|
2008-02-08 20:59:28 +05:30
|
|
|
def build_menu_names(self, person):
|
2006-11-27 02:48:30 +05:30
|
|
|
return (_('Edit Media Object'), self.get_menu_title())
|
2006-02-03 01:13:42 +05:30
|
|
|
|
2006-12-10 09:54:11 +05:30
|
|
|
def button_press_event(self, obj, event):
|
2008-02-08 20:59:28 +05:30
|
|
|
if event.button == 1 and event.type == gtk.gdk._2BUTTON_PRESS:
|
2006-12-10 09:54:11 +05:30
|
|
|
self.view_media(obj)
|
|
|
|
|
|
|
|
def view_media(self, obj):
|
|
|
|
ref_obj = self.dbstate.db.get_object_from_handle(self.obj.handle)
|
2008-04-11 03:06:35 +05:30
|
|
|
|
|
|
|
if ref_obj:
|
2009-02-01 09:51:17 +05:30
|
|
|
media_path = Utils.media_path_full(self.dbstate.db,
|
|
|
|
ref_obj.get_path())
|
2009-06-24 02:19:18 +05:30
|
|
|
open_file_with_default_application(media_path)
|
2006-12-10 09:54:11 +05:30
|
|
|
|
2008-01-06 16:21:20 +05:30
|
|
|
def select_file(self, val):
|
2008-02-12 03:57:24 +05:30
|
|
|
self.determine_mime()
|
|
|
|
path = self.file_path.get_text()
|
|
|
|
self.obj.set_path(Utils.get_unicode_path(path))
|
2008-01-06 16:21:20 +05:30
|
|
|
AddMediaObject(self.dbstate, self.uistate, self.track, self.obj,
|
|
|
|
self._update_addmedia)
|
|
|
|
|
|
|
|
def _update_addmedia(self, obj):
|
|
|
|
"""
|
|
|
|
Called when the add media dialog has been called.
|
|
|
|
This allows us to update the main form in response to
|
|
|
|
any changes: Redraw relevant fields: description, mimetype and path
|
|
|
|
"""
|
|
|
|
for obj in (self.descr_window, ):
|
|
|
|
obj.update()
|
2007-11-28 01:21:45 +05:30
|
|
|
fname = self.obj.get_path()
|
|
|
|
self.file_path.set_text(fname)
|
2008-02-12 03:57:24 +05:30
|
|
|
self.determine_mime()
|
2008-01-06 16:21:20 +05:30
|
|
|
self.draw_preview()
|
|
|
|
|
2006-03-01 10:38:11 +05:30
|
|
|
def save(self, *obj):
|
2008-01-06 16:21:20 +05:30
|
|
|
self.ok_button.set_sensitive(False)
|
2008-02-12 03:57:24 +05:30
|
|
|
|
2008-02-09 06:37:23 +05:30
|
|
|
if self.object_is_empty():
|
|
|
|
ErrorDialog(_("Cannot save media object"),
|
|
|
|
_("No data exists for this media object. Please "
|
|
|
|
"enter data or cancel the edit."))
|
|
|
|
self.ok_button.set_sensitive(True)
|
|
|
|
return
|
|
|
|
|
|
|
|
(uses_dupe_id, id) = self._uses_duplicate_id()
|
|
|
|
if uses_dupe_id:
|
|
|
|
prim_object = self.get_from_gramps_id(id)
|
|
|
|
name = prim_object.get_description()
|
|
|
|
msg1 = _("Cannot save media object. ID already exists.")
|
2009-11-18 01:44:53 +05:30
|
|
|
msg2 = _("You have attempted to use the existing Gramps ID with "
|
2008-02-09 06:37:23 +05:30
|
|
|
"value %(id)s. This value is already used by '"
|
|
|
|
"%(prim_object)s'. Please enter a different ID or leave "
|
|
|
|
"blank to get the next available ID value.") % {
|
|
|
|
'id' : id, 'prim_object' : name }
|
|
|
|
ErrorDialog(msg1, msg2)
|
|
|
|
self.ok_button.set_sensitive(True)
|
|
|
|
return
|
|
|
|
|
2008-02-12 03:57:24 +05:30
|
|
|
path = self.file_path.get_text()
|
|
|
|
self.determine_mime()
|
2006-08-26 19:48:47 +05:30
|
|
|
|
2007-11-28 01:21:45 +05:30
|
|
|
self.obj.set_path(Utils.get_unicode_path(path))
|
2006-02-03 01:13:42 +05:30
|
|
|
|
|
|
|
trans = self.db.transaction_begin()
|
2008-02-08 19:10:04 +05:30
|
|
|
if not self.obj.get_handle():
|
2008-01-06 16:21:20 +05:30
|
|
|
self.db.add_object(self.obj, trans)
|
2008-02-08 19:10:04 +05:30
|
|
|
msg = _("Add Media Object (%s)") % self.obj.get_description()
|
|
|
|
else:
|
|
|
|
if not self.obj.get_gramps_id():
|
|
|
|
self.obj.set_gramps_id(self.db.find_next_object_gramps_id())
|
|
|
|
self.db.commit_media_object(self.obj, trans)
|
|
|
|
msg = _("Edit Media Object (%s)") % self.obj.get_description()
|
|
|
|
|
|
|
|
self.db.transaction_commit(trans, msg)
|
2008-01-06 16:21:20 +05:30
|
|
|
|
|
|
|
if self.callback:
|
|
|
|
self.callback(self.obj)
|
2006-04-24 04:13:36 +05:30
|
|
|
self.close()
|
2006-02-03 01:13:42 +05:30
|
|
|
|
2006-11-26 06:59:58 +05:30
|
|
|
def _cleanup_on_exit(self):
|
|
|
|
self.backref_list.close()
|
|
|
|
|
2008-09-06 14:48:57 +05:30
|
|
|
def data_has_changed(self):
|
|
|
|
"""
|
|
|
|
A date comparison can fail incorrectly because we have made the
|
|
|
|
decision to store entered text in the date. However, there is no
|
|
|
|
entered date when importing from a XML file, so we can get an
|
|
|
|
incorrect fail.
|
|
|
|
"""
|
|
|
|
|
|
|
|
if self.db.readonly:
|
|
|
|
return False
|
|
|
|
elif self.obj.handle:
|
|
|
|
orig = self.get_from_handle(self.obj.handle)
|
|
|
|
if orig:
|
|
|
|
cmp_obj = orig
|
|
|
|
else:
|
|
|
|
cmp_obj = self.empty_object()
|
|
|
|
return cmp(cmp_obj.serialize(True)[1:],
|
|
|
|
self.obj.serialize(True)[1:]) != 0
|
|
|
|
else:
|
|
|
|
cmp_obj = self.empty_object()
|
|
|
|
return cmp(cmp_obj.serialize(True)[1:],
|
|
|
|
self.obj.serialize()[1:]) != 0
|
|
|
|
|
2009-05-21 22:49:50 +05:30
|
|
|
class DeleteMediaQuery(object):
|
2006-02-03 01:13:42 +05:30
|
|
|
|
2008-02-08 20:59:28 +05:30
|
|
|
def __init__(self, dbstate, uistate, media_handle, the_lists):
|
2007-01-23 09:07:13 +05:30
|
|
|
self.db = dbstate.db
|
|
|
|
self.uistate = uistate
|
2006-02-03 01:13:42 +05:30
|
|
|
self.media_handle = media_handle
|
|
|
|
self.the_lists = the_lists
|
|
|
|
|
|
|
|
def query_response(self):
|
|
|
|
trans = self.db.transaction_begin()
|
|
|
|
self.db.disable_signals()
|
|
|
|
|
2008-02-08 20:59:28 +05:30
|
|
|
(person_list, family_list, event_list,
|
2006-02-03 01:13:42 +05:30
|
|
|
place_list,source_list) = self.the_lists
|
|
|
|
|
|
|
|
for handle in person_list:
|
|
|
|
person = self.db.get_person_from_handle(handle)
|
|
|
|
new_list = [ photo for photo in person.get_media_list() \
|
|
|
|
if photo.get_reference_handle() != self.media_handle ]
|
|
|
|
person.set_media_list(new_list)
|
2008-02-08 20:59:28 +05:30
|
|
|
self.db.commit_person(person, trans)
|
2006-02-03 01:13:42 +05:30
|
|
|
|
|
|
|
for handle in family_list:
|
|
|
|
family = self.db.get_family_from_handle(handle)
|
|
|
|
new_list = [ photo for photo in family.get_media_list() \
|
|
|
|
if photo.get_reference_handle() != self.media_handle ]
|
|
|
|
family.set_media_list(new_list)
|
2008-02-08 20:59:28 +05:30
|
|
|
self.db.commit_family(family, trans)
|
2006-02-03 01:13:42 +05:30
|
|
|
|
|
|
|
for handle in event_list:
|
|
|
|
event = self.db.get_event_from_handle(handle)
|
|
|
|
new_list = [ photo for photo in event.get_media_list() \
|
|
|
|
if photo.get_reference_handle() != self.media_handle ]
|
|
|
|
event.set_media_list(new_list)
|
2008-02-08 20:59:28 +05:30
|
|
|
self.db.commit_event(event, trans)
|
2006-02-03 01:13:42 +05:30
|
|
|
|
|
|
|
for handle in place_list:
|
|
|
|
place = self.db.get_place_from_handle(handle)
|
|
|
|
new_list = [ photo for photo in place.get_media_list() \
|
|
|
|
if photo.get_reference_handle() != self.media_handle ]
|
|
|
|
place.set_media_list(new_list)
|
2008-02-08 20:59:28 +05:30
|
|
|
self.db.commit_place(place, trans)
|
2006-02-03 01:13:42 +05:30
|
|
|
|
|
|
|
for handle in source_list:
|
|
|
|
source = self.db.get_source_from_handle(handle)
|
|
|
|
new_list = [ photo for photo in source.get_media_list() \
|
|
|
|
if photo.get_reference_handle() != self.media_handle ]
|
|
|
|
source.set_media_list(new_list)
|
2008-02-08 20:59:28 +05:30
|
|
|
self.db.commit_source(source, trans)
|
2006-02-03 01:13:42 +05:30
|
|
|
|
|
|
|
self.db.enable_signals()
|
2008-02-08 20:59:28 +05:30
|
|
|
self.db.remove_object(self.media_handle, trans)
|
|
|
|
self.db.transaction_commit(trans, _("Remove Media Object"))
|