Reorganize custom widgets into a new 'widgets' package.

1. moving existing widget modules under src/widgets/


svn: r10694
This commit is contained in:
Zsolt Foldvari
2008-05-08 19:40:56 +00:00
parent 9fb61bec46
commit 05b39c5c15
38 changed files with 210 additions and 170 deletions

View File

@@ -51,7 +51,7 @@ from _EditSecondary import EditSecondary
from gen.lib import NoteType
from DisplayTabs import SourceEmbedList, NoteTab
from GrampsWidgets import MonitoredDate, MonitoredEntry, PrivacyButton
from widgets import MonitoredDate, MonitoredEntry, PrivacyButton
#-------------------------------------------------------------------------
#

View File

@@ -51,7 +51,7 @@ from _EditSecondary import EditSecondary
from gen.lib import NoteType
from DisplayTabs import SourceEmbedList, NoteTab
from GrampsWidgets import MonitoredEntry, PrivacyButton, MonitoredDataType
from widgets import MonitoredEntry, PrivacyButton, MonitoredDataType
#-------------------------------------------------------------------------
#

View File

@@ -51,7 +51,7 @@ from gen.lib import NoteType
import Errors
from DisplayTabs import SourceEmbedList, NoteTab
from GrampsWidgets import MonitoredDataType, PrivacyButton
from widgets import MonitoredDataType, PrivacyButton
from BasicUtils import name_displayer
#-------------------------------------------------------------------------

View File

@@ -48,8 +48,8 @@ from Editors import EditPrimary
from QuestionDialog import ErrorDialog
from DisplayTabs import (SourceEmbedList, NoteTab, GalleryTab,
EventBackRefList, AttrEmbedList)
from GrampsWidgets import (MonitoredEntry, PlaceEntry, PrivacyButton,
MonitoredDataType, MonitoredDate)
from widgets import (MonitoredEntry, PlaceEntry, PrivacyButton,
MonitoredDataType, MonitoredDate)
#-------------------------------------------------------------------------
#

View File

@@ -45,8 +45,8 @@ import gen.lib
from DisplayTabs import (SourceEmbedList, NoteTab, GalleryTab,
EventBackRefList, AttrEmbedList)
from GrampsWidgets import (PrivacyButton, MonitoredEntry, PlaceEntry,
MonitoredDate, MonitoredDataType)
from widgets import (PrivacyButton, MonitoredEntry, PlaceEntry,
MonitoredDate, MonitoredDataType)
from _EditReference import RefTab, EditReference
#-------------------------------------------------------------------------

View File

@@ -64,8 +64,8 @@ from ReportBase import ReportUtils
from DisplayTabs import (EmbeddedList, EventEmbedList, SourceEmbedList,
FamilyAttrEmbedList, NoteTab, GalleryTab,
FamilyLdsEmbedList, ChildModel)
from GrampsWidgets import (PrivacyButton, MonitoredEntry, MonitoredDataType,
IconButton, LinkBox, BasicLabel)
from widgets import (PrivacyButton, MonitoredEntry, MonitoredDataType,
IconButton, LinkBox, BasicLabel)
from ReportBase import CATEGORY_QR_FAMILY
from QuestionDialog import (ErrorDialog, RunDatabaseRepair, WarningDialog,
MessageHideDialog)

View File

