Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGonzalo Odiard <godiard@gmail.com>2013-05-23 21:35:53 (GMT)
committer Gonzalo Odiard <godiard@gmail.com>2013-05-23 21:35:53 (GMT)
commit1bdaa38656f1ed3da04d0d52620e0988356900cc (patch)
tree5c34a237653258b20d83b61b009c8790db6d14c8
parent29a1a65e4f6279a20769b2e1c4769c2e811adfe6 (diff)
Changes in support of touch
In support of touch, we have: * switched from combo boxes to menu items; * used gtk.fixed to position a gtk.textview in order to invoke the OSK * added support for screen rotation * added support for dragging the screen Signed-by: Walter Bender <walter@sugarlabs.org> Reviewed-by: Gonzalo Odiard <gonzalo@laptop.org>
-rw-r--r--NEWS5
-rw-r--r--labyrinthactivity.py744
-rw-r--r--src/MMapArea.py178
-rw-r--r--src/TextThought.py411
-rw-r--r--src/utils.py2
5 files changed, 973 insertions, 367 deletions
diff --git a/NEWS b/NEWS
index 584de76..d0cd8af 100644
--- a/NEWS
+++ b/NEWS
@@ -133,3 +133,8 @@ v0.0.2
------
Initial release
+
+* Add gtk.fixed for positioning textviews (for OSK)
+* Reconfigure toolbars to fit in portrait mode
+* Refresh display when changing screen dimensions (rotation)
+* Replace combo boxes with buttons/palettes (more touch friendly)
diff --git a/labyrinthactivity.py b/labyrinthactivity.py
index 3340bc6..5879835 100644
--- a/labyrinthactivity.py
+++ b/labyrinthactivity.py
@@ -23,28 +23,32 @@ from gettext import gettext as _
import xml.dom.minidom as dom
import gtk
+import gobject
import pango
import pangocairo
import cairo
from sugar.activity import activity
from sugar.graphics.toolbutton import ToolButton
-from sugar.graphics.toolcombobox import ToolComboBox
from sugar.graphics.radiotoolbutton import RadioToolButton
from sugar.graphics.colorbutton import ColorToolButton
from sugar.graphics.menuitem import MenuItem
+from sugar.graphics.icon import Icon
from sugar.datastore import datastore
+from sugar.graphics import style
from port.tarball import Tarball
try:
- # >= 0.86 toolbars
- from sugar.graphics.toolbarbox import ToolbarButton, ToolbarBox
- from sugar.activity.widgets import ActivityToolbarButton
- from sugar.activity.widgets import StopButton
+ from sugar.graphics.toolbarbox import ToolbarBox
+ HASTOOLBARBOX = True
except ImportError:
- # <= 0.84 toolbars
+ HASTOOLBARBOX = False
pass
+if HASTOOLBARBOX:
+ from sugar.graphics.toolbarbox import ToolbarButton
+ from sugar.activity.widgets import ActivityToolbarButton
+ from sugar.activity.widgets import StopButton
# labyrinth sources are shipped inside the 'src' subdirectory
sys.path.append(os.path.join(activity.get_bundle_path(), 'src'))
@@ -56,6 +60,78 @@ import utils
EMPTY = -800
+def stop_editing(main_area):
+ if len(main_area.selected) == 1:
+ if hasattr(main_area.selected[0], 'textview'):
+ main_area.selected[0].remove_textview()
+
+
+class MyMenuItem(MenuItem):
+
+ def __init__(self, text_label=None, icon_name=None, text_maxlen=60,
+ xo_color=None, file_name=None, image=None):
+ super(MenuItem, self).__init__()
+ self._accelerator = None
+ self.props.submenu = None
+
+ label = gtk.AccelLabel(text_label)
+ label.set_alignment(0.0, 0.5)
+ label.set_accel_widget(self)
+ if text_maxlen > 0:
+ label.set_ellipsize(pango.ELLIPSIZE_MIDDLE)
+ label.set_max_width_chars(text_maxlen)
+ self.add(label)
+ label.show()
+
+ if image is not None:
+ self.set_image(image)
+ image.show()
+
+ elif icon_name is not None:
+ icon = Icon(icon_name=icon_name,
+ icon_size=gtk.ICON_SIZE_SMALL_TOOLBAR)
+ if xo_color is not None:
+ icon.props.xo_color = xo_color
+ self.set_image(icon)
+ icon.show()
+
+ elif file_name is not None:
+ icon = Icon(file=file_name, icon_size=gtk.ICON_SIZE_SMALL_TOOLBAR)
+ if xo_color is not None:
+ icon.props.xo_color = xo_color
+ self.set_image(icon)
+ icon.show()
+
+
+class FontImage(gtk.Image):
+
+ _FONT_ICON = \
+'<?xml version="1.0" encoding="UTF-8" standalone="no"?>\
+<svg\
+ version="1.1"\
+ width="27.5"\
+ height="27.5"\
+ viewBox="0 0 27.5 27.5">\
+<text\
+ x="5"\
+ y="21"\
+ style="font-size:25px;fill:#ffffff;stroke:none"><tspan\
+ x="5"\
+ y="21"\
+ style="font-family:%s">F</tspan></text>\
+</svg>'
+
+ def __init__(self, font_name):
+ super(gtk.Image, self).__init__()
+
+ pl = gtk.gdk.PixbufLoader('svg')
+ pl.write(self._FONT_ICON % (font_name))
+ pl.close()
+ pixbuf = pl.get_pixbuf()
+ self.set_from_pixbuf(pixbuf)
+ self.show()
+
+
class EditToolbar(activity.EditToolbar):
def __init__(self, _parent):
activity.EditToolbar.__init__(self)
@@ -72,10 +148,19 @@ class EditToolbar(activity.EditToolbar):
menu_item.show()
self.copy.get_palette().menu.append(menu_item)
+ self.insert(gtk.SeparatorToolItem(), -1)
+
+ self.erase_button = ToolButton('edit-delete')
+ self.erase_button.set_tooltip(_('Erase selected thought(s)'))
+ self.erase_button.connect('clicked', self.__delete_cb)
+ self.insert(self.erase_button, -1)
+
+ self.show_all()
self.clipboard = gtk.Clipboard()
self.copy.child.set_sensitive(False)
self.paste.child.set_sensitive(False)
+ self.erase_button.set_sensitive(False)
def __undo_cb(self, button):
self._parent._undo.undo_action(None)
@@ -92,6 +177,18 @@ class EditToolbar(activity.EditToolbar):
def __paste_cb(self, event):
self._parent._main_area.paste_clipboard(self.clipboard)
+ def __delete_cb(self, widget):
+ self._stop_moving()
+ self.stop_dragging()
+ self._parent._main_area.delete_selected_elements()
+
+ def stop_dragging(self):
+ if self._parent._main_area.is_dragging():
+ self._parent._main_area.drag_menu_cb(self._sw, False)
+
+ def _stop_moving(self):
+ self._parent._main_area.move_mode = False
+
class ViewToolbar(gtk.Toolbar):
def __init__(self, main_area):
@@ -126,22 +223,50 @@ class ViewToolbar(gtk.Toolbar):
self.show_all()
def __zoom_in_cb(self, button):
+ stop_editing(self._main_area)
self._main_area.scale_fac *= 1.2
+ hadj = self._main_area.sw.get_hadjustment()
+ hadj.set_upper(hadj.get_upper() * 1.2)
+ vadj = self._main_area.sw.get_vadjustment()
+ vadj.set_upper(vadj.get_upper() * 1.2)
self._main_area.invalidate()
def __zoom_out_cb(self, button):
+ stop_editing(self._main_area)
self._main_area.scale_fac /= 1.2
+ hadj = self._main_area.sw.get_hadjustment()
+ hadj.set_upper(hadj.get_upper() / 1.2)
+ vadj = self._main_area.sw.get_vadjustment()
+ vadj.set_upper(vadj.get_upper() / 1.2)
self._main_area.invalidate()
def __zoom_original_cb(self, button):
+ stop_editing(self._main_area)
self._main_area.scale_fac = 1.0
+ self._main_area.translation[0] = 0
+ self._main_area.translation[1] = 0
+ hadj = self._main_area.sw.get_hadjustment()
+ hadj.set_lower(0)
+ hadj.set_upper(max(gtk.gdk.screen_width(), gtk.gdk.screen_height()))
+ vadj = self._main_area.sw.get_vadjustment()
+ vadj.set_lower(0)
+ vadj.set_upper(max(gtk.gdk.screen_width(), gtk.gdk.screen_height()))
self._main_area.invalidate()
def __zoom_tofit_cb(self, button):
+ stop_editing(self._main_area)
bounds = self.__get_thought_bounds()
self._main_area.translation[0] = bounds['x']
self._main_area.translation[1] = bounds['y']
self._main_area.scale_fac = bounds['scale']
+ hadj = self._main_area.sw.get_hadjustment()
+ hadj.set_lower(0)
+ hadj.set_upper(max(gtk.gdk.screen_width(),
+ gtk.gdk.screen_height()) * bounds['scale'])
+ vadj = self._main_area.sw.get_vadjustment()
+ vadj.set_lower(0)
+ vadj.set_upper(max(gtk.gdk.screen_width(),
+ gtk.gdk.screen_height()) * bounds['scale'])
self._main_area.invalidate()
def __get_thought_bounds(self):
@@ -164,13 +289,18 @@ class ViewToolbar(gtk.Toolbar):
upper = t.ul[1]
width = right - left
height = lower - upper
- geom = self._main_area.window.get_geometry()
- overlap = (width - geom[2], height - geom[3])
- # Leave 10% space around the edge
- width_scale = float(geom[2]) / (width * 1.1)
- height_scale = float(geom[3]) / (height * 1.1)
- return {'x': (geom[2] / 2.0) - (width / 2.0 + left),
- 'y': (geom[3] / 2.0) - (height / 2.0 + upper),
+ '''
+ screen_width = self._main_area.window.get_geometry()[2]
+ screen_height = self._main_area.window.get_geometry()[3]
+ '''
+ screen_width = gtk.gdk.screen_width()
+ screen_height = gtk.gdk.screen_height() - style.GRID_CELL_SIZE
+ overlap = (width - screen_width, height - screen_height)
+ width_scale = float(screen_width) / (width * 1.1)
+ height_scale = float(screen_height) / (height * 1.1)
+ return {'x': (screen_width / 2.0) - (width / 2.0 + left),
+ 'y': (screen_height / 2.0) - (height / 2.0 + upper) + \
+ style.GRID_CELL_SIZE,
'scale': min(width_scale, height_scale)}
@@ -179,16 +309,83 @@ class TextAttributesToolbar(gtk.Toolbar):
gtk.Toolbar.__init__(self)
self._main_area = main_area
+ self._font_list = ['ABC123', 'Sans', 'Serif', 'Monospace', 'Symbol']
+ self._font_sizes = ['8', '9', '10', '11', '12', '14', '16', '20',
+ '22', '24', '26', '28', '36', '48', '72']
+ self._font_black_list = ['abc123',
+ 'Bitstream Charter',
+ 'Cantarell',
+ 'Century Schoolbook L',
+ 'Courier 10 Pitch',
+ 'Cursor',
+ 'DejaVu LGC Sans Mono',
+ 'DejaVu Sans',
+ 'DejaVu Sans Condensed',
+ 'DejaVu Sans Mono',
+ 'DejaVu Serif',
+ 'DejaVu Serif Condensed',
+ 'Droid Sans',
+ 'Droid Sans Mono',
+ 'Droid Serif',
+ 'FreeMono',
+ 'FreeSans',
+ 'FreeSerif',
+ 'Khmer OS Content',
+ 'Khmer OS System',
+ 'Liberation Mono',
+ 'Liberation Sans',
+ 'Liberation Serif',
+ 'LKLUG',
+ 'Nimbus Mono L',
+ 'Nimbus Roman No9 L',
+ 'Nimbus Sans L',
+ 'PT Sans',
+ 'PT Sans Narrow',
+ 'Standard Symbols L',
+ 'STIX',
+ 'STIX Math',
+ 'URW Bookman L',
+ 'URW Chancery L',
+ 'URW Gothic L',
+ 'URW Palladio L',
+ 'Utopia',
+ 'VL Gothic',
+ 'VL PGothic',
+ 'cmex10', 'cmmi10', 'cmr10', 'cmsy10',
+ 'esint10', 'eufm10', 'msam10', 'msbm10',
+ 'rsfs10', 'uming', 'wasy10',
+ ]
+
+ self.font_button = ToolButton('font-text')
+ self.font_button.set_tooltip(_('Select font'))
+ self.font_button.connect('clicked', self.__font_selection_cb)
+ self.insert(self.font_button, -1)
+ self._setup_font_palette()
+
+ self.insert(gtk.SeparatorToolItem(), -1)
+
+ self.font_size_up = ToolButton('resize+')
+ self.font_size_up.set_tooltip(_('Bigger'))
+ self.font_size_up.connect('clicked', self.__font_sizes_cb, True)
+ self.insert(self.font_size_up, -1)
+
+ if len(self._main_area.selected) > 0:
+ font_size = self._main_area.font_size
+ else:
+ font_size = utils.default_font_size
+ self.size_label = gtk.Label(str(font_size))
+ self.size_label.show()
+ toolitem = gtk.ToolItem()
+ toolitem.add(self.size_label)
+ toolitem.show()
+ self.insert(toolitem, -1)
- self.fonts_combo_box = ToolComboBox(self.__get_fonts_combo_box())
- self.fonts_combo_box.combo.connect('changed', self.__fonts_cb)
- self.insert(self.fonts_combo_box, -1)
+ self.font_size_down = ToolButton('resize-')
+ self.font_size_down.set_tooltip(_('Smaller'))
+ self.font_size_down.connect('clicked', self.__font_sizes_cb, False)
+ self.insert(self.font_size_down, -1)
- self.font_sizes_combo_box = ToolComboBox(self.
- __get_font_sizes_combo_box())
- self.font_sizes_combo_box.combo.connect('changed',
- self.__font_sizes_cb)
- self.insert(self.font_sizes_combo_box, -1)
+ self.insert(gtk.SeparatorToolItem(), -1)
self.bold = ToolButton('bold-text')
self.bold.set_tooltip(_('Bold'))
@@ -205,39 +402,54 @@ class TextAttributesToolbar(gtk.Toolbar):
self.underline.connect('clicked', self.__underline_cb)
self.insert(self.underline, -1)
- self.foreground_color = ColorToolButton()
- self.foreground_color.connect('color-set', self.__foreground_color_cb)
- self.insert(self.foreground_color, -1)
+ foreground_color = ColorToolButton()
+ foreground_color.set_title(_('Set font color'))
+ foreground_color.connect('color-set', self.__foreground_color_cb)
+ self.insert(foreground_color, -1)
+
+ bakground_color = ColorToolButton()
+ bakground_color.set_title(_('Set background color'))
+ bakground_color.connect('color-set', self.__background_color_cb)
+ bakground_color.set_color(gtk.gdk.Color(65535, 65535, 65535))
+ self.insert(bakground_color, -1)
self.show_all()
- def __get_fonts_combo_box(self):
- context = self._main_area.pango_context
- fonts_combo_box = gtk.combo_box_new_text()
- fonts = context.list_families()
- index_tnr = -1
- for index, font in enumerate(fonts):
- pango_font = pango.FontDescription(font.get_name())
- font_name = pango_font.to_string()
- fonts_combo_box.append_text(font_name)
- if font_name in ['Times New', 'Times New Roman', 'Sans']:
- index_tnr = index
- if index_tnr == -1:
- fonts_combo_box.set_active(0)
- else:
- fonts_combo_box.set_active(index_tnr)
+ def __font_selection_cb(self, widget):
+ if self._font_palette:
+ if not self._font_palette.is_up():
+ self._font_palette.popup(immediate=True,
+ state=self._font_palette.SECONDARY)
+ else:
+ self._font_palette.popdown(immediate=True)
+ return
- return fonts_combo_box
+ def _setup_font_palette(self):
+ context = self._main_area.pango_context
+ for family in context.list_families():
+ name = pango.FontDescription(family.get_name()).to_string()
+ if name not in self._font_list and \
+ name not in self._font_black_list:
+ self._font_list.append(name)
+
+ self._font_palette = self.font_button.get_palette()
+ for font in self._font_list:
+ menu_item = MyMenuItem(image=FontImage(font.replace(' ', '-')),
+ text_label=font)
+ menu_item.connect('activate', self.__font_selected_cb, font)
+ self._font_palette.menu.append(menu_item)
+ menu_item.show()
- def __get_font_sizes_combo_box(self):
- font_sizes_combo_box = gtk.combo_box_new_text()
- self.__font_sizes = ['8', '9', '10', '11', '12', '14', '16', '20', \
- '22', '24', '26', '28', '36', '48', '72']
- for index, size in enumerate(self.__font_sizes):
- font_sizes_combo_box.append_text(size)
- if size == '11':
- font_sizes_combo_box.set_active(index)
- return font_sizes_combo_box
+ def __font_selected_cb(self, widget, font_name):
+ if not hasattr(self._main_area, 'font_name'):
+ return
+ if len(self._main_area.selected) > 0:
+ font_size = self._main_area.font_size
+ else:
+ font_size = utils.default_font_size
+ self._main_area.set_font(font_name, font_size)
+ self._main_area.font_name = font_name
+ self._main_area.font_size = font_size
def __attribute_values(self):
attributes = {"bold": True, "italics": True, "underline": True,
@@ -291,33 +503,44 @@ class TextAttributesToolbar(gtk.Toolbar):
return attributes
- def __fonts_cb(self, combo_box):
- font_name = combo_box.get_active_text()
- font_size = self.font_sizes_combo_box.combo.get_active_text()
- start_index = self._main_area.selected[0].index
- end_index = self._main_area.selected[0].end_index
- #if start_index != end_index:
- self._main_area.set_font(font_name, font_size)
- self._main_area.font_name = font_name
+ def __font_sizes_cb(self, button, increase):
+ if not hasattr(self._main_area, 'font_size'):
+ return
+ if len(self._main_area.selected) < 1:
+ return
+ font_size = self._main_area.font_size
+ if font_size in self._font_sizes:
+ i = self._font_sizes.index(font_size)
+ if increase:
+ if i < len(self._font_sizes) - 2:
+ i += 1
+ else:
+ if i > 0:
+ i -= 1
+ else:
+ i = self._font_sizes.index(utils.default_font_size)
- def __font_sizes_cb(self, combo_box):
- font_size = combo_box.get_active_text()
- font_name = self.fonts_combo_box.combo.get_active_text()
- start_index = self._main_area.selected[0].index
- end_index = self._main_area.selected[0].end_index
- #if start_index != end_index:
- self._main_area.set_font(font_name, font_size)
- self._main_area.font_name = font_size
+ font_size = self._font_sizes[i]
+ self.size_label.set_text(str(font_size))
+ self.font_size_down.set_sensitive(i != 0)
+ self.font_size_up.set_sensitive(i < len(self._font_sizes) - 2)
+ self._main_area.set_font(self._main_area.font_name, font_size)
def __bold_cb(self, button):
+ if len(self._main_area.selected) < 1:
+ return
value = self.__attribute_values()["bold"]
self._main_area.set_bold(value)
def __italics_cb(self, button):
+ if len(self._main_area.selected) < 1:
+ return
value = self.__attribute_values()["italics"]
self._main_area.set_italics(value)
def __underline_cb(self, button):
+ if len(self._main_area.selected) < 1:
+ return
value = self.__attribute_values()["underline"]
self._main_area.set_underline(value)
@@ -325,82 +548,162 @@ class TextAttributesToolbar(gtk.Toolbar):
color = button.get_color()
self._main_area.set_foreground_color(color)
+ def __background_color_cb(self, button):
+ color = button.get_color()
+ self._parent._main_area.set_background_color(color)
+
def change_active_font(self):
- current_font = str(self.__attribute_values()["font"])
- for index, size in enumerate(self.__font_sizes):
- index_size = current_font.find(size)
- if index_size != -1:
- current_font_name = current_font[:int(index_size)].rstrip()
- current_font_size = current_font[int(index_size):]
- index_cfs = index
- fonts = self._main_area.pango_context.list_families()
- for index, font in enumerate(fonts):
- pango_font = pango.FontDescription(font.get_name())
- font_name = pango_font.to_string()
- if font_name == current_font_name:
- index_cf = index
- break
- self.fonts_combo_box.combo.set_active(index_cf)
- self.font_sizes_combo_box.combo.set_active(index_cfs)
+ # TODO: update the toolbar
+ return
class ThoughtsToolbar(gtk.Toolbar):
+
def __init__(self, parent):
gtk.Toolbar.__init__(self)
-
self._parent = parent
- self._parent.mods[1] = RadioToolButton(named_icon='text-mode')
- self._parent.mods[1].set_tooltip(_('Text mode'))
- self._parent.mods[1].set_accelerator(_('<ctrl>t'))
- self._parent.mods[1].set_group(None)
- self._parent.mods[1].connect('clicked', self._parent.mode_cb,
- MMapArea.MODE_TEXT)
- self.insert(self._parent.mods[1], -1)
-
- self._parent.mods[2] = RadioToolButton(named_icon='image-mode')
- self._parent.mods[2].set_group(self._parent.mods[1])
- self._parent.mods[2].set_tooltip(_('Image add mode'))
- self._parent.mods[2].set_accelerator(_('<ctrl>i'))
- self._parent.mods[2].connect('clicked', self._parent.mode_cb,
- MMapArea.MODE_IMAGE)
- self.insert(self._parent.mods[2], -1)
-
- self._parent.mods[3] = RadioToolButton(named_icon='draw-mode')
- self._parent.mods[3].set_group(self._parent.mods[1])
- self._parent.mods[3].set_tooltip(_('Drawing mode'))
- self._parent.mods[3].set_accelerator(_('<ctrl>d'))
- self._parent.mods[3].connect('clicked', self._parent.mode_cb,
- MMapArea.MODE_DRAW)
- self.insert(self._parent.mods[3], -1)
-
- self._parent.mods[5] = RadioToolButton(named_icon='label-mode')
- self._parent.mods[5].set_tooltip(_('Label mode'))
- self._parent.mods[5].set_accelerator(_('<ctrl>a'))
- self._parent.mods[5].set_group(self._parent.mods[1])
- self._parent.mods[5].connect('clicked', self._parent.mode_cb,
- MMapArea.MODE_LABEL)
- self.insert(self._parent.mods[5], -1)
-
- bakground_color = ColorToolButton()
- bakground_color.connect('color-set', self.__background_color_cb)
- self.insert(bakground_color, -1)
+ text_mode_btn = RadioToolButton(named_icon='text-mode')
+ text_mode_btn.set_tooltip(_('Text mode'))
+ text_mode_btn.set_accelerator(_('<ctrl>t'))
+ text_mode_btn.set_group(None)
+ text_mode_btn.connect('clicked', self._parent.mode_cb,
+ MMapArea.MODE_TEXT)
+ self._parent.btn_group = text_mode_btn
+ self.insert(text_mode_btn, -1)
+
+ image_mode_btn = RadioToolButton(named_icon='image-mode')
+ image_mode_btn.set_group(text_mode_btn)
+ image_mode_btn.set_tooltip(_('Image add mode'))
+ image_mode_btn.set_accelerator(_('<ctrl>i'))
+ image_mode_btn.connect('clicked', self._parent.mode_cb,
+ MMapArea.MODE_IMAGE)
+ self.insert(image_mode_btn, -1)
+
+ draw_mode_btn = RadioToolButton(named_icon='draw-mode')
+ draw_mode_btn.set_group(text_mode_btn)
+ draw_mode_btn.set_tooltip(_('Drawing mode'))
+ draw_mode_btn.set_accelerator(_('<ctrl>d'))
+ draw_mode_btn.connect('clicked', self._parent.mode_cb,
+ MMapArea.MODE_DRAW)
+ self.insert(draw_mode_btn, -1)
+
+ label_mode_btn = RadioToolButton(named_icon='label-mode')
+ label_mode_btn.set_tooltip(_('Label mode'))
+ label_mode_btn.set_accelerator(_('<ctrl>a'))
+ label_mode_btn.set_group(text_mode_btn)
+ label_mode_btn.connect('clicked', self._parent.mode_cb,
+ MMapArea.MODE_LABEL)
+ self.insert(label_mode_btn, -1)
self.show_all()
- def __background_color_cb(self, button):
- color = button.get_color()
- self._parent._main_area.set_background_color(color)
+
+class ActionButtons():
+ ''' This class manages the action buttons that move among toolsbars '''
+
+ def __init__(self, parent):
+ self._main_toolbar = parent.get_toolbar_box().toolbar
+ self._main_area = parent._main_area
+ self._erase_button = parent.edit_toolbar.erase_button
+ self._sw = parent._sw
+
+ if HASTOOLBARBOX:
+ target_toolbar = self._main_toolbar
+ else:
+ target_toolbar = self.parent.edit_toolbar
+
+ self._mods = RadioToolButton(named_icon='select-mode')
+ self._mods.set_tooltip(_('Select thoughts'))
+ self._mods.set_group(parent.btn_group)
+ self._mods.set_accelerator(_('<ctrl>e'))
+ self._mods.connect('clicked', parent.mode_cb, MMapArea.MODE_NULL)
+ target_toolbar.insert(self._mods, -1)
+
+ self._link_button = RadioToolButton(named_icon='link')
+ self._link_button.set_tooltip(_('Link/unlink two selected thoughts'))
+ self._link_button.set_group(parent.btn_group)
+ self._link_button.set_accelerator(_('<ctrl>l'))
+ self._link_button.connect('clicked', self.__link_cb)
+ target_toolbar.insert(self._link_button, -1)
+
+ self.move_button = RadioToolButton(named_icon='move')
+ self.move_button.set_tooltip(_('Move selected thoughs'))
+ self.move_button.set_group(parent.btn_group)
+ self.move_button.set_accelerator(_('<ctrl>m'))
+ self.move_button.connect('clicked', self.__move_cb)
+ target_toolbar.insert(self.move_button, -1)
+
+ self.drag_button = RadioToolButton(named_icon='drag')
+ self.drag_button.set_tooltip(_('Scroll the screen'))
+ self.drag_button.set_group(parent.btn_group)
+ self.drag_button.connect('clicked', self.__drag_cb)
+ target_toolbar.insert(self.drag_button, -1)
+
+ if HASTOOLBARBOX:
+ self._separator_2 = gtk.SeparatorToolItem()
+ self._separator_2.props.draw = False
+ #self._separator_2.set_size_request(0, -1)
+ self._separator_2.set_expand(True)
+ self._separator_2.show()
+ target_toolbar.insert(self._separator_2, -1)
+
+ self._stop_button = StopButton(parent)
+ target_toolbar.insert(self._stop_button, -1)
+
+ def stop_dragging(self):
+ if self._main_area.is_dragging():
+ self._main_area.drag_menu_cb(self._sw, False)
+
+ def _stop_moving(self):
+ self._main_area.move_mode = False
+
+ def __link_cb(self, widget):
+ self._stop_moving()
+ self.stop_dragging()
+ self._main_area.link_menu_cb()
+
+ def __move_cb(self, widget):
+ self.stop_dragging()
+ if self._main_area.move_mode:
+ self._main_area.stop_moving()
+ else:
+ self._main_area.start_moving(self.move_button)
+ self._erase_button.set_sensitive(False)
+
+ def __drag_cb(self, widget):
+ # If we were moving, stop
+ self._stop_moving()
+ if not self._main_area.is_dragging():
+ self._main_area.drag_menu_cb(self._sw, True)
+ else:
+ self.stop_dragging()
+ self._erase_button.set_sensitive(False)
+
+ def reconfigure(self):
+ ''' If screen width has changed, we may need to reconfigure
+ the toolbars '''
+ if not HASTOOLBARBOX:
+ return
+
+ if hasattr(self, '_separator_2'):
+ if gtk.gdk.screen_width() / 13 > style.GRID_CELL_SIZE:
+ if self._separator_2.get_parent() is None:
+ self._main_toolbar.remove(self._stop_button)
+ self._main_toolbar.insert(self._separator_2, -1)
+ self._main_toolbar.insert(self._stop_button, -1)
+ else:
+ self._main_toolbar.remove(self._separator_2)
class LabyrinthActivity(activity.Activity):
def __init__(self, handle):
activity.Activity.__init__(self, handle)
- try:
- # Use new >= 0.86 toolbar design
+ if HASTOOLBARBOX:
self.max_participants = 1
toolbar_box = ToolbarBox()
+ self.set_toolbar_box(toolbar_box)
activity_button = ActivityToolbarButton(self)
toolbar_box.toolbar.insert(activity_button, 0)
@@ -421,15 +724,16 @@ class LabyrinthActivity(activity.Activity):
activity_button.props.page.insert(tool, -1)
tool.show()
- self.edit_toolbar = ToolbarButton()
- self.edit_toolbar.props.page = EditToolbar(self)
- self.edit_toolbar.props.icon_name = 'toolbar-edit'
- self.edit_toolbar.props.label = _('Edit'),
- toolbar_box.toolbar.insert(self.edit_toolbar, -1)
+ tool = ToolbarButton()
+ self.edit_toolbar = EditToolbar(self)
+ tool.props.page = self.edit_toolbar
+ tool.props.icon_name = 'toolbar-edit'
+ tool.props.label = _('Edit'),
+ toolbar_box.toolbar.insert(tool, -1)
self._undo = UndoManager.UndoManager(self,
- self.edit_toolbar.props.page.undo.child,
- self.edit_toolbar.props.page.redo.child)
+ self.edit_toolbar.undo.child,
+ self.edit_toolbar.redo.child)
self.__build_main_canvas_area()
@@ -439,40 +743,25 @@ class LabyrinthActivity(activity.Activity):
tool.props.label = _('View'),
toolbar_box.toolbar.insert(tool, -1)
- self.text_format_toolbar = ToolbarButton()
- self.text_format_toolbar.props.page = \
- TextAttributesToolbar(self._main_area)
- self.text_format_toolbar.props.icon_name = 'toolbar-text'
- self.text_format_toolbar.props.label = _('Text')
- toolbar_box.toolbar.insert(self.text_format_toolbar, -1)
- self._main_area.set_text_attributes(self.text_format_toolbar)
-
- separator = gtk.SeparatorToolItem()
- toolbar_box.toolbar.insert(separator, -1)
+ tool = ToolbarButton()
+ self.text_format_toolbar = TextAttributesToolbar(self._main_area)
+ tool.props.page = self.text_format_toolbar
+ tool.props.icon_name = 'toolbar-text'
+ tool.props.label = _('Text')
+ toolbar_box.toolbar.insert(tool, -1)
+ # self._main_area.set_text_attributes(self.text_format_toolbar)
- self.mods = [None] * 6
self.thought_toolbar = ToolbarButton()
self.thought_toolbar.props.page = ThoughtsToolbar(self)
self.thought_toolbar.props.icon_name = 'thought'
self.thought_toolbar.props.label = _('Thought Type')
toolbar_box.toolbar.insert(self.thought_toolbar, -1)
- separator = gtk.SeparatorToolItem()
- separator.props.draw = False
- separator.set_expand(True)
- separator.show()
- toolbar_box.toolbar.insert(separator, -1)
-
- target_toolbar = toolbar_box.toolbar
- tool_offset = 6
-
- tool = StopButton(self)
- toolbar_box.toolbar.insert(tool, -1)
+ self.action_buttons = ActionButtons(self)
toolbar_box.show_all()
- self.set_toolbar_box(toolbar_box)
- except NameError:
+ else:
# Use old <= 0.84 toolbar design
toolbox = activity.ActivityToolbox(self)
self.set_toolbox(toolbox)
@@ -496,9 +785,6 @@ class LabyrinthActivity(activity.Activity):
self.edit_toolbar.insert(separator, 0)
self.edit_toolbar.show()
- target_toolbar = self.edit_toolbar
- tool_offset = 0
-
self._undo = UndoManager.UndoManager(self,
self.edit_toolbar.undo.child,
self.edit_toolbar.redo.child)
@@ -508,43 +794,36 @@ class LabyrinthActivity(activity.Activity):
view_toolbar = ViewToolbar(self._main_area)
toolbox.add_toolbar(_('View'), view_toolbar)
- self.mods = [None] * 6
- self.thought_toolbar = ThoughtsToolbar(self)
- toolbox.add_toolbar(_('Thought Type'), self.thought_toolbar)
-
activity_toolbar = toolbox.get_activity_toolbar()
activity_toolbar.share.props.visible = False
toolbox.set_current_toolbar(1)
- self.mods[0] = ToolButton('select-mode')
- self.mods[0].set_tooltip(_('Edit mode'))
- self.mods[0].set_accelerator(_('<ctrl>e'))
- self.mods[0].connect('clicked', self.mode_cb, MMapArea.MODE_NULL)
- target_toolbar.insert(self.mods[0], tool_offset)
-
- #separator = gtk.SeparatorToolItem()
- #target_toolbar.insert(separator, tool_offset + 5)
-
- tool = ToolButton('link')
- tool.set_tooltip(_('Link/unlink two selected thoughts'))
- tool.set_accelerator(_('<ctrl>l'))
- tool.connect('clicked', self.__link_cb)
- target_toolbar.insert(tool, tool_offset + 1)
+ self.show_all()
- tool = ToolButton('edit-delete')
- tool.set_tooltip(_('Erase selected thought(s)'))
- tool.connect('clicked', self.__delete_cb)
- target_toolbar.insert(tool, tool_offset + 2)
+ self.__configure_cb(None)
- self.show_all()
self._mode = MMapArea.MODE_TEXT
self._main_area.set_mode(self._mode)
- self.mods[MMapArea.MODE_TEXT].set_active(True)
self.set_focus_child(self._main_area)
def __build_main_canvas_area(self):
- self._undo.block()
+ self.fixed = gtk.Fixed()
+ self.fixed.show()
+ self.set_canvas(self.fixed)
+
+ self._vbox = gtk.VBox()
+ self._vbox.set_size_request(
+ gtk.gdk.screen_width(),
+ gtk.gdk.screen_height() - style.GRID_CELL_SIZE)
+
self._main_area = MMapArea.MMapArea(self._undo)
+
+ self._undo.block()
+
+ self._main_area.set_size_request(
+ max(gtk.gdk.screen_width(), gtk.gdk.screen_height()),
+ max(gtk.gdk.screen_width(), gtk.gdk.screen_height()))
+ self._main_area.show()
self._main_area.connect("set_focus", self.__main_area_focus_cb)
self._main_area.connect("button-press-event",
self.__main_area_focus_cb)
@@ -553,21 +832,63 @@ class LabyrinthActivity(activity.Activity):
self.__text_selection_cb)
self._main_area.connect("thought_selection_changed",
self.__thought_selected_cb)
- self.set_canvas(self._main_area)
+ gtk.gdk.screen_get_default().connect('size-changed',
+ self.__configure_cb)
+
+ self._sw = gtk.ScrolledWindow()
+ self._sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ self._sw.add_with_viewport(self._main_area)
+ self._vbox.pack_end(self._sw, True, True)
+ self._sw.show()
+ self._main_area.show()
+ self._vbox.show()
+ self.fixed.put(self._vbox, 0, 0)
+
+ self.hadj = self._sw.get_hadjustment()
+ self.hadj.connect("value_changed", self._hadj_adjusted_cb,
+ self.hadj)
+
+ self.vadj = self._sw.get_vadjustment()
+ self.vadj.connect("value_changed", self._vadj_adjusted_cb,
+ self.vadj)
+
+ self._main_area.drag_menu_cb(self._sw, True)
+ self._main_area.drag_menu_cb(self._sw, False)
self._undo.unblock()
+ def _hadj_adjusted_cb(self, adj, data=None):
+ self._main_area.hadj = adj.value
+ stop_editing(self._main_area)
+
+ def _vadj_adjusted_cb(self, adj, data=None):
+ self._main_area.vadj = adj.value
+ stop_editing(self._main_area)
+
+ def __configure_cb(self, event):
+ ''' Screen size has changed '''
+ self._vbox.set_size_request(
+ gtk.gdk.screen_width(),
+ gtk.gdk.screen_height() - style.GRID_CELL_SIZE)
+
+ self._vbox.show()
+
+ self.action_buttons.reconfigure()
+ self.show_all()
+
def __text_selection_cb(self, thought, start, end, text):
- """Update state of copy button based on text selection
+ """Update state of edit buttons based on text selection
"""
+ self.__change_erase_state(True)
if start != end:
self.__change_copy_state(True)
- self.text_format_toolbar.props.page.change_active_font()
+ self.text_format_toolbar.change_active_font()
else:
self.__change_copy_state(False)
if self._mode == (MMapArea.MODE_TEXT and
- len(self._main_area.selected) and
- self._main_area.selected[0].editing):
+ len(self._main_area.selected)):
+ # With textview, we are always editing
+ # and self._main_area.selected[0].editing):
self.__change_paste_state(True)
else:
self.__change_paste_state(False)
@@ -576,20 +897,18 @@ class LabyrinthActivity(activity.Activity):
def __thought_selected_cb(self, arg, background_color, foreground_color):
"""Disable copy button if whole thought object is selected
"""
+ self.__change_erase_state(True)
self.__change_copy_state(False)
self.__change_paste_state(False)
+ def __change_erase_state(self, state):
+ self.edit_toolbar.erase_button.set_sensitive(state)
+
def __change_copy_state(self, state):
- try:
- self.edit_toolbar.props.page.copy.child.set_sensitive(state)
- except AttributeError:
- self.edit_toolbar.copy.child.set_sensitive(state)
+ self.edit_toolbar.copy.child.set_sensitive(state)
def __change_paste_state(self, state):
- try:
- self.edit_toolbar.props.page.paste.child.set_sensitive(state)
- except AttributeError:
- self.edit_toolbar.paste.child.set_sensitive(state)
+ self.edit_toolbar.paste.child.set_sensitive(state)
def __expose(self, widget, event):
"""Create canvas hint message at start
@@ -607,17 +926,25 @@ class LabyrinthActivity(activity.Activity):
geom = list(self._main_area.window.get_geometry())
geom[3] = geom[3] - ((self.window.get_geometry()[3] - geom[3]) / 2)
+ # Make sure initial thought is "above the fold"
+ if geom[2] < geom[3]:
+ xf = 2
+ yf = 4
+ else:
+ xf = 4
+ yf = 2
+
layout.set_alignment(pango.ALIGN_CENTER)
layout.set_text(_('Click to add\ncentral thought'))
width, height = layout.get_pixel_size()
- context.move_to(geom[2] / 2 - (width / 2), geom[3] / 2 - (height / 2))
+ context.move_to(geom[2] / xf - (width / 2), geom[3] / yf - (height / 2))
context.show_layout(layout)
round = 40
- ul = (geom[2] / 2 - (width / 2) - round,
- geom[3] / 2 - (height / 2) - round)
- lr = (geom[2] / 2 + (width / 2) + round,
- geom[3] / 2 + (height / 2) + round)
+ ul = (geom[2] / xf - (width / 2) - round,
+ geom[3] / yf - (height / 2) - round)
+ lr = (geom[2] / xf + (width / 2) + round,
+ geom[3] / yf + (height / 2) + round)
context.move_to(ul[0], ul[1] + round)
context.line_to(ul[0], lr[1] - round)
context.curve_to(ul[0], lr[1], ul[0], lr[1], ul[0] + round, lr[1])
@@ -639,8 +966,13 @@ class LabyrinthActivity(activity.Activity):
return False
def mode_cb(self, button, mode):
+ self.action_buttons.stop_dragging()
+ if self._mode == MMapArea.MODE_TEXT:
+ if len(self._main_area.selected) > 0:
+ self._main_area.selected[0].leave()
self._mode = mode
self._main_area.set_mode(self._mode)
+ # self.edit_toolbar.erase_button.set_sensitive(True)
def __export_pdf_cb(self, event):
maxx, maxy = self._main_area.get_max_area()
@@ -707,7 +1039,9 @@ class LabyrinthActivity(activity.Activity):
del fileObject
def __main_area_focus_cb(self, arg, event, extended=False):
- self._main_area.grab_focus()
+ # Don't steal focus from textview
+ # self._main_area.grab_focus()
+ pass
def read_file(self, file_path):
tar = Tarball(file_path)
@@ -730,13 +1064,9 @@ class LabyrinthActivity(activity.Activity):
x, y = utils.parse_coords(tmp)
self._main_area.translation = [x, y]
- self.thought_toolbar.props.page.mods[self._mode].set_active(True)
-
tar.close()
def write_file(self, file_path):
- logging.debug('write_file')
-
tar = Tarball(file_path, 'w')
self._main_area.update_save()
@@ -761,9 +1091,3 @@ class LabyrinthActivity(activity.Activity):
str(self._main_area.translation))
string = doc.toxml()
return string.encode("utf-8")
-
- def __link_cb(self, widget):
- self._main_area.link_menu_cb()
-
- def __delete_cb(self, widget):
- self._main_area.delete_selected_elements()
diff --git a/src/MMapArea.py b/src/MMapArea.py
index 838175a..b007080 100644
--- a/src/MMapArea.py
+++ b/src/MMapArea.py
@@ -22,6 +22,7 @@
import math
import time
+import string
import gtk
import pango
import gobject
@@ -84,30 +85,32 @@ class MMapArea (gtk.DrawingArea):
It is responsible for processing signals and such from the whole area and \
passing these on to the correct child. It also informs things when to draw'''
- __gsignals__ = dict (title_changed = (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- (gobject.TYPE_STRING, )),
- change_mode = (gobject.SIGNAL_RUN_LAST,
- gobject.TYPE_NONE,
- (gobject.TYPE_INT, )),
- change_buffer = (gobject.SIGNAL_RUN_LAST,
- gobject.TYPE_NONE,
- (gobject.TYPE_OBJECT, )),
- text_selection_changed = (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- (gobject.TYPE_INT, gobject.TYPE_INT, gobject.TYPE_STRING)),
- thought_selection_changed = (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)),
- set_focus = (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- (gobject.TYPE_PYOBJECT, gobject.TYPE_BOOLEAN)),
- set_attrs = (gobject.SIGNAL_RUN_LAST,
- gobject.TYPE_NONE,
- (gobject.TYPE_BOOLEAN, gobject.TYPE_BOOLEAN, gobject.TYPE_BOOLEAN, pango.FontDescription)),
- link_selected = (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ()))
+ __gsignals__ = dict (
+ title_changed = (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE, (gobject.TYPE_STRING, )),
+ change_mode = (gobject.SIGNAL_RUN_LAST,
+ gobject.TYPE_NONE,
+ (gobject.TYPE_INT, )),
+ change_buffer = (gobject.SIGNAL_RUN_LAST,
+ gobject.TYPE_NONE,
+ (gobject.TYPE_OBJECT, )),
+ text_selection_changed = (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ (gobject.TYPE_INT, gobject.TYPE_INT,
+ gobject.TYPE_STRING)),
+ thought_selection_changed = (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ (gobject.TYPE_PYOBJECT,
+ gobject.TYPE_PYOBJECT)),
+ set_focus = (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ (gobject.TYPE_PYOBJECT, gobject.TYPE_BOOLEAN)),
+ set_attrs = (gobject.SIGNAL_RUN_LAST,
+ gobject.TYPE_NONE,
+ (gobject.TYPE_BOOLEAN, gobject.TYPE_BOOLEAN,
+ gobject.TYPE_BOOLEAN, pango.FontDescription)),
+ link_selected = (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ()))
def __init__(self, undo):
super (MMapArea, self).__init__()
@@ -146,7 +149,15 @@ class MMapArea (gtk.DrawingArea):
self.connect ("scroll_event", self.scroll)
self.commit_handler = None
self.title_change_handler = None
+ self.drag_mode = False
+ self._dragging = False
+ self.sw = None
+ self.hadj = 0
+ self.vadj = 0
+ self.origin_x = None
+ self.origin_y = None
self.moving = False
+ self.move_mode = False
self.move_origin = None
self.move_origin_new = None
self.focus = None
@@ -170,11 +181,17 @@ class MMapArea (gtk.DrawingArea):
w.realize()
style = w.get_style()
self.pango_context.set_font_description(style.font_desc)
- self.font_name = style.font_desc.to_string()
+
+ # FIXME: rude hack to remove fontsize from font name
+ parts = style.font_desc.to_string().split()
+ try:
+ float(parts[-1])
+ self.font_name = string.join(parts[0:-2])
+ except ValueError:
+ self.font_name = style.font_desc.to_string()
+
utils.default_font = self.font_name
-
- self.font_size = utils.default_font
-
+ self.font_size = utils.default_font_size
utils.default_colors["text"] = utils.gtk_to_cairo_color(style.text[gtk.STATE_NORMAL])
utils.default_colors["base"] = utils.gtk_to_cairo_color(style.base[gtk.STATE_NORMAL])
# Match the fixed white canvas colour (makes thought focus visible)
@@ -189,8 +206,11 @@ class MMapArea (gtk.DrawingArea):
utils.selected_colors["fill"] = utils.gtk_to_cairo_color(style.base[gtk.STATE_SELECTED])
def set_text_attributes(self, text_attributes):
+ return
+ '''
self.font_combo_box = text_attributes.props.page.fonts_combo_box.combo
- self.font_sizes_combo_box = text_attributes.props.page.font_sizes_combo_box.combo
+ self.font_sizes_combo_box = utils.default_font_size #text_attributes.props.page.font_sizes_combo_box.combo
+ '''
def transform_coords(self, loc_x, loc_y):
if hasattr(self, "transform"):
@@ -201,6 +221,13 @@ class MMapArea (gtk.DrawingArea):
return self.untransform.transform_point(loc_x, loc_y)
def button_down (self, widget, event):
+ if self.drag_mode:
+ self.set_cursor(gtk.gdk.HAND2)
+ self.origin_x = event.x
+ self.origin_y = event.y
+ self._dragging = True
+ return
+
if event.button == 2 or \
event.button == 1 and self.translate == True:
self.set_cursor (gtk.gdk.FLEUR)
@@ -213,8 +240,11 @@ class MMapArea (gtk.DrawingArea):
obj = self.find_object_at (coords)
if obj:
- if event.button == 3:
- self.moving = not (event.state & gtk.gdk.CONTROL_MASK)
+ if event.button == 3 or self.move_mode:
+ if self.move_mode:
+ self.moving = True
+ else:
+ self.moving = not (event.state & gtk.gdk.CONTROL_MASK)
if self.moving:
self.set_cursor(gtk.gdk.FLEUR)
self.move_origin = (coords[0], coords[1])
@@ -252,6 +282,10 @@ class MMapArea (gtk.DrawingArea):
self.invalidate ((old_coords[0], old_coords[1], new_coords[0], new_coords[1]))
def button_release (self, widget, event):
+ if self._dragging:
+ self.set_cursor(gtk.gdk.LEFT_PTR)
+ self._dragging = False
+
coords = self.transform_coords (event.get_coords()[0], event.get_coords()[1])
if self.is_bbox_selecting:
@@ -272,8 +306,10 @@ class MMapArea (gtk.DrawingArea):
self.undo.add_undo (self.move_action)
self.move_action = None
- self.moving = False
- self.move_origin = None
+ was_moving = False
+ if self.moving:
+ was_moving = True
+ self.stop_moving()
obj = self.find_object_at (coords)
@@ -295,6 +331,9 @@ class MMapArea (gtk.DrawingArea):
return True
self.invalidate ()
+
+ if was_moving:
+ self.start_moving(self.move_button)
return True
def undo_transform_cb (self, action, mode):
@@ -354,6 +393,16 @@ class MMapArea (gtk.DrawingArea):
return True
def motion (self, widget, event):
+ if self._dragging:
+ if self.origin_x is None:
+ self.origin_x = event.get_coords()[0]
+ self.origin_y = event.get_coords()[1]
+ dx = self.origin_x - event.get_coords()[0]
+ dy = self.origin_y - event.get_coords()[1]
+ self.origin_x = event.get_coords()[0]
+ self.origin_y = event.get_coords()[1]
+ self._adjust_sw(dx, dy)
+ return True
coords = self.transform_coords (event.get_coords()[0], event.get_coords()[1])
if event.state & gtk.gdk.BUTTON1_MASK and self.is_bbox_selecting:
@@ -482,8 +531,8 @@ class MMapArea (gtk.DrawingArea):
self.commit_handler = None
if thought:
try:
- self.commit_handler = self.im_context.connect ("commit", thought.commit_text, self.mode, self.font_combo_box, \
- self.font_sizes_combo_box)
+ self.commit_handler = self.im_context.connect ("commit", thought.commit_text, self.mode, None, None)
+ # self.font_combo_box, self.font_sizes_combo_box)
self.delete_handler = self.im_context.connect ("delete-surrounding", thought.delete_surroundings, self.mode)
self.preedit_changed_handler = self.im_context.connect ("preedit-changed", thought.preedit_changed, self.mode)
self.preedit_end_handler = self.im_context.connect ("preedit-end", thought.preedit_end, self.mode)
@@ -733,7 +782,8 @@ class MMapArea (gtk.DrawingArea):
type = self.mode
if type == MODE_TEXT:
- thought = TextThought.TextThought (coords, self.pango_context, self.nthoughts, self.save, self.undo, loading, self.background_color, self.foreground_color)
+ # fixed<-_vbox<-_sw<-_main_area
+ thought = TextThought.TextThought (coords, self.pango_context, self.nthoughts, self.save, self.undo, loading, self.background_color, self.foreground_color, fixed=self.parent.parent.parent.parent, parent=self)
elif type == MODE_LABEL:
thought = LabelThought.LabelThought (coords, self.pango_context, self.nthoughts, self.save, self.undo, loading, self.background_color, self.foreground_color)
elif type == MODE_IMAGE:
@@ -774,6 +824,9 @@ class MMapArea (gtk.DrawingArea):
else:
action = None
+ if hasattr(thought, 'textview'):
+ thought.remove_textview()
+
if thought.element in self.element.childNodes:
self.element.removeChild (thought.element)
self.thoughts.remove (thought)
@@ -1179,6 +1232,59 @@ class MMapArea (gtk.DrawingArea):
return True
return False
+ def drag_menu_cb(self, sw, mode):
+ if len(self.selected) == 1:
+ if hasattr(self.selected[0], 'textview'):
+ self.selected[0].remove_textview()
+ if mode == True:
+ self.sw = sw
+ self.drag_mode = True
+ else:
+ self.drag_mode = False
+
+ def is_dragging(self):
+ return self.drag_mode
+
+ def _adjust_sw(self, dx, dy):
+ if self.sw is None:
+ return
+ if not self.drag_mode:
+ return
+ hadj = self.sw.get_hadjustment()
+ hvalue = hadj.get_value() + dx
+ try:
+ if hvalue < hadj.get_lower():
+ hvalue = hadj.get_lower()
+ elif hvalue > hadj.get_upper():
+ hvalue = hadj.get_upper()
+ except AttributeError:
+ pass
+ hadj.set_value(hvalue)
+ self.sw.set_hadjustment(hadj)
+ vadj = self.sw.get_vadjustment()
+ vvalue = vadj.get_value() + dy
+ try:
+ if vvalue < vadj.get_lower():
+ vvalue = vadj.get_lower()
+ elif vvalue > vadj.get_upper():
+ vvalue = vadj.get_upper()
+ except AttributeError:
+ pass
+ vadj.set_value(vvalue)
+ self.sw.set_vadjustment(vadj)
+
+ def stop_moving(self):
+ self.moving = False
+ self.move_mode = False
+ self.move_origin = None
+
+ def start_moving(self, move_button):
+ if len(self.selected) == 1:
+ if hasattr(self.selected[0], 'textview'):
+ self.selected[0].remove_textview()
+ self.move_mode = True
+ self.move_button = move_button
+
def link_menu_cb (self):
if len (self.selected) != 2:
return
diff --git a/src/TextThought.py b/src/TextThought.py
index 1fef255..28d11dc 100644
--- a/src/TextThought.py
+++ b/src/TextThought.py
@@ -20,9 +20,17 @@
# Boston, MA 02110-1301 USA
#
+# In order to support an on-screen keyboard, a textview widget is used
+# instead of capturing individual keyboard events. The down side to
+# this is that the maintenance of text attributes is mangled. The good
+# news is that much of the complexity disappears.
+# --Walter Bender <walter@sugarlabs.org> 2013
+
import gtk
+import gobject
import pango
import utils
+import string
import os
import xml.dom
import logging
@@ -37,7 +45,9 @@ UNDO_REMOVE_ATTR=66
UNDO_REMOVE_ATTR_SELECTION=67
class TextThought (ResizableThought):
- def __init__ (self, coords, pango_context, thought_number, save, undo, loading, background_color, foreground_color, name="thought"):
+ def __init__ (self, coords, pango_context, thought_number, save, undo,
+ loading, background_color, foreground_color, name="thought",
+ fixed=None, parent=None):
super (TextThought, self).__init__(coords, save, name, undo, background_color, foreground_color)
self.index = 0
@@ -56,6 +66,11 @@ class TextThought (ResizableThought):
self.current_attrs = []
self.double_click = False
self.orig_text = None
+ self._parent = parent
+ self._fixed = fixed
+ self.textview = None
+ self._textview_handler = None
+ self._clipboard = None
if prefs.get_direction () == gtk.TEXT_DIR_LTR:
self.pango_context.set_base_dir (pango.DIRECTION_LTR)
@@ -71,7 +86,7 @@ class TextThought (ResizableThought):
self.all_okay = True
- def index_from_bindex (self, bindex):
+ def index_from_bindex (self, bindex):
if bindex == 0:
return 0
index = 0
@@ -102,7 +117,7 @@ class TextThought (ResizableThought):
self.attrlist = pango.AttrList ()
# TODO: splice instead of own method
it = self.attributes.get_iterator()
-
+
while 1:
at = it.get_attrs()
for x in at:
@@ -123,7 +138,7 @@ class TextThought (ResizableThought):
self.attrlist.splice(ins_style, self.index, len(ins_text))
else:
show_text = self.text
-
+
it = self.attributes.get_iterator()
while(1):
found = False
@@ -199,13 +214,13 @@ class TextThought (ResizableThought):
self.emit("update-attrs", bold, italics, underline, pango_font)
return show_text
- def recalc_text_edges (self):
+ def recalc_text_edges (self):
if (not hasattr(self, "layout")):
return
del self.layout
-
+
show_text = self.attrs_changed ()
-
+ '''
r,g,b = utils.selected_colors["fill"]
r *= 65536
g *= 65536
@@ -215,6 +230,7 @@ class TextThought (ResizableThought):
else:
bgsel = pango.AttrBackground (int(r), int(g), int(b), self.index, self.end_index)
self.attrlist.insert (bgsel)
+ '''
self.layout = pango.Layout (self.pango_context)
self.layout.set_text (show_text)
@@ -246,21 +262,21 @@ class TextThought (ResizableThought):
self.ul = (self.lr[0] - margin[0] - margin[2] - text_w, tmp1)
"""
- def recalc_edges (self):
+ def recalc_edges (self):
self.lr = (self.ul[0]+self.width, self.ul[1]+self.height)
if not self.creating:
self.recalc_text_edges()
- def commit_text (self, context, string, mode, font_combo_box, font_sizes_combo_box):
+ def commit_text (self, context, string, mode, font_combo_box, font_sizes_combo_box):
font_name = font_combo_box.get_active_text()
- font_size = font_sizes_combo_box.get_active_text()
+ font_size = utils.default_font_size # font_sizes_combo_box.get_active_text()
self.set_font(font_name, font_size)
self.add_text (string)
self.recalc_edges ()
self.emit ("title_changed", self.text)
self.emit ("update_view")
- def add_text (self, string):
+ def add_text (self, string):
if self.index > self.end_index:
left = self.text[:self.end_index]
right = self.text[self.index:]
@@ -276,12 +292,12 @@ class TextThought (ResizableThought):
bleft = self.bytes[:self.b_f_i (self.index)]
bright = self.bytes[self.b_f_i (self.end_index):]
change = self.index - self.end_index + len(string)
- else:
+ else:
left = self.text[:self.index]
right = self.text[self.index:]
bleft = self.bytes[:self.b_f_i(self.index)]
bright = self.bytes[self.b_f_i(self.index):]
- change = len(string)
+ change = len(string)
it = self.attributes.get_iterator()
changes= []
@@ -317,7 +333,7 @@ class TextThought (ResizableThought):
changes.append(x)
if it.next() == False:
break
-
+
del self.attributes
self.attributes = pango.AttrList()
for x in changes:
@@ -329,14 +345,14 @@ class TextThought (ResizableThought):
self.index += len (string)
self.bytes = bleft + str(len(string)) + bright
self.bindex = self.b_f_i (self.index)
- self.end_index = self.index
+ self.end_index = self.index
- def draw (self, context):
+ def draw (self, context):
self.recalc_edges ()
ResizableThought.draw(self, context)
if self.creating:
return
-
+
(textx, texty) = (self.min_x, self.min_y)
if self.am_primary:
r, g, b = utils.primary_colors["text"]
@@ -364,14 +380,21 @@ class TextThought (ResizableThought):
context.set_source_rgb (0,0,0)
context.stroke ()
- def process_key_press (self, event, mode):
+ def process_key_press (self, event, mode):
+ # Since we are using textviews, we don't use the
+ # keypress code anymore
+ if not self.editing:
+ return False
+ else:
+ return True
+
modifiers = gtk.accelerator_get_default_mod_mask ()
shift = event.state & modifiers == gtk.gdk.SHIFT_MASK
handled = True
clear_attrs = True
if not self.editing:
return False
-
+
if (event.state & modifiers) & gtk.gdk.CONTROL_MASK:
if event.keyval == gtk.keysyms.a:
self.index = self.bindex = 0
@@ -410,7 +433,7 @@ class TextThought (ResizableThought):
else:
handled = False
- if clear_attrs:
+ if clear_attrs:
del self.current_attrs
self.current_attrs = []
@@ -422,7 +445,7 @@ class TextThought (ResizableThought):
return handled
- def undo_text_action (self, action, mode):
+ def undo_text_action (self, action, mode):
self.undo.block ()
if action.undo_type == UndoManager.DELETE_LETTER or action.undo_type == UndoManager.DELETE_WORD:
real_mode = not mode
@@ -442,7 +465,7 @@ class TextThought (ResizableThought):
self.add_text (action.text)
self.rebuild_byte_table ()
self.bindex = self.b_f_i (self.index)
-
+
del self.attributes
self.attributes = pango.AttrList()
map(lambda a : self.attributes.change(a), attrs)
@@ -452,7 +475,7 @@ class TextThought (ResizableThought):
self.emit ("grab_focus", False)
self.undo.unblock ()
- def delete_char (self):
+ def delete_char (self):
if self.index == self.end_index == len (self.text):
return
if self.index > self.end_index:
@@ -477,7 +500,7 @@ class TextThought (ResizableThought):
changes= []
old_attrs = []
accounted = -change
-
+
it = self.attributes.get_iterator()
while (1):
(start,end) = it.range()
@@ -516,11 +539,11 @@ class TextThought (ResizableThought):
changes.append(x)
if it.next() == False:
break
-
+
del self.attributes
self.attributes = pango.AttrList()
map(lambda a : self.attributes.change(a), changes)
-
+
self.undo.add_undo (UndoManager.UndoAction (self, UndoManager.DELETE_LETTER, self.undo_text_action,
self.b_f_i (self.index), local_text, len(local_text), local_bytes, old_attrs,
changes))
@@ -528,7 +551,7 @@ class TextThought (ResizableThought):
self.bytes = bleft+bright
self.end_index = self.index
- def backspace_char (self):
+ def backspace_char (self):
if self.index == self.end_index == 0:
return
if self.index > self.end_index:
@@ -594,12 +617,11 @@ class TextThought (ResizableThought):
changes.append(x)
if it.next() == False:
break
-
-
+
del self.attributes
self.attributes = pango.AttrList()
map(lambda a : self.attributes.change(a), changes)
-
+
self.text = left+right
self.bytes = bleft+bright
self.end_index = self.index
@@ -609,7 +631,7 @@ class TextThought (ResizableThought):
if self.index < 0:
self.index = 0
- def move_index_back (self, mod):
+ def move_index_back (self, mod):
if self.index <= 0:
self.index = 0
return
@@ -617,7 +639,7 @@ class TextThought (ResizableThought):
if not mod:
self.end_index = self.index
- def move_index_forward (self, mod):
+ def move_index_forward (self, mod):
if self.index >= len(self.text):
self.index = len(self.text)
return
@@ -625,7 +647,7 @@ class TextThought (ResizableThought):
if not mod:
self.end_index = self.index
- def move_index_up (self, mod):
+ def move_index_up (self, mod):
tmp = self.text.decode ()
lines = tmp.splitlines ()
if len (lines) == 1:
@@ -659,7 +681,7 @@ class TextThought (ResizableThought):
if not mod:
self.end_index = self.index
- def move_index_down (self, mod):
+ def move_index_down (self, mod):
tmp = self.text.decode ()
lines = tmp.splitlines ()
if len (lines) == 1:
@@ -683,7 +705,7 @@ class TextThought (ResizableThought):
if not mod:
self.end_index = self.index
- def move_index_horizontal(self, mod, home=False):
+ def move_index_horizontal(self, mod, home=False):
lines = self.text.splitlines ()
loc = 0
line = 0
@@ -698,12 +720,18 @@ class TextThought (ResizableThought):
return
line += 1
- def process_button_down (self, event, coords):
+ def process_button_down (self, event, coords):
+ if not self._parent.move_mode and self.textview is None:
+ self._create_textview()
+ if self.textview is not None:
+ self.textview.grab_focus()
+
if ResizableThought.process_button_down(self, event, coords):
return True
- if not self.editing:
- return False
+ # With textview, we are always editing
+ # if not self.editing:
+ # return False
modifiers = gtk.accelerator_get_default_mod_mask ()
@@ -722,6 +750,7 @@ class TextThought (ResizableThought):
self.index = len(self.text)
self.end_index = 0 # and mark all
self.double_click = True
+
elif event.button == 2:
x = int ((coords[0] - self.min_x)*pango.SCALE)
y = int ((coords[1] - self.min_y)*pango.SCALE)
@@ -734,15 +763,141 @@ class TextThought (ResizableThought):
if os.name != 'nt':
clip = gtk.Clipboard (selection="PRIMARY")
self.paste_text (clip)
-
+
del self.current_attrs
- self.current_attrs = []
+ self.current_attrs = []
self.recalc_edges()
self.emit ("update_view")
self.selection_changed()
- def process_button_release (self, event, transformed):
+ def _create_textview(self):
+ # When the button is pressed inside a text thought,
+ # create a textview (necessary for invoking the
+ # on-screen keyboard) instead of processing the text
+ # by grabbing keyboard events.
+ if self.textview is None:
+ self.textview = gtk.TextView()
+ margin = utils.margin_required (utils.STYLE_NORMAL)
+ x, y, w, h = self.textview_rescale()
+ self.textview.set_size_request(w, h)
+ self._fixed.put(self.textview, x, y)
+ self.textview.set_justification(gtk.JUSTIFY_CENTER)
+
+ font, size = None, None
+ bold, italic, underline = False, False, False
+ # Get current attributes and set them here
+ it = self.attributes.get_iterator()
+ while (1):
+ r = it.range()
+ for x in it.get_attrs():
+ if x.type == pango.ATTR_WEIGHT and x.value == pango.WEIGHT_BOLD:
+ bold = True
+ elif x.type == pango.ATTR_STYLE and x.value == pango.STYLE_ITALIC:
+ italic = True
+ elif x.type == pango.ATTR_UNDERLINE and x.value == pango.UNDERLINE_SINGLE:
+ underline = True
+ elif x.type == pango.ATTR_FONT_DESC:
+ parts = x.desc.to_string ().split()
+ font = string.join(parts[0:-2])
+ size = parts[-1]
+
+ if not it.next():
+ break
+
+ if font is None:
+ font = 'Sans'
+ if size is None:
+ size = utils.default_font_size
+ font_desc = pango.FontDescription(font)
+ font_desc.set_size(
+ int(int(size) * pango.SCALE * self._parent.scale_fac))
+ if bold:
+ font_desc.set_weight(pango.WEIGHT_BOLD)
+ if italic:
+ font_desc.set_style(pango.STYLE_ITALIC)
+ self.textview.modify_font(font_desc)
+
+ r, g, b = utils.gtk_to_cairo_color(self.foreground_color)
+ rgba = gtk.gdk.Color(
+ int(65535 * r), int(65535 * g), int(65535 * b))
+ self.textview.modify_text(gtk.STATE_NORMAL, rgba)
+
+ self.textview.get_buffer().set_text(self.text)
+ self.textview.show()
+ if self._textview_handler is None:
+ self._textview_handler = self.textview.connect(
+ 'focus-out-event', self._textview_focus_out_cb)
+ self.copy_handler = self.textview.connect(
+ 'copy-clipboard', self._textview_copy_cb)
+ self.cut_handler = self.textview.connect(
+ 'cut-clipboard', self._textview_cut_cb)
+ self.paste_handler = self.textview.connect(
+ 'paste-clipboard', self._textview_paste_cb)
+ self.select_handler = self.textview.connect(
+ 'select-all', self._textview_select_cb)
+ self.textview.grab_focus()
+ self._fixed.show()
+
+ def textview_rescale(self):
+ tx = self._parent.translation[0] * self._parent.scale_fac
+ ty = self._parent.translation[1] * self._parent.scale_fac
+ margin = utils.margin_required (utils.STYLE_NORMAL)
+ hadj = int(self._parent.hadj)
+ vadj = int(self._parent.vadj)
+ w = int((self.width - margin[0] - margin[2]) \
+ * self._parent.scale_fac)
+ # w = max(w, margin[0] + margin[2])
+ h = int((self.height - margin[1] - margin[3]) \
+ * self._parent.scale_fac)
+ # h = max(h, margin[1] + margin[3])
+ xo = gtk.gdk.screen_width() \
+ * (1. - self._parent.scale_fac) / 2.
+ yo = gtk.gdk.screen_height() \
+ * (1. - self._parent.scale_fac) / 1.25 # FIXME
+ x = (self.ul[0] + margin[0]) * self._parent.scale_fac
+ y = (self.ul[1] + margin[1]) * self._parent.scale_fac
+ return int(x + xo - hadj + tx), int(y + yo - vadj + ty), \
+ int(w), int(h)
+
+ def _textview_copy_cb(self, widget=None, event=None):
+ self.textview.get_buffer().copy_clipboard(self._clipboard)
+ return True
+
+ def _textview_cut_cb(self, widget=None, event=None):
+ self.textview.get_buffer().cut_clipboard(
+ self._clipboard, self.textview.get_editable())
+ self._textview_process()
+ return True
+
+ def _textview_paste_cb(self, widget=None, event=None):
+ self.textview.get_buffer().paste_clipboard(
+ self._clipboard, None, self.textview.get_editable())
+ self._textview_process()
+ return True
+
+ def _textview_select_cb(self, widget=None, event=None):
+ buffer = self.textview.get_buffer()
+ buffer.select_range(buffer.get_start_iter(),
+ buffer.get_end_iter())
+ return True
+
+ def _textview_focus_out_cb(self, widget=None, event=None):
+ self._textview_process()
+ return False
+
+ def _textview_process(self):
+ self.index = 0
+ self.end_index = len(self.text)
+ self.delete_char()
+ bounds = self.textview.get_buffer().get_bounds()
+ self.add_text(self.textview.get_buffer().get_text(
+ bounds[0], bounds[1], True))
+ self.emit ("title_changed", self.text)
+ self.emit ("update_view")
+ return False
+
+ def process_button_release (self, event, transformed):
if self.orig_size:
if self.creating:
orig_size = self.width >= MIN_SIZE or self.height >= MIN_SIZE
@@ -757,15 +912,22 @@ class TextThought (ResizableThought):
self.double_click = False
return ResizableThought.process_button_release(self, event, transformed)
- def selection_changed (self):
- (start, end) = (min(self.index, self.end_index), max(self.index, self.end_index))
+ def selection_changed (self):
+ # Fix me: We are forcing selection to entire buffer
+ # (start, end) = (min(self.index, self.end_index), max(self.index, self.end_index))
+ start, end = 0, len(self.text)
self.emit ("text_selection_changed", start, end, self.text[start:end])
- def handle_motion (self, event, transformed):
+ def handle_motion (self, event, transformed):
if ResizableThought.handle_motion(self, event, transformed):
self.recalc_edges()
return True
+ if self.textview is not None and self.ul is not None:
+ x, y, w, h = self.textview_rescale()
+ self.textview.set_size_request(w, h)
+ self._fixed.move(self.textview, x, y)
+
if not self.editing or self.resizing:
return False
@@ -784,7 +946,7 @@ class TextThought (ResizableThought):
return False
- def export (self, context, move_x, move_y):
+ def export (self, context, move_x, move_y):
utils.export_thought_outline (context, self.ul, self.lr, self.background_color, self.am_selected, self.am_primary, utils.STYLE_NORMAL,
(move_x, move_y))
@@ -867,7 +1029,7 @@ class TextThought (ResizableThought):
if not it.next():
break
- def rebuild_byte_table (self):
+ def rebuild_byte_table (self):
# Build the Byte table
del self.bytes
self.bytes = ''
@@ -941,34 +1103,18 @@ class TextThought (ResizableThought):
self.recalc_edges()
def copy_text (self, clip):
- if self.end_index > self.index:
- clip.set_text (self.text[self.index:self.end_index])
- else:
- clip.set_text (self.text[self.end_index:self.index])
+ self._clipboard = clip
+ self._textview_copy_cb()
def cut_text (self, clip):
- if self.end_index > self.index:
- clip.set_text (self.text[self.index:self.end_index])
- else:
- clip.set_text (self.text[self.end_index:self.index])
- self.delete_char ()
- self.recalc_edges ()
- self.emit ("title_changed", self.text)
- self.bindex = self.bindex_from_index (self.index)
- self.emit ("update_view")
+ self._clipboard = clip
+ self._textview_copy_cb()
def paste_text (self, clip):
- text = clip.wait_for_text()
- if not text:
- return
- self.add_text (text)
- self.rebuild_byte_table ()
- self.recalc_edges ()
- self.emit ("title_changed", self.text)
- self.bindex = self.bindex_from_index (self.index)
- self.emit ("update_view")
+ self._clipboard = clip
+ self._textview_paste_cb()
- def delete_surroundings(self, imcontext, offset, n_chars, mode):
+ def delete_surroundings(self, imcontext, offset, n_chars, mode):
# TODO: Add in Attr stuff
orig = len(self.text)
left = self.text[:offset]
@@ -979,7 +1125,7 @@ class TextThought (ResizableThought):
new = len(self.text)
if self.index > len(self.text):
self.index = len(self.text)
-
+
change = old - new
changes = []
old_attrs = []
@@ -1025,26 +1171,27 @@ class TextThought (ResizableThought):
changes.append(x)
if it.next() == False:
break
-
+
del self.attributes
self.attributes = pango.AttrList()
map(lambda x : self.attributes.change(x), changes)
self.recalc_edges ()
self.undo.add_undo (UndoManager.UndoAction (self, UndoManager.DELETE_LETTER, self.undo_text_action,
- self.b_f_i (offset), local_text, len(local_text), local_bytes, old_attrs, changes))
+ self.b_f_i (offset), local_text, len(local_text),
+ local_bytes, old_attrs, changes))
self.emit ("title_changed", self.text)
self.bindex = self.bindex_from_index (self.index)
self.emit ("update_view")
- def preedit_changed (self, imcontext, mode):
+ def preedit_changed (self, imcontext, mode):
self.preedit = imcontext.get_preedit_string ()
if self.preedit[0] == '':
self.preedit = None
self.recalc_edges ()
self.emit ("update_view")
- def retrieve_surroundings (self, imcontext, mode):
+ def retrieve_surroundings (self, imcontext, mode):
imcontext.set_surrounding (self.text, -1, self.bindex)
return True
@@ -1071,18 +1218,18 @@ class TextThought (ResizableThought):
self.recalc_edges()
self.emit("update_view")
self.undo.unblock()
-
- def create_attribute(self, attribute, start, end):
+
+ def create_attribute(self, attribute, start, end):
if attribute == 'bold':
return pango.AttrWeight(pango.WEIGHT_BOLD, start, end)
elif attribute == 'italic':
return pango.AttrStyle(pango.STYLE_ITALIC, start, end)
elif attribute == 'underline':
return pango.AttrUnderline(pango.UNDERLINE_SINGLE, start, end)
-
- def set_attribute(self, active, attribute):
- if not self.editing:
- return
+
+ def set_attribute(self, active, attribute):
+ # if not self.editing:
+ # return
if attribute == 'bold':
pstyle, ptype, pvalue = (pango.WEIGHT_NORMAL, pango.ATTR_WEIGHT, pango.WEIGHT_BOLD)
@@ -1090,14 +1237,18 @@ class TextThought (ResizableThought):
pstyle, ptype, pvalue = (pango.STYLE_NORMAL, pango.ATTR_STYLE, pango.STYLE_ITALIC)
elif attribute == 'underline':
pstyle, ptype, pvalue = (pango.UNDERLINE_NONE, pango.ATTR_UNDERLINE, pango.UNDERLINE_SINGLE)
-
+
+ # Always modify whole string
+ self.index = 0
+ self.end_index = len(self.text)
+
index, end_index = (self.index, self.end_index)
init = min(index, end_index)
end = max(index, end_index)
if not active:
attr = pango.AttrStyle(pstyle, init, end)
- #if index == end_index:
+ #if index == end_index:
# self.current_attrs.change(attr)
#else:
self.attributes.change(attr)
@@ -1112,15 +1263,15 @@ class TextThought (ResizableThought):
tmp.append(x)
self.current_attrs = tmp
self.recalc_edges()
- self.undo.add_undo(UndoManager.UndoAction(self, UNDO_REMOVE_ATTR, \
- self.undo_attr_cb,\
- attr))
+ self.undo.add_undo(UndoManager.UndoAction(self, UNDO_REMOVE_ATTR,
+ self.undo_attr_cb,
+ attr))
return
-
+
it = self.attributes.get_iterator()
old_attrs = self.attributes.copy()
changed = []
-
+
while True:
r = it.range()
if r[0] <= init and r[1] >= end:
@@ -1135,7 +1286,7 @@ class TextThought (ResizableThought):
if not it.next():
break
-
+
del self.attributes
self.attributes = pango.AttrList()
map(lambda x : self.attributes.change(x), changed)
@@ -1145,77 +1296,97 @@ class TextThought (ResizableThought):
tmp.append(x)
self.current_attrs = tmp
self.undo.add_undo(UndoManager.UndoAction(self, UNDO_REMOVE_ATTR_SELECTION,
- self.undo_attr_cb,
- old_attrs,
- self.attributes.copy()))
+ self.undo_attr_cb,
+ old_attrs,
+ self.attributes.copy()))
else:
- if index == end_index:
+ if index == end_index:
attr = self.create_attribute(attribute, index, end_index)
self.undo.add_undo(UndoManager.UndoAction(self, UNDO_ADD_ATTR,
- self.undo_attr_cb,
- attr))
+ self.undo_attr_cb,
+ attr))
self.current_attrs.append(attr)
#self.attributes.insert(attr)
- else:
- attr = self.create_attribute(attribute, init, end)
+ else:
+ attr = self.create_attribute(attribute, init, end)
old_attrs = self.attributes.copy()
self.attributes.change(attr)
self.undo.add_undo(UndoManager.UndoAction(self, UNDO_ADD_ATTR_SELECTION,
- self.undo_attr_cb,
- old_attrs,
- self.attributes.copy()))
+ self.undo_attr_cb,
+ old_attrs,
+ self.attributes.copy()))
self.recalc_edges()
+ self.remove_textview()
- def set_bold (self, active):
+ def set_bold (self, active):
self.set_attribute(active, 'bold')
-
- def set_italics (self, active):
+
+ def set_italics (self, active):
self.set_attribute(active, 'italic')
- def set_underline (self, active):
+ def set_underline (self, active):
self.set_attribute(active, 'underline')
- def set_font (self, font_name, font_size):
- if not self.editing:
- return
-
+ def set_font (self, font_name, font_size):
+ # With textview, we are always editing
+ # if not self.editing:
+ # return
+
+ # Always modify whole string
+ self.index = 0
+ self.end_index = len(self.text)
+
start = min(self.index, self.end_index)
end = max(self.index, self.end_index)
-
- pango_font = pango.FontDescription(font_name+" "+font_size)
-
+
+
+ pango_font = pango.FontDescription('%s %s' % (font_name, str(font_size)))
+
attr = pango.AttrFontDesc (pango_font, start, end)
if start == end:
self.undo.add_undo(UndoManager.UndoAction(self, UNDO_ADD_ATTR,
- self.undo_attr_cb,
- attr))
- try:
+ self.undo_attr_cb,
+ attr))
+ try:
self.current_attrs.change(attr)
- except AttributeError:
+ except AttributeError:
self.current_attrs.append(attr)
else:
old_attrs = self.attributes.copy()
self.attributes.change(attr)
self.undo.add_undo(UndoManager.UndoAction(self, UNDO_ADD_ATTR_SELECTION,
- self.undo_attr_cb,
- old_attrs,
- self.attributes.copy()))
+ self.undo_attr_cb,
+ old_attrs,
+ self.attributes.copy()))
self.recalc_edges()
+ self.remove_textview()
- def inside(self, inside):
+ def inside(self, inside):
+ # FIXME: with switch to textview, we don't need cursor update
if self.editing:
+ if self.textview is not None:
+ self.textview.grab_focus()
self.emit ("change_mouse_cursor", gtk.gdk.XTERM)
else:
ResizableThought.inside(self, inside)
- def enter(self):
+ def enter(self):
if self.editing:
return
self.orig_text = self.text
self.editing = True
- def leave(self):
+ def remove_textview(self):
+ if self.textview is not None:
+ self._textview_process()
+ self._textview_handler = None
+ self.textview.hide()
+ self.textview.destroy()
+ self.textview = None
+
+ def leave(self):
+ self.remove_textview()
if not self.editing:
return
ResizableThought.leave(self)
diff --git a/src/utils.py b/src/utils.py
index 4f19d92..559d960 100644
--- a/src/utils.py
+++ b/src/utils.py
@@ -72,7 +72,7 @@ selected_colors = {
default_font = None
-default_font_size = 10
+default_font_size = '10'
try:
# Sugar specific tweak