# Copyright (c) 2007 Mitchell N. Charity
# Copyright (c) 2009-2011 Walter Bender
#
# This file is part of Ruler.
#
# Ruler 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 3 of the License, or
# (at your option) any later version.
#
# Ruler 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 Ruler. If not, see
import pygtk
pygtk.require('2.0')
import gtk
import gobject
import cairo
import os.path
import sugar
from sugar.activity import activity
try: # 0.86+ toolbar widgets
from sugar.graphics.toolbarbox import ToolbarBox
HAS_TOOLARBOX = True
except ImportError:
HAS_TOOLARBOX = False
if HAS_TOOLARBOX:
from sugar.bundle.activitybundle import ActivityBundle
from sugar.activity.widgets import ActivityToolbarButton
from sugar.activity.widgets import StopButton
from sugar.graphics.toolbarbox import ToolbarButton
from sugar.graphics.radiotoolbutton import RadioToolButton
from sugar.graphics.toolbutton import ToolButton
from sugar.graphics.menuitem import MenuItem
from sugar.graphics.icon import Icon
from sugar.datastore import datastore
try:
from sugar.graphics import style
GRID_CELL_SIZE = style.GRID_CELL_SIZE
except ImportError:
GRID_CELL_SIZE = 0
import logging
_logger = logging.getLogger("ruler-activity")
from gettext import gettext as _
from util import get_hardware
import show_rulers
import show_grids
import show_checkers
import show_angles
class MyCanvas(gtk.DrawingArea):
''' Create a GTK+ widget on which we will draw using Cairo '''
def __init__(self):
gtk.DrawingArea.__init__(self)
self._draw_ruler = False
self._object = None
self.connect('expose-event', self.__expose_event_cb)
self.dpi = 200
def __expose_event_cb(self, drawing_area, event):
cr = self.window.cairo_create()
if self._draw_ruler:
self._object.draw(cr, self._dpi)
# Restrict Cairo to the exposed area; avoid extra work
cr.rectangle(event.area.x, event.area.y,
event.area.width, event.area.height)
cr.clip()
def add_a_ruler(self, r):
self._draw_ruler = True
self._object = r
self.queue_draw()
def get_dpi(self):
return self._dpi
def set_dpi(self, dpi):
self._dpi = dpi
#
# Sugar activity
#
class RulerActivity(activity.Activity):
def __init__(self, handle):
super(RulerActivity, self).__init__(handle)
self.button_dict = {}
self.callback_dict = {}
self._ready = False
_font = 'helvetica 12'
_font_bold = 'helvetica bold 12'
#
# We need a canvas
#
self._canvas = MyCanvas()
self.set_canvas(self._canvas)
self._canvas.show()
_width = gtk.gdk.screen_width()
_height = gtk.gdk.screen_height() - GRID_CELL_SIZE
# Read the dpi from the Journal
if get_hardware()[0:2] == 'XO':
self._canvas.set_dpi(200) # OLPC XO
self.known_dpi = True
else:
self.known_dpi = False
try:
dpi = self.metadata['dpi']
_logger.debug("Read dpi: " + str(dpi))
self._canvas.set_dpi(int(dpi))
except KeyError:
self._canvas.set_dpi(96) # Just a guess
# Create instances of our graphics
self._r = show_rulers.ScreenOfRulers(_font, _font_bold, _width,
_height)
self._gcm = show_grids.ScreenGrid_cm(_font, _font_bold, _width,
_height)
self._gmm = show_grids.ScreenGrid_mm(_font, _font_bold, _width,
_height)
self._a90 = show_angles.Angles90(_font, _font_bold, _width, _height)
self._a360 = show_angles.Angles360(_font, _font_bold, _width, _height)
self._c = show_checkers.ScreenOfCircles(_font, _font_bold, _width,
_height)
# start with a ruler
self._current = self._r
self._canvas.add_a_ruler(self._current)
# other settings
self._grids_mode = "cm"
self._angles_mode = "90"
#
# We need some toolbars
#
self.max_participants = 1
if HAS_TOOLARBOX:
# Use 0.86 toolbar design
toolbar_box = ToolbarBox()
# Buttons added to the Activity toolbar
activity_button = ActivityToolbarButton(self)
toolbar_box.toolbar.insert(activity_button, 0)
activity_button.show()
self.rulers = MyButton(self, 'ruler',
icon_name='ruler',
callback=self._rulers_cb,
tooltip=_('Ruler'))
toolbar_box.toolbar.insert(self.rulers, -1)
self.grids = MyButton(self, 'grids',
icon_name='grid-a',
callback=self._grids_cb,
tooltip=_('Grid'),
group=self.rulers)
toolbar_box.toolbar.insert(self.grids, -1)
self.angles = MyButton(self, 'angles',
icon_name='angles-90',
callback=self._angles_cb,
tooltip=_('Angles'),
group=self.rulers)
toolbar_box.toolbar.insert(self.angles, -1)
self.checker = MyButton(self, 'checker',
icon_name='checker',
callback=self._checker_cb,
tooltip=_('Checker'),
group=self.rulers)
toolbar_box.toolbar.insert(self.checker, -1)
if not self.known_dpi:
separator = gtk.SeparatorToolItem()
separator.show()
toolbar_box.toolbar.insert(separator, -1)
dpi = self._canvas.get_dpi()
self._dpi_spin_adj = gtk.Adjustment(dpi, 72, 200, 2, 32, 0)
self._dpi_spin = gtk.SpinButton(self._dpi_spin_adj, 0, 0)
self._dpi_spin_id = self._dpi_spin.connect('value-changed',
self._dpi_spin_cb)
self._dpi_spin.set_numeric(True)
self._dpi_spin.show()
self.tool_item_dpi = gtk.ToolItem()
self.tool_item_dpi.add(self._dpi_spin)
toolbar_box.toolbar.insert(self.tool_item_dpi, -1)
self.tool_item_dpi.show()
separator = gtk.SeparatorToolItem()
separator.props.draw = False
separator.set_expand(True)
separator.show()
toolbar_box.toolbar.insert(separator, -1)
# The ever-present Stop Button
stop_button = StopButton(self)
stop_button.props.accelerator = 'Q'
toolbar_box.toolbar.insert(stop_button, -1)
stop_button.show()
self.set_toolbar_box(toolbar_box)
toolbar_box.show()
else:
# Use pre-0.86 toolbar design
toolbox = activity.ActivityToolbox(self)
self.set_toolbox(toolbox)
self.projectToolbar = ProjectToolbar(self)
toolbox.add_toolbar(_('Rulers'), self.projectToolbar)
toolbox.show()
toolbox.set_current_toolbar(1)
self.show_all()
# Restore state if previously saved
self._ready = True
if 'ruler' in self.metadata and \
self.metadata['ruler'] in self.button_dict:
_logger.debug('restoring %s', self.metadata['ruler'])
if HAS_TOOLARBOX:
self.button_dict[self.metadata['ruler']].set_active(True)
self.callback_dict[self.metadata['ruler']]
else:
self._rulers_cb()
if HAS_TOOLARBOX:
self.rulers.set_active(True)
#
# Button callbacks
#
def _rulers_cb(self, button=None):
if self._ready:
self._current = self._r
self._canvas.add_a_ruler(self._current)
_logger.debug('selecting ruler')
self.metadata['ruler'] = 'ruler'
return False
def _grids_cb(self, button=None):
if self._ready:
if self._grids_mode == "cm":
self._current = self._gcm
if hasattr(self, 'grids'):
self.grids.set_icon("grid-c")
self._grids_mode = "mm"
else:
self._current = self._gmm
if hasattr(self, 'grids'):
self.grids.set_icon("grid-a")
self._grids_mode = "cm"
self._canvas.add_a_ruler(self._current)
_logger.debug('selecting grids')
self.metadata['ruler'] = 'grids'
return False
def _angles_cb(self, button=None):
if self._ready:
if self._angles_mode == "90":
self._current = self._a90
if hasattr(self, 'angles'):
self.angles.set_icon("angles-360")
self._angles_mode = "360"
else:
self._current = self._a360
if hasattr(self, 'angles'):
self.angles.set_icon("angles-90")
self._angles_mode = "90"
self._canvas.add_a_ruler(self._current)
_logger.debug('selecting angles')
self.metadata['ruler'] = 'angles'
return False
def _checker_cb(self, button=None):
if self._ready:
self._current = self._c
self._canvas.add_a_ruler(self._current)
_logger.debug('selecting checker')
self.metadata['ruler'] = 'checker'
return False
def _dpi_spin_cb(self, button):
self._canvas.set_dpi(self._dpi_spin.get_value_as_int())
self._canvas.add_a_ruler(self._current)
return
def write_file(self, file_path):
''' Write the dpi to the Journal '''
dpi = self._canvas.get_dpi()
_logger.debug("Write dpi: " + str(dpi))
self.metadata['dpi'] = str(dpi)
class ProjectToolbar(gtk.Toolbar):
''' Project toolbar for pre-0.86 toolbars '''
def __init__(self, pc):
gtk.Toolbar.__init__(self)
self.activity = pc
# Ruler
self.activity.rulers = ToolButton("ruler")
self.activity.rulers.set_tooltip(_('Ruler'))
self.activity.rulers.props.sensitive = True
self.activity.rulers.connect('clicked', self.activity._rulers_cb)
self.insert(self.activity.rulers, -1)
self.activity.rulers.show()
# Grid
self.activity.grids = ToolButton("grid-a")
self.activity.grids.set_tooltip(_('Grid'))
self.activity.grids.props.sensitive = True
self.activity.grids.connect('clicked', self.activity._grids_cb)
self.insert(self.activity.grids, -1)
self.activity.grids.show()
# Angles
self.activity.angles = ToolButton("angles-90")
self.activity.angles.set_tooltip(_('Angles'))
self.activity.angles.props.sensitive = True
self.activity.angles.connect('clicked', self.activity._angles_cb)
self.insert(self.activity.angles, -1)
self.activity.angles.show()
# Checker
self.activity.checker = ToolButton("checker")
self.activity.checker.set_tooltip(_('Checker'))
self.activity.checker.props.sensitive = True
self.activity.checker.connect('clicked', self.activity._checker_cb)
self.insert(self.activity.checker, -1)
self.activity.checker.show()
if not self.activity.known_dpi:
separator = gtk.SeparatorToolItem()
separator.set_draw(True)
self.insert(separator, -1)
separator.show()
dpi = self.activity._canvas.get_dpi()
self.activity._dpi_spin_adj = gtk.Adjustment(
dpi, 72, 200, 2, 32, 0)
self.activity._dpi_spin = \
gtk.SpinButton(self.activity._dpi_spin_adj, 0, 0)
self.activity._dpi_spin_id = self.activity._dpi_spin.connect(
'value-changed', self.activity._dpi_spin_cb)
self.activity._dpi_spin.set_numeric(True)
self.activity._dpi_spin.show()
self.activity.tool_item_dpi = gtk.ToolItem()
self.activity.tool_item_dpi.add(self.activity._dpi_spin)
self.insert(self.activity.tool_item_dpi, -1)
self.activity.tool_item_dpi.show()
class MyButton(RadioToolButton):
def __init__(self, parent, name, icon_name='', callback=None,
tooltip=None, group=None):
RadioToolButton.__init__(self)
if icon_name == '':
icon_name = 'computer-xo'
icon = Icon(icon_name=icon_name,
icon_size=gtk.ICON_SIZE_LARGE_TOOLBAR)
self.set_icon_widget(icon)
icon.show()
if tooltip is not None:
self.set_tooltip(tooltip)
self.props.sensitive = True
self.connect('clicked', callback)
self.set_group(group)
self.show()
parent.button_dict[name] = self
parent.callback_dict[name] = callback
def set_icon(self, name):
icon = Icon(icon_name=name,
icon_size=gtk.ICON_SIZE_LARGE_TOOLBAR)
self.set_icon_widget(icon)
icon.show()