Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/gui/page.py
diff options
context:
space:
mode:
Diffstat (limited to 'gui/page.py')
-rw-r--r--gui/page.py357
1 files changed, 357 insertions, 0 deletions
diff --git a/gui/page.py b/gui/page.py
new file mode 100644
index 0000000..54dd596
--- /dev/null
+++ b/gui/page.py
@@ -0,0 +1,357 @@
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+
+import os
+import gtk
+import cairo # for getting png for CanvasImage
+import pango
+import hippo
+import logging
+import StringIO
+
+from gettext import gettext as _
+
+from sugar.graphics import style
+from sugar.graphics.objectchooser import ObjectChooser
+
+# argh!
+try:
+ from sugar.graphics.roundbox import RoundBox
+except ImportError:
+ try:
+ from sugar.graphics.roundbox import CanvasRoundBox as RoundBox
+ except ImportError:
+ from sugar.graphics.canvasroundbox import CanvasRoundBox as RoundBox
+
+
+from util.persistence import Persistent, PersistentProperty
+
+from util.audioplayer import AudioPlayer
+
+
+from gui import theme
+from globals import Globals
+
+
+class Page(hippo.CanvasBox):
+
+ def __init__(self, **kwargs):
+ hippo.CanvasBox.__init__(self, **kwargs)
+
+ self.__alternate_color_listrows = False
+ self.__color_listrow = theme.COLOR_LIST_ROW_ALT.get_int()
+
+
+ def append(self, item, **kwargs):
+ hippo.CanvasBox.append(self, item, **kwargs)
+
+
+ @property
+ def color_listrow(self):
+ if not self.__alternate_color_listrows:
+ return theme.COLOR_LIST_ROW.get_int()
+ if self.__color_listrow == theme.COLOR_LIST_ROW_ALT.get_int():
+ self.__color_listrow = theme.COLOR_LIST_ROW.get_int()
+ else:
+ self.__color_listrow = theme.COLOR_LIST_ROW_ALT.get_int()
+ return self.__color_listrow
+
+
+ def make_listrow(self, contents = None):
+ list_row = RoundBox()
+ list_row.props.border = 0 # properties not being set properly by constructor
+ list_row.props.padding = theme.DEFAULT_PADDING
+ list_row.props.padding_right=0
+ list_row.props.background_color = self.color_listrow
+ if contents is not None:
+ list_row.append(contents)
+ return list_row
+
+
+ def make_audiobox(self, obj, property, width):
+
+ image_file = os.path.join(Globals.pwd, theme.AUDIO_CHOOSE)
+ if not os.path.exists(image_file):
+ logging.debug('cannot find %s' % image_file)
+ return hippo.CanvasBox()
+
+ surface = cairo.ImageSurface.create_from_png(image_file)
+ preview_sound = hippo.CanvasImage(image=surface,
+ xalign=hippo.ALIGNMENT_START,
+ yalign=hippo.ALIGNMENT_CENTER)
+ preview_sound.connect('button-press-event', self.__do_clicked_preview_sound, obj, property)
+
+ if hasattr(obj, property) and getattr(obj, property) != None:
+ sound_name = getattr(obj, property)
+ else:
+ sound_name = _('Click to choose a sound')
+
+ choose_sound = hippo.CanvasText(text=sound_name,
+ xalign=hippo.ALIGNMENT_START,
+ font_desc=theme.FONT_BODY.get_pango_desc())
+ choose_sound.connect('button-press-event', self.__do_clicked_choose_sound, obj, property)
+
+ sound_box = RoundBox()
+ sound_box.props.padding = 2
+ sound_box.props.spacing = 10
+ sound_box.props.box_width = width
+ sound_box.props.border=theme.BORDER_WIDTH_CONTROL / 2
+ sound_box.props.border_color=theme.COLOR_DARK_GREEN.get_int()
+ sound_box.props.orientation=hippo.ORIENTATION_HORIZONTAL
+ sound_box.props.xalign=hippo.ALIGNMENT_START
+ sound_box.append(preview_sound)
+ sound_box.append(choose_sound)
+
+ deglitch_box = hippo.CanvasBox(xalign=hippo.ALIGNMENT_START, box_width=width)
+ deglitch_box.append(sound_box)
+ return deglitch_box
+
+
+ def make_imagebox(self, obj, property = None, width=-1, height=-1, editable=True, padding=0):
+ image = self.__get_property_value(obj, property)
+ if image == '' or image == None:
+ image = theme.IMAGE_CHOOSE
+ file_name = os.path.join(Globals.pwd, image)
+ logging.debug('make_imagebox(%r)' % file_name)
+
+ # TODO -> handle landscape/portrait properly
+
+ # load image - could be cleaner on the whole... :)
+ try:
+ if hasattr(obj, 'image_blob') and getattr(obj, 'image_blob') is not None:
+ image_file = StringIO.StringIO(obj.image_blob)
+ surface = cairo.ImageSurface.create_from_png(image_file)
+ else:
+ surface = cairo.ImageSurface.create_from_png(file_name)
+ except Exception, e:
+ logging.error('Error while loading image: %r' % e)
+
+ # set border
+ if editable:
+ border_width = 0
+ else:
+ border_width = theme.BORDER_WIDTH_IMAGE
+
+ # the image itself
+ cover_image = hippo.CanvasImage(image=surface,
+ border=border_width,
+ border_color=theme.COLOR_BLACK.get_int(),
+ xalign=hippo.ALIGNMENT_CENTER,
+ yalign=hippo.ALIGNMENT_CENTER,
+ scale_width=width,
+ scale_height=height)
+ if editable:
+ cover_image.set_clickable(True)
+ cover_image.connect('button-press-event', self.__do_clicked_image, obj, 'image_blob')
+ image_box = RoundBox()
+ image_box.props.padding = 0
+ image_box.props.spacing = 0
+ image_box.props.border=theme.BORDER_WIDTH_CONTROL
+ image_box.props.border_color=theme.COLOR_DARK_GREEN.get_int()
+ image_box.append(cover_image)
+ else:
+ image_box = cover_image
+
+ # Grrr... RoundedBoxes and CanvasImages expand their width to their parent
+ # unless they're in a CanvasBox
+ deglitch_box = hippo.CanvasBox(xalign=hippo.ALIGNMENT_CENTER, padding=padding)
+ deglitch_box.append(image_box)
+ return deglitch_box
+
+
+ def make_bodytext(self, text, width=-1, xalign = hippo.ALIGNMENT_START, text_color = theme.COLOR_BLACK):
+ return hippo.CanvasText(text=text,
+ size_mode=hippo.CANVAS_SIZE_WRAP_WORD,
+ box_width=width,
+ xalign=xalign,
+ color=text_color.get_int(),
+ font_desc=theme.FONT_BODY.get_pango_desc())
+
+
+ def make_textbox(self, obj, property, width=300, height=100, editable=True):
+ value = self.__get_property_value(obj, property)
+ textbox = self.__textview(value, width, height, editable, True)
+ textbox.control.get_buffer().connect('changed', self.__do_changed_control, obj, property)
+ return textbox
+
+
+ def make_field(self, label, label_width, obj, property, field_width, editable=True):
+ value = self.__get_property_value(obj, property)
+ field_box = hippo.CanvasBox(orientation=hippo.ORIENTATION_HORIZONTAL,
+ xalign=hippo.ALIGNMENT_START,
+ spacing=10)
+ field_box.append(hippo.CanvasText(text=label,
+ box_width=label_width,
+ xalign=hippo.ALIGNMENT_START,
+ color=theme.COLOR_DARK_GREEN.get_int(),
+ font_desc=theme.FONT_BODY.get_pango_desc()))
+ #if editable:
+ textfield = self.__textview(value, field_width, -1, editable, False)
+ textfield.control.get_buffer().connect('changed', self.__do_changed_control, obj, property)
+ field_box.append(textfield)
+ #else: # TODO - move to __textview()
+ #glitch_box = CanvasBox(box_width=field_width)
+ #glitch_box.append(hippo.CanvasText(text=value,
+ #size_mode=hippo.CANVAS_SIZE_WRAP_WORD,
+ #box_width=field_width,
+ #xalign=hippo.ALIGNMENT_START,
+ #font_desc=theme.FONT_BODY.get_pango_desc()))
+ #field_box.append(glitch_box)
+ return field_box
+
+
+ # Refactor into a CanvasTextView class
+ # TODO: Implement editable and multiline
+ # TODO: Lose multiline and change height variable to num_lines
+ def __textview(self, text, width=300, height=-1, editable=True, multiline=False):
+ textview = gtk.TextView()
+ textview.get_buffer().set_text(text)
+
+ # control props
+ textview.set_wrap_mode(gtk.WRAP_WORD)
+ font = font_desc=theme.FONT_TEXTBOX.get_pango_desc()
+ font.set_weight(pango.WEIGHT_LIGHT)
+ textview.modify_font(font)
+ textview.modify_base(gtk.STATE_NORMAL, theme.COLOR_TEXTBOX.get_gdk_color())
+ textview.set_editable(editable)
+ textview.set_cursor_visible(editable)
+ if height == -1:
+ context = textview.create_pango_context()
+ layout = pango.Layout(context)
+ layout.set_font_description(font) # TODO theme.FONT_BODY should be a pango.FontDescription, not a string
+ layout.set_text(text[ : text.find('\n')])
+ (w, h) = layout.get_pixel_size()
+ height = h #+ theme.BORDER_WIDTH_CONTROL / 2 # fudge factor - on the XO-1 hardware all known solutions evaporate
+ textview.set_size_request(width, height)
+ textview.set_border_window_size(gtk.TEXT_WINDOW_LEFT, 0)
+ textview.set_border_window_size(gtk.TEXT_WINDOW_RIGHT, 0)
+ textview.set_border_window_size(gtk.TEXT_WINDOW_TOP, 0)
+ textview.set_border_window_size(gtk.TEXT_WINDOW_BOTTOM, 0)
+ textview.show()
+
+ if editable: # because rounded corners are well... pretty
+ border_box = RoundBox()
+ border_box.control = textview
+ border_box.props.padding = 2
+ border_box.props.spacing = 0
+ border_box.props.border=theme.BORDER_WIDTH_CONTROL / 2
+ border_box.props.border_color=theme.COLOR_DARK_GREEN.get_int()
+ border_box.props.background_color=theme.COLOR_TEXTBOX.get_int()
+ border_box.props.xalign=hippo.ALIGNMENT_START
+ #border_box.props.box_width = width
+ #border_box.props.box_height = height
+
+ # TODO - File bug: RoundBox seriously messes with TextView's
+ # (and other things) width !!
+ deglitch_box = hippo.CanvasBox()
+ deglitch_box.append(hippo.CanvasWidget(widget=textview))
+ border_box.append(deglitch_box)
+ return border_box
+
+ no_edit_box = hippo.CanvasWidget(widget=textview)
+ no_edit_box.control = textview
+ return no_edit_box
+
+
+ def __get_property_value(self, obj, property):
+ # TODO - Clean entire Model/View mechanism up so that we're not
+ # passing objects and text properties around at all
+ if obj is None:
+ return ''
+ if type(obj) is str:
+ return obj
+ if hasattr(obj, '__metaclass__') and obj.__metaclass__ is Persistent and hasattr(obj, property):
+ value = getattr(obj, property)
+ if value is None:
+ return ''
+ return value
+ logging.debug('__get_property_value error: Unknown object type %r', type(obj))
+ return obj
+
+
+ def __do_changed_control(self, control, obj, property):
+ '''Update object model with control values'''
+ if hasattr(obj, property) and hasattr(control.props, 'text'):
+ setattr(obj, property, control.props.text)
+ else:
+ print 'NO PROPERTY OR TEXT'
+
+
+ def __do_clicked_image(self, control, event, obj, property):
+ # Courtesy of Write.activity - toolbar.py
+ chooser = ObjectChooser(_('Choose image'),
+ Globals.JokeMachineActivity, #._parent,
+ gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
+ try:
+ result = chooser.run()
+ if result == gtk.RESPONSE_ACCEPT:
+ logging.debug('ObjectChooser: %r' % chooser.get_selected_object())
+ journal_object = chooser.get_selected_object()
+ if hasattr(obj, 'image_blob') and journal_object and journal_object.file_path:
+ logging.debug('Getting journal object: %r, %s', journal_object, journal_object.file_path)
+ # Set the image now
+ f = open(journal_object.file_path, 'r')
+ raw = f.read()
+ f.close()
+ obj.image = str(journal_object.metadata['title'])
+ obj.image_blob = raw
+ # refresh the image
+ image_file = StringIO.StringIO(obj.image_blob)
+ surface = cairo.ImageSurface.create_from_png(image_file)
+ control.props.image = surface
+ finally:
+ chooser.hide()
+ chooser.destroy()
+ del chooser
+
+
+ def __do_clicked_choose_sound(self, control, event, obj, property):
+ logging.debug('choosing sound file')
+ chooser = ObjectChooser(_('Choose Sound'),
+ Globals.JokeMachineActivity, #._parent,
+ gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
+ try:
+ result = chooser.run()
+ if result == gtk.RESPONSE_ACCEPT:
+ logging.debug('ObjectChooser: %r' % chooser.get_selected_object())
+ journal_object = chooser.get_selected_object()
+ if hasattr(obj, 'sound_blob') and journal_object and journal_object.file_path:
+ logging.debug('Getting journal object: %r, %s, %s', journal_object, journal_object.file_path, journal_object.metadata['title'])
+ # Set the sound now
+ f = open(journal_object.file_path, 'r')
+ raw = f.read()
+ f.close()
+ obj.sound = str(journal_object.metadata['title'])
+ obj.sound_blob = raw
+ control.props.text = obj.sound
+ finally:
+ chooser.hide()
+ chooser.destroy()
+ del chooser
+
+
+ def __do_clicked_preview_sound(self, control, event, obj, property):
+
+ if not hasattr(obj, 'sound_blob') or getattr(obj, 'sound_blob') == None:
+ logging.debug('No sound to preview')
+ return
+
+ player = AudioPlayer()
+ #player.uri = sound_file
+ player.raw = obj.sound_blob
+ player.play()
+
+
+ \ No newline at end of file