Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Francis <francis@sugarlabs.org>2012-10-03 00:23:16 (GMT)
committer Daniel Francis <francis@sugarlabs.org>2012-10-03 00:23:16 (GMT)
commit5ceda38732c57211d3f38e6cbbd7b8a521a0a9e5 (patch)
tree7fa653cb7ccc6d8c4fc9c34967f3b5dd66dbd726
parente1a599bb8508e1a0607e052a2f086289844dee60 (diff)
Start sugarizing; use Gtk3
-rw-r--r--activity.info9
-rw-r--r--activity.py15
-rw-r--r--activity/activity.info1
-rwxr-xr-xapplication.py111
-rw-r--r--canvas.py33
-rw-r--r--defaults.py58
-rw-r--r--desktop/sweetener/basic_options.py13
-rw-r--r--desktop/sweetener/item.py23
-rw-r--r--desktop/sweetener/itembox.py12
-rw-r--r--desktop/sweetener/itemgroup.py25
-rw-r--r--desktop/sweetener/stock.py50
-rw-r--r--number_box.py805
-rw-r--r--options.py2
-rwxr-xr-xsetup.py2
-rw-r--r--sugar/sweetener/basic_options.py2
-rw-r--r--sugar/sweetener/item.py20
-rw-r--r--sugar/sweetener/itembox.py8
-rw-r--r--sugar/sweetener/stock.py51
-rw-r--r--tracker_info.py208
19 files changed, 1278 insertions, 170 deletions
diff --git a/activity.info b/activity.info
deleted file mode 100644
index 475641f..0000000
--- a/activity.info
+++ /dev/null
@@ -1,9 +0,0 @@
-[Activity]
-name = Sudoku
-activity_version = 1
-show_launcher = 1
-bundle_id = org.gnome.Sudoku
-exec = sugar-activity activity.Activity -s
-icon = activity-sudoku
-license = GPLv3
-
diff --git a/activity.py b/activity.py
index c868c66..1c3603c 100644
--- a/activity.py
+++ b/activity.py
@@ -23,13 +23,15 @@ import os
os.environ['PROGRAMRUNNING'] = 'SUGAR'
import logging
logging.basicConfig(level=logging.DEBUG)
-logger = logging.getLogger('graph-plotter')
+logger = logging.getLogger('activity')
from gettext import gettext as _
-import gtk
+from gi.repository import Gtk
+logger.debug('Import basic core')
-from sugar.datastore import datastore
-from sugar.activity import activity
+from sugar3.datastore import datastore
+from sugar3.activity import activity
+logger.debug('Import Sugar toolkit')
try:
import sweetener
@@ -37,12 +39,14 @@ except ImportError:
import sys
sys.path.append(os.path.abspath('./sugar'))
import sweetener
+logger.debug('Import Sweetener')
os.environ['DATA_DIR'] = os.path.abspath('./data')
from options import Options
+logger.debug('Import options')
from canvas import Canvas
-
+logger.debug('Imported extra modules')
class Activity(activity.Activity):
def __init__(self, handle):
@@ -53,6 +57,7 @@ class Activity(activity.Activity):
self.canvas = Canvas(self.options, self)
self.canvas.show()
self.set_canvas(self.canvas)
+ logger.debug('Finish loading')
def export(self, widget, data):
jobject = datastore.create()
diff --git a/activity/activity.info b/activity/activity.info
index 475641f..b103f13 100644
--- a/activity/activity.info
+++ b/activity/activity.info
@@ -1,7 +1,6 @@
[Activity]
name = Sudoku
activity_version = 1
-show_launcher = 1
bundle_id = org.gnome.Sudoku
exec = sugar-activity activity.Activity -s
icon = activity-sudoku
diff --git a/application.py b/application.py
index fe11039..aa4ef72 100755
--- a/application.py
+++ b/application.py
@@ -21,15 +21,16 @@
import os
import logging
logging.basicConfig(level=logging.DEBUG)
-import glib
+from gi.repository import GLib
import info
logger = logging.getLogger(info.lower_name)
appname = info.name
# Some desktops like Gnome3-Shell show the Glib Prgname
-glib.set_prgname(appname)
-glib.set_application_name(appname)
+GLib.set_prgname(appname)
+GLib.set_application_name(appname)
-import gtk
+from gi.repository import Gtk
+from gi.repository import Gdk
from options import Options
from canvas import Canvas
@@ -42,35 +43,22 @@ os.chdir(this_dir)
from gettext import gettext as _
-if 'programabspath' in os.environ['DATADIR']:
- datadir = os.environ['DATADIR'].replace('programabspath',
- this_dir)
-else:
- datadir = os.environ['DATADIR']
-
-if os.environ['ICONDIR'] != 'SYSTEM':
- logger.debug(this_dir)
- icondir = os.environ['ICONDIR'].replace('programabspath',
- this_dir)
- icon_theme = gtk.icon_theme_get_for_screen(gtk.gdk.screen_get_default())
- icon_theme.append_search_path(icondir)
-
-class UnfullscreenButton(gtk.Window):
+class UnfullscreenButton(Gtk.Window):
def __init__(self):
- gtk.Window.__init__(self)
+ Gtk.Window.__init__(self)
self.set_decorated(False)
self.set_resizable(False)
- self.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
+ self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
self.set_border_width(0)
self.props.accept_focus = False
#Setup estimate of width, height
- w, h = gtk.icon_size_lookup(gtk.ICON_SIZE_LARGE_TOOLBAR)
+ w, h = Gtk.icon_size_lookup(Gtk.IconSize.LARGE_TOOLBAR)
self._width = w
self._height = h
@@ -79,8 +67,8 @@ class UnfullscreenButton(gtk.Window):
screen = self.get_screen()
screen.connect('size-changed', self._screen_size_changed_cb)
- self._button = gtk.Button(stock=gtk.STOCK_LEAVE_FULLSCREEN)
- self._button.set_relief(gtk.RELIEF_NONE)
+ self._button = Gtk.Button(stock=Gtk.STOCK_LEAVE_FULLSCREEN)
+ self._button.set_relief(Gtk.ReliefStyle.NONE)
self._button.show()
self.add(self._button)
@@ -100,28 +88,30 @@ class UnfullscreenButton(gtk.Window):
self._reposition()
-class Application(gtk.Window):
+class Application(Gtk.Window):
def __init__(self):
- gtk.Window.__init__(self)
+ Gtk.Window.__init__(self)
self.save_type = info.io_mode
self.started = False
self.file_path = None
+ self.filter = Gtk.FileFilter()
if info.file_filter_name:
- self.filter = gtk.FileFilter()
self.filter.set_name(info.file_filter_name)
if info.file_filter_mime:
self.filter.add_mime_type(info.file_filter_mime)
- for i in info.file_filter_patterns:
- self.filter.add_pattern(i)
- self.accel_group = gtk.AccelGroup()
+ if info.file_filter_pattern:
+ self.filter.add_pattern(info.file_filter_pattern)
+ else:
+ self.filter.set_name(_('All'))
+ self.filter.add_pattern('*')
+ self.accel_group = Gtk.AccelGroup()
self.add_accel_group(self.accel_group)
self.set_title(appname)
logger.debug(info.lower_name)
- self.set_icon(gtk.gdk.pixbuf_new_from_file(os.path.join(datadir,
- 'appicon.svg')))
+ self.set_icon_name('gtkslidy')
self.connect('delete-event', lambda w, e: self.stop())
self.maximize()
- self._vbox = gtk.VBox()
+ self._vbox = Gtk.VBox()
self.options = Options(self)
self.options.show()
self._vbox.pack_start(self.options, False, True, 0)
@@ -132,12 +122,12 @@ class Application(gtk.Window):
self._vbox.show()
self._is_fullscreen = False
- self.set_events(gtk.gdk.ALL_EVENTS_MASK)
+ self.set_events(Gdk.EventMask.ALL_EVENTS_MASK)
self.connect('motion-notify-event', self.motion_cb)
- self.canvas.connect('expose-event', self.expose_event)
+ self.canvas.connect('draw', self.expose_event)
- def expose_event(self, widget, event):
- logger.debug('Exposing')
+ def expose_event(self, widget, context):
+ #logger.debug('Exposing')
if not self.started:
if self.file_path != None:
self.canvas.read_file(self.file_path)
@@ -171,17 +161,19 @@ class Application(gtk.Window):
self.options.show()
self._is_fullscreen = False
self._unfullscreen_button.destroy()
- gtk.Window.unfullscreen(self)
+ Gtk.Window.unfullscreen(self)
def export(self, widget, data):
format_name = data[3]
filter_mime = data[2]
mime_type = data[1]
- filechooser = gtk.FileChooserDialog(_("Export..."), self,
- gtk.FILE_CHOOSER_ACTION_SAVE,
- (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
- gtk.STOCK_OK, gtk.RESPONSE_OK))
- file_filter = gtk.FileFilter()
+ filechooser = Gtk.FileChooserDialog(_("Export..."), self,
+ Gtk.FileChooserAction.SAVE,
+ (Gtk.STOCK_CANCEL,
+ Gtk.ResponseType.CANCEL,
+ Gtk.STOCK_OK,
+ Gtk.ResponseType.OK))
+ file_filter = Gtk.FileFilter()
file_filter.set_name(format_name)
file_filter.add_mime_type(filter_mime)
filechooser.add_filter(file_filter)
@@ -200,18 +192,19 @@ class Application(gtk.Window):
if info.io_mode == info.CONFIG:
self.file_path = os.path.join(os.environ['HOME'], '.' + info.lower_name)
self.save()
- gtk.main_quit()
+ Gtk.main_quit()
def open(self):
- filechooser = gtk.FileChooserDialog(_('Open...'), self,
- gtk.FILE_CHOOSER_ACTION_OPEN,
- (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
- gtk.STOCK_OPEN, gtk.RESPONSE_OK))
+ filechooser = Gtk.FileChooserDialog(_('Open...'), self,
+ Gtk.FileChooserAction.OPEN,
+ (Gtk.STOCK_CANCEL,
+ Gtk.ResponseType.CANCEL,
+ Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
filechooser.add_filter(self.filter)
response = filechooser.run()
self.file_path = filechooser.get_filename()
filechooser.destroy()
- if response == gtk.RESPONSE_OK:
+ if response == Gtk.ResponseType.OK:
logger.debug('Read file %s' % self.file_path)
self.canvas.read_file(self.file_path)
self.set_title('%s - %s' % (self.file_path, appname))
@@ -224,23 +217,25 @@ class Application(gtk.Window):
self.canvas.write_file(self.file_path)
def save_as(self):
- filechooser = gtk.FileChooserDialog(_('Save...'), self,
- gtk.FILE_CHOOSER_ACTION_SAVE,
- (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
- gtk.STOCK_OK, gtk.RESPONSE_OK))
+ filechooser = Gtk.FileChooserDialog(_('Save...'), self,
+ Gtk.FileChooserAction.SAVE,
+ (Gtk.STOCK_CANCEL,
+ Gtk.ResponseType.CANCEL,
+ Gtk.STOCK_OK,
+ Gtk.ResponseType.OK))
filechooser.add_filter(self.filter)
filechooser.set_do_overwrite_confirmation(True)
response = filechooser.run()
self.file_path = filechooser.get_filename()
filechooser.destroy()
- if response == gtk.RESPONSE_OK:
+ if response == Gtk.ResponseType.OK:
logger.debug('Save file as %s' % self.file_path)
self.canvas.write_file(self.file_path)
self.set_title('%s - %s' % (self.file_path, appname))
-if __name__ == '__main__':
- logger.debug('Initializing Graph Plotter')
+def start():
+ logger.debug('Initializing %s' % info.name)
import sys
if info.io_mode == info.DOCUMENT:
if len(sys.argv) > 1:
@@ -262,6 +257,8 @@ if __name__ == '__main__':
window = Application()
window.file_path = file_path
window.show()
- gtk.main()
- logger.debug('Closing Graph Plotter')
+ Gtk.main()
+ logger.debug('Closing')
exit()
+
+start()
diff --git a/canvas.py b/canvas.py
index 34eb00e..ba58b28 100644
--- a/canvas.py
+++ b/canvas.py
@@ -20,13 +20,40 @@
import logging
logger = logging.getLogger('canvas')
-import gtk
+logger.debug('start canvas')
+import math
+from gi.repository import Gtk
+from number_box import SudokuNumberBox
+logger.debug('Import all')
-class Canvas(gtk.AspectFrame):
+class Canvas(Gtk.AspectFrame):
def __init__(self, toolbar_box, activity):
- gtk.AspectFrame.__init__(self)
+ Gtk.AspectFrame.__init__(self)
+ self.group_size = 9
+ self.set_shadow_type(Gtk.ShadowType.NONE)
self.activity = activity
+ self.table = Gtk.Table(rows = self.group_size, columns = self.group_size, homogeneous = True)
+ self.__entries__ = {}
+ for x in range(self.group_size):
+ for y in range(self.group_size):
+ e = SudokuNumberBox(upper = self.group_size)
+ e.x = x
+ e.y = y
+ self.table.attach(e, x, x+1, y, y+1,
+ )
+ self.__entries__[(x, y)] = e
+
+ self.eb = Gtk.EventBox()
+ self.eb.add(self.table)
+ self.add(self.eb)
+ self.table.set_row_spacings(1)
+ self.table.set_col_spacings(1)
+ box_side = int(math.sqrt(self.group_size))
+ for n in range(1, box_side):
+ self.table.set_row_spacing(box_side*n-1, 2)
+ self.table.set_col_spacing(box_side*n-1, 2)
+ self.table.set_border_width(2)
def write_file(self, path):
pass
diff --git a/defaults.py b/defaults.py
new file mode 100644
index 0000000..93c6a53
--- /dev/null
+++ b/defaults.py
@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+#
+# defaults.py.in sets many important default global variables
+# used throughout the game. Note that this file is processed by
+# automake to set prefix paths etc. Please keep defaults.py.in
+# in sync between glchess and gnome-sudoku.
+
+import os
+import sys
+import errno
+import locale
+import gettext
+
+from gi.repository import GLib
+
+try:
+ from defs import VERSION, PREFIX
+except ImportError:
+ PREFIX = "/usr"
+ VERSION = "0.0.0"
+
+root_dir = os.path.dirname(os.path.dirname(__file__))
+if not os.path.exists(os.path.join(root_dir, "Makefile.am")):
+ # Running in installed mode
+ APP_DATA_DIR = os.path.join(PREFIX, 'share')
+ BASE_DIR = os.path.join(APP_DATA_DIR, 'gnome-sudoku')
+ IMAGE_DIR = os.path.join(BASE_DIR, 'images')
+ LOCALEDIR = os.path.join(APP_DATA_DIR, 'locale')
+ UI_DIR = BASE_DIR
+ PUZZLE_DIR = os.path.join(BASE_DIR, 'puzzles')
+else:
+ # Running in uninstalled mode
+ sys.path.insert(0, os.path.abspath(root_dir))
+ APP_DATA_DIR = os.path.join(root_dir, '../data')
+ IMAGE_DIR = os.path.join(root_dir, '../images')
+ LOCALEDIR = os.path.join(APP_DATA_DIR, 'locale')
+ UI_DIR = os.path.join(root_dir, '../data')
+ BASE_DIR = os.path.join(root_dir, '../data')
+ PUZZLE_DIR = BASE_DIR
+
+DOMAIN = 'gnome-games'
+locale.bind_textdomain_codeset(DOMAIN, "UTF-8") # See Bug 608425
+gettext.bindtextdomain(DOMAIN, LOCALEDIR)
+gettext.textdomain(DOMAIN)
+from gettext import gettext as _
+
+APPNAME = _("GNOME Sudoku")
+APPNAME_SHORT = _("Sudoku")
+COPYRIGHT = 'Copyright \xc2\xa9 2005-2008, Thomas M. Hinkle'
+DESCRIPTION = _('GNOME Sudoku is a simple Sudoku generator and player. Sudoku is a Japanese logic puzzle.\n\nGNOME Sudoku is a part of GNOME Games.')
+AUTHORS = ("Thomas M. Hinkle","John Stowers")
+WEBSITE = 'http://www.gnome.org/projects/gnome-games/'
+WEBSITE_LABEL = _('GNOME Games web site')
+AUTO_SAVE = True
+MIN_NEW_PUZZLES = 90
+
+DATA_DIR = os.path.join(GLib.get_user_config_dir(),"gnome-sudoku/")
+
diff --git a/desktop/sweetener/basic_options.py b/desktop/sweetener/basic_options.py
index 83636e1..44869fe 100644
--- a/desktop/sweetener/basic_options.py
+++ b/desktop/sweetener/basic_options.py
@@ -20,7 +20,7 @@ See class BasicOptions.
# MA 02110-1301, USA.
from gettext import gettext as _
-import gtk
+from gi.repository import Gtk
import stock
from item import Item
@@ -42,19 +42,20 @@ class BasicOptions(ItemGroup):
ItemGroup.__init__(self, box, _('_File'), None)
if activity.save_type != CONFIG:
- new = Item(gtk.STOCK_NEW, True)
+ new = Item(Gtk.STOCK_NEW, True)
new.connect('activate', lambda w: activity.new())
self.append_item(new)
- _open = Item(gtk.STOCK_OPEN, True)
+ _open = Item(Gtk.STOCK_OPEN, True)
_open.connect('activate', lambda w: activity.open())
self.append_item(_open)
self.append_separator()
- save_option = Item(gtk.STOCK_SAVE, True)
+ save_option = Item(Gtk.STOCK_SAVE, True)
save_option.connect('activate', lambda w: activity.save())
self.append_item(save_option)
- save_as_option = Item(gtk.STOCK_SAVE_AS)
+ save_as_option = Item(Gtk.STOCK_SAVE_AS)
save_as_option.connect('activate', lambda w: activity.save_as())
self.append_item(save_as_option)
+ self.append_separator()
if export_formats != None:
if len(export_formats) == 1:
stock.register('sweetener-%s' % export_formats[0][1],
@@ -66,6 +67,6 @@ class BasicOptions(ItemGroup):
export_formats[0])
self.append_item(export)
self.append_separator()
- _quit = Item(gtk.STOCK_QUIT)
+ _quit = Item(Gtk.STOCK_QUIT)
_quit.connect('activate', lambda w: activity.stop())
self.append_item(_quit)
diff --git a/desktop/sweetener/item.py b/desktop/sweetener/item.py
index 1b5fc8e..80a2984 100644
--- a/desktop/sweetener/item.py
+++ b/desktop/sweetener/item.py
@@ -21,21 +21,21 @@
import logging
logger = logging.getLogger('option')
-import gobject
-import gtk
+from gi.repository import GObject
+from gi.repository import Gtk
import stock
-class Item(gobject.GObject):
- __gsignals__ = {'activate': (gobject.SIGNAL_RUN_LAST,
- gobject.TYPE_NONE,
+class Item(GObject.GObject):
+ __gsignals__ = {'activate': (GObject.SignalFlags.RUN_LAST,
+ None,
tuple())}
menuitem = None
toolitem = None
def __init__(self, stock_id=None, important=False):
- gobject.GObject.__init__(self)
+ GObject.GObject.__init__(self)
self._stock_id = stock_id
self.accel_group = None
self.important = important
@@ -52,9 +52,10 @@ class Item(gobject.GObject):
stock_id = property(get_stock_id, set_stock_id)
def get_menu_item(self):
- self.menuitem = gtk.ImageMenuItem(self._stock_id)
+ self.menuitem = Gtk.ImageMenuItem.new_from_stock(self._stock_id,
+ self.accel_group)
self.menuitem.connect('activate', self.activate_cb)
- self.setup_accelerator()
+ #self.setup_accelerator()
return self.menuitem
def activate_cb(self, widget):
@@ -66,10 +67,10 @@ class Item(gobject.GObject):
if accelerator[1] > 0:
self.menuitem.add_accelerator('activate',
self.accel_group, accelerator[1], accelerator[0],
- gtk.ACCEL_VISIBLE)
+ Gtk.AccelFlags.VISIBLE)
def get_tool_item(self):
- self.toolitem = gtk.ToolButton(self._stock_id)
+ self.toolitem = Gtk.ToolButton(self._stock_id)
self.toolitem.connect('clicked', self.activate_cb)
self.setup_tooltip()
return self.toolitem
@@ -78,7 +79,7 @@ class Item(gobject.GObject):
if self.tooltip:
self.toolitem.set_tooltip_text(self.tooltip)
else:
- text = gtk.stock_lookup(self.stock_id)[1]
+ text = Gtk.stock_lookup(self.stock_id).label
self.toolitem.set_tooltip_text(text.replace('_', ''))
def emit_signal(self, widget, signal_name):
diff --git a/desktop/sweetener/itembox.py b/desktop/sweetener/itembox.py
index b7ff60d..088996d 100644
--- a/desktop/sweetener/itembox.py
+++ b/desktop/sweetener/itembox.py
@@ -18,16 +18,16 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
-import gtk
+from gi.repository import GObject
+from gi.repository import Gtk
-class ItemBox(gtk.VBox):
+class ItemBox(Gtk.VBox):
def __init__(self, activity):
- gtk.VBox.__init__(self)
+ GObject.GObject.__init__(self)
self._parent = activity
- self.menubar = gtk.MenuBar()
- self.toolbar = gtk.Toolbar()
+ self.menubar = Gtk.MenuBar()
+ self.toolbar = Gtk.Toolbar()
self.pack_start(self.menubar, False, True, 0)
self.pack_start(self.toolbar, False, True, 0)
self.menubar.show()
- self.toolbar.show()
diff --git a/desktop/sweetener/itemgroup.py b/desktop/sweetener/itemgroup.py
index 3e4780e..16ac46c 100644
--- a/desktop/sweetener/itemgroup.py
+++ b/desktop/sweetener/itemgroup.py
@@ -18,18 +18,18 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
-import gobject
-import gtk
+from gi.repository import GObject
+from gi.repository import Gtk
-class ItemGroup(gobject.GObject):
+class ItemGroup(GObject.GObject):
def __init__(self, box, name=None, icon=None):
- gobject.GObject.__init__(self)
+ GObject.GObject.__init__(self)
self.items = []
self.first_important = True
- self.item = gtk.MenuItem(name)
+ self.item = Gtk.MenuItem.new_with_mnemonic(name)
box.menubar.append(self.item)
- self.menu = gtk.Menu()
+ self.menu = Gtk.Menu()
self.item.set_submenu(self.menu)
self.menu.show()
self.item.show()
@@ -44,9 +44,10 @@ class ItemGroup(gobject.GObject):
self.menu.append(menuitem)
if item.important:
if self.first_important and len(self.toolbar):
- separator = gtk.SeparatorToolItem()
+ separator = Gtk.SeparatorToolItem()
separator.show()
self.toolbar.insert(separator, -1)
+ self.toolbar.show()
self.first_important = False
tool_item = item.get_tool_item()
self.toolbar.insert(tool_item, -1)
@@ -54,11 +55,11 @@ class ItemGroup(gobject.GObject):
self.items.append(item)
def append_separator(self, important=False):
- menuitem = gtk.SeparatorMenuItem()
+ menuitem = Gtk.SeparatorMenuItem()
menuitem.show()
self.menu.append(menuitem)
if important:
- toolitem = gtk.SeparatorToolItem()
+ toolitem = Gtk.SeparatorToolItem()
toolitem.show()
self.toolbar.insert(toolitem, -1)
return toolitem
@@ -72,11 +73,11 @@ class GhostGroup(ItemGroup):
class SubGroup(ItemGroup):
def __init__(self, group, name=None):
- gobject.GObject.__init__(self)
+ GObject.GObject.__init__(self)
self.items = []
- self.item = gtk.MenuItem(name)
+ self.item = Gtk.MenuItem(name)
group.menu.append(self.item)
- self.menu = gtk.Menu()
+ self.menu = Gtk.Menu()
self.item.set_submenu(self.menu)
self.menu.show()
self.item.show()
diff --git a/desktop/sweetener/stock.py b/desktop/sweetener/stock.py
index 4f3b213..b92666d 100644
--- a/desktop/sweetener/stock.py
+++ b/desktop/sweetener/stock.py
@@ -20,9 +20,9 @@
import logging
logger = logging.getLogger('stock')
-import gtk
+from gi.repository import Gtk
-icon_factory = gtk.IconFactory()
+icon_factory = Gtk.IconFactory()
def register(name, label, accelerator, icon_name):
@@ -30,46 +30,54 @@ def register(name, label, accelerator, icon_name):
keyval = 0
mask = 0
else:
- keyval, mask = gtk.accelerator_parse(accelerator)
- gtk.stock_add([(name, label, mask, keyval, '')])
+ keyval, mask = Gtk.accelerator_parse(accelerator)
+ logger.debug(keyval)
+ logger.debug(mask)
+ item = Gtk.StockItem.new()
+ item.stock_id = name
+ item.label = label
+ item.modifier = mask
+ item.keyval = keyval
+ Gtk.stock_add([item])
if icon_name:
- icon_source = gtk.IconSource()
+ icon_source = Gtk.IconSource()
icon_source.set_icon_name(icon_name)
- icon = gtk.IconSet()
+ icon = Gtk.IconSet()
icon.add_source(icon_source)
icon_factory.add(name, icon)
icon_factory.add_default()
def overwrite_stock(stock_id, new_accelerator):
- info = list(gtk.stock_lookup(stock_id))
- keyval, mask = gtk.accelerator_parse(new_accelerator)
- info[2] = mask
- info[3] = keyval
+ info = Gtk.stock_lookup(stock_id)
+ keyval, mask = Gtk.accelerator_parse(new_accelerator)
+ info.modifier = mask
+ info.keyval = keyval
logger.debug(str(info))
- gtk.stock_add([(info[0], info[1], info[2], info[3], info[4])])
+ Gtk.stock_add([info])
# Here we overwrite the key accelerators for some stock ids.
# Feel free to add here any other stock id if you need it at your activity,
# and send us a patch.
-overwrite_stock(gtk.STOCK_SAVE_AS, '<Shift><Ctrl>S')
-overwrite_stock(gtk.STOCK_ZOOM_IN, '<Ctrl>plus')
-overwrite_stock(gtk.STOCK_ZOOM_OUT, '<Ctrl>minus')
-overwrite_stock(gtk.STOCK_ZOOM_100, '<Ctrl>0')
+overwrite_stock(Gtk.STOCK_SAVE_AS, '<Shift><Ctrl>S')
+overwrite_stock(Gtk.STOCK_ZOOM_IN, '<Ctrl>plus')
+overwrite_stock(Gtk.STOCK_ZOOM_OUT, '<Ctrl>minus')
+overwrite_stock(Gtk.STOCK_ZOOM_100, '<Ctrl>0')
# Key accelerator will be F11 on desktops and <Alt>return on Sugar.
-overwrite_stock(gtk.STOCK_FULLSCREEN, 'F11')
-overwrite_stock(gtk.STOCK_ADD, '<Ctrl>A')
-overwrite_stock(gtk.STOCK_REMOVE, '<Shift>Delete')
-overwrite_stock(gtk.STOCK_SELECT_COLOR, '<Ctrl>L')
+overwrite_stock(Gtk.STOCK_FULLSCREEN, 'F11')
+overwrite_stock(Gtk.STOCK_ADD, '<Ctrl>A')
+overwrite_stock(Gtk.STOCK_REMOVE, '<Shift>Delete')
+overwrite_stock(Gtk.STOCK_SELECT_COLOR, '<Ctrl>L')
def get_label(stock, underline):
- text = gtk.stock_lookup(stock)[1]
+ text = Gtk.stock_lookup(stock)[1]
if underline:
text = text.replace('_', '')
return text
def get_accelerator(stock):
- return gtk.stock_lookup(stock)[2:-1]
+ stock_id = Gtk.stock_lookup(stock)
+ return stock_id.modifier, stock_id.keyval
diff --git a/number_box.py b/number_box.py
new file mode 100644
index 0000000..a95c378
--- /dev/null
+++ b/number_box.py
@@ -0,0 +1,805 @@
+# -*- coding: utf-8 -*-
+# This code is from GNOME Sudoku
+# Adapted by: Daniel Francis
+
+from gi.repository import Gtk,Gdk,GObject,Pango,PangoCairo
+import math
+import random
+import tracker_info
+from gettext import gettext as _
+
+# simple_debug
+import logging
+logger = logging.getLogger('number_box')
+simple_debug = logger.debug
+
+ERROR_HIGHLIGHT_COLOR = (1.0, 0, 0)
+
+BASE_SIZE = 35 # The "normal" size of a box (in pixels)
+
+# And the standard font-sizes -- these should fit nicely with the
+# BASE_SIZE
+BASE_FONT_SIZE = Pango.SCALE * 13
+NOTE_FONT_SIZE = Pango.SCALE * 6
+
+BORDER_WIDTH = 9.0 # The size of space we leave for a box
+NORMAL_LINE_WIDTH = 1 # The size of the line we draw around a box
+
+DEBUG_COLORS = False
+
+def debug_set_color_rgba(cr, rgba):
+ COLORS = ("red","green","blue","yellow","purple","wheat","maroon","gray")
+
+ if DEBUG_COLORS:
+ rgba = Gdk.RGBA()
+ rgba.parse(COLORS[random.randint(0,len(COLORS)-1)])
+
+ Gdk.cairo_set_source_rgba(cr, rgba)
+
+class NumberSelector (Gtk.EventBox):
+
+ __gsignals__ = {
+ 'changed':(GObject.SignalFlags.RUN_LAST, None, ()),
+ }
+
+ def __init__ (self, default = None, upper = 9):
+ self.value = default
+ GObject.GObject.__init__(self)
+ self.table = Gtk.Table()
+ self.add(self.table)
+ side = int(math.sqrt(upper))
+ n = 1
+ for y in range(side):
+ for x in range(side):
+ b = Gtk.Button()
+ l = Gtk.Label()
+ if n == self.value:
+ l.set_markup('<b><span size="x-small">%s</span></b>'%n)
+ else:
+ l.set_markup('<span size="x-small">%s</span>'%n)
+ b.add(l)
+ b.set_relief(Gtk.ReliefStyle.HALF)
+ l = b.get_children()[0]
+ b.set_border_width(0)
+ l.set_padding(0, 0)
+ l.get_alignment()
+ b.connect('clicked', self.number_clicked, n)
+ self.table.attach(b, x, x+1, y, y+1)
+ n += 1
+ if self.value:
+ db = Gtk.Button()
+ l = Gtk.Label()
+ l.set_markup_with_mnemonic('<span size="x-small">'+_('_Clear')+'</span>')
+ db.add(l)
+ l.show()
+ db.connect('clicked', self.number_clicked, 0)
+ self.table.attach(db, 0, side, side + 1, side + 2)
+ self.show_all()
+
+ def number_clicked (self, button, n):
+ self.value = n
+ self.emit('changed')
+
+ def get_value (self):
+ return self.value
+
+ def set_value (self, n):
+ self.value = n
+
+class NumberBox (Gtk.DrawingArea):
+
+ text = ''
+ top_note_text = ''
+ bottom_note_text = ''
+ read_only = False
+ _layout = None
+ _top_note_layout = None
+ _bottom_note_layout = None
+ text_color = None
+ highlight_color = None
+ shadow_color = None
+ custom_background_color = None
+ border_color = None
+
+ __gsignals__ = {
+ 'value-about-to-change':(GObject.SignalFlags.RUN_LAST, None, ()),
+ 'notes-about-to-change':(GObject.SignalFlags.RUN_LAST, None, ()),
+ 'changed':(GObject.SignalFlags.RUN_LAST, None, ()),
+ # undo-change - A hacky way to handle the fact that we want to
+ # respond to undo's changes but we don't want undo to respond
+ # to itself...
+ 'undo-change':(GObject.SignalFlags.RUN_LAST, None, ()),
+ 'notes-changed':(GObject.SignalFlags.RUN_LAST, None, ()),
+ }
+
+ base_state = Gtk.StateFlags.NORMAL
+ npicker = None
+ draw_boxes = False
+
+ def __init__ (self, upper = 9, text = ''):
+ Gtk.DrawingArea.__init__(self)
+ self.upper = upper
+ self.parent_win = None
+ self.timer = None
+ self.font = self.get_style().font_desc
+ self.font.set_size(BASE_FONT_SIZE)
+ self.note_font = self.font.copy()
+ self.note_font.set_size(NOTE_FONT_SIZE)
+ self._top_note_layout = Pango.Layout(self.create_pango_context())
+ self._top_note_layout.set_font_description(self.note_font)
+ self._bottom_note_layout = Pango.Layout(self.create_pango_context())
+ self._bottom_note_layout.set_font_description(self.note_font)
+ self._base_stateflags = Gtk.StateFlags.NORMAL
+ self.top_note_list = []
+ self.bottom_note_list = []
+ self.tinfo = tracker_info.TrackerInfo()
+ self.set_property('can-focus', True)
+ self.set_property('events', Gdk.EventMask.ALL_EVENTS_MASK)
+ self.connect('button-press-event', self.button_press_cb)
+ self.connect('key-release-event', self.key_press_cb)
+ self.connect('enter-notify-event', self.pointer_enter_cb)
+ self.connect('leave-notify-event', self.pointer_leave_cb)
+ self.connect('focus-in-event', self.focus_in_cb)
+ self.connect('focus-out-event', self.focus_out_cb)
+ self.connect('motion-notify-event', self.motion_notify_cb)
+ self.set_text(text)
+
+ def set_parent_win(self, new_parent):
+ self.parent_win = new_parent
+
+ def set_timer(self, new_timer):
+ self.timer = new_timer
+
+ def pointer_enter_cb (self, *args):
+ if not self.is_focus():
+ self.set_state_flags(Gtk.StateFlags.PRELIGHT, False)
+
+ def pointer_leave_cb (self, *args):
+ self.set_state_flags(self._base_stateflags, True)
+ self._toggle_box_drawing_(False)
+
+ def focus_in_cb (self, *args):
+ self.set_state_flags(Gtk.StateFlags.SELECTED, True)
+ self._base_stateflags = Gtk.StateFlags.SELECTED
+
+ def focus_out_cb (self, *args):
+ self.set_state_flags(Gtk.StateFlags.NORMAL, True)
+ self._base_stateflags = Gtk.StateFlags.NORMAL
+ self.destroy_npicker()
+
+ def destroy_npicker (self):
+ if self.npicker:
+ self.npicker.destroy()
+ self.npicker = None
+
+ def motion_notify_cb (self, *args):
+ if self.is_focus() and not self.read_only:
+ self._toggle_box_drawing_(True)
+ else:
+ self._toggle_box_drawing_(False)
+
+ def _toggle_box_drawing_ (self, val):
+ if val and not self.draw_boxes:
+ self.draw_boxes = True
+ self.queue_draw()
+ if (not val) and self.draw_boxes:
+ self.draw_boxes = False
+ self.queue_draw()
+
+ def button_press_cb (self, w, e):
+ if self.read_only:
+ return
+ if e.type == Gdk.EventType._2BUTTON_PRESS:
+ # ignore second click (this makes a double click in the
+ # middle of a cell get us a display of the numbers, rather
+ # than selecting a number.
+ return
+ if self.is_focus():
+ x, y = e.get_coords()
+ alloc = self.get_allocation()
+ my_w = alloc.width
+ my_h = alloc.height
+ border_height = float(BORDER_WIDTH)/BASE_SIZE
+
+ if float(y)/my_h < border_height:
+ self.show_note_editor(top = True)
+ elif float(y)/my_h > (1-border_height):
+ self.show_note_editor(top = False)
+ elif not self.npicker:
+ # In this case we're a normal old click...
+ # makes sure there is only one numer selector
+ self.show_number_picker()
+ else:
+ self.grab_focus()
+
+ def key_press_cb (self, w, e):
+ if self.read_only:
+ return
+ if self.npicker: # kill number picker no matter what is pressed
+ self.destroy_npicker()
+ txt = Gdk.keyval_name(e.keyval)
+ if type(txt) == type(None):
+ # Make sure we don't trigger on unplugging the A/C charger etc
+ return
+ txt = txt.replace('KP_', '')
+
+ # Add the new value if need be
+ if txt in [str(n) for n in range(1, self.upper+1)]:
+ if e.state & Gdk.ModifierType.CONTROL_MASK:
+ self.add_note_text(txt, top = True)
+ elif e.state & Gdk.ModifierType.MOD1_MASK:
+ self.remove_note_text(txt, top = True)
+ elif self.get_text() != txt or \
+ (self.tracker_id != tracker_info.NO_TRACKER and
+ self.tinfo.current_tracker == tracker_info.NO_TRACKER):
+ # If there's no change, do nothing unless the player wants to
+ # change a tracked item while not tracking(ie commit a tracked
+ # change)
+ self.set_text_interactive(txt)
+ elif txt in ['0', 'Delete', 'BackSpace']:
+ self.set_text_interactive('')
+ elif txt in ['n', 'N']:
+ if e.state & Gdk.ModifierType.MOD1_MASK:
+ self.set_note_text_interactive(top_text = '')
+ else:
+ self.show_note_editor(top = True)
+ elif txt in ['m', 'M']:
+ if e.state & Gdk.ModifierType.MOD1_MASK:
+ self.set_note_text_interactive(bottom_text = '')
+ else:
+ self.show_note_editor(top = False)
+
+ def add_note_text(self, txt, top = False):
+ if top:
+ note = self.top_note_text
+ else:
+ note = self.bottom_note_text
+ if txt not in note:
+ tmp = list(note)
+ tmp.append(txt)
+ tmp.sort()
+ note = ''.join(tmp)
+ if top:
+ self.set_note_text_interactive(top_text = note)
+ else:
+ self.set_note_text_interactive(bottom_text = note)
+
+ def remove_note_text(self, txt, top = False):
+ if top:
+ note = self.top_note_text
+ else:
+ note = self.bottom_note_text
+ if txt in note:
+ note = note.replace(txt,'')
+ if top:
+ self.set_note_text_interactive(top_text = note)
+ else:
+ self.set_note_text_interactive(bottom_text = note)
+
+ def note_changed_cb (self, w, top = False):
+ if top:
+ self.set_note_text_interactive(top_text = w.get_text())
+ else:
+ self.set_note_text_interactive(bottom_text = w.get_text())
+
+ def note_focus_in(self, win, evt):
+ if (self.timer):
+ self.timer.resume_timing()
+
+ def note_focus_out(self, wgt, evt):
+ if (self.timer):
+ self.timer.pause_timing()
+
+ def show_note_editor (self, top = True):
+ alloc = self.get_allocation()
+ w = Gtk.Window()
+ w.set_property('skip-pager-hint', True)
+ w.set_property('skip-taskbar-hint', True)
+ w.set_decorated(False)
+ w.set_position(Gtk.WindowPosition.MOUSE)
+ w.set_size_request(alloc.width, alloc.height/2)
+ if self.parent_win:
+ w.set_transient_for(self.parent_win)
+ f = Gtk.Frame()
+ e = Gtk.Entry()
+ f.add(e)
+ if top:
+ e.set_text(self.top_note_text)
+ else:
+ e.set_text(self.bottom_note_text)
+ w.add(f)
+ e.connect('changed', self.note_changed_cb, top)
+ e.connect('focus-in-event', self.note_focus_in)
+ e.connect('focus-out-event', lambda e, ev, w: w.destroy(), w)
+ e.connect('focus-out-event', self.note_focus_out)
+ e.connect('activate', lambda e, w: w.destroy(), w)
+ _, x, y = self.get_window().get_origin()
+ if top:
+ w.move(x, y)
+ else:
+ w.move(x, y+int(alloc.height*0.6))
+ w.show_all()
+ e.grab_focus()
+
+ def number_changed_cb (self, num_selector):
+ self.destroy_npicker()
+ newval = num_selector.get_value()
+ if newval:
+ self.set_text_interactive(str(newval))
+ else:
+ self.set_text_interactive('')
+
+ def show_number_picker (self):
+ w = Gtk.Window(type = Gtk.WindowType.POPUP)
+ ns = NumberSelector(upper = self.upper, default = self.get_value())
+ ns.connect('changed', self.number_changed_cb)
+ w.grab_focus()
+ w.add(ns)
+ _, xorigin, yorigin = self.get_window().get_origin()
+ x, y = (self.get_allocated_width(), self.get_allocated_height())
+ popupx, popupy = w.get_size()
+ overlapx = popupx-x
+ overlapy = popupy-y
+ w.move(xorigin - (overlapx/2), yorigin - (overlapy/2))
+ w.show()
+ self.npicker = w
+
+ def set_text_interactive (self, text):
+ self.emit('value-about-to-change')
+ self.set_text(text)
+ self.queue_draw()
+ self.emit('changed')
+
+ def set_font (self, font):
+ if type(font) == str:
+ font = Pango.FontDescription(font)
+ self.font = font
+ if self.text:
+ self.set_text(self.text)
+ self.queue_draw()
+
+ def set_note_font (self, font):
+ if type(font) == str:
+ font = Pango.FontDescription(font)
+ self.note_font = font
+ self._top_note_layout.set_font_description(font)
+ self._bottom_note_layout.set_font_description(font)
+ self.queue_draw()
+
+ def set_text (self, text):
+ self.text = text
+ self._layout = self.create_pango_layout(text)
+ self._layout.set_font_description(self.font)
+
+ def show_note_text (self):
+ '''Display the notes for the current view
+ '''
+ self.top_note_text = self.get_note_display(self.top_note_list)[1]
+ self._top_note_layout.set_markup(self.get_note_display(self.top_note_list)[2], -1)
+ self.bottom_note_text = self.get_note_display(self.bottom_note_list)[1]
+ self._bottom_note_layout.set_markup(self.get_note_display(self.bottom_note_list)[2], -1)
+ self.queue_draw()
+
+ def set_note_text (self, top_text = None, bottom_text = None, for_hint = False):
+ '''Change the notes
+ '''
+ if top_text is not None:
+ self.update_notelist(self.top_note_list, top_text)
+ if bottom_text is not None:
+ self.update_notelist(self.bottom_note_list, bottom_text, for_hint)
+ self.show_note_text()
+
+ def set_note_text_interactive (self, *args, **kwargs):
+ self.emit('notes-about-to-change')
+ self.set_note_text(*args, **kwargs)
+ self.emit('notes-changed')
+
+ def set_notelist(self, top_notelist, bottom_notelist):
+ '''Assign new note lists
+ '''
+ if top_notelist:
+ self.top_note_list = top_notelist
+ if bottom_notelist:
+ self.bottom_note_list = bottom_notelist
+
+ def get_note_display(self, notelist, tracker_id = None, include_untracked = True):
+ '''Parse a notelist for display
+
+ Parse a notelist for the display.
+ notelist - This method works on one notelist at a time, so
+ top_note_list or bottom_note_list must be passed in.
+ tracker_id - can specify a particular tracker. The default is to use
+ tracker that is currently showing.
+ include_untracked - When set to True(default), the untracked notes will
+ be included in the output. Set it to false to exclude untracked
+ notes.
+
+ The output is returned in 3 formats:
+ display_list - is tuple list in the format (notelist_index, tid, note)
+ notelist_index - the index within the notelist
+ tid - tracker id
+ note - value of the note
+ display_text - vanilla string representing all the values
+ markup_text - pango markup string that colors each note for its tracker
+ '''
+ display_list = []
+ display_text = ''
+ markup_text = ''
+ if tracker_id == None:
+ tracker_id = self.tinfo.showing_tracker
+ if include_untracked:
+ track_filter = [tracker_info.NO_TRACKER, tracker_id]
+ else:
+ track_filter = [tracker_id]
+ last_tracker = tracker_info.NO_TRACKER
+ for notelist_index, (tid, note) in enumerate(notelist[:]):
+ if tid not in track_filter:
+ continue
+ display_list.append((notelist_index, tid, note))
+ display_text += note
+ if tid != last_tracker:
+ if self.tinfo.get_color_markup(last_tracker):
+ markup_text += '</span>'
+ if self.tinfo.get_color_markup(tid):
+ markup_text += '<span foreground="' + str(self.tinfo.get_color_markup(tid)) + '">'
+ last_tracker = tid
+ markup_text += note
+ if self.tinfo.get_color_markup(last_tracker):
+ markup_text += '</span>'
+ return((display_list, display_text, markup_text))
+
+ def update_notelist(self, notelist, new_notes, for_hint = False):
+ '''Parse notes for a notelist
+
+ A notelist stores individual notes in the format (tracker, note). The
+ sequence is also meaningful - it dictates the order in which the notes
+ are displayed. One notelist is maintained for the top
+ notes(top_note_list), and one for the bottom(bottom_note_list). This
+ method is responsible for maintaining those lists.
+
+ When updating for hints(for_hint == True), the old notes are replaced
+ completely by the new notes and set with NO_TRACKER.
+ '''
+ # Remove any duplicates
+ unique_notes = ""
+ for note in new_notes:
+ if note not in unique_notes:
+ unique_notes += note
+ # Create a list and text version of the notelist
+ display_list = self.get_note_display(notelist)[0]
+ display_text = self.get_note_display(notelist)[1]
+ if display_text == unique_notes:
+ return
+ # Remove deleted values from the notelist
+ del_offset = 0
+ for display_index, (notelist_index, tid, old_note) in enumerate(display_list[:]):
+ if old_note not in unique_notes or for_hint:
+ del notelist[notelist_index + del_offset]
+ del display_list[display_index + del_offset]
+ del_offset -= 1
+ else:
+ # Adjust the display_list index
+ display_list[display_index + del_offset] = (notelist_index + del_offset, tid, old_note)
+ # Insert any new values into the notelist
+ ins_offset = 0
+ display_index = 0
+ for new_index, new_note in enumerate(unique_notes):
+ add_note = False
+ # if the new notes are longer than the current ones - append
+ if len(display_list) <= display_index:
+ notelist_index = len(notelist)
+ ins_offset = 0
+ add_note = True
+ # Otherwise - advance until we find the appropriate place to insert
+ else:
+ old_note = display_list[display_index][2]
+ if new_note != old_note:
+ notelist_index = display_list[display_index][0]
+ add_note = True
+ display_index += 1
+ if add_note:
+ if for_hint:
+ use_tracker = tracker_info.NO_TRACKER
+ else:
+ use_tracker = self.tinfo.current_tracker
+ notelist.insert(notelist_index + ins_offset, (use_tracker, new_note))
+ display_list.insert(new_index, (notelist_index + ins_offset, self.tinfo.current_tracker, new_note))
+ ins_offset = ins_offset + 1
+ self.trim_untracked_notes(notelist)
+
+ def trim_untracked_notes(self, notelist):
+ untracked_text = self.get_note_display(notelist, tracker_info.NO_TRACKER)[1]
+ for tid, note in notelist[:]:
+ if note in untracked_text and tid != tracker_info.NO_TRACKER:
+ notelist.remove((tid, note))
+
+ def get_notes_for_undo(self):
+ '''Return the top and bottom notelists
+ '''
+ return((self.top_note_list[:], self.bottom_note_list[:]))
+
+ def set_notes_for_undo(self, notelists):
+ '''Reset the top and bottom notelists from an undo
+ '''
+ self.top_note_list, self.bottom_note_list = notelists
+ self.show_note_text()
+
+ @simple_debug
+ def do_draw(self, cr):
+
+ w = self.get_allocated_width()
+ h = self.get_allocated_height()
+ style_ctx = self.get_style_context()
+
+ self.draw_background_color(cr, style_ctx, w, h)
+ if self.is_focus():
+ self.draw_highlight_box(cr, style_ctx, w, h)
+ if self.border_color is not None:
+ border_width = 3.0
+ cr.set_source_rgb(*self.border_color)
+ cr.rectangle(border_width*0.5, border_width*0.5, w-border_width, h-border_width)
+ cr.set_line_width(border_width)
+ cr.stroke()
+
+ if h < w:
+ scale = h/float(BASE_SIZE)
+ else:
+ scale = w/float(BASE_SIZE)
+ cr.scale(scale, scale)
+
+ self.draw_text(cr, style_ctx)
+ if self.draw_boxes and self.is_focus():
+ self.draw_note_area_highlight_box(cr, style_ctx)
+
+ def draw_background_color (self, cr, style_ctx, w, h):
+ if self.read_only:
+ if self.custom_background_color:
+ r, g, b = self.custom_background_color
+ cr.set_source_rgb(
+ r*0.6, g*0.6, b*0.6
+ )
+ else:
+ #cr.set_source_color(self.style.base[Gtk.StateFlags.INSENSITIVE])
+ #Gdk.cairo_set_source_rgba(
+ debug_set_color_rgba(
+ cr, style_ctx.get_color(Gtk.StateFlags.INSENSITIVE))
+ elif self.is_focus():
+ #cr.set_source_color(self.style.base[Gtk.StateFlags.SELECTED])
+ #Gdk.cairo_set_source_rgba(
+ debug_set_color_rgba(
+ cr, style_ctx.get_color(Gtk.StateFlags.SELECTED))
+ elif self.custom_background_color:
+ cr.set_source_rgb(*self.custom_background_color)
+ else:
+ #cr.set_source_color(
+ # self.style.base[self.state]
+ # )
+ #Gdk.cairo_set_source_rgba(
+ cr.set_source_rgb(1.0, 1.0, 1.0)
+ cr.rectangle(
+ 0, 0, w, h,
+ )
+ cr.fill()
+
+ def draw_highlight_box (self, cr, style_ctx, w, h):
+ #cr.set_source_color(
+ # self.style.base[Gtk.StateFlags.SELECTED]
+ # )
+ #Gdk.cairo_set_source_rgba(
+ debug_set_color_rgba(
+ cr, style_ctx.get_background_color(Gtk.StateFlags.SELECTED))
+
+ border = 4 * w / BASE_SIZE
+ cr.rectangle(
+ # left-top
+ border*0.5,
+ border*0.5,
+ # bottom-right
+ w-border,
+ h-border,
+ )
+ cr.set_line_width(border)
+ cr.stroke()
+
+ def draw_note_area_highlight_box (self, cr, style_ctx):
+ # set up our paint brush...
+ #cr.set_source_color(
+ # self.style.mid[self.state]
+ # )
+ #Gdk.cairo_set_source_rgba(
+ debug_set_color_rgba(
+ cr, style_ctx.get_border_color(self.get_state_flags()))
+
+ cr.set_line_width(NORMAL_LINE_WIDTH)
+ # top rectangle
+ cr.rectangle(NORMAL_LINE_WIDTH*0.5,
+ NORMAL_LINE_WIDTH*0.5,
+ BASE_SIZE-NORMAL_LINE_WIDTH,
+ BORDER_WIDTH-NORMAL_LINE_WIDTH)
+ cr.stroke()
+ # bottom rectangle
+ cr.rectangle(NORMAL_LINE_WIDTH*0.5, #x
+ BASE_SIZE - BORDER_WIDTH-(NORMAL_LINE_WIDTH*0.5), #y
+ BASE_SIZE-NORMAL_LINE_WIDTH, #x2
+ BASE_SIZE-NORMAL_LINE_WIDTH #y2
+ )
+ cr.stroke()
+
+ def draw_text (self, cr, style_ctx):
+ fontw, fonth = self._layout.get_pixel_size()
+ # Draw a shadow for tracked conflicts. This is done to
+ # differentiate between tracked and untracked conflicts.
+ if self.shadow_color:
+ cr.set_source_rgb(*self.shadow_color)
+ for xoff, yoff in [(1,1),(2,2)]:
+ cr.move_to((BASE_SIZE/2)-(fontw/2) + xoff, (BASE_SIZE/2) - (fonth/2) + yoff)
+ PangoCairo.show_layout(cr, self._layout)
+ if self.text_color:
+ cr.set_source_rgb(*self.text_color)
+ elif self.read_only:
+ #cr.set_source_color(self.style.text[Gtk.StateFlags.NORMAL])
+ #Gdk.cairo_set_source_rgba(
+ debug_set_color_rgba(
+ cr, style_ctx.get_color(Gtk.StateFlags.NORMAL))
+ else:
+ #cr.set_source_color(self.style.text[self.state])
+ #Gdk.cairo_set_source_rgba(
+ debug_set_color_rgba(
+ cr, style_ctx.get_color(Gtk.StateFlags.NORMAL))
+
+ # And draw the text in the middle of the allocated space
+ if self._layout:
+ cr.move_to(
+ (BASE_SIZE/2)-(fontw/2),
+ (BASE_SIZE/2) - (fonth/2),
+ )
+ PangoCairo.update_layout(cr, self._layout)
+ PangoCairo.show_layout(cr, self._layout)
+
+ #cr.set_source_color(self.style.text[self.state])
+ #Gdk.cairo_set_source_rgba(
+ debug_set_color_rgba(
+ cr, style_ctx.get_color(Gtk.StateFlags.NORMAL))
+
+ # And draw any note text...
+ if self._top_note_layout:
+ fontw, fonth = self._top_note_layout.get_pixel_size()
+ cr.move_to(
+ NORMAL_LINE_WIDTH,
+ 0,
+ )
+ PangoCairo.update_layout(cr, self._top_note_layout)
+ PangoCairo.show_layout(cr, self._top_note_layout)
+ if self._bottom_note_layout:
+ fontw, fonth = self._bottom_note_layout.get_pixel_size()
+ cr.move_to(
+ NORMAL_LINE_WIDTH,
+ BASE_SIZE-fonth,
+ )
+ PangoCairo.update_layout(cr, self._bottom_note_layout)
+ PangoCairo.show_layout(cr, self._bottom_note_layout)
+
+ def set_text_color (self, color, shadow = None):
+ self.shadow_color = shadow
+ self.text_color = color
+ self.queue_draw()
+
+ def set_background_color (self, color):
+ self.custom_background_color = color
+ self.queue_draw()
+
+ def set_border_color (self, color):
+ self.border_color = color
+ self.queue_draw()
+
+ def hide_notes (self):
+ pass
+
+ def show_notes (self):
+ pass
+
+ def set_value (self, v):
+ if 0 < v <= self.upper:
+ self.set_text(str(v))
+ else:
+ self.set_text('')
+ self.queue_draw()
+
+ def get_value (self):
+ try:
+ return int(self.text)
+ except:
+ return None
+
+ def get_text (self):
+ return self.text
+
+ def get_note_text (self):
+ return self.top_note_text, self.bottom_note_text
+
+class SudokuNumberBox (NumberBox):
+
+ normal_color = None
+ tracker_id = None
+ error_color = (1.0, 0, 0)
+ highlight_color = ERROR_HIGHLIGHT_COLOR
+
+ def set_value(self, val, tracker_id = None):
+ if tracker_id == None:
+ self.tracker_id = self.tinfo.current_tracker
+ else:
+ self.tracker_id = tracker_id
+ self.normal_color = self.tinfo.get_color(self.tracker_id)
+ self.set_text_color(self.normal_color)
+ super(SudokuNumberBox, self).set_value(val)
+
+ def get_value_for_undo(self):
+ return(self.tracker_id, self.get_value(), self.tinfo.get_trackers_for_cell(self.x, self.y))
+
+ def set_value_for_undo (self, undo_val):
+ tracker_id, value, all_traces = undo_val
+ # When undo sets a value, switch to that tracker
+ if value:
+ self.tinfo.ui.select_tracker(tracker_id)
+ self.set_value(value, tracker_id)
+ self.tinfo.reset_trackers_for_cell(self.x, self.y, all_traces)
+ self.emit('undo_change')
+
+ def recolor(self, tracker_id):
+ self.normal_color = self.tinfo.get_color(tracker_id)
+ self.set_text_color(self.normal_color)
+
+ def set_error_highlight (self, val):
+ if val:
+ if (self.tracker_id != tracker_info.NO_TRACKER):
+ self.set_text_color(self.error_color, self.normal_color)
+ else:
+ self.set_text_color(self.error_color)
+ else:
+ self.set_text_color(self.normal_color)
+
+ def set_read_only (self, val):
+ self.read_only = val
+ if not hasattr(self, 'bold_font'):
+ self.normal_font = self.font
+ self.bold_font = self.font.copy()
+ self.bold_font.set_weight(Pango.Weight.BOLD)
+ if self.read_only:
+ self.set_font(self.bold_font)
+ else:
+ self.set_font(self.normal_font)
+ self.queue_draw()
+
+ def set_impossible (self, val):
+ if val:
+ if not self.get_text():
+ self.set_text('X')
+ self.set_text_color(self.error_color)
+ elif self.get_text() == 'X':
+ self.set_text('')
+ self.set_text_color(self.normal_color)
+ self.queue_draw()
+
+
+GObject.type_register(NumberBox)
+
+if __name__ == '__main__':
+ window = Gtk.Window()
+ window.connect('delete-event', Gtk.main_quit)
+
+ def test_number_selector ():
+ nselector = NumberSelector(default = 3)
+ def tell_me (b):
+ print 'value->', b.get_value()
+ nselector.connect('changed', tell_me)
+ window.add(nselector)
+
+ def test_number_box ():
+ window.set_size_request(100, 100)
+ nbox = NumberBox()
+ window.add(nbox)
+
+# test_number_selector()
+ test_number_box()
+ window.show_all()
+ Gtk.main()
diff --git a/options.py b/options.py
index 9e06358..4d3e58f 100644
--- a/options.py
+++ b/options.py
@@ -19,8 +19,6 @@
# MA 02110-1301, USA.
from gettext import gettext as _
-import gobject
-import gtk
from sweetener.itembox import ItemBox
from sweetener.basic_options import BasicOptions
diff --git a/setup.py b/setup.py
index 530f97c..c60f4d0 100755
--- a/setup.py
+++ b/setup.py
@@ -16,6 +16,6 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-from sugar.activity import bundlebuilder
+from sugar3.activity import bundlebuilder
bundlebuilder.start()
diff --git a/sugar/sweetener/basic_options.py b/sugar/sweetener/basic_options.py
index 52f4ebd..0f5d579 100644
--- a/sugar/sweetener/basic_options.py
+++ b/sugar/sweetener/basic_options.py
@@ -19,7 +19,7 @@
from gettext import gettext as _
-from sugar.activity.widgets import ActivityToolbarButton
+from sugar3.activity.widgets import ActivityToolbarButton
import stock
from item import Item
diff --git a/sugar/sweetener/item.py b/sugar/sweetener/item.py
index 291204a..8371aaf 100644
--- a/sugar/sweetener/item.py
+++ b/sugar/sweetener/item.py
@@ -21,21 +21,21 @@
import logging
logger = logging.getLogger('option')
-import gobject
-import gtk
-from sugar.graphics.toolbutton import ToolButton
+from gi.repository import GObject
+from gi.repository import Gtk
+from sugar3.graphics.toolbutton import ToolButton
import stock
-class Item(gobject.GObject):
- __gsignals__ = {'activate': (gobject.SIGNAL_RUN_LAST,
- gobject.TYPE_NONE,
+class Item(GObject.GObject):
+ __gsignals__ = {'activate': (GObject.SignalFlags.RUN_LAST,
+ None,
tuple())}
toolitem = None
- def __init__(self, stock_id=gtk.STOCK_CLEAR, important=False):
- gobject.GObject.__init__(self)
+ def __init__(self, stock_id=Gtk.STOCK_CLEAR, important=False):
+ GObject.GObject.__init__(self)
self._stock_id = stock_id
self.accel_group = None
self.important = important
@@ -62,7 +62,7 @@ class Item(gobject.GObject):
logger.debug(str(accelerator))
try:
if accelerator[1] > 0:
- self.toolitem.props.accelerator = gtk.accelerator_name(
+ self.toolitem.props.accelerator = Gtk.accelerator_name(
accelerator[1], accelerator[0])
except:
logger.error(
@@ -82,6 +82,6 @@ class Item(gobject.GObject):
if self.tooltip:
self.toolitem.set_tooltip(self.tooltip)
else:
- text = gtk.stock_lookup(self._stock_id)[1]
+ text = Gtk.stock_lookup(self._stock_id)[1]
self.toolitem.set_tooltip(text.replace('_', ''))
self.setup_accelerator()
diff --git a/sugar/sweetener/itembox.py b/sugar/sweetener/itembox.py
index d861c52..99c45bd 100644
--- a/sugar/sweetener/itembox.py
+++ b/sugar/sweetener/itembox.py
@@ -17,16 +17,16 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
-import gtk
-from sugar.graphics.toolbarbox import ToolbarBox
-from sugar.activity.widgets import StopButton
+from gi.repository import Gtk
+from sugar3.graphics.toolbarbox import ToolbarBox
+from sugar3.activity.widgets import StopButton
class ItemBox(ToolbarBox):
def __init__(self, activity):
ToolbarBox.__init__(self)
self._parent = activity
- separator = gtk.SeparatorToolItem()
+ separator = Gtk.SeparatorToolItem()
separator.set_draw(False)
separator.set_expand(True)
separator.show()
diff --git a/sugar/sweetener/stock.py b/sugar/sweetener/stock.py
index a6451a7..28f3956 100644
--- a/sugar/sweetener/stock.py
+++ b/sugar/sweetener/stock.py
@@ -19,12 +19,12 @@
import logging
logger = logging.getLogger('stock')
-import gtk
+from gi.repository import Gtk
-icon_factory = gtk.IconFactory()
+icon_factory = Gtk.IconFactory()
# Set the icon name for the stock items, this is used only in Sugar.
-icons = {gtk.STOCK_ADD: 'list-add'}
+icons = {Gtk.STOCK_ADD: 'list-add'}
def register(name, label, accelerator, icon_name):
@@ -32,12 +32,19 @@ def register(name, label, accelerator, icon_name):
keyval = 0
mask = 0
else:
- keyval, mask = gtk.accelerator_parse(accelerator)
- gtk.stock_add([(name, label, mask, keyval, '')])
+ keyval, mask = Gtk.accelerator_parse(accelerator)
+ logger.debug(keyval)
+ logger.debug(mask)
+ item = Gtk.StockItem.new()
+ item.stock_id = name
+ item.label = label
+ item.modifier = mask
+ item.keyval = keyval
+ Gtk.stock_add([item])
if icon_name:
- icon_source = gtk.IconSource()
+ icon_source = Gtk.IconSource()
icon_source.set_icon_name(icon_name)
- icon = gtk.IconSet()
+ icon = Gtk.IconSet()
icon.add_source(icon_source)
icon_factory.add(name, icon)
icon_factory.add_default()
@@ -45,33 +52,35 @@ def register(name, label, accelerator, icon_name):
def overwrite_stock(stock_id, new_accelerator):
- info = list(gtk.stock_lookup(stock_id))
- keyval, mask = gtk.accelerator_parse(new_accelerator)
- info[2] = mask
- info[3] = keyval
+ info = Gtk.stock_lookup(stock_id)
+ keyval, mask = Gtk.accelerator_parse(new_accelerator)
+ info.modifier = mask
+ info.keyval = keyval
logger.debug(str(info))
- gtk.stock_add([(info[0], info[1], info[2], info[3], info[4])])
+ Gtk.stock_add([info])
# Here we overwrite the key accelerators for some stock ids.
# Feel free to add here any other stock id if you need it at your activity,
# and send us a patch.
-overwrite_stock(gtk.STOCK_ZOOM_IN, '<Ctrl>plus')
-overwrite_stock(gtk.STOCK_ZOOM_OUT, '<Ctrl>minus')
-overwrite_stock(gtk.STOCK_ZOOM_100, '<Ctrl>0')
+overwrite_stock(Gtk.STOCK_SAVE_AS, '<Shift><Ctrl>S')
+overwrite_stock(Gtk.STOCK_ZOOM_IN, '<Ctrl>plus')
+overwrite_stock(Gtk.STOCK_ZOOM_OUT, '<Ctrl>minus')
+overwrite_stock(Gtk.STOCK_ZOOM_100, '<Ctrl>0')
# Key accelerator will be F11 on desktops and <Alt>return on Sugar.
-overwrite_stock(gtk.STOCK_FULLSCREEN, '<Alt>Return')
-overwrite_stock(gtk.STOCK_ADD, '<Ctrl>A')
-overwrite_stock(gtk.STOCK_REMOVE, '<Ctrl>R')
-overwrite_stock(gtk.STOCK_SELECT_COLOR, '<Ctrl>L')
+overwrite_stock(Gtk.STOCK_FULLSCREEN, '<Alt>Return')
+overwrite_stock(Gtk.STOCK_ADD, '<Ctrl>A')
+overwrite_stock(Gtk.STOCK_REMOVE, '<Ctrl>R')
+overwrite_stock(Gtk.STOCK_SELECT_COLOR, '<Ctrl>L')
def get_label(stock, underline):
- text = gtk.stock_lookup(stock)[1]
+ text = Gtk.stock_lookup(stock)[1]
if underline:
text = text.replace('_', '')
return text
def get_accelerator(stock):
- return gtk.stock_lookup(stock)[2:-1]
+ stock_id = Gtk.stock_lookup(stock)
+ return stock_id.modifier, stock_id.keyval
diff --git a/tracker_info.py b/tracker_info.py
new file mode 100644
index 0000000..798c4ac
--- /dev/null
+++ b/tracker_info.py
@@ -0,0 +1,208 @@
+# -*- coding: utf-8 -*-
+#!/usr/bin/python
+
+import random
+import copy
+
+NO_TRACKER = -1 # Tracker id for untracked values
+
+class TrackerInfo(object):
+ '''Tracker state machine(singleton)
+
+ The singleton instance of this class is used to manipulate tracker
+ selection and tracked values, as well as interrogate tracker colors.
+
+ _tracks - dictionary for tracked values. The tracker id is used as the
+ key. A tracker is a dictionary that stored tracked values keyed by
+ its coordinates(x, y). _tracks[tracker_id][(x, y)] == tracked value
+
+ current_tracker - The tracker id for the currently selected tracker
+ showing_tracker - The tracker id for the tracker that is currently being
+ viewed. The point to this member is to store off the tracker when
+ the player switches to "Untracked" so that the last tracker they were
+ working on stays in view after the switch.
+ '''
+ __single = None
+ _tracks = {}
+ _colors = {}
+ current_tracker = NO_TRACKER
+ showing_tracker = NO_TRACKER
+
+ def __new__(cls, *args, **kwargs):
+ '''Overridden to implement as a singleton
+ '''
+ # Check to see if a __single exists already for this class
+ # Compare class types instead of just looking for None so
+ # that subclasses will create their own __single objects
+ if cls != type(cls.__single):
+ cls.__single = object.__new__(cls, *args, **kwargs)
+ return cls.__single
+
+ def __init__(self):
+ # Only initialize the colors once
+ if self._colors:
+ return
+ # Use tango colors recommended here:
+ # http://tango.freedesktop.org/Tango_Icon_Theme_Guidelines
+ for tracker_id, cols in enumerate(
+ [(32, 74, 135), # Sky Blue 3
+ (78, 154, 6), # Chameleon 3
+ (206, 92, 0), # Orange 3
+ (143, 89, 2), # Chocolate 3
+ (92, 53, 102), # Plum 3
+ (85, 87, 83), # Aluminium 5
+ (196, 160, 0) # Butter 3
+ ]):
+ self._colors[tracker_id] = tuple([x / 255.0 for x in cols])
+
+ def load(self, pickle):
+ self.current_tracker, self.showing_tracker, self._tracks = pickle
+
+ def save(self):
+ return (self.current_tracker, self.showing_tracker, self.get_trackers())
+
+ def create_tracker (self, tracker_id = 0):
+ '''Create storage for a new tracker
+
+ tracker_id can be passed in to attempt creation of a specific id, but
+ if the tracker_id already exists then the passed number will be
+ incremented until a suitable key can be allocated.
+ '''
+ if not tracker_id:
+ tracker_id = 0
+ while self._tracks.has_key(tracker_id):
+ tracker_id += 1
+ self._tracks[tracker_id] = {}
+ return tracker_id
+
+ def get_tracker(self, tracker_id):
+ if self._tracks.has_key(tracker_id):
+ return self._tracks[tracker_id]
+
+ def delete_tracker(self, tracker_id):
+ if self._tracks.has_key(tracker_id):
+ del self._tracks[tracker_id]
+
+ def reset (self):
+ ''' Reset the tracker information
+ '''
+ self._tracks = {}
+ self.current_tracker = NO_TRACKER
+ self.showing_tracker = NO_TRACKER
+
+ def use_trackers (self, trackers):
+ self._tracks = trackers
+
+ def get_trackers(self):
+ return copy.deepcopy(self._tracks)
+
+ def set_tracker(self, tracker_id):
+ self.current_tracker = tracker_id
+ if tracker_id != NO_TRACKER:
+ self.showing_tracker = tracker_id
+
+ def hide_tracker(self):
+ self.showing_tracker = NO_TRACKER
+
+ def get_tracker_view(self):
+ return((self.current_tracker, self.showing_tracker))
+
+ def set_tracker_view(self, tview):
+ self.current_tracker, self.showing_tracker = tview
+
+ def get_color (self, tracker_id):
+ # Untracked items don't get specially colored
+ if tracker_id == NO_TRACKER:
+ return None
+ # Create a random color for new trackers that are beyond the defaults
+ if not self._colors.has_key(tracker_id):
+ random_color = self._colors[0]
+ while random_color in self._colors.values():
+ # If we have generated all possible colors, this will
+ # enter an infinite loop
+ random_color = (random.randint(0, 100)/100.0,
+ random.randint(0, 100)/100.0,
+ random.randint(0, 100)/100.0)
+ self._colors[tracker_id] = random_color
+ return self._colors[tracker_id]
+
+ def get_color_markup(self, tracker_id):
+ color_tuple = self.get_color (tracker_id)
+ if not color_tuple:
+ return None
+ color_markup = '#'
+ color_markup += str(hex(int(color_tuple[0]*255))[2:]).zfill(2)
+ color_markup += str(hex(int(color_tuple[1]*255))[2:]).zfill(2)
+ color_markup += str(hex(int(color_tuple[2]*255))[2:]).zfill(2)
+ return color_markup.upper()
+
+ def get_current_color(self):
+ return self.get_color(self.current_tracker)
+
+ def get_showing_color(self):
+ return self.get_color(self.showing_tracker)
+
+ def add_trace(self, x, y, value, tracker_id = None):
+ '''Add a tracked value
+
+ By default(tracker_id set to None) this method adds a value to the
+ current tracker. tracker_id can be passed in to add it to a specific
+ tracker.
+ '''
+ if tracker_id == None:
+ to_tracker = self.current_tracker
+ else:
+ to_tracker = tracker_id
+ # Need a tracker
+ if to_tracker == NO_TRACKER:
+ return
+ # Make sure the dictionary is available for the tracker.
+ if not self._tracks.has_key(to_tracker):
+ self._tracks[to_tracker] = {}
+ # Add it
+ self._tracks[to_tracker][(x, y)] = value
+
+ def remove_trace(self, x, y, from_tracker = None):
+ '''Remove a tracked value
+
+ By default(from_tracker set to None) this method removes all tracked
+ values for a particular cell(x, y coords). from_tracker can be passed
+ to remove tracked values from a particular tracker only.
+ '''
+ if from_tracker == None:
+ from_tracks = self._tracks.keys()
+ else:
+ from_tracks = [from_tracker]
+ # Delete them
+ for tracker in from_tracks:
+ if self._tracks.has_key(tracker) and self._tracks[tracker].has_key((x, y)):
+ del self._tracks[tracker][(x, y)]
+
+ def get_trackers_for_cell(self, x, y):
+ '''Return all trackers for a cell
+
+ This function is used for the undo mechanism. A list in the format
+ (tracker, value) is returned so that it may later be reinstated with
+ reset_trackers_for_cell().
+ '''
+ ret = []
+ for tracker, track in self._tracks.items():
+ if track.has_key((x, y)):
+ ret.append((tracker, track[(x, y)]))
+ return ret
+
+ def reset_trackers_for_cell(self, x, y, old_trackers):
+ '''Reset all trackers to a previous state for a cell
+
+ This function is used for the undo mechanism. It reinstates the
+ tracked values the list created by get_trackers_for_cell().
+ '''
+ # Remove all the current traces
+ for tracker, track in self._tracks.items():
+ if track.has_key((x, y)):
+ del self._tracks[tracker][(x, y)]
+ # Add the old ones back
+ for tracker, value in old_trackers:
+ self._tracks[tracker][(x, y)] = value
+
+