2007-03-13 Zsolt Foldvari <zfoldvar@users.sourceforge.net>

* src/RelLib/_Note.py: import const from MarkupText module
	* src/MarkupText.py: use actions instead of widgets on the interface;
	new helper functions
	* src/Editors/_EditNote.py: move widgets out to glade; implement new
	MarkupText buffer interface; use uimanager/toolbar for formatting
	* src/glade/gramps.glade: edit_note update



svn: r8292
This commit is contained in:
Zsolt Foldvari 2007-03-13 20:31:50 +00:00
parent 7593dbfe69
commit 63e344754f
5 changed files with 196 additions and 206 deletions

View File

@ -1,3 +1,11 @@
2007-03-13 Zsolt Foldvari <zfoldvar@users.sourceforge.net>
* src/RelLib/_Note.py: import const from MarkupText module
* src/MarkupText.py: use actions instead of widgets on the interface;
new helper functions
* src/Editors/_EditNote.py: move widgets out to glade; implement new
MarkupText buffer interface; use uimanager/toolbar for formatting
* src/glade/gramps.glade: edit_note update
2007-03-11 Brian Matherly <brian@gramps-project.org>
* src/ReportBase/_ReportDialog.py: don't catch all exceptions - we won't
get a traceback.

View File

@ -69,10 +69,10 @@ class EditNote(EditPrimary):
return RelLib.Note()
def get_menu_title(self):
if self.obj.get_handle():
title = _('Note') + ': %s' % self.obj.get_gramps_id()
else:
title = _('New Note')
if self.obj.get_handle():
title = _('Note') + ': %s' % self.obj.get_gramps_id()
else:
title = _('New Note')
return title
def _local_init(self):
@ -89,28 +89,25 @@ class EditNote(EditPrimary):
height = Config.get(Config.NOTE_HEIGHT)
self.window.set_default_size(width, height)
self.type = self.top.get_widget('type')
self.format = self.top.get_widget('format')
container = self.top.get_widget('container')
container.pack_start(self.build_interface())
container.show_all()
self.build_interface()
self.window.show_all()
def _setup_fields(self):
"""Get control widgets and attached them to Note's attributes."""
self.type_selector = MonitoredDataType(
self.top.get_widget("type"),
self.top.get_widget('type'),
self.obj.set_type,
self.obj.get_type,
self.db.readonly)
self.check = MonitoredCheckbox(
self.obj,
self.format,
self.top.get_widget('format'),
self.obj.set_format,
self.obj.get_format,
on_toggle = self.flow_changed,
readonly = self.db.readonly)
self.gid = MonitoredEntry(
self.top.get_widget('id'),
self.obj.set_gramps_id,
@ -125,118 +122,102 @@ class EditNote(EditPrimary):
self.db.get_marker_types())
def _connect_signals(self):
"""
Connects any signals that need to be connected. Called by the
init routine of the base class (_EditPrimary).
"""Connects any signals that need to be connected.
Called by the init routine of the base class (_EditPrimary).
"""
self.define_ok_button(self.top.get_widget('ok'),self.save)
self.define_cancel_button(self.top.get_widget('cancel'))
self.define_help_button(self.top.get_widget('help'), '')
def build_interface(self):
BUTTON = [(_('Italic'),gtk.STOCK_ITALIC,'<i>i</i>','<Control>I'),
(_('Bold'),gtk.STOCK_BOLD,'<b>b</b>','<Control>B'),
(_('Underline'),gtk.STOCK_UNDERLINE,'<u>u</u>','<Control>U'),
#('Separator', None, None, None),
]
FORMAT_TOOLBAR = '''
<ui>
<toolbar name="ToolBar">
<toolitem action="italic"/>
<toolitem action="bold"/>
<toolitem action="underline"/>
</toolbar>
</ui>
'''
format_actions = [
('<i>i</i>','<Control>I',
('italic',_('Italic'),_('Italic'),gtk.STOCK_ITALIC)),
('<b>b</b>','<Control>B',
('bold',_('Bold'),_('Bold'),gtk.STOCK_BOLD)),
('<u>u</u>','<Control>U',
('underline',_('Underline'),_('Underline'),gtk.STOCK_UNDERLINE)),
]
vbox = gtk.VBox()
buffer = EditorBuffer()
self.text = gtk.TextView()
self.text.set_accepts_tab(True)
self.text = self.top.get_widget('text')
self.text.set_editable(not self.dbstate.db.readonly)
self.spellcheck = Spell.Spell(self.text)
self.text.set_buffer(buffer)
if self.obj and self.obj.get_format():
self.format.set_active(True)
self.text.set_wrap_mode(gtk.WRAP_NONE)
else:
self.format.set_active(False)
self.text.set_wrap_mode(gtk.WRAP_WORD)
scroll = gtk.ScrolledWindow()
scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
scroll.set_shadow_type(gtk.SHADOW_IN)
scroll.add(self.text)
self.buf = EditorBuffer()
self.text.set_buffer(self.buf)
# create a formatting toolbar and pass the actions
# together with the related markup tag to the buffer
if not self.dbstate.db.readonly:
uimanager = gtk.UIManager()
accelgroup = uimanager.get_accel_group()
self.window.add_accel_group(accelgroup)
action_group = gtk.ActionGroup('Format')
for markup, accel, action_desc in format_actions:
action = gtk.ToggleAction(*action_desc)
action_group.add_action_with_accel(action, accel)
# FIXME why are these needed?
# Shouldn't uimanager do it automatically!?
action.set_accel_group(accelgroup)
action.connect_accelerator()
#
buffer.setup_action_from_xml(action, markup)
uimanager.insert_action_group(action_group, 0)
uimanager.add_ui_from_string(FORMAT_TOOLBAR)
uimanager.ensure_update()
toolbar = uimanager.get_widget('/ToolBar')
toolbar.set_style(gtk.TOOLBAR_ICONS)
vbox = self.top.get_widget('container')
vbox.pack_start(toolbar, False)
# setup initial values for textview and buffer
if self.obj:
self.empty = False
self.buf.set_text(self.obj.get(markup=True))
self.flow_changed(self.obj.get_format())
buffer.set_text(self.obj.get(markup=True))
log.debug("Initial Note: %s" % buffer.get_text())
else:
self.empty = True
if not self.dbstate.db.readonly:
self.accelerator = {}
hbox = gtk.HBox()
hbox.set_spacing(0)
hbox.set_border_width(0)
vbox.pack_start(hbox, False)
# connection to buffer signals must be after the initial values are set
buffer.connect('changed', self.update_note)
buffer.connect_after('apply-tag', self.update_note)
buffer.connect_after('remove-tag', self.update_note)
tooltips = gtk.Tooltips()
for tip, stock, markup, accel in BUTTON:
if markup:
button = gtk.ToggleButton()
image = gtk.Image()
image.set_from_stock(stock, gtk.ICON_SIZE_MENU)
button.set_image(image)
button.set_relief(gtk.RELIEF_NONE)
tooltips.set_tip(button, tip)
self.buf.setup_widget_from_xml(button, markup)
key, mod = gtk.accelerator_parse(accel)
self.accelerator[(key, mod)] = button
hbox.pack_start(button, False)
else:
hbox.pack_start(gtk.VSeparator(), False)
vbox.pack_start(scroll, True)
vbox.set_spacing(6)
vbox.set_border_width(6)
if self.dbstate.db.readonly:
self.text.set_editable(False)
return vbox
# Accelerator dictionary used for formatting shortcuts
# key: tuple(key, modifier)
# value: widget, to emit 'activate' signal on
self.text.connect('key-press-event', self._on_key_press_event)
self.spellcheck = Spell.Spell(self.text)
self.format.connect('toggled', self.flow_changed)
self.buf.connect('changed', self.update)
self.buf.connect_after('apply-tag', self.update)
self.buf.connect_after('remove-tag', self.update)
#self.rebuild()
return vbox
def _on_key_press_event(self, widget, event):
#log.debug("Key %s (%d) was pressed on %s" %
#(gtk.gdk.keyval_name(event.keyval), event.keyval, widget))
key = event.keyval
mod = event.state
if self.accelerator.has_key((key, mod)):
self.accelerator[(key, mod)].emit('activate')
return True
def update(self, obj, *args):
def update_note(self, buffer, *args):
"""Update the Note object with current value.
This happens after each change in the text or the formatting.
"""
if self.obj:
start = self.buf.get_start_iter()
stop = self.buf.get_end_iter()
text = self.buf.get_text(start, stop)
start = buffer.get_start_iter()
stop = buffer.get_end_iter()
text = buffer.get_text(start, stop)
self.obj.set(text)
else:
print "NOTE OBJ DOES NOT EXIST"
log.debug("NOTE OBJ DOES NOT EXIST")
return False
def flow_changed(self, obj):
if obj.get_active():
def flow_changed(self, active):
if active:
self.text.set_wrap_mode(gtk.WRAP_NONE)
self.obj.set_format(True)
else:
self.text.set_wrap_mode(gtk.WRAP_WORD)
self.obj.set_format(False)
def save(self, *obj):
"""

