Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWalter Bender <walter.bender@gmail.com>2013-02-22 20:16:03 (GMT)
committer Walter Bender <walter.bender@gmail.com>2013-02-22 20:16:03 (GMT)
commit1e622866f18294b956faa178c0a8675d3dac4315 (patch)
tree6b3a46ce4f02ab7cc5c890f4a8f3e3653d9f2dd7
parent2aa74e9ac304a5d56af4a5b83047c85f173765ed (diff)
changes in support of touch
-rw-r--r--icons/move-active.svg56
-rw-r--r--icons/move.svg28
-rw-r--r--labyrinthactivity.py101
-rw-r--r--src/MMapArea.py24
-rw-r--r--src/TextThought.py277
5 files changed, 370 insertions, 116 deletions
diff --git a/icons/move-active.svg b/icons/move-active.svg
new file mode 100644
index 0000000..f33ba33
--- /dev/null
+++ b/icons/move-active.svg
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ version="1.1"
+ width="55"
+ height="55"
+ id="svg2">
+ <metadata
+ id="metadata18">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs16" />
+ <rect
+ width="54.388165"
+ height="54.388165"
+ ry="6.1525073"
+ x="0.30591774"
+ y="0.30591777"
+ id="rect2995"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.61183548;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <g
+ id="g4"
+ style="stroke:#000000;stroke-opacity:1">
+ <path
+ d="M 47.66325,27.5 28,27.5 m 12,-6 8,6 -8,6"
+ id="path6"
+ style="fill:none;stroke:#000000;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="M 7.33675,27.5 27,27.5 m -12,-6 -8,6 8,6"
+ id="path8"
+ style="fill:none;stroke:#000000;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="M 27.5,7.33675 27.5,27 m -6,-12 6,-8 6,8"
+ id="path10"
+ style="fill:none;stroke:#000000;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="M 27.5,47.66325 27.5,28 m -6,12 6,8 6,-8"
+ id="path12"
+ style="fill:none;stroke:#000000;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ </g>
+</svg>
diff --git a/icons/move.svg b/icons/move.svg
new file mode 100644
index 0000000..2e84448
--- /dev/null
+++ b/icons/move.svg
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ version="1.1"
+ width="55"
+ height="55">
+ <g
+ style="stroke:#ffffff;stroke-opacity:1">
+ <path
+ d="M 47.66325,27.5 28,27.5 m 12,-6 8,6 -8,6"
+ style="fill:none;stroke:#ffffff;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"/>
+ <path
+ d="M 7.33675,27.5 27,27.5 m -12,-6 -8,6 8,6"
+ style="fill:none;stroke:#ffffff;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"/>
+ <path
+ d="M 27.5,7.33675 27.5,27 m -6,-12 6,-8 6,8"
+ style="fill:none;stroke:#ffffff;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"/>
+ <path
+ d="M 27.5,47.66325 27.5,28 m -6,12 6,8 6,-8"
+ style="fill:none;stroke:#ffffff;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"/>
+ </g>
+</svg>
diff --git a/labyrinthactivity.py b/labyrinthactivity.py
index 3340bc6..a17eb93 100644
--- a/labyrinthactivity.py
+++ b/labyrinthactivity.py
@@ -37,14 +37,16 @@ from sugar.datastore import datastore
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'))
@@ -128,14 +130,23 @@ class ViewToolbar(gtk.Toolbar):
def __zoom_in_cb(self, button):
self._main_area.scale_fac *= 1.2
self._main_area.invalidate()
+ if len(self._main_area.selected) == 1:
+ if hasattr(self._main_area.selected[0], 'textview'):
+ self._main_area.selected[0].remove_textview()
def __zoom_out_cb(self, button):
self._main_area.scale_fac /= 1.2
self._main_area.invalidate()
+ if len(self._main_area.selected) == 1:
+ if hasattr(self._main_area.selected[0], 'textview'):
+ self._main_area.selected[0].remove_textview()
def __zoom_original_cb(self, button):
self._main_area.scale_fac = 1.0
self._main_area.invalidate()
+ if len(self._main_area.selected) == 1:
+ if hasattr(self._main_area.selected[0], 'textview'):
+ self._main_area.selected[0].remove_textview()
def __zoom_tofit_cb(self, button):
bounds = self.__get_thought_bounds()
@@ -143,6 +154,9 @@ class ViewToolbar(gtk.Toolbar):
self._main_area.translation[1] = bounds['y']
self._main_area.scale_fac = bounds['scale']
self._main_area.invalidate()
+ if len(self._main_area.selected) == 1:
+ if hasattr(self._main_area.selected[0], 'textview'):
+ self._main_area.selected[0].remove_textview()
def __get_thought_bounds(self):
if len(self._main_area.thoughts) == 0:
@@ -326,6 +340,7 @@ class TextAttributesToolbar(gtk.Toolbar):
self._main_area.set_foreground_color(color)
def change_active_font(self):
+ current_font_name = None
current_font = str(self.__attribute_values()["font"])
for index, size in enumerate(self.__font_sizes):
index_size = current_font.find(size)
@@ -397,8 +412,7 @@ 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()
activity_button = ActivityToolbarButton(self)
@@ -447,9 +461,6 @@ class LabyrinthActivity(activity.Activity):
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)
-
self.mods = [None] * 6
self.thought_toolbar = ToolbarButton()
self.thought_toolbar.props.page = ThoughtsToolbar(self)
@@ -457,22 +468,12 @@ class LabyrinthActivity(activity.Activity):
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)
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)
@@ -497,7 +498,6 @@ class LabyrinthActivity(activity.Activity):
self.edit_toolbar.show()
target_toolbar = self.edit_toolbar
- tool_offset = 0
self._undo = UndoManager.UndoManager(self,
self.edit_toolbar.undo.child,
@@ -518,23 +518,39 @@ class LabyrinthActivity(activity.Activity):
self.mods[0] = ToolButton('select-mode')
self.mods[0].set_tooltip(_('Edit mode'))
- self.mods[0].set_accelerator(_('<ctrl>e'))
+ # 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)
+ target_toolbar.insert(self.mods[0], -1)
tool = ToolButton('link')
tool.set_tooltip(_('Link/unlink two selected thoughts'))
- tool.set_accelerator(_('<ctrl>l'))
+ # tool.set_accelerator(_('<ctrl>l'))
tool.connect('clicked', self.__link_cb)
- target_toolbar.insert(tool, tool_offset + 1)
+ target_toolbar.insert(tool, -1)
+
+ self.move_button = ToolButton('move')
+ self.move_button.set_tooltip(_('Move selected though'))
+ # tool.set_accelerator(_('<ctrl>m'))
+ self.move_button.connect('clicked', self.__move_cb)
+ target_toolbar.insert(self.move_button, -1)
+
+ separator = gtk.SeparatorToolItem()
+ target_toolbar.insert(separator, -1)
tool = ToolButton('edit-delete')
tool.set_tooltip(_('Erase selected thought(s)'))
tool.connect('clicked', self.__delete_cb)
- target_toolbar.insert(tool, tool_offset + 2)
+ target_toolbar.insert(tool, -1)
+
+ if HASTOOLBARBOX:
+ separator = gtk.SeparatorToolItem()
+ separator.props.draw = False
+ separator.set_expand(True)
+ separator.show()
+ toolbar_box.toolbar.insert(separator, -1)
+
+ tool = StopButton(self)
+ toolbar_box.toolbar.insert(tool, -1)
self.show_all()
self._mode = MMapArea.MODE_TEXT
@@ -543,6 +559,10 @@ class LabyrinthActivity(activity.Activity):
self.set_focus_child(self._main_area)
def __build_main_canvas_area(self):
+ self.fixed = gtk.Fixed()
+ self.fixed.show()
+ self.set_canvas(self.fixed)
+
self._undo.block()
self._main_area = MMapArea.MMapArea(self._undo)
self._main_area.connect("set_focus", self.__main_area_focus_cb)
@@ -553,12 +573,19 @@ 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)
+
+ self._vbox = gtk.VBox()
+ self._vbox.set_size_request(gtk.gdk.screen_width(),
+ gtk.gdk.screen_height())
+ self._vbox.pack_end(self._main_area, True, True)
+ self.fixed.put(self._vbox, 0, 0)
+ self._vbox.show()
self._undo.unblock()
def __text_selection_cb(self, thought, start, end, text):
"""Update state of copy button based on text selection
"""
+ logging.debug('text_selection_cb %d %d %s' % (start, end, text))
if start != end:
self.__change_copy_state(True)
self.text_format_toolbar.props.page.change_active_font()
@@ -707,7 +734,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,8 +759,6 @@ 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):
@@ -745,6 +772,8 @@ class LabyrinthActivity(activity.Activity):
tar.write('MANIFEST', manifest)
self._main_area.save_thyself(tar)
+ self.mods[self._mode].set_active(True)
+
tar.close()
def serialize_to_xml(self, doc, top_element):
@@ -765,5 +794,9 @@ class LabyrinthActivity(activity.Activity):
def __link_cb(self, widget):
self._main_area.link_menu_cb()
+ def __move_cb(self, widget):
+ self.move_button.set_icon('move-active')
+ self._main_area.move_menu_cb(self.move_button)
+
def __delete_cb(self, widget):
self._main_area.delete_selected_elements()
diff --git a/src/MMapArea.py b/src/MMapArea.py
index 838175a..37473a5 100644
--- a/src/MMapArea.py
+++ b/src/MMapArea.py
@@ -147,6 +147,7 @@ class MMapArea (gtk.DrawingArea):
self.commit_handler = None
self.title_change_handler = None
self.moving = False
+ self.move_mode = False
self.move_origin = None
self.move_origin_new = None
self.focus = None
@@ -213,8 +214,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])
@@ -273,7 +277,10 @@ class MMapArea (gtk.DrawingArea):
self.move_action = None
self.moving = False
+ self.move_mode = False
self.move_origin = None
+ if hasattr(self, 'move_button'):
+ self.move_button.set_icon('move')
obj = self.find_object_at (coords)
@@ -733,7 +740,7 @@ 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)
+ 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=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 +781,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 +1189,14 @@ class MMapArea (gtk.DrawingArea):
return True
return False
+ def move_menu_cb (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
+ logging.debug('caching 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..d376da0 100644
--- a/src/TextThought.py
+++ b/src/TextThought.py
@@ -20,7 +20,14 @@
# 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 os
@@ -37,7 +44,7 @@ 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 +63,10 @@ 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
if prefs.get_direction () == gtk.TEXT_DIR_LTR:
self.pango_context.set_base_dir (pango.DIRECTION_LTR)
@@ -71,7 +82,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 +113,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 +134,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 +210,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
@@ -246,12 +257,12 @@ 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()
self.set_font(font_name, font_size)
@@ -260,7 +271,7 @@ class TextThought (ResizableThought):
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 +287,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 +328,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 +340,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 +375,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 +428,7 @@ class TextThought (ResizableThought):
else:
handled = False
- if clear_attrs:
+ if clear_attrs:
del self.current_attrs
self.current_attrs = []
@@ -422,7 +440,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 +460,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 +470,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 +495,7 @@ class TextThought (ResizableThought):
changes= []
old_attrs = []
accounted = -change
-
+
it = self.attributes.get_iterator()
while (1):
(start,end) = it.range()
@@ -516,11 +534,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 +546,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 +612,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 +626,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 +634,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 +642,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 +676,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 +700,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 +715,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 +745,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 +758,87 @@ 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")
+ logging.debug('calling selection_changed')
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)
+ self.textview.modify_font(
+ pango.FontDescription(utils.default_font))
+ 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.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):
+ margin = utils.margin_required(utils.STYLE_NORMAL)
+ w = int((self.width - margin[0] - margin[2]) \
+ * self._parent.scale_fac)
+ h = int((self.height - margin[1] - margin[3]) \
+ * self._parent.scale_fac)
+ xo = gtk.gdk.screen_width() \
+ * (1. - self._parent.scale_fac) / 2.
+ yo = gtk.gdk.screen_height() \
+ * (1. - self._parent.scale_fac) / 2.
+ x = (self.ul[0] + margin[0]) * self._parent.scale_fac
+ y = (self.ul[1] + margin[1]) * self._parent.scale_fac
+ return int(x + xo), int(y + yo), int(w), int(h)
+
+ def _textview_copy_cb(self, widget=None, event=None):
+ logging.debug('copy')
+ return False
+
+ def _textview_paste_cb(self, widget=None, event=None):
+ logging.debug('paste')
+ return False
+
+ def _textview_select_cb(self, widget=None, event=None):
+ logging.debug('select all')
+ return False
+
+ 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 +853,21 @@ class TextThought (ResizableThought):
self.double_click = False
return ResizableThought.process_button_release(self, event, transformed)
- def selection_changed (self):
+ def selection_changed (self):
(start, end) = (min(self.index, self.end_index), max(self.index, self.end_index))
+ logging.debug('emit text_selection_changed')
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 +886,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 +969,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,12 +1043,14 @@ class TextThought (ResizableThought):
self.recalc_edges()
def copy_text (self, clip):
+ logging.debug('copy text')
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])
def cut_text (self, clip):
+ logging.debug('cut text')
if self.end_index > self.index:
clip.set_text (self.text[self.index:self.end_index])
else:
@@ -958,6 +1062,7 @@ class TextThought (ResizableThought):
self.emit ("update_view")
def paste_text (self, clip):
+ logging.debug('paste text')
text = clip.wait_for_text()
if not text:
return
@@ -968,7 +1073,7 @@ class TextThought (ResizableThought):
self.bindex = self.bindex_from_index (self.index)
self.emit ("update_view")
- 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 +1084,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,7 +1130,7 @@ 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)
@@ -1037,14 +1142,14 @@ class TextThought (ResizableThought):
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 +1176,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 +1195,14 @@ 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)
-
+
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)
@@ -1116,11 +1221,11 @@ class TextThought (ResizableThought):
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 +1240,7 @@ class TextThought (ResizableThought):
if not it.next():
break
-
+
del self.attributes
self.attributes = pango.AttrList()
map(lambda x : self.attributes.change(x), changed)
@@ -1149,15 +1254,15 @@ class TextThought (ResizableThought):
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.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,
@@ -1165,34 +1270,36 @@ class TextThought (ResizableThought):
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
+
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(font_name+" "+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:
+ try:
self.current_attrs.change(attr)
- except AttributeError:
+ except AttributeError:
self.current_attrs.append(attr)
else:
old_attrs = self.attributes.copy()
@@ -1203,19 +1310,31 @@ class TextThought (ResizableThought):
self.attributes.copy()))
self.recalc_edges()
- 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)