2008-04-04 17:21:49 +05:30
|
|
|
#
|
|
|
|
# Gramps - a GTK+/GNOME based genealogy program
|
|
|
|
#
|
|
|
|
# Copyright (C) 2008 Zsolt Foldvari
|
|
|
|
#
|
|
|
|
# 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$
|
|
|
|
|
|
|
|
"Text editor subclassed from gtk.TextView handling L{StyledText}."
|
|
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
|
|
#
|
|
|
|
# Python modules
|
|
|
|
#
|
|
|
|
#-------------------------------------------------------------------------
|
2008-04-05 01:39:26 +05:30
|
|
|
from gettext import gettext as _
|
|
|
|
|
2008-04-04 17:21:49 +05:30
|
|
|
import logging
|
2008-05-09 18:20:04 +05:30
|
|
|
_LOG = logging.getLogger(".widgets.styledtexteditor")
|
2008-04-04 17:21:49 +05:30
|
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
|
|
#
|
|
|
|
# GTK libraries
|
|
|
|
#
|
|
|
|
#-------------------------------------------------------------------------
|
|
|
|
import gobject
|
|
|
|
import gtk
|
|
|
|
from pango import UNDERLINE_SINGLE
|
|
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
|
|
#
|
|
|
|
# GRAMPS modules
|
|
|
|
#
|
|
|
|
#-------------------------------------------------------------------------
|
2008-04-23 14:37:02 +05:30
|
|
|
from gen.lib import StyledTextTagType
|
2008-05-09 01:10:56 +05:30
|
|
|
from widgets import (StyledTextBuffer, ALLOWED_STYLES,
|
|
|
|
MATCH_START, MATCH_END,
|
2008-05-09 18:20:04 +05:30
|
|
|
MATCH_FLAVOR, MATCH_STRING,
|
|
|
|
ComboToolAction, SpringSeparatorAction)
|
2008-04-04 17:21:49 +05:30
|
|
|
from Spell import Spell
|
|
|
|
from GrampsDisplay import url as display_url
|
|
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
|
|
#
|
|
|
|
# Constants
|
|
|
|
#
|
|
|
|
#-------------------------------------------------------------------------
|
|
|
|
HAND_CURSOR = gtk.gdk.Cursor(gtk.gdk.HAND2)
|
|
|
|
REGULAR_CURSOR = gtk.gdk.Cursor(gtk.gdk.XTERM)
|
|
|
|
|
|
|
|
FORMAT_TOOLBAR = '''
|
|
|
|
<ui>
|
|
|
|
<toolbar name="ToolBar">
|
2008-04-23 14:37:02 +05:30
|
|
|
<toolitem action="%d"/>
|
|
|
|
<toolitem action="%d"/>
|
|
|
|
<toolitem action="%d"/>
|
|
|
|
<toolitem action="%d"/>
|
|
|
|
<toolitem action="%d"/>
|
|
|
|
<toolitem action="%d"/>
|
|
|
|
<toolitem action="%d"/>
|
2008-04-23 18:05:44 +05:30
|
|
|
<toolitem action="spring"/>
|
2008-04-04 17:21:49 +05:30
|
|
|
<toolitem action="clear"/>
|
|
|
|
</toolbar>
|
|
|
|
</ui>
|
2008-04-23 14:37:02 +05:30
|
|
|
''' % (StyledTextTagType.ITALIC,
|
|
|
|
StyledTextTagType.BOLD,
|
|
|
|
StyledTextTagType.UNDERLINE,
|
|
|
|
StyledTextTagType.FONTFACE,
|
|
|
|
StyledTextTagType.FONTSIZE,
|
|
|
|
StyledTextTagType.FONTCOLOR,
|
|
|
|
StyledTextTagType.HIGHLIGHT,
|
|
|
|
)
|
|
|
|
|
|
|
|
FONT_SIZES = [8, 9, 10, 11, 12, 13, 14, 16, 18, 20, 22,
|
|
|
|
24, 26, 28, 32, 36, 40, 48, 56, 64, 72]
|
2008-04-04 17:21:49 +05:30
|
|
|
|
|
|
|
USERCHARS = "-A-Za-z0-9"
|
|
|
|
PASSCHARS = "-A-Za-z0-9,?;.:/!%$^*&~\"#'"
|
|
|
|
HOSTCHARS = "-A-Za-z0-9"
|
|
|
|
PATHCHARS = "-A-Za-z0-9_$.+!*(),;:@&=?/~#%"
|
|
|
|
#SCHEME = "(news:|telnet:|nntp:|file:/|https?:|ftps?:|webcal:)"
|
|
|
|
SCHEME = "(file:/|https?:|ftps?:|webcal:)"
|
|
|
|
USER = "[" + USERCHARS + "]+(:[" + PASSCHARS + "]+)?"
|
|
|
|
URLPATH = "/[" + PATHCHARS + "]*[^]'.}>) \t\r\n,\\\"]"
|
|
|
|
|
|
|
|
(GENURL, HTTP, MAIL) = range(3)
|
|
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
|
|
#
|
|
|
|
# StyledTextEditor
|
|
|
|
#
|
|
|
|
#-------------------------------------------------------------------------
|
|
|
|
class StyledTextEditor(gtk.TextView):
|
2008-04-05 01:39:26 +05:30
|
|
|
"""StyledTextEditor is an enhanced gtk.TextView to edit L{StyledText}.
|
|
|
|
|
|
|
|
StyledTextEditor is a gui object for L{StyledTextBuffer}. It offers
|
|
|
|
L{set_text} and L{get_text} convenience methods to set and get the
|
|
|
|
buffer's text.
|
|
|
|
|
|
|
|
StyledTextEditor provides a formatting toolbar, which can be retrieved
|
|
|
|
by the L{get_toolbar} method.
|
|
|
|
|
|
|
|
StyledTextEdit defines a new signal: 'match-changed', which is raised
|
|
|
|
whenever the mouse cursor reaches or leaves a matched string in the text.
|
|
|
|
The feature uses the regexp pattern mathing mechanism L{StyledTextBuffer}.
|
|
|
|
The signal's default handler highlights the URL strings. URL's can be
|
|
|
|
followed from the editor's popup menu.
|
|
|
|
|
|
|
|
@ivar last_match: previously matched string, used for generating the
|
|
|
|
'match-changed' signal.
|
|
|
|
@type last_match: tuple or None
|
|
|
|
@ivar match: currently matched string, used for generating the
|
|
|
|
'match-changed' signal.
|
|
|
|
@type match: tuple or None
|
|
|
|
@ivar show_unicode: stores the user's gtk.settings['gtk-show-unicode-menu']
|
|
|
|
value.
|
|
|
|
@type show_unicode: bool
|
|
|
|
@ivar spellcheck: spell checker object created for the editor instance.
|
|
|
|
@type spellcheck: L{Spell}
|
|
|
|
@ivar textbuffer: text buffer assigned to the edit instance.
|
|
|
|
@type textbuffer: L{StyledTextBuffer}
|
|
|
|
@ivar toolbar: toolbar to be used for text formatting.
|
|
|
|
@type toolbar: gtk.Toolbar
|
|
|
|
@ivar url_match: stores the matched URL and other mathing parameters.
|
|
|
|
@type url_match: tuple or None
|
|
|
|
|
2008-04-04 17:21:49 +05:30
|
|
|
"""
|
|
|
|
__gtype_name__ = 'StyledTextEditor'
|
|
|
|
|
|
|
|
__gsignals__ = {
|
|
|
|
'match-changed': (gobject.SIGNAL_RUN_FIRST,
|
|
|
|
gobject.TYPE_NONE, #return value
|
|
|
|
(gobject.TYPE_PYOBJECT,)), # arguments
|
|
|
|
}
|
|
|
|
|
|
|
|
def __init__(self):
|
2008-04-05 01:39:26 +05:30
|
|
|
"""Setup initial instance variable values."""
|
2008-04-04 17:21:49 +05:30
|
|
|
self.textbuffer = StyledTextBuffer()
|
2008-04-23 14:37:02 +05:30
|
|
|
self.textbuffer.connect('style-changed', self._on_buffer_style_changed)
|
2008-04-04 17:21:49 +05:30
|
|
|
gtk.TextView.__init__(self, self.textbuffer)
|
|
|
|
|
|
|
|
self.match = None
|
|
|
|
self.last_match = None
|
|
|
|
self._init_url_match()
|
2008-04-05 01:39:26 +05:30
|
|
|
self.url_match = None
|
2008-04-04 17:21:49 +05:30
|
|
|
|
2008-04-05 01:39:26 +05:30
|
|
|
self.toolbar = self._create_toolbar()
|
2008-04-04 17:21:49 +05:30
|
|
|
self.spellcheck = Spell(self)
|
2008-04-23 14:37:02 +05:30
|
|
|
self._internal_style_change = False
|
2008-04-04 17:21:49 +05:30
|
|
|
|
2008-04-05 01:39:26 +05:30
|
|
|
self._connect_signals()
|
|
|
|
|
2008-04-04 17:21:49 +05:30
|
|
|
# we want to disable the unicode menu in the popup
|
|
|
|
settings = gtk.settings_get_default()
|
|
|
|
self.show_unicode = settings.get_property('gtk-show-unicode-menu')
|
|
|
|
settings.set_property('gtk-show-unicode-menu', False)
|
|
|
|
|
|
|
|
# virtual methods
|
|
|
|
|
|
|
|
def do_match_changed(self, match):
|
|
|
|
"""Default signal handler.
|
|
|
|
|
|
|
|
URL highlighting.
|
|
|
|
|
2008-04-05 01:39:26 +05:30
|
|
|
@param match: the new match parameters
|
|
|
|
@type match: tuple or None
|
|
|
|
|
2008-04-04 17:21:49 +05:30
|
|
|
@attention: Do not override the handler, but connect to the signal.
|
|
|
|
|
|
|
|
"""
|
|
|
|
window = self.get_window(gtk.TEXT_WINDOW_TEXT)
|
|
|
|
start, end = self.textbuffer.get_bounds()
|
|
|
|
self.textbuffer.remove_tag_by_name('hyperlink', start, end)
|
|
|
|
if match and (match[MATCH_FLAVOR] in (GENURL, HTTP, MAIL)):
|
|
|
|
start_offset = match[MATCH_START]
|
|
|
|
end_offset = match[MATCH_END]
|
|
|
|
|
|
|
|
start = self.textbuffer.get_iter_at_offset(start_offset)
|
|
|
|
end = self.textbuffer.get_iter_at_offset(end_offset)
|
|
|
|
|
|
|
|
self.textbuffer.apply_tag_by_name('hyperlink', start, end)
|
|
|
|
window.set_cursor(HAND_CURSOR)
|
|
|
|
self.url_match = match
|
|
|
|
else:
|
|
|
|
window.set_cursor(REGULAR_CURSOR)
|
|
|
|
self.url_match = None
|
|
|
|
|
|
|
|
def on_unrealize(self, widget):
|
2008-04-05 01:39:26 +05:30
|
|
|
"""Signal handler.
|
2008-04-04 17:21:49 +05:30
|
|
|
|
|
|
|
Set the default Gtk settings back before leaving.
|
|
|
|
|
|
|
|
"""
|
|
|
|
settings = gtk.settings_get_default()
|
|
|
|
settings.set_property('gtk-show-unicode-menu', self.show_unicode)
|
|
|
|
|
|
|
|
def on_key_press_event(self, widget, event):
|
2008-04-05 01:39:26 +05:30
|
|
|
"""Signal handler.
|
|
|
|
|
2008-04-23 14:37:02 +05:30
|
|
|
Handle formatting shortcuts.
|
2008-04-05 01:39:26 +05:30
|
|
|
|
|
|
|
"""
|
2008-04-23 14:37:02 +05:30
|
|
|
for accel in self.action_accels.keys():
|
|
|
|
key, mod = gtk.accelerator_parse(accel)
|
|
|
|
if (event.keyval, event.state) == (key, mod):
|
|
|
|
action_name = self.action_accels[accel]
|
|
|
|
action = self.action_group.get_action(action_name)
|
|
|
|
action.activate()
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
2008-04-04 17:21:49 +05:30
|
|
|
def on_insert_at_cursor(self, widget, string):
|
2008-04-05 01:39:26 +05:30
|
|
|
"""Signal handler. for debugging only."""
|
2008-04-04 17:21:49 +05:30
|
|
|
_LOG.debug("Textview insert '%s'" % string)
|
|
|
|
|
2008-04-05 01:39:26 +05:30
|
|
|
def on_delete_from_cursor(self, widget, mode, count):
|
|
|
|
"""Signal handler. for debugging only."""
|
|
|
|
_LOG.debug("Textview delete type %d count %d" % (mode, count))
|
2008-04-04 17:21:49 +05:30
|
|
|
|
|
|
|
def on_paste_clipboard(self, widget):
|
2008-04-05 01:39:26 +05:30
|
|
|
"""Signal handler. for debugging only."""
|
2008-04-04 17:21:49 +05:30
|
|
|
_LOG.debug("Textview paste clipboard")
|
|
|
|
|
|
|
|
def on_motion_notify_event(self, widget, event):
|
2008-04-05 01:39:26 +05:30
|
|
|
"""Signal handler.
|
|
|
|
|
|
|
|
As the mouse cursor moves the handler checks if there's a new
|
|
|
|
regexp match at the new location. If match changes the
|
|
|
|
'match-changed' signal is raised.
|
|
|
|
|
|
|
|
"""
|
2008-04-04 17:21:49 +05:30
|
|
|
x, y = self.window_to_buffer_coords(gtk.TEXT_WINDOW_WIDGET,
|
|
|
|
int(event.x), int(event.y))
|
2008-04-05 01:39:26 +05:30
|
|
|
iter_at_location = self.get_iter_at_location(x, y)
|
|
|
|
self.match = self.textbuffer.match_check(iter_at_location.get_offset())
|
2008-04-04 17:21:49 +05:30
|
|
|
|
|
|
|
if self.match != self.last_match:
|
|
|
|
self.emit('match-changed', self.match)
|
|
|
|
|
|
|
|
self.last_match = self.match
|
|
|
|
|
|
|
|
self.window.get_pointer()
|
|
|
|
return False
|
|
|
|
|
|
|
|
def on_button_press_event(self, widget, event):
|
2008-04-05 01:39:26 +05:30
|
|
|
"""Signal handler.
|
|
|
|
|
|
|
|
Handles the <CTRL> + Left click over a URL match.
|
|
|
|
|
|
|
|
"""
|
2008-04-04 17:21:49 +05:30
|
|
|
if ((event.type == gtk.gdk.BUTTON_PRESS) and
|
|
|
|
(event.button == 1) and
|
|
|
|
(event.state and gtk.gdk.CONTROL_MASK) and
|
|
|
|
(self.url_match)):
|
|
|
|
|
|
|
|
flavor = self.url_match[MATCH_FLAVOR]
|
|
|
|
url = self.url_match[MATCH_STRING]
|
|
|
|
self._open_url_cb(None, url, flavor)
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
def on_populate_popup(self, widget, menu):
|
2008-04-05 01:39:26 +05:30
|
|
|
"""Signal handler.
|
2008-04-04 17:21:49 +05:30
|
|
|
|
2008-04-05 01:39:26 +05:30
|
|
|
Insert extra menuitems:
|
2008-04-04 17:21:49 +05:30
|
|
|
1. Insert language selector submenu for spell checking.
|
|
|
|
2. Insert extra menus depending on ULR match result.
|
|
|
|
|
|
|
|
"""
|
2008-04-05 01:39:26 +05:30
|
|
|
# spell checker submenu
|
2008-04-04 17:21:49 +05:30
|
|
|
spell_menu = gtk.MenuItem(_('Spell'))
|
|
|
|
spell_menu.set_submenu(self._create_spell_menu())
|
|
|
|
spell_menu.show_all()
|
|
|
|
menu.prepend(spell_menu)
|
|
|
|
|
2008-04-05 01:39:26 +05:30
|
|
|
# url menu items
|
2008-04-04 17:21:49 +05:30
|
|
|
if self.url_match:
|
|
|
|
flavor = self.url_match[MATCH_FLAVOR]
|
|
|
|
url = self.url_match[MATCH_STRING]
|
|
|
|
|
|
|
|
if flavor == MAIL:
|
|
|
|
open_menu = gtk.MenuItem(_('_Send Mail To...'))
|
|
|
|
copy_menu = gtk.MenuItem(_('Copy _E-mail Address'))
|
|
|
|
else:
|
|
|
|
open_menu = gtk.MenuItem(_('_Open Link'))
|
|
|
|
copy_menu = gtk.MenuItem(_('Copy _Link Address'))
|
|
|
|
|
|
|
|
copy_menu.connect('activate', self._copy_url_cb, url, flavor)
|
|
|
|
copy_menu.show()
|
|
|
|
menu.prepend(copy_menu)
|
|
|
|
|
|
|
|
open_menu.connect('activate', self._open_url_cb, url, flavor)
|
|
|
|
open_menu.show()
|
|
|
|
menu.prepend(open_menu)
|
|
|
|
|
|
|
|
# private methods
|
|
|
|
|
|
|
|
def _connect_signals(self):
|
2008-04-05 01:39:26 +05:30
|
|
|
"""Connect to several signals of the super class gtk.TextView."""
|
2008-04-04 17:21:49 +05:30
|
|
|
self.connect('key-press-event', self.on_key_press_event)
|
|
|
|
self.connect('insert-at-cursor', self.on_insert_at_cursor)
|
|
|
|
self.connect('delete-from-cursor', self.on_delete_from_cursor)
|
|
|
|
self.connect('paste-clipboard', self.on_paste_clipboard)
|
|
|
|
self.connect('motion-notify-event', self.on_motion_notify_event)
|
|
|
|
self.connect('button-press-event', self.on_button_press_event)
|
|
|
|
self.connect('populate-popup', self.on_populate_popup)
|
|
|
|
self.connect('unrealize', self.on_unrealize)
|
|
|
|
|
|
|
|
def _create_toolbar(self):
|
2008-04-05 01:39:26 +05:30
|
|
|
"""Create a formatting toolbar.
|
|
|
|
|
2008-04-23 14:37:02 +05:30
|
|
|
@returns: toolbar containing text formatting toolitems.
|
2008-04-05 01:39:26 +05:30
|
|
|
@returntype: gtk.Toolbar
|
|
|
|
|
|
|
|
"""
|
2008-04-23 14:37:02 +05:30
|
|
|
# define the actions...
|
|
|
|
# ...first the toggle actions, which have a ToggleToolButton as proxy
|
|
|
|
format_toggle_actions = [
|
|
|
|
(str(StyledTextTagType.ITALIC), gtk.STOCK_ITALIC, None, None,
|
|
|
|
_('Italic'), self._on_toggle_action_activate),
|
|
|
|
(str(StyledTextTagType.BOLD), gtk.STOCK_BOLD, None, None,
|
|
|
|
_('Bold'), self._on_toggle_action_activate),
|
|
|
|
(str(StyledTextTagType.UNDERLINE), gtk.STOCK_UNDERLINE, None, None,
|
|
|
|
_('Underline'), self._on_toggle_action_activate),
|
|
|
|
]
|
|
|
|
|
|
|
|
self.toggle_actions = [action[0] for action in format_toggle_actions]
|
|
|
|
|
|
|
|
# ...then the normal actions, which have a ToolButton as proxy
|
|
|
|
format_actions = [
|
|
|
|
(str(StyledTextTagType.FONTCOLOR), 'gramps-font-color', None, None,
|
|
|
|
_('Font Color'), self._on_action_activate),
|
|
|
|
(str(StyledTextTagType.HIGHLIGHT), 'gramps-font-bgcolor', None, None,
|
|
|
|
_('Background Color'), self._on_action_activate),
|
|
|
|
('clear', gtk.STOCK_CLEAR, None, None,
|
|
|
|
_('Clear Markup'), self._format_clear_cb),
|
|
|
|
]
|
|
|
|
|
|
|
|
# ...last the custom actions, which have custom proxies
|
2008-05-09 18:20:04 +05:30
|
|
|
items = [f.get_name() for f in self.get_pango_context().list_families()]
|
|
|
|
default = StyledTextTagType.STYLE_DEFAULT[StyledTextTagType.FONTFACE]
|
2008-04-23 14:37:02 +05:30
|
|
|
fontface_action = ComboToolAction(str(StyledTextTagType.FONTFACE),
|
|
|
|
_("Font family"),
|
2008-05-09 18:20:04 +05:30
|
|
|
_("Font family"),
|
|
|
|
None,
|
|
|
|
items,
|
|
|
|
str(default),
|
|
|
|
editable=False)
|
|
|
|
fontface_action.connect('activate', self._on_comboaction_activate)
|
|
|
|
|
|
|
|
items = [str(size) for size in FONT_SIZES]
|
|
|
|
default = StyledTextTagType.STYLE_DEFAULT[StyledTextTagType.FONTSIZE]
|
2008-04-23 14:37:02 +05:30
|
|
|
fontsize_action = ComboToolAction(str(StyledTextTagType.FONTSIZE),
|
|
|
|
_("Font size"),
|
2008-05-09 18:20:04 +05:30
|
|
|
_("Font size"),
|
|
|
|
None,
|
|
|
|
items,
|
|
|
|
str(default),
|
|
|
|
sortable=False,
|
|
|
|
validator=is_valid_fontsize)
|
|
|
|
fontsize_action.connect('activate', self._on_comboaction_activate)
|
|
|
|
|
2008-04-23 18:05:44 +05:30
|
|
|
spring = SpringSeparatorAction("spring", "", "", None)
|
|
|
|
|
|
|
|
# action accelerators
|
2008-04-23 14:37:02 +05:30
|
|
|
self.action_accels = {
|
2008-05-09 18:20:04 +05:30
|
|
|
'<Control>i': str(StyledTextTagType.ITALIC),
|
|
|
|
'<Control>b': str(StyledTextTagType.BOLD),
|
|
|
|
'<Control>u': str(StyledTextTagType.UNDERLINE),
|
2008-04-23 14:37:02 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
# create the action group and insert all the actions
|
|
|
|
self.action_group = gtk.ActionGroup('Format')
|
|
|
|
self.action_group.add_toggle_actions(format_toggle_actions)
|
|
|
|
self.action_group.add_actions(format_actions)
|
|
|
|
self.action_group.add_action(fontface_action)
|
|
|
|
self.action_group.add_action(fontsize_action)
|
2008-04-23 18:05:44 +05:30
|
|
|
self.action_group.add_action(spring)
|
2008-04-23 14:37:02 +05:30
|
|
|
|
|
|
|
# define the toolbar and create the proxies via ensure_update()
|
2008-04-04 17:21:49 +05:30
|
|
|
uimanager = gtk.UIManager()
|
2008-04-23 14:37:02 +05:30
|
|
|
uimanager.insert_action_group(self.action_group, 0)
|
2008-04-04 17:21:49 +05:30
|
|
|
uimanager.add_ui_from_string(FORMAT_TOOLBAR)
|
|
|
|
uimanager.ensure_update()
|
|
|
|
|
2008-04-23 18:05:44 +05:30
|
|
|
# get the toolbar and set it's style
|
2008-04-05 01:39:26 +05:30
|
|
|
toolbar = uimanager.get_widget('/ToolBar')
|
|
|
|
toolbar.set_style(gtk.TOOLBAR_ICONS)
|
|
|
|
|
|
|
|
return toolbar
|
2008-04-04 17:21:49 +05:30
|
|
|
|
|
|
|
def _init_url_match(self):
|
2008-04-05 01:39:26 +05:30
|
|
|
"""Setup regexp matching for URL match."""
|
2008-04-04 17:21:49 +05:30
|
|
|
self.textbuffer.create_tag('hyperlink',
|
|
|
|
underline=UNDERLINE_SINGLE,
|
|
|
|
foreground='blue')
|
|
|
|
self.textbuffer.match_add("(www|ftp)[" + HOSTCHARS + "]*\\.[" +
|
|
|
|
HOSTCHARS + ".]+" + "(:[0-9]+)?(" +
|
|
|
|
URLPATH + ")?/?", HTTP)
|
|
|
|
self.textbuffer.match_add("(mailto:)?[a-z0-9][a-z0-9.-]*@[a-z0-9]"
|
|
|
|
"[a-z0-9-]*(\\.[a-z0-9][a-z0-9-]*)+", MAIL)
|
|
|
|
self.textbuffer.match_add(SCHEME + "//(" + USER + "@)?[" +
|
|
|
|
HOSTCHARS + ".]+" + "(:[0-9]+)?(" +
|
|
|
|
URLPATH + ")?/?", GENURL)
|
|
|
|
|
|
|
|
def _create_spell_menu(self):
|
2008-04-05 01:39:26 +05:30
|
|
|
"""Create a menu with all the installed languages.
|
|
|
|
|
|
|
|
It is called each time the popup menu is opened. Each language
|
|
|
|
forms a radio menu item, and the selected language is set as active.
|
|
|
|
|
|
|
|
@returns: menu containing all the installed languages.
|
|
|
|
@returntype: gtk.Menu
|
|
|
|
|
2008-04-04 17:21:49 +05:30
|
|
|
"""
|
|
|
|
active_language = self.spellcheck.get_active_language()
|
|
|
|
|
|
|
|
menu = gtk.Menu()
|
|
|
|
group = None
|
|
|
|
for lang in self.spellcheck.get_all_languages():
|
|
|
|
menuitem = gtk.RadioMenuItem(group, lang)
|
|
|
|
menuitem.set_active(lang == active_language)
|
2008-04-05 01:39:26 +05:30
|
|
|
menuitem.connect('activate', self._spell_change_cb, lang)
|
2008-04-04 17:21:49 +05:30
|
|
|
menu.append(menuitem)
|
|
|
|
|
|
|
|
if group is None:
|
|
|
|
group = menuitem
|
|
|
|
|
|
|
|
return menu
|
|
|
|
|
2008-04-05 01:39:26 +05:30
|
|
|
# Callback functions
|
|
|
|
|
2008-04-23 14:37:02 +05:30
|
|
|
def _on_toggle_action_activate(self, action):
|
|
|
|
"""Toggle a style.
|
|
|
|
|
|
|
|
Toggle styles are e.g. 'bold', 'italic', 'underline'.
|
|
|
|
|
|
|
|
"""
|
|
|
|
if self._internal_style_change:
|
|
|
|
return
|
|
|
|
|
|
|
|
style = int(action.get_name())
|
|
|
|
value = action.get_active()
|
|
|
|
_LOG.debug("applying style '%d' with value '%s'" % (style, str(value)))
|
|
|
|
self.textbuffer.apply_style(style, value)
|
|
|
|
|
|
|
|
def _on_action_activate(self, action):
|
|
|
|
"""Apply a format."""
|
|
|
|
style = int(action.get_name())
|
|
|
|
current_value = self.textbuffer.get_style_at_cursor(style)
|
|
|
|
|
|
|
|
if style == StyledTextTagType.FONTCOLOR:
|
|
|
|
color_selection = gtk.ColorSelectionDialog(_("Select font color"))
|
|
|
|
elif style == StyledTextTagType.HIGHLIGHT:
|
|
|
|
color_selection = gtk.ColorSelectionDialog(_("Select "
|
|
|
|
"background color"))
|
|
|
|
else:
|
|
|
|
_LOG.debug("unknown style: '%d'" % style)
|
|
|
|
return
|
|
|
|
|
|
|
|
if current_value:
|
|
|
|
color_selection.colorsel.set_current_color(
|
|
|
|
hex_to_color(current_value))
|
|
|
|
|
|
|
|
response = color_selection.run()
|
|
|
|
color = color_selection.colorsel.get_current_color()
|
|
|
|
value = color_to_hex(color)
|
|
|
|
color_selection.destroy()
|
|
|
|
|
|
|
|
if response == gtk.RESPONSE_OK:
|
|
|
|
_LOG.debug("applying style '%d' with value '%s'" %
|
|
|
|
(style, str(value)))
|
|
|
|
self.textbuffer.apply_style(style, value)
|
|
|
|
|
2008-05-09 18:20:04 +05:30
|
|
|
def _on_comboaction_activate(self, action):
|
2008-04-23 14:37:02 +05:30
|
|
|
if self._internal_style_change:
|
|
|
|
return
|
|
|
|
|
2008-05-09 18:20:04 +05:30
|
|
|
style = int(action.get_name())
|
|
|
|
|
|
|
|
text = action.get_active_value()
|
|
|
|
try:
|
|
|
|
value = StyledTextTagType.STYLE_TYPE[style](text)
|
|
|
|
_LOG.debug("applying style '%d' with value '%s'" %
|
|
|
|
(style, str(value)))
|
|
|
|
self.textbuffer.apply_style(style, value)
|
|
|
|
except ValueError:
|
|
|
|
_LOG.debug("unable to convert '%s' to '%s'" %
|
|
|
|
(text, StyledTextTagType.STYLE_TYPE[style]))
|
2008-04-23 14:37:02 +05:30
|
|
|
|
|
|
|
def _format_clear_cb(self, action):
|
|
|
|
"""Remove all formats from the selection.
|
|
|
|
|
|
|
|
Remove only our own tags without touching other ones (e.g. gtk.Spell),
|
|
|
|
thus remove_all_tags() can not be used.
|
|
|
|
|
|
|
|
"""
|
|
|
|
for style in ALLOWED_STYLES:
|
|
|
|
self.textbuffer.remove_style(style)
|
|
|
|
|
|
|
|
def _on_buffer_style_changed(self, buffer, changed_styles):
|
2008-05-09 18:20:04 +05:30
|
|
|
# update action values
|
2008-04-23 14:37:02 +05:30
|
|
|
for style in changed_styles.keys():
|
|
|
|
if str(style) in self.toggle_actions:
|
|
|
|
action = self.action_group.get_action(str(style))
|
|
|
|
self._internal_style_change = True
|
|
|
|
action.set_active(changed_styles[style])
|
|
|
|
self._internal_style_change = False
|
|
|
|
|
|
|
|
if ((style == StyledTextTagType.FONTFACE) or
|
|
|
|
(style == StyledTextTagType.FONTSIZE)):
|
|
|
|
action = self.action_group.get_action(str(style))
|
|
|
|
self._internal_style_change = True
|
2008-05-09 18:20:04 +05:30
|
|
|
action.set_active_value(str(changed_styles[style]))
|
2008-04-23 14:37:02 +05:30
|
|
|
self._internal_style_change = False
|
|
|
|
|
2008-04-05 01:39:26 +05:30
|
|
|
def _spell_change_cb(self, menuitem, language):
|
|
|
|
"""Set spell checker language according to user selection."""
|
|
|
|
self.spellcheck.set_active_language(language)
|
|
|
|
|
2008-04-04 17:21:49 +05:30
|
|
|
def _open_url_cb(self, menuitem, url, flavor):
|
2008-04-05 01:39:26 +05:30
|
|
|
"""Open the URL in a browser."""
|
2008-04-04 17:21:49 +05:30
|
|
|
if not url:
|
|
|
|
return
|
|
|
|
|
|
|
|
if flavor == HTTP:
|
|
|
|
url = 'http:' + url
|
|
|
|
elif flavor == MAIL:
|
|
|
|
if not url.startswith('mailto:'):
|
|
|
|
url = 'mailto:' + url
|
|
|
|
elif flavor == GENURL:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
return
|
|
|
|
|
|
|
|
display_url(url)
|
|
|
|
|
|
|
|
def _copy_url_cb(self, menuitem, url, flavor):
|
|
|
|
"""Copy url to both useful selections."""
|
|
|
|
clipboard = gtk.Clipboard(selection="CLIPBOARD")
|
|
|
|
clipboard.set_text(url)
|
|
|
|
|
|
|
|
clipboard = gtk.Clipboard(selection="PRIMARY")
|
|
|
|
clipboard.set_text(url)
|
|
|
|
|
|
|
|
# public methods
|
|
|
|
|
|
|
|
def set_text(self, text):
|
2008-04-05 01:39:26 +05:30
|
|
|
"""Set the text of the text buffer of the editor.
|
|
|
|
|
|
|
|
@param text: formatted text to edit in the view.
|
|
|
|
@type text: L{StyledText}
|
|
|
|
|
|
|
|
"""
|
2008-04-04 17:21:49 +05:30
|
|
|
self.textbuffer.set_text(text)
|
2008-04-23 14:37:02 +05:30
|
|
|
self.textbuffer.place_cursor(self.textbuffer.get_start_iter())
|
2008-04-04 17:21:49 +05:30
|
|
|
|
|
|
|
def get_text(self):
|
2008-04-05 01:39:26 +05:30
|
|
|
"""Get the text of the text buffer of the editor.
|
|
|
|
|
|
|
|
@returns: the formatted text from the editor.
|
|
|
|
@returntype: L{StyledText}
|
|
|
|
|
|
|
|
"""
|
2008-04-04 17:21:49 +05:30
|
|
|
return self.textbuffer.get_text()
|
|
|
|
|
|
|
|
def get_toolbar(self):
|
2008-04-05 01:39:26 +05:30
|
|
|
"""Get the formatting toolbar of the editor.
|
|
|
|
|
|
|
|
@returns: toolbar widget to use as formatting GUI.
|
|
|
|
@returntype: gtk.Toolbar
|
|
|
|
|
|
|
|
"""
|
2008-04-23 14:37:02 +05:30
|
|
|
return self.toolbar
|
|
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
|
|
#
|
|
|
|
# Module functions
|
|
|
|
#
|
|
|
|
#-------------------------------------------------------------------------
|
|
|
|
def color_to_hex(color):
|
|
|
|
"""Convert gtk.gdk.Color to hex string."""
|
|
|
|
hexstring = ""
|
|
|
|
for col in 'red', 'green', 'blue':
|
|
|
|
hexfrag = hex(getattr(color, col) / (16 * 16)).split("x")[1]
|
|
|
|
if len(hexfrag) < 2:
|
|
|
|
hexfrag = "0" + hexfrag
|
|
|
|
hexstring += hexfrag
|
|
|
|
return '#' + hexstring
|
|
|
|
|
|
|
|
def hex_to_color(hex):
|
|
|
|
"""Convert hex string to gtk.gdk.Color."""
|
|
|
|
color = gtk.gdk.color_parse(hex)
|
|
|
|
return color
|
2008-05-09 18:20:04 +05:30
|
|
|
|
|
|
|
def is_valid_fontsize(text):
|
|
|
|
try:
|
|
|
|
size = int(text)
|
|
|
|
return (size > 0) and (size < 73)
|
|
|
|
except ValueError:
|
|
|
|
return False
|