View File

@ -53,12 +53,29 @@ log = logging.getLogger(".MarkupText")
#-------------------------------------------------------------------------
import gtk
#-------------------------------------------------------------------------
#
# Constants
#
#-------------------------------------------------------------------------
ROOT_ELEMENT = 'gramps'
ROOT_START_TAG = '<' + ROOT_ELEMENT + '>'
ROOT_END_TAG = '</' + ROOT_ELEMENT + '>'
LEN_ROOT_START_TAG = len(ROOT_START_TAG)
LEN_ROOT_END_TAG = len(ROOT_END_TAG)
def is_gramps_markup(text):
return (text[:LEN_ROOT_START_TAG] == ROOT_START_TAG and
text[-LEN_ROOT_END_TAG:] == ROOT_END_TAG)
def clear_root_tags(text):
return text[LEN_ROOT_START_TAG:len(text)-LEN_ROOT_END_TAG]
class MarkupParser(ContentHandler):
"""A simple ContentHandler class to parse Gramps markup'ed text.
Use it with xml.sax.parse() or xml.sax.parseString(). A root tag, 'gramps',
is required. Parsing result can be obtained via the public attributes of
Use it with xml.sax.parse() or xml.sax.parseString(). A root tag is
required. Parsing result can be obtained via the public attributes of
the class:
@attr content: clean text
@attr type: str
@ -77,7 +94,7 @@ class MarkupParser(ContentHandler):
def startElement(self, name, attrs):
if not self._open_document:
if name == 'gramps':
if name == ROOT_ELEMENT:
self._open_document = True
else:
raise SAXParseException('Root element missing')
@ -89,7 +106,7 @@ class MarkupParser(ContentHandler):
def endElement(self, name):
# skip root element
if name == 'gramps':
if name == ROOT_ELEMENT:
return
for e in self._open_elements:
@ -221,7 +238,7 @@ class MarkupWriter:
def generate(self, text, elements):
# reset output and start root element
self._output.truncate(0)
self._writer.startElement('gramps', self._attrs)
self._writer.startElement(ROOT_ELEMENT, self._attrs)
# split the elements to events
events = self._elements_to_events(elements)
@ -241,7 +258,7 @@ class MarkupWriter:
self._writer.characters(text[last_pos:])
# close root element and end doc
self._writer.endElement('gramps')
self._writer.endElement(ROOT_ELEMENT)
self._writer.endDocument()
# copy result
@ -444,6 +461,7 @@ class EditorBuffer(MarkupBuffer):
if normal_button:
normal_button.connect('clicked',lambda *args: self.remove_all_tags())
self.tag_widgets = {}
self.tag_actions = {}
self.internal_toggle = False
self.insert = self.get_insert()
for widg, name in toggle_widget_alist:
@ -462,7 +480,8 @@ class EditorBuffer(MarkupBuffer):
if old_itr != insert_itr:
# Use the state of our widgets to determine what
# properties to apply...
for tag, w in self.tag_widgets.items():
for tag, w in self.tag_actions.items():
##for tag, w in self.tag_widgets.items():
if w.get_active():
self.apply_tag(tag, old_itr, insert_itr)
@ -474,7 +493,8 @@ class EditorBuffer(MarkupBuffer):
self._in_mark_set = True
if mark.get_name() == 'insert':
for tag,widg in self.tag_widgets.items():
##for tag,widg in self.tag_widgets.items():
for tag,widg in self.tag_actions.items():
active = True
if not iter.has_tag(tag):
active = False
@ -503,10 +523,12 @@ class EditorBuffer(MarkupBuffer):
def setup_widget_from_xml(self, widg, xmlstring):
"""Setup widget from an xml markup string."""
try:
parseString("<gramps>%s</gramps>" % xmlstring, self.parser)
parseString((ROOT_START_TAG + '%s' + ROOT_END_TAG) % xmlstring,
self.parser)
except:
log.error('"%s" is not a valid Gramps XML format.' % xmlstring)
# whatever is included we'll use only the first element
(start, end), name, attrs = self.parser.elements[0]
return self.setup_widget(widg, name)
@ -517,92 +539,24 @@ class EditorBuffer(MarkupBuffer):
self.tag_widgets[tag] = widg
return widg.connect('toggled', self._toggle, tag)
def setup_action_from_xml(self, action, xmlstring):
"""Setup action from an xml markup string."""
try:
parseString((ROOT_START_TAG + '%s' + ROOT_END_TAG) % xmlstring,
self.parser)
except:
log.error('"%s" is not a valid Gramps XML format.' % xmlstring)
# whatever is included we'll use only the first element
(start, end), name, attrs = self.parser.elements[0]
return self.setup_action(action, name)
def setup_action(self, action, name):
"""Setup action from Gramps tag name."""
tag = self.get_tag_from_element(name)
self.tag_actions[tag] = action
return action.connect('activate', self._toggle, tag)
if gtk.pygtk_version < (2,8,0):
gobject.type_register(EditorBuffer)
if __name__ == '__main__':
import sys
def main(args):
win = gtk.Window()
win.set_title('MarkupBuffer test window')
win.set_position(gtk.WIN_POS_CENTER)
def cb(window, event):
gtk.main_quit()
win.connect('delete-event', cb)
accel_group = gtk.AccelGroup()
win.add_accel_group(accel_group)
vbox = gtk.VBox()
win.add(vbox)
text = gtk.TextView()
text.set_accepts_tab(True)
flowed = gtk.RadioButton(None, 'Flowed')
format = gtk.RadioButton(flowed, 'Formatted')
#if self.note_obj and self.note_obj.get_format():
#self.format.set_active(True)
#self.text.set_wrap_mode(gtk.WRAP_NONE)
#else:
#self.flowed.set_active(True)
#self.text.set_wrap_mode(gtk.WRAP_WORD)
#self.spellcheck = Spell.Spell(self.text)
#flowed.connect('toggled', flow_changed)
scroll = gtk.ScrolledWindow()
scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
scroll.add(text)
vbox.pack_start(scroll, True)
vbox.set_spacing(6)
vbox.set_border_width(6)
hbox = gtk.HBox()
hbox.set_spacing(12)
hbox.set_border_width(6)
hbox.pack_start(flowed, False)
hbox.pack_start(format, False)
vbox.pack_start(hbox, False)
#self.pack_start(vbox, True)
buf = EditorBuffer()
text.set_buffer(buf)
tooltips = gtk.Tooltips()
for tip,stock,font,accel in [('Italic',gtk.STOCK_ITALIC,'<i>i</i>','<Control>I'),
('Bold',gtk.STOCK_BOLD,'<b>b</b>','<Control>B'),
('Underline',gtk.STOCK_UNDERLINE,'<u>u</u>','<Control>U'),
]:
button = gtk.ToggleButton()
image = gtk.Image()
image.set_from_stock(stock, gtk.ICON_SIZE_MENU)
button.set_image(image)
tooltips.set_tip(button, tip)
button.set_relief(gtk.RELIEF_NONE)
buf.setup_widget_from_xml(button,font)
key, mod = gtk.accelerator_parse(accel)
button.add_accelerator('activate', accel_group,
key, mod, gtk.ACCEL_VISIBLE)
hbox.pack_start(button, False)
buf.set_text('<gramps>'
'<b>Bold</b>. <i>Italic</i>. <u>Underline</u>.'
'</gramps>')
win.show_all()
gtk.main()
stderrh = logging.StreamHandler(sys.stderr)
stderrh.setLevel(logging.DEBUG)
log = logging.getLogger()
log.setLevel(logging.DEBUG)
log.addHandler(stderrh)
sys.exit(main(sys.argv))

View File

@ -40,6 +40,7 @@ import re
#-------------------------------------------------------------------------
from _BasicPrimaryObject import BasicPrimaryObject
from _NoteType import NoteType
from MarkupText import ROOT_START_TAG, LEN_ROOT_START_TAG
#-------------------------------------------------------------------------
#
@ -114,7 +115,7 @@ class Note(BasicPrimaryObject):
"""
text = self.text
if not markup and text[0:8] == '<gramps>':
if not markup and text[0:LEN_ROOT_START_TAG] == ROOT_START_TAG:
text = self.delete_tags(text)
return text