@@ -54,8 +54,8 @@ import LdsUtils
from _EditSecondary import EditSecondary
from DisplayTabs import SourceEmbedList,NoteTab
from GrampsWidgets import (PrivacyButton, MonitoredDate, PlaceEntry,
MonitoredMenu, MonitoredStrMenu)
from widgets import (PrivacyButton, MonitoredDate, PlaceEntry,
MonitoredMenu, MonitoredStrMenu)
_DATA_MAP = {
gen.lib.LdsOrd.BAPTISM : [

View File

@@ -36,7 +36,7 @@ import const
import Config
from _EditSecondary import EditSecondary
from GrampsWidgets import MonitoredEntry
from widgets import MonitoredEntry
from gettext import gettext as _
#-------------------------------------------------------------------------

View File

@@ -47,7 +47,7 @@ import Mime
import ThumbNails
import Utils
from Editors import EditPrimary
from GrampsWidgets import MonitoredDate, MonitoredEntry, PrivacyButton
from widgets import MonitoredDate, MonitoredEntry, PrivacyButton
from DisplayTabs import (SourceEmbedList, AttrEmbedList, NoteTab,
MediaBackRefList)
from Editors.AddMedia import AddMediaObject

View File

@@ -49,7 +49,7 @@ from gen.lib import NoteType
from DisplayTabs import (SourceEmbedList, AttrEmbedList, MediaBackRefList,
NoteTab)
from GrampsWidgets import MonitoredSpinButton, MonitoredEntry, PrivacyButton
from widgets import MonitoredSpinButton, MonitoredEntry, PrivacyButton
from _EditReference import RefTab, EditReference
from AddMedia import AddMediaObject

View File

@@ -46,8 +46,8 @@ from BasicUtils import name_displayer
from _EditSecondary import EditSecondary
from gen.lib import NoteType
from DisplayTabs import GrampsTab,SourceEmbedList,NoteTab
from GrampsWidgets import (MonitoredEntry, MonitoredMenu, MonitoredDate,
MonitoredDataType, PrivacyButton)
from widgets import (MonitoredEntry, MonitoredMenu, MonitoredDate,
MonitoredDataType, PrivacyButton)
#-------------------------------------------------------------------------

View File

@@ -47,11 +47,11 @@ import pango
#-------------------------------------------------------------------------
import Config
from const import GLADE_FILE
from Editors._StyledTextEditor import StyledTextEditor
from widgets import StyledTextEditor
from Editors._EditPrimary import EditPrimary
from DisplayTabs import GrampsTab, NoteBackRefList
from GrampsWidgets import (MonitoredDataType, MonitoredCheckbox,
MonitoredEntry, PrivacyButton)
from widgets import (MonitoredDataType, MonitoredCheckbox,
MonitoredEntry, PrivacyButton)
from gen.lib import Note
from QuestionDialog import ErrorDialog

View File

@@ -50,7 +50,7 @@ import const
import Utils
import Mime
import gen.lib
import GrampsWidgets
import widgets
from BasicUtils import name_displayer
import Errors
@@ -224,12 +224,12 @@ class EditPerson(EditPrimary):
"""
self.private = GrampsWidgets.PrivacyButton(
self.private = widgets.PrivacyButton(
self.top.get_widget('private'),
self.obj,
self.db.readonly)
self.gender = GrampsWidgets.MonitoredMenu(
self.gender = widgets.MonitoredMenu(
self.top.get_widget('gender'),
self.obj.set_gender,
self.obj.get_gender,
@@ -240,7 +240,7 @@ class EditPerson(EditPrimary):
),
self.db.readonly)
self.marker = GrampsWidgets.MonitoredDataType(
self.marker = widgets.MonitoredDataType(
self.top.get_widget('marker'),
self.obj.set_marker,
self.obj.get_marker,
@@ -248,7 +248,7 @@ class EditPerson(EditPrimary):
self.db.get_marker_types(),
)
self.ntype_field = GrampsWidgets.MonitoredDataType(
self.ntype_field = widgets.MonitoredDataType(
self.top.get_widget("ntype"),
self.pname.set_type,
self.pname.get_type,
@@ -256,7 +256,7 @@ class EditPerson(EditPrimary):
self.db.get_name_types())
if self.use_patronymic:
self.prefix = GrampsWidgets.MonitoredEntry(
self.prefix = widgets.MonitoredEntry(
self.top.get_widget("prefix"),
self.pname.set_patronymic,
self.pname.get_patronymic,
@@ -266,44 +266,44 @@ class EditPerson(EditPrimary):
prefix_label.set_text(_('Patronymic:'))
prefix_label.set_use_underline(True)
else:
self.prefix = GrampsWidgets.MonitoredEntry(
self.prefix = widgets.MonitoredEntry(
self.top.get_widget("prefix"),
self.pname.set_surname_prefix,
self.pname.get_surname_prefix,
self.db.readonly)
self.suffix = GrampsWidgets.MonitoredEntry(
self.suffix = widgets.MonitoredEntry(
self.top.get_widget("suffix"),
self.pname.set_suffix,
self.pname.get_suffix,
self.db.readonly)
self.call = GrampsWidgets.MonitoredEntry(
self.call = widgets.MonitoredEntry(
self.top.get_widget("call"),
self.pname.set_call_name,
self.pname.get_call_name,
self.db.readonly)
self.given = GrampsWidgets.MonitoredEntry(
self.given = widgets.MonitoredEntry(
self.top.get_widget("given_name"),
self.pname.set_first_name,
self.pname.get_first_name,
self.db.readonly)
self.title = GrampsWidgets.MonitoredEntry(
self.title = widgets.MonitoredEntry(
self.top.get_widget("title"),
self.pname.set_title,
self.pname.get_title,
self.db.readonly)
self.surname_field = GrampsWidgets.MonitoredEntry(
self.surname_field = widgets.MonitoredEntry(
self.top.get_widget("surname"),
self.pname.set_surname,
self.pname.get_surname,
self.db.readonly,
autolist=self.db.get_surname_list())
self.gid = GrampsWidgets.MonitoredEntry(
self.gid = widgets.MonitoredEntry(
self.top.get_widget("gid"),
self.obj.set_gramps_id,
self.obj.get_gramps_id,

View File

@@ -50,7 +50,7 @@ import Config
from BasicUtils import name_displayer
from _EditSecondary import EditSecondary
from gen.lib import NoteType
from GrampsWidgets import MonitoredEntry, PrivacyButton
from widgets import MonitoredEntry, PrivacyButton
from DisplayTabs import SourceEmbedList, NoteTab
#-------------------------------------------------------------------------

View File

@@ -49,7 +49,7 @@ import gen.lib
from Editors._EditPrimary import EditPrimary
from DisplayTabs import (GrampsTab, LocationEmbedList, SourceEmbedList,
GalleryTab, NoteTab, WebEmbedList, PlaceBackRefList)
from GrampsWidgets import MonitoredEntry, PrivacyButton
from widgets import MonitoredEntry, PrivacyButton
from Errors import ValidationError
from PlaceUtils import conv_lat_lon
from QuestionDialog import ErrorDialog

View File

@@ -45,7 +45,7 @@ import Config
from gen.lib import NoteType
from DisplayTabs import NoteTab,AddrEmbedList,WebEmbedList,SourceBackRefList
from GrampsWidgets import MonitoredEntry, PrivacyButton, MonitoredDataType
from widgets import MonitoredEntry, PrivacyButton, MonitoredDataType
from _EditReference import RefTab, EditReference
#-------------------------------------------------------------------------

View File

@@ -44,7 +44,7 @@ import const
import Config
import gen.lib
from GrampsWidgets import MonitoredEntry, MonitoredDataType, PrivacyButton
from widgets import MonitoredEntry, MonitoredDataType, PrivacyButton
from DisplayTabs import AddrEmbedList, WebEmbedList, NoteTab, SourceBackRefList
from Editors._EditPrimary import EditPrimary
from QuestionDialog import ErrorDialog

View File

@@ -50,7 +50,7 @@ from Editors._EditPrimary import EditPrimary
from DisplayTabs import (NoteTab, GalleryTab, DataEmbedList,
SourceBackRefList, RepoEmbedList)
from GrampsWidgets import MonitoredEntry, PrivacyButton
from widgets import MonitoredEntry, PrivacyButton
from QuestionDialog import ErrorDialog
#-------------------------------------------------------------------------

View File

@@ -45,8 +45,8 @@ import gen.lib
from DisplayTabs import (NoteTab, GalleryTab, SourceBackRefList,
DataEmbedList, RepoEmbedList)
from GrampsWidgets import (PrivacyButton, MonitoredEntry, MonitoredMenu,
MonitoredDate)
from widgets import (PrivacyButton, MonitoredEntry, MonitoredMenu,
MonitoredDate)
from _EditReference import RefTab, EditReference
#-------------------------------------------------------------------------

View File

@@ -42,7 +42,7 @@ from gtk import glade
import const
import Config
from _EditSecondary import EditSecondary
from GrampsWidgets import MonitoredEntry, PrivacyButton, MonitoredDataType
from widgets import MonitoredEntry, PrivacyButton, MonitoredDataType
#-------------------------------------------------------------------------
#

View File

@@ -1,600 +0,0 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2007-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 buffer subclassed from gtk.TextBuffer handling L{StyledText}."
#-------------------------------------------------------------------------
#
# Python modules
#
#-------------------------------------------------------------------------
from gettext import gettext as _
import re
import logging
_LOG = logging.getLogger(".Editors.StyledTextBuffer")
#-------------------------------------------------------------------------
#
# GTK modules
#
#-------------------------------------------------------------------------
import gobject
import gtk
from pango import WEIGHT_BOLD, STYLE_ITALIC, UNDERLINE_SINGLE
#-------------------------------------------------------------------------
#
# GRAMPS modules
#
#-------------------------------------------------------------------------
from gen.lib import (StyledText, StyledTextTag, StyledTextTagType)
#-------------------------------------------------------------------------
#
# Constants
#
#-------------------------------------------------------------------------
ALLOWED_STYLES = (
StyledTextTagType.BOLD,
StyledTextTagType.ITALIC,
StyledTextTagType.UNDERLINE,
StyledTextTagType.FONTCOLOR,
StyledTextTagType.HIGHLIGHT,
StyledTextTagType.FONTFACE,
StyledTextTagType.FONTSIZE,
)
STYLE_TO_PROPERTY = {
StyledTextTagType.BOLD: 'weight', # permanent tag is used instead
StyledTextTagType.ITALIC: 'style', # permanent tag is used instead
StyledTextTagType.UNDERLINE: 'underline', # permanent tag is used instead
StyledTextTagType.FONTCOLOR: 'foreground',
StyledTextTagType.HIGHLIGHT: 'background',
StyledTextTagType.FONTFACE: 'family',
StyledTextTagType.FONTSIZE: 'size-points',
}
(MATCH_START,
MATCH_END,
MATCH_FLAVOR,
MATCH_STRING,) = range(4)
#-------------------------------------------------------------------------
#
# GtkSpellState class
#
#-------------------------------------------------------------------------
class GtkSpellState:
"""A simple state machine kinda thingy.
Trying to track gtk.Spell activities on a buffer and re-apply formatting
after gtk.Spell replaces a misspelled word.
"""
(STATE_NONE,
STATE_CLICKED,
STATE_DELETED,
STATE_INSERTING) = range(4)
def __init__(self, textbuffer):
if not isinstance(textbuffer, gtk.TextBuffer):
raise TypeError("Init parameter must be instance of gtk.TextBuffer")
textbuffer.connect('mark-set', self.on_buffer_mark_set)
textbuffer.connect('delete-range', self.on_buffer_delete_range)
textbuffer.connect('insert-text', self.on_buffer_insert_text)
textbuffer.connect_after('insert-text', self.after_buffer_insert_text)
self.reset_state()
def reset_state(self):
self.state = self.STATE_NONE
self.start = 0
self.end = 0
self.tags = None
def on_buffer_mark_set(self, textbuffer, iter, mark):
mark_name = mark.get_name()
if mark_name == 'gtkspell-click':
self.state = self.STATE_CLICKED
self.start, self.end = self.get_word_extents_from_mark(textbuffer,
mark)
_LOG.debug("SpellState got start %d end %d" % (self.start, self.end))
elif mark_name == 'insert':
self.reset_state()
def on_buffer_delete_range(self, textbuffer, start, end):
if ((self.state == self.STATE_CLICKED) and
(start.get_offset() == self.start) and
(end.get_offset() == self.end)):
self.state = self.STATE_DELETED
self.tags = start.get_tags()
def on_buffer_insert_text(self, textbuffer, iter, text, length):
if self.state == self.STATE_DELETED and iter.get_offset() == self.start:
self.state = self.STATE_INSERTING
def after_buffer_insert_text(self, textbuffer, iter, text, length):
if self.state == self.STATE_INSERTING:
mark = textbuffer.get_mark('gtkspell-insert-start')
insert_start = textbuffer.get_iter_at_mark(mark)
for tag in self.tags:
textbuffer.apply_tag(tag, insert_start, iter)
self.reset_state()
def get_word_extents_from_mark(self, textbuffer, mark):
"""Get the word extents as gtk.Spell does.
Used to get the beginning of the word, in which user right clicked.
Formatting found at that position used after gtk.Spell replaces
misspelled words.
"""
start = textbuffer.get_iter_at_mark(mark)
if not start.starts_word():
#start.backward_word_start()
self.backward_word_start(start)
end = start.copy()
if end.inside_word():
#end.forward_word_end()
self.forward_word_end(end)
return start.get_offset(), end.get_offset()
def forward_word_end(self, iter):
"""gtk.Spell style gtk.TextIter.forward_word_end.
The parameter 'iter' is changing as side effect.
"""
if not iter.forward_word_end():
return False
if iter.get_char() != "'":
return True
i = iter.copy()
if i.forward_char():
if i.get_char().isalpha():
return iter.forward_word_end()
return True
def backward_word_start(self, iter):
"""gtk.Spell style gtk.TextIter.backward_word_start.
The parameter 'iter' is changing as side effect.
"""
if not iter.backward_word_start():
return False
i = iter.copy()
if i.backward_char():
if i.get_char() == "'":
if i.backward_char():
if i.get_char().isalpha():
return iter.backward_word_start()
return True
#-------------------------------------------------------------------------
#
# StyledTextBuffer class
#
#-------------------------------------------------------------------------
class StyledTextBuffer(gtk.TextBuffer):
"""An extended TextBuffer for handling StyledText strings.
StyledTextBuffer is an interface between GRAMPS' L{StyledText} format
and gtk.TextBuffer. To set and get the text use the L{set_text} and
L{get_text} methods.
To set a style to (a portion of) the text (e.g. from GUI) use the
L{apply_style} and L{remove_style} methods.
To receive information about the style of the text at the cursor position
StyledTextBuffer provides two mechanism: message driven and polling.
To receive notification of style change as cursor moves connect to the
C{style-changed} signal. To get the value of a certain style at the cursor
use the L{get_style_at_cursor) method.
StyledTextBuffer has a regexp pattern matching mechanism too. To add a
regexp pattern to match in the text use the L{match_add} method. To check
if there's a match at a certain position in the text use the L{match_check}
method. For an example how to use the matching see L{EditNote}.
"""
__gtype_name__ = 'StyledTextBuffer'
__gsignals__ = {
'style-changed': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, #return value
(gobject.TYPE_PYOBJECT,)), # arguments
}
def __init__(self):
gtk.TextBuffer.__init__(self)
# Create fix tags.
# Other tags (e.g. color) have to be created on the fly
# see self._find_tag_by_name
self.create_tag(str(StyledTextTagType.BOLD), weight=WEIGHT_BOLD)
self.create_tag(str(StyledTextTagType.ITALIC), style=STYLE_ITALIC)
self.create_tag(str(StyledTextTagType.UNDERLINE),
underline=UNDERLINE_SINGLE)
# internal format state attributes
## 1. are used to format inserted characters (self.after_insert_text)
## 2. are set each time the Insert marker is set (self.do_mark_set)
## 3. are set when a style is set (self._apply_style_to_selection)
self.style_state = StyledTextTagType.STYLE_DEFAULT.copy()
# internally used attribute
self._insert = self.get_insert()
# create a mark used for text formatting
start, end = self.get_bounds()
self.mark_insert = self.create_mark('insert-start', start, True)
# pattern matching attributes
self.patterns = []
self.matches = []
# hook up on some signals whose default handler cannot be overriden
self.connect('insert-text', self.on_insert_text)
self.connect_after('insert-text', self.after_insert_text)
self.connect_after('delete-range', self.after_delete_range)
# init gtkspell "state machine"
self.gtkspell_state = GtkSpellState(self)
# Virtual methods
def on_insert_text(self, textbuffer, iter, text, length):
_LOG.debug("Will insert at %d length %d" % (iter.get_offset(), length))
# let's remember where we started inserting
self.move_mark(self.mark_insert, iter)
def after_insert_text(self, textbuffer, iter, text, length):
"""Format inserted text."""
_LOG.debug("Have inserted at %d length %d (%s)" %
(iter.get_offset(), length, text))
if not length:
return
# where did we start inserting
insert_start = self.get_iter_at_mark(self.mark_insert)
# apply active formats for the inserted text
for style in ALLOWED_STYLES:
value = self.style_state[style]
if value and (value != StyledTextTagType.STYLE_DEFAULT[style]):
self.apply_tag(self._find_tag_by_name(style, value),
insert_start, iter)
def after_delete_range(self, textbuffer, start, end):
_LOG.debug("Deleted from %d till %d" %
(start.get_offset(), end.get_offset()))
# move 'insert' marker to have the format attributes updated
self.move_mark(self._insert, start)
def do_changed(self):
"""Parse for patterns in the text."""
self.matches = []
text = unicode(gtk.TextBuffer.get_text(self,
self.get_start_iter(),
self.get_end_iter()))
for regex, flavor in self.patterns:
iter = regex.finditer(text)
while True:
try:
match = iter.next()
self.matches.append((match.start(), match.end(),
flavor, match.group()))
_LOG.debug("Matches: %d, %d: %s [%d]" %
(match.start(), match.end(),
match.group(), flavor))
except StopIteration:
break
def do_mark_set(self, iter, mark):
"""Update style state each time the cursor moves."""
_LOG.debug("Setting mark %s at %d" %
(mark.get_name(), iter.get_offset()))
if mark.get_name() != 'insert':
return
if not iter.starts_line():
iter.backward_char()
tag_names = [tag.get_property('name') for tag in iter.get_tags()]
changed_styles = {}
for style in ALLOWED_STYLES:
if StyledTextTagType.STYLE_TYPE[style] == bool:
value = str(style) in tag_names
else:
value = StyledTextTagType.STYLE_DEFAULT[style]
for tname in tag_names:
if tname.startswith(str(style)):
value = tname.split(' ', 1)[1]
value = StyledTextTagType.STYLE_TYPE[style](value)
if self.style_state[style] != value:
changed_styles[style] = value
self.style_state[style] = value
if changed_styles:
self.emit('style-changed', changed_styles)
# Private
##def get_tag_value_at_insert(self, name):
##"""Get the value of the given tag at the insertion point."""
##tags = self.get_iter_at_mark(self._insert).get_tags()
##if name in self.toggle_actions:
##for tag in tags:
##if tag.get_name() == name:
##return True
##return False
##else:
##for tag in tags:
##if tag.get_name().startswith(name):
##return tag.get_name().split()[1]
##return None
def _get_selection(self):
bounds = self.get_selection_bounds()
if not bounds:
iter = self.get_iter_at_mark(self._insert)
if iter.inside_word():
start_pos = iter.get_offset()
iter.forward_word_end()
word_end = iter.get_offset()
iter.backward_word_start()
word_start = iter.get_offset()
iter.set_offset(start_pos)
bounds = (self.get_iter_at_offset(word_start),
self.get_iter_at_offset(word_end))
else:
bounds = (iter, self.get_iter_at_offset(iter.get_offset() + 1))
return bounds
def _apply_tag_to_selection(self, tag):
selection = self._get_selection()
if selection:
self.apply_tag(tag, *selection)
def _remove_tag_from_selection(self, tag):
selection = self._get_selection()
if selection:
self.remove_tag(tag, *selection)
def _apply_style_to_selection(self, style, value):
# FIXME can this be unified?
if StyledTextTagType.STYLE_TYPE[style] == bool:
start, end = self._get_selection()
if value:
self.apply_tag_by_name(str(style), start, end)
else:
self.remove_tag_by_name(str(style), start, end)
elif StyledTextTagType.STYLE_TYPE[style] == str:
tag = self._find_tag_by_name(style, value)
self._remove_style_from_selection(style)
self._apply_tag_to_selection(tag)
elif StyledTextTagType.STYLE_TYPE[style] == int:
tag = self._find_tag_by_name(style, value)
self._remove_style_from_selection(style)
self._apply_tag_to_selection(tag)
else:
# we should never get until here
return
self.style_state[style] = value
def _remove_style_from_selection(self, style):
start, end = self._get_selection()
tags = self._get_tag_from_range(start.get_offset(), end.get_offset())
for tag_name in tags.keys():
if tag_name.startswith(str(style)):
for start, end in tags[tag_name]:
self.remove_tag_by_name(tag_name,
self.get_iter_at_offset(start),
self.get_iter_at_offset(end+1))
def _get_tag_from_range(self, start=None, end=None):
"""Extract gtk.TextTags from buffer.
Return only the name of the TextTag from the specified range.
If range is not given, tags extracted from the whole buffer.
@note: TextTag names are always composed like: (%s %s) % (style, value)
@param start: an offset pointing to the start of the range of text
@param type: int
@param end: an offset pointing to the end of the range of text
@param type: int
@return: tagdict
@rtype: {TextTag_Name: [(start, end),]}
"""
if start is None:
start = 0
if end is None:
end = self.get_char_count()
tagdict = {}
for pos in range(start, end):
iter = self.get_iter_at_offset(pos)
for tag in iter.get_tags():
name = tag.get_property('name')
if tagdict.has_key(name):
if tagdict[name][-1][1] == pos - 1:
tagdict[name][-1] = (tagdict[name][-1][0], pos)
else:
tagdict[name].append((pos, pos))
else:
tagdict[name]=[(pos, pos)]
return tagdict
def _find_tag_by_name(self, style, value):
"""Fetch TextTag from buffer's tag table by it's name.
If TextTag does not exist yet, it is created.
"""
if StyledTextTagType.STYLE_TYPE[style] == bool:
tag_name = str(style)
elif StyledTextTagType.STYLE_TYPE[style] == str:
tag_name = "%d %s" % (style, value)
elif StyledTextTagType.STYLE_TYPE[style] == int:
tag_name = "%d %d" % (style, value)
else:
raise ValueError("Unknown style (%s) value type: %s" %
(style, value.__class__))
tag = self.get_tag_table().lookup(tag_name)
if not tag:
if StyledTextTagType.STYLE_TYPE[style] != bool:
# bool style tags are not created here, but in constuctor
tag = self.create_tag(tag_name)
tag.set_property(STYLE_TO_PROPERTY[style], value)
else:
return None
return tag
# Public API
def set_text(self, s_text):
"""Set the content of the buffer with markup tags.
@note: 's_' prefix means StyledText*, while 'g_' prefix means gtk.*.
"""
gtk.TextBuffer.set_text(self, str(s_text))
s_tags = s_text.get_tags()
for s_tag in s_tags:
g_tag = self._find_tag_by_name(int(s_tag.name), s_tag.value)
if g_tag is not None:
for (start, end) in s_tag.ranges:
start_iter = self.get_iter_at_offset(start)
end_iter = self.get_iter_at_offset(end)
self.apply_tag(g_tag, start_iter, end_iter)
def get_text(self, start=None, end=None, include_hidden_chars=True):
"""Return the buffer text.
@note: 's_' prefix means StyledText*, while 'g_' prefix means gtk.*.
"""
if start is None:
start = self.get_start_iter()
if end is None:
end = self.get_end_iter()
txt = gtk.TextBuffer.get_text(self, start, end, include_hidden_chars)
txt = unicode(txt)
# extract tags out of the buffer
g_tags = self._get_tag_from_range()
s_tags = []
for g_tagname, g_ranges in g_tags.items():
style_and_value = g_tagname.split(' ', 1)
try:
style = int(style_and_value[0])
if len(style_and_value) == 1:
s_value = None
else:
s_value = StyledTextTagType.STYLE_TYPE[style]\
(style_and_value[1])
if style in ALLOWED_STYLES:
s_ranges = [(start, end+1) for (start, end) in g_ranges]
s_tag = StyledTextTag(style, s_value, s_ranges)
s_tags.append(s_tag)
except ValueError:
_LOG.debug("silently skipping gtk.TextTag '%s'" % g_tagname)
return StyledText(txt, s_tags)
def apply_style(self, style, value):
"""Apply a style with the given value to the selection.
@param style: style type to apply
@type style: L{StyledTextTagStyle} int value
@param value: value of the style type
@type value: depends on the I{style} type
"""
if not isinstance(value, StyledTextTagType.STYLE_TYPE[style]):
raise TypeError("Style (%d) value must be %s and not %s" %
(style, StyledTextTagType.STYLE_TYPE[style],
value.__class__))
self._apply_style_to_selection(style, value)
def remove_style(self, style):
"""Delete all occurences with any value of the given style.
@param style: style type to apply
@type style: L{StyledTextTagStyle} int value
"""
self._remove_style_from_selection(style)
def get_style_at_cursor(self, style):
"""Get the actual value of the given style at the cursor position.
@param style: style type to apply
@type style: L{StyledTextTagStyle} int value
@returns: value of the style type
@returntype: depends on the C{style} type
"""
return self.style_state[style]
def match_add(self, pattern, flavor):
"""Add a pattern to look for in the text."""
regex = re.compile(pattern)
self.patterns.append((regex, flavor))
def match_check(self, pos):
"""Check if pos falls into any of the matched patterns."""
for match in self.matches:
if pos >= match[MATCH_START] and pos <= match[MATCH_END]:
return match
return None

View File

@@ -1,726 +0,0 @@
#
# 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
#
#-------------------------------------------------------------------------
from gettext import gettext as _
import logging
_LOG = logging.getLogger(".Editors.StyledTextEditor")
#-------------------------------------------------------------------------
#
# GTK libraries
#
#-------------------------------------------------------------------------
import gobject
import gtk
from pango import UNDERLINE_SINGLE
#-------------------------------------------------------------------------
#
# GRAMPS modules
#
#-------------------------------------------------------------------------
from gen.lib import StyledTextTagType
from Editors._StyledTextBuffer import (StyledTextBuffer, ALLOWED_STYLES,
MATCH_START, MATCH_END,
MATCH_FLAVOR, MATCH_STRING)
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">
<toolitem action="%d"/>
<toolitem action="%d"/>
<toolitem action="%d"/>
<toolitem action="%d"/>
<toolitem action="%d"/>
<toolitem action="%d"/>
<toolitem action="%d"/>
<toolitem action="spring"/>
<toolitem action="clear"/>
</toolbar>
</ui>
''' % (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]
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):
"""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
"""
__gtype_name__ = 'StyledTextEditor'
__gsignals__ = {
'match-changed': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, #return value
(gobject.TYPE_PYOBJECT,)), # arguments
}
def __init__(self):
"""Setup initial instance variable values."""
self.textbuffer = StyledTextBuffer()
self.textbuffer.connect('style-changed', self._on_buffer_style_changed)
gtk.TextView.__init__(self, self.textbuffer)
self.match = None
self.last_match = None
self._init_url_match()
self.url_match = None
self.toolbar = self._create_toolbar()
self.spellcheck = Spell(self)
self._internal_style_change = False
self._connect_signals()
# 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.
@param match: the new match parameters
@type match: tuple or None
@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):
"""Signal handler.
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):
"""Signal handler.
Handle formatting shortcuts.
"""
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
def on_insert_at_cursor(self, widget, string):
"""Signal handler. for debugging only."""
_LOG.debug("Textview insert '%s'" % string)
def on_delete_from_cursor(self, widget, mode, count):
"""Signal handler. for debugging only."""
_LOG.debug("Textview delete type %d count %d" % (mode, count))
def on_paste_clipboard(self, widget):
"""Signal handler. for debugging only."""
_LOG.debug("Textview paste clipboard")
def on_motion_notify_event(self, widget, event):
"""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.
"""
x, y = self.window_to_buffer_coords(gtk.TEXT_WINDOW_WIDGET,
int(event.x), int(event.y))
iter_at_location = self.get_iter_at_location(x, y)
self.match = self.textbuffer.match_check(iter_at_location.get_offset())
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):
"""Signal handler.
Handles the <CTRL> + Left click over a URL match.
"""
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):
"""Signal handler.
Insert extra menuitems:
1. Insert language selector submenu for spell checking.
2. Insert extra menus depending on ULR match result.
"""
# spell checker submenu
spell_menu = gtk.MenuItem(_('Spell'))
spell_menu.set_submenu(self._create_spell_menu())
spell_menu.show_all()
menu.prepend(spell_menu)
# url menu items
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):
"""Connect to several signals of the super class gtk.TextView."""
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):
"""Create a formatting toolbar.
@returns: toolbar containing text formatting toolitems.
@returntype: gtk.Toolbar
"""
# 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
fontface_action = ComboToolAction(str(StyledTextTagType.FONTFACE),
_("Font family"),
_("Font family"), None)
fontsize_action = ComboToolAction(str(StyledTextTagType.FONTSIZE),
_("Font size"),
_("Font size"), None)
spring = SpringSeparatorAction("spring", "", "", None)
# action accelerators
self.action_accels = {
'<Control>i': 'italic',
'<Control>b': 'bold',
'<Control>u': 'underline',
}
# 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)
self.action_group.add_action(spring)
# define the toolbar and create the proxies via ensure_update()
uimanager = gtk.UIManager()
uimanager.insert_action_group(self.action_group, 0)
uimanager.add_ui_from_string(FORMAT_TOOLBAR)
uimanager.ensure_update()
# now that widget is created for the custom actions set them up
fontface = uimanager.get_widget('/ToolBar/%d' %
StyledTextTagType.FONTFACE)
set_fontface_toolitem(fontface, self._on_combotoolitem_changed)
fontsize = uimanager.get_widget('/ToolBar/%d' %
StyledTextTagType.FONTSIZE)
set_fontsize_toolitem(fontsize, self._on_combotoolitem_changed)
# get the toolbar and set it's style
toolbar = uimanager.get_widget('/ToolBar')
toolbar.set_style(gtk.TOOLBAR_ICONS)
return toolbar
def _init_url_match(self):
"""Setup regexp matching for URL match."""
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):
"""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
"""
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)
menuitem.connect('activate', self._spell_change_cb, lang)
menu.append(menuitem)
if group is None:
group = menuitem
return menu
# Callback functions
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)
def _on_combotoolitem_changed(self, combobox, style):
if self._internal_style_change:
return
value = StyledTextTagType.STYLE_TYPE[style](combobox.get_active_text())
_LOG.debug("applying style '%d' with value '%s'" % (style, str(value)))
self.textbuffer.apply_style(style, value)
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):
# set state of toggle action
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))
combo = action.get_proxies()[0].child
model = combo.get_model()
iter = model.get_iter_first()
while iter:
if (StyledTextTagType.STYLE_TYPE[style](
model.get_value(iter, 0)) == changed_styles[style]):
break
iter = model.iter_next(iter)
self._internal_style_change = True
if iter is None:
combo.child.set_text(str(changed_styles[style]))
if style == StyledTextTagType.FONTFACE:
_LOG.debug('font family "%s" is not installed' %
changed_styles[style])
else:
combo.set_active_iter(iter)
self._internal_style_change = False
def _spell_change_cb(self, menuitem, language):
"""Set spell checker language according to user selection."""
self.spellcheck.set_active_language(language)
def _open_url_cb(self, menuitem, url, flavor):
"""Open the URL in a browser."""
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):
"""Set the text of the text buffer of the editor.
@param text: formatted text to edit in the view.
@type text: L{StyledText}
"""
self.textbuffer.set_text(text)
self.textbuffer.place_cursor(self.textbuffer.get_start_iter())
def get_text(self):
"""Get the text of the text buffer of the editor.
@returns: the formatted text from the editor.
@returntype: L{StyledText}
"""
return self.textbuffer.get_text()
def get_toolbar(self):
"""Get the formatting toolbar of the editor.
@returns: toolbar widget to use as formatting GUI.
@returntype: gtk.Toolbar
"""
return self.toolbar
#-------------------------------------------------------------------------
#
# ComboToolItem class
#
#-------------------------------------------------------------------------
class ComboToolItem(gtk.ToolItem):
__gtype_name__ = "ComboToolItem"
def __init__(self):
gtk.ToolItem.__init__(self)
self.set_border_width(2)
self.set_homogeneous(False)
self.set_expand(False)
self.combobox = gtk.combo_box_entry_new_text()
self.combobox.show()
self.add(self.combobox)
def set_entry_editable(self, editable):
self.combobox.child.set_editable(editable)
#-------------------------------------------------------------------------
#
# ComboToolAction class
#
#-------------------------------------------------------------------------
class ComboToolAction(gtk.Action):
__gtype_name__ = "ComboToolAction"
def __init__(self, name, label, tooltip, stock_id):
gtk.Action.__init__(self, name, label, tooltip, stock_id)
##self.set_tool_item_type(ComboToolItem)
##def create_tool_item(self):
##combobox = ComboToolButton()
###self.connect_proxy(combobox)
##return combobox
##def connect_proxy(self, proxy):
##gtk.Action.connect_proxy(self, proxy)
##if isinstance(proxy, ComboToolButton):
##proxy.combobox.connect('changed', self.changed)
##def changed(self, combobox):
##self.activate()
ComboToolAction.set_tool_item_type(ComboToolItem)
#-------------------------------------------------------------------------
#
# SpringSeparatorToolItem class
#
#-------------------------------------------------------------------------
class SpringSeparatorToolItem(gtk.SeparatorToolItem):
__gtype_name__ = "SpringSeparatorToolItem"
def __init__(self):
gtk.SeparatorToolItem.__init__(self)
self.set_draw(False)
self.set_expand(True)
#-------------------------------------------------------------------------
#
# SpringSeparatorAction class
#
#-------------------------------------------------------------------------
class SpringSeparatorAction(gtk.Action):
__gtype_name__ = "SpringSeparatorAction"
def __init__(self, name, label, tooltip, stock_id):
gtk.Action.__init__(self, name, label, tooltip, stock_id)
SpringSeparatorAction.set_tool_item_type(SpringSeparatorToolItem)
#-------------------------------------------------------------------------
#
# Module functions
#
#-------------------------------------------------------------------------
def set_fontface_toolitem(combotoolitem, callback):
"""Setup font family comboboxentry."""
combotoolitem.set_entry_editable(False)
fontface = combotoolitem.child
families = [family.get_name()
for family in fontface.get_pango_context().list_families()]
families.sort()
for family in families:
fontface.append_text(family)
try:
def_fam = StyledTextTagType.STYLE_DEFAULT[StyledTextTagType.FONTFACE]
default = families.index(def_fam)
except ValueError:
default = 0
fontface.set_active(default)
fontface.connect('changed', callback, StyledTextTagType.FONTFACE)
def set_fontsize_toolitem(combotoolitem, callback):
"""Setup font size comboboxentry."""
combotoolitem.set_size_request(60, -1)
fontsize = combotoolitem.child
for size in FONT_SIZES:
fontsize.append_text(str(size))
try:
def_size = StyledTextTagType.STYLE_DEFAULT[StyledTextTagType.FONTSIZE]
default = FONT_SIZES.index(def_size)
except ValueError:
default = 0
fontsize.set_active(default)
fontsize.connect('changed', callback, StyledTextTagType.FONTSIZE)
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