View File

@ -14983,7 +14983,6 @@ Very High</property>
</widget>
<widget class="GtkDialog" id="edit_note">
<property name="visible">True</property>
<property name="title" translatable="yes"></property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
<property name="window_position">GTK_WIN_POS_NONE</property>
@ -15008,7 +15007,7 @@ Very High</property>
<property name="spacing">0</property>
<child internal-child="action_area">
<widget class="GtkHButtonBox" id="dialog-action_area25">
<widget class="GtkHButtonBox" id="action_area">
<property name="visible">True</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
@ -15037,6 +15036,19 @@ Very High</property>
<property name="response_id">-5</property>
</widget>
</child>
<child>
<widget class="GtkButton" id="help">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-help</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<property name="response_id">-11</property>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
@ -15054,12 +15066,46 @@ Very High</property>
<child>
<widget class="GtkVBox" id="container">
<property name="border_width">6</property>
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<property name="spacing">6</property>
<child>
<placeholder/>
<widget class="GtkScrolledWindow" id="scroll">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
<widget class="GtkTextView" id="text">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">True</property>
<property name="overwrite">False</property>
<property name="accepts_tab">True</property>
<property name="justification">GTK_JUSTIFY_LEFT</property>
<property name="wrap_mode">GTK_WRAP_NONE</property>
<property name="cursor_visible">True</property>
<property name="pixels_above_lines">0</property>
<property name="pixels_below_lines">0</property>
<property name="pixels_inside_wrap">0</property>
<property name="left_margin">0</property>
<property name="right_margin">0</property>
<property name="indent">0</property>
<property name="text" translatable="yes"></property>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="pack_type">GTK_PACK_END</property>
</packing>
</child>
</widget>
<packing>