gnome-games r7128 - trunk/gnome-sudoku/src/lib



Author: andreasr
Date: Thu Jan 10 23:24:18 2008
New Revision: 7128
URL: http://svn.gnome.org/viewvc/gnome-games?rev=7128&view=rev

Log:
Make gnome-sudoku startup process more consistent with glChess.


Added:
   trunk/gnome-sudoku/src/lib/main.py
Modified:
   trunk/gnome-sudoku/src/lib/Makefile.am
   trunk/gnome-sudoku/src/lib/defaults.py.in
   trunk/gnome-sudoku/src/lib/gnome_sudoku.py

Modified: trunk/gnome-sudoku/src/lib/Makefile.am
==============================================================================
--- trunk/gnome-sudoku/src/lib/Makefile.am	(original)
+++ trunk/gnome-sudoku/src/lib/Makefile.am	Thu Jan 10 23:24:18 2008
@@ -9,6 +9,7 @@
 	gnome_sudoku.py	\
 	gsudoku.py	\
 	__init__.py	\
+	main.py 	\
 	pausable.py	\
 	printing.py	\
 	saver.py	\

Modified: trunk/gnome-sudoku/src/lib/defaults.py.in
==============================================================================
--- trunk/gnome-sudoku/src/lib/defaults.py.in	(original)
+++ trunk/gnome-sudoku/src/lib/defaults.py.in	Thu Jan 10 23:24:18 2008
@@ -1,8 +1,8 @@
-import os, os.path
+import sys, os, os.path
 import errno
 import gettext
 
-if sys.modules["glchess"].installed_mode:
+if sys.modules["gnome_sudoku"].installed_mode:
     APP_DATA_DIR = os.path.join('@prefix@', 'share') 
     IMAGE_DIR = os.path.join(APP_DATA_DIR, 'pixmaps', 'gnome-sudoku')
     LOCALEDIR = os.path.join(APP_DATA_DIR, 'locale')
@@ -10,7 +10,7 @@
     BASE_DIR = os.path.join(APP_DATA_DIR,'gnome-sudoku')
 else:
     APP_DATA_DIR = os.path.join('@abs_top_builddir@', 'gnome-sudoku', 'data') 
-    IMAGE_DIR = os.path.join('@abs_top_builddir@', 'glchess', 'images')
+    IMAGE_DIR = os.path.join('@abs_top_builddir@', 'gnome-sudoku', 'images')
     LOCALEDIR = os.path.join(APP_DATA_DIR, 'locale')
     GLADE_DIR = os.path.join('@abs_top_builddir@', 'gnome-sudoku', 'glade')
     BASE_DIR = os.path.join('@abs_top_builddir@', 'gnome-sudoku', 'data') 

Modified: trunk/gnome-sudoku/src/lib/gnome_sudoku.py
==============================================================================
--- trunk/gnome-sudoku/src/lib/gnome_sudoku.py	(original)
+++ trunk/gnome-sudoku/src/lib/gnome_sudoku.py	Thu Jan 10 23:24:18 2008
@@ -1,1034 +1,21 @@
-try:
-    import pygtk
-    pygtk.require('2.0')
-except ImportError, err:
-    print ("PyGTK not found. Please make sure it is installed properly and referenced in your PYTHONPATH environment variable.")
+import sys
 
-import gtk, gobject, gtk.glade
-import gnome, gnome.ui, pango
-import os, os.path
-from gtk_goodies import gconf_wrapper, Undo, dialog_extras, image_extras
-import gsudoku, sudoku, saver, sudoku_maker, printing, sudoku_generator_gui
-import game_selector
-import time, threading
-from gettext import gettext as _
-from gettext import ngettext
-from defaults import *
-from timer import ActiveTimer
-from simple_debug import simple_debug,options
-from dialog_swallower import SwappableArea
-
-icon_factory = gtk.IconFactory()
-STOCK_PIXBUFS = {}
-for filename,stock_id in [('footprints.png','tracks'),]:
-    pb = gtk.gdk.pixbuf_new_from_file(os.path.join(IMAGE_DIR,filename))
-    STOCK_PIXBUFS[stock_id]=pb
-    iconset = gtk.IconSet(pb)
-    icon_factory.add(stock_id,iconset)
-    icon_factory.add_default()
-
-gtk.stock_add([('tracks',
-                _('Track moves'),
-                0,0,""),])
-
-try:
-    STOCK_FULLSCREEN = gtk.STOCK_FULLSCREEN
-except:
-    STOCK_FULLSCREEN = _('Full Screen')
-
-def inactivate_new_game_etc (fun):
-    def _ (ui, *args, **kwargs):
-        paths = [
-            '/MenuBar/Game/New',
-            #'/MenuBar/Game/Open',
-            #'/MenuBar/Game/ByHand',
-            '/MenuBar/Game/Print',
-            '/MenuBar/Edit/Undo',
-            '/MenuBar/Edit/Redo',
-            '/MenuBar/Edit/Clear',
-            '/MenuBar/Edit/ClearNotes',
-            '/MenuBar/Tools/ShowPossible',
-            '/MenuBar/Tools/AutofillCurrentSquare',
-            '/MenuBar/Tools/Autofill',
-            '/MenuBar/Tools/AlwaysShowPossible',
-            '/MenuBar/Tools/ShowImpossibleImplications',
-            '/MenuBar/Tools/Tracker',
-            '/MenuBar/Game/PuzzleInfo',
-            #'/MenuBar/Game/HighScores',
-            ]
-        for p in paths:
-            action = ui.uimanager.get_action(p)
-            if not action: action = ui.uimanager.get_widget(p)
-            if not action: print 'No action at path',p
-            else: action.set_sensitive(False)
-        ret = fun(ui,*args,**kwargs)
-        for p in paths:
-            action = ui.uimanager.get_action(p)
-            if not action: action = ui.uimanager.get_widget(p)
-            if not action: print 'No action at path',p
-            else: action.set_sensitive(True)
-        return ret
-    return _
-
-class UI (gconf_wrapper.GConfWrapper):
-    ui='''<ui>
-    <menubar name="MenuBar">
-      <menu name="Game" action="Game">
-        <menuitem action="New"/>
-        <!--<menuitem action="ByHand"/>-->
-        <!--<menuitem action="Open"/>-->
-        <!--<separator/>
-        <menuitem action="Save"/>-->
-        <separator/>
-        <menuitem action="PuzzleInfo"/>
-        <!--<menuitem action="HighScores"/>-->
-        <separator/>        
-        <menuitem action="Print"/>
-        <menuitem action="PrintMany"/>
-        <separator/>
-        <menuitem action="Close"/>
-        <!--<menuitem action="Quit"/>-->
-      </menu>
-      <menu action="Edit">
-        <menuitem action="Undo"/>
-        <menuitem action="Redo"/>
-        <separator/>
-        <menuitem action="Clear"/>
-        <menuitem action="ClearNotes"/>        
-      </menu>
-      <menu action="View">
-        <menuitem action="FullScreen"/>
-        <separator/>
-        <menuitem action="ToggleToolbar"/>
-        <menuitem action="ToggleHighlight"/>        
-      </menu>
-      <menu action="Tools">
-         <menuitem action="ShowPossible"/>
-          <menuitem action="AutofillCurrentSquare"/>
-          <menuitem action="Autofill"/>
-          <separator/>
-          <menuitem action="AlwaysShowPossible"/>
-          <menuitem action="ShowImpossibleImplications"/>
-
-        <separator/>
-        <menuitem action="Generator"/>
-        <menuitem action="BackgroundGenerator"/>
-        <separator/>
-        <menuitem action="Tracker"/>
-        </menu>
-       <menu action="Help">
-         <menuitem action="ShowHelp"/>
-         <menuitem action="About"/>
-       </menu>
-     </menubar>
-     <toolbar name="Toolbar">
-      <!--<toolitem action="Quit"/>-->
-      <toolitem action="New"/>
-      <!--<toolitem action="Open"/>-->
-      <toolitem action="Print"/>
-      <!--<toolitem action="Save"/>-->
-      <!--<separator/>-->
-      <!--<toolitem action="Clear"/>      -->
-      <!--<toolitem action="ClearNotes"/>-->
-      <separator/>
-      <toolitem action="Undo"/>
-      <toolitem action="Redo"/>
-      <separator/>
-      <toolitem action="ShowPossible"/>
-      <!--<toolitem action="AlwaysShowPossible"/>-->
-      <toolitem action="AutofillCurrentSquare"/>      
-      <separator/>
-      <toolitem action="ToggleHighlight"/>
-      <!--<toolitem action="AlwaysShowPossible"/>-->
-      
-      <toolitem action="Tracker"/>
-     </toolbar>
-     </ui>'''
-
-    initial_prefs = {'group_size':9,
-                     'font_zoom':0,
-                     'zoom_on_resize':1,
-                     'always_show_hints':0,
-                     'player':os.environ.get('USERNAME',''),
-                     'difficulty':0.0,
-                     'minimum_number_of_new_puzzles':MIN_NEW_PUZZLES,
-                     'highlight':0,
-                     'bg_black':1,
-                     'bg_custom_color':'',
-                     'show_tracker':False,
-                     'width': 700,
-                     'height': 675,
-                     'auto_save_interval':60 # auto-save interval in seconds...
-                     #'show_notes':0
-                     }    
-
-    @simple_debug
-    def __init__ (self):
-        gconf_wrapper.GConfWrapper.__init__(self,
-                                            gconf_wrapper.GConf('gnome-sudoku')
-                                            )
-        self.setup_gui()
-        self.timer = ActiveTimer(self.w)
-        self.won = False
-        # add the accelerator group to our toplevel window
-        self.worker_connections=[]
-        self.timer.start_timing()
-        # setup sudoku maker...
-        self.sudoku_maker = sudoku_maker.SudokuMaker()
-        self.sudoku_tracker = saver.SudokuTracker()
-        #if not self.sudoku_tracker.playing:
-        #    self.main_actions.get_action('Open').set_sensitive(False)
-        #else:
-        #self.main_actions.get_action('Open').set_sensitive(True)
-        #if not self.sudoku_tracker.are_finished_games():
-        #    self.main_actions.get_action('HighScores').set_sensitive(False)
-        # select an easy puzzle...
-        #puz,d=self.sudoku_maker.get_new_puzzle(self.gconf['difficulty'])
-        #print 'Default to ',puz
-        #self.gsd.change_grid(puz, 9)
-        # generate puzzles while our use is working...        
-        self.show()
-        #print 'Select game!'
-        if self.select_game():
-            # If this return True, the user closed...
-            #print 'select game returned True - exit'
-            self.quit = True
-        else:
-            self.quit = False
-            # Generate puzzles in background...
-            if self.gconf['generate_puzzles_in_background']:
-                gobject.timeout_add(1000,lambda *args: self.start_worker_thread() and True)
-                
-
-    @inactivate_new_game_etc
-    def select_game (self):
-        self.tb.hide()
-        choice = game_selector.NewOrSavedGameSelector().run_swallowed_dialog(self.swallower)
-        #print "The user's choice is ",choice
-        if not choice:
-            return True
-        #print 'choice is ',choice
-        if choice[0] == game_selector.NewOrSavedGameSelector.NEW_GAME:
-            self.gsd.change_grid(choice[1],9)
-        if choice[0] == game_selector.NewOrSavedGameSelector.SAVED_GAME:
-            saver.open_game(self,choice[1])
-        if self.gconf['show_toolbar']: self.tb.show()
-
-    def show (self):
-        self.gsd.show()
-        self.w.show()
-
-    def setup_gui (self):
-        gnome.program_init('gnome-sudoku',VERSION, properties={gnome.PARAM_APP_DATADIR:APP_DATA_DIR})
-        self.initialize_prefs()
-        self.setup_main_window()
-        self.gsd = gsudoku.SudokuGameDisplay()
-        self.gsd.connect('puzzle-finished',self.you_win_callback)        
-        self.setup_color()
-        self.setup_actions()
-        self.setup_undo()
-        self.setup_autosave()
-        self.w.add_accel_group(self.uimanager.get_accel_group())
-        self.setup_main_boxes()        
-        self.setup_tracker_interface()
-        self.setup_toggles()        
-
-    def setup_main_window (self):
-        gtk.window_set_default_icon_name('gnome-sudoku')
-        self.w = gtk.Window()
-        self.w.set_default_size(self.gconf['width'], self.gconf['height'])
-        self.w.set_title(APPNAME_SHORT)
-        self.w.connect('configure-event',self.resize_cb)
-        self.w.connect('delete-event',self.quit_cb) 
-        self.uimanager = gtk.UIManager()
-
-    def setup_actions (self):
-        self.main_actions = gtk.ActionGroup('MainActions')        
-        self.main_actions.add_actions([
-            ('Game',None,_('_Game')),
-            ('New',gtk.STOCK_NEW,None,
-             '<Control>n',_('New game'),self.new_cb),
-            ('Print',gtk.STOCK_PRINT,None,
-             None,_('Print current game'),self.print_game),
-            ('PrintMany',gtk.STOCK_PRINT,_('Print _Multiple Sudokus'),
-             None,_('Print more than one sudoku at a time.'),self.print_multiple_games),
-            #('Quit',gtk.STOCK_QUIT,None,'<Control>q',
-            # 'Quit Sudoku game',self.quit_cb),
-            ('Close',gtk.STOCK_CLOSE,None,'<Control>w',
-             _('Close Sudoku'),self.quit_cb),
-            #('Save',gtk.STOCK_SAVE,_('_Save'),
-            # '<Control>s','Save game to play later.',
-            # self.save_game_cb),
-            #('ByHand',gtk.STOCK_EDIT,_('_Enter custom game'),
-            # None,_('Enter new puzzle by hand (use this to copy a puzzle from another source).'),
-            # self.enter_game_by_hand),
-            #('Open',gtk.STOCK_OPEN,_('Open game'),
-            # '<Control>o',_('Open a saved game from file.'),
-            # self.open_game),
-            ('Tools',None,_('_Tools')),
-            ('View',None,_('_View')),
-            ('ShowPossible',gtk.STOCK_DIALOG_INFO,_('_Hint'),
-             '<Control>h',
-             _('Show which numbers could go in the current square.'),
-             self.show_hint_cb),
-            ('AutofillCurrentSquare',gtk.STOCK_APPLY,_('_Fill'),'<Control>f',
-             _('Automatically fill in the current square if possible.'),
-             self.auto_fill_current_square_cb),
-            ('Autofill',gtk.STOCK_REFRESH,_('Fill _all squares'),'<Control>a',
-             _('Automatically fill in all squares for which there is only one valid value.'),
-             self.auto_fill_cb),
-            ('FullScreen',STOCK_FULLSCREEN,None,
-             'F11',None,self.full_screen_cb),
-            ('PuzzleInfo',gtk.STOCK_ABOUT,_('Puzzle _Statistics'),
-             None,_('Show statistics about current puzzle'),
-             self.show_info_cb),
-            ('Help',None,_('_Help'),
-             None,None,None),
-            ('About',gtk.STOCK_ABOUT,None,
-             None,None,self.show_about),
-            ('ShowHelp',gtk.STOCK_HELP, _('_Contents'),
-             'F1',None,self.show_help),
-            #('HighScores',None,_('High _Scores'),
-            # None,_('Show high scores or replay old games.'),
-            # self.show_high_scores_cb),
-            ])
-        self.main_actions.add_toggle_actions([
-            ('AlwaysShowPossible',
-             None,
-             _('_Always show hint'),
-             None,
-             _('Always show possible numbers in a square'),
-             self.auto_hint_cb),
-            ('ShowImpossibleImplications',
-             None,
-             _('Warn about _unfillable squares'),
-             None,
-             _('Warn about squares made unfillable by a move'),
-             self.impossible_implication_cb),
-            ('Tracker','tracks',_('_Track additions'),
-             '<Control>T',
-             _('Mark new additions in a separate color so you can keep track of them.'),
-             self.tracker_toggle_cb,False),
-            ('ToggleToolbar',None,_('Show _Toolbar'),None,None,self.toggle_toolbar_cb,True),
-            ('ToggleHighlight',gtk.STOCK_SELECT_COLOR,_('_Highlighter'),
-             None,_('Highlight the current row, column and box'),self.toggle_highlight_cb,False),
-            ('BackgroundGenerator',None,_('Generate new puzzles _while you play'),
-             None,
-             _('Generate new puzzles in the background while you play. This will automatically pause when the game goes into the background.'),
-             self.toggle_generator_cb, True),
-            ])
-        
-        self.edit_actions = gtk.ActionGroup('EditActions')
-        self.edit_actions.add_actions(
-            [('Edit',None,_('_Edit')),
-             ('Undo',gtk.STOCK_UNDO,_('_Undo'),'<Control>z',_('Undo last action')),
-             ('Redo',gtk.STOCK_REDO,_('_Redo'),'<Shift><Control>z',_('Redo last action')),
-             ('Clear',gtk.STOCK_CLEAR,_('_Clear'),'<Control>b',_("Clear entries you've filled in"),self.clear_cb),
-             ('ClearNotes',None,_('Clear _Notes'),None,_("Clear notes and hints"),self.clear_notes_cb),             
-             # Trackers...
-             ('Tracker%s',None,_('No Tracker'),'<Control>0',None,lambda *args: self.set_tracker(-1)),
-             ('Generator',None,_('_Generate new puzzles'),None,_('Generate new puzzles.'),
-              self.generate_puzzle_gui,),
-             ])
-        self.edit_actions.add_actions(
-            [('Tracker%s'%n,None,'Tracker _%s'%n,'<Control>%s'%n,None,lambda *args: self.set_tracker(n-1)) for
-             n in range(1,9)])
-        self.uimanager.insert_action_group(self.main_actions,0)
-        self.uimanager.insert_action_group(self.edit_actions,0)
-        self.uimanager.add_ui_from_string(self.ui)
-
-    def setup_undo (self):
-        self.cleared = [] # used for Undo memory
-        self.cleared_notes = [] # used for Undo memory
-        # Set up our UNDO stuff
-        undo_widg = self.edit_actions.get_action('Undo')
-        redo_widg = self.edit_actions.get_action('Redo')
-        self.history = Undo.UndoHistoryList(undo_widg,redo_widg)
-        for e in self.gsd.__entries__.values():
-            Undo.UndoableGenericWidget(e,self.history,
-                                       set_method='set_value_from_undo',
-                                       pre_change_signal='value-about-to-change'
-                                       )
-            Undo.UndoableGenericWidget(e,self.history,
-                                       set_method='set_notes',
-                                       get_method='get_note_text',
-                                       signal='notes-changed',
-                                       pre_change_signal='value-about-to-change',
-                                       )
-
-    def setup_color (self):
-        # setup background colors
-        if self.gconf['bg_custom_color']:
-            bgcol = self.gconf['bg_custom_color']
-        elif self.gconf['bg_black']:
-            bgcol = 'black'
-        else:
-            bgcol = None
-        if bgcol: self.gsd.set_bg_color(bgcol)            
-
-    def setup_autosave (self):
-        gobject.timeout_add(1000*(self.gconf['auto_save_interval'] or 60), # in seconds...
-                            self.autosave)
-
-    def setup_main_boxes (self):
-        self.vb = gtk.VBox()
-        # Add menu bar and toolbar...
-        mb = self.uimanager.get_widget('/MenuBar'); mb.show()
-        self.vb.pack_start(mb,fill=False,expand=False)
-        self.tb = self.uimanager.get_widget('/Toolbar')
-        self.vb.pack_start(self.tb,fill=False,expand=False)
-        self.main_area = gtk.HBox()
-        self.swallower = SwappableArea(self.main_area)
-        self.swallower.show()
-        self.vb.pack_start(self.swallower,True,padding=12)
-        self.main_area.pack_start(self.gsd,padding=6)
-        self.main_actions.set_visible(True)
-        self.game_box = gtk.VBox()
-        self.main_area.show()
-        self.vb.show()
-        self.game_box.show()
-        self.main_area.pack_start(self.game_box,False,padding=12)
-        self.statusbar = gtk.Statusbar(); self.statusbar.show()
-        gobject.timeout_add(500,self.update_statusbar_cb)        
-        self.vb.pack_end(self.statusbar,fill=False,expand=False)        
-        self.w.add(self.vb)
-
-    def setup_by_hand_area (self):
-        # Set up area for by-hand editing...
-        self.by_hand_label = gtk.Label()
-        self.by_hand_label.set_alignment(0,0)
-        self.by_hand_label.set_markup('<i>%s</i>'%_('Entering custom grid...'))
-        self.game_box.pack_start(self.by_hand_label,False,)#padding=12)        
-        self.by_hand_buttonbox = gtk.HButtonBox()
-        self.by_hand_buttonbox.set_spacing(12)
-        self.by_hand_save_button = gtk.Button(_('_Play game'))
-        self.by_hand_save_button.connect('clicked',self.save_handmade_grid)
-        self.by_hand_cancel_button = gtk.Button(stock=gtk.STOCK_CANCEL)
-        self.by_hand_cancel_button.connect('clicked',self.cancel_handmade_grid)
-        self.by_hand_buttonbox.add(self.by_hand_cancel_button)
-        self.by_hand_buttonbox.add(self.by_hand_save_button)
-        self.game_box.pack_start(self.by_hand_buttonbox,False,padding=18)
-        self.by_hand_widgets = [self.by_hand_label,self.by_hand_buttonbox]        
-
-    def setup_toggles (self):
-        # sync up toggles with gconf values...
-        map(lambda tpl: self.gconf_wrap_toggle(*tpl),
-            [('always_show_hints',
-              self.main_actions.get_action('AlwaysShowPossible')),
-             ('show_impossible_implications',
-              self.main_actions.get_action('ShowImpossibleImplications')),
-             ('generate_puzzles_in_background',
-              self.main_actions.get_action('BackgroundGenerator')),
-             ('show_toolbar',
-              self.main_actions.get_action('ToggleToolbar')),
-             ('highlight',
-              self.main_actions.get_action('ToggleHighlight')),
-             ('show_tracker',
-              self.main_actions.get_action('Tracker')),
-             ])        
-
-    @simple_debug
-    def start_worker_thread (self, *args):
-        n_new_puzzles = self.sudoku_maker.n_puzzles(new=True)
-        if n_new_puzzles < self.gconf['minimum_number_of_new_puzzles']:
-            #print 'Generate puzzles'
-            self.worker = threading.Thread(target=lambda *args: self.sudoku_maker.work(limit=5))
-            self.worker_connections = [
-                self.timer.connect('timing-started',self.sudoku_maker.resume),
-                self.timer.connect('timing-stopped',self.sudoku_maker.pause)
-                ]
-            self.worker.start()
-        #else:
-        #    print "Don't generate...",'We already have ',n_new_puzzles,'!'
-
-    @simple_debug
-    def stop_worker_thread (self, *args):
-        if hasattr(self,'worker'):
-            self.sudoku_maker.stop()
-            for c in self.worker_connections:
-                self.timer.disconnect(c)
-
-    @simple_debug
-    def you_win_callback (self,grid):
-        self.won = True
-        # increase difficulty for next time.
-        self.gconf['difficulty']=self.gconf['difficulty']+0.1
-        self.timer.finish_timing()
-        self.sudoku_tracker.finish_game(self)
-        sublabel = _("You completed the puzzle in %(totalTime)s (%(activeTime)s active)")%{'totalTime': self.timer.total_time_string(),
-        'activeTime': self.timer.active_time_string()
-                }
-        sublabel += "\n"
-        sublabel += ngettext("You got %(n)s hint","You got %(n)s hints",self.gsd.hints)%{'n':self.gsd.hints}
-        sublabel += "\n"
-        if self.gsd.impossible_hints:
-            sublabel += ngettext("You had %(n)s impossibility pointed out.",
-                                 "You had %(n)s impossibilities pointed out.",
-                                 self.gsd.impossible_hints)%{'n':self.gsd.impossible_hints}
-            sublabel += "\n"
-        if self.gsd.auto_fills:
-            sublabel += ngettext("You used the auto-fill %(n)s time",
-                                 "You used the auto-fill %(n)s times",
-                                 self.gsd.auto_fills)%{'n':self.gsd.auto_fills}
-        dialog_extras.show_message(_("You win!"),label=_("You win!"),
-                                   #icon=os.path.join(IMAGE_DIR,'winner2.png'),
-                                   sublabel=sublabel
-                                   )
-        # High scores is complicated and kind of unnecessary for now...
-        # If we reimplement, we should use the proper gnome-games system.
-        #hs = game_selector.HighScores(self.sudoku_tracker)
-        #hs.highlight_newest=True
-        #hs.run_swallowed_dialog(self.swallower)
-        #print 'Run high scores dialog!'
-        #hs.run_dialog()
-        #self.main_actions.get_action('HighScores').set_sensitive(True)
-        #self.gsd.blank_grid()
-        self.new_cb()
-
-    @simple_debug
-    def initialize_prefs (self):
-        for k,v in self.initial_prefs.items():
-            try:
-                self.gconf[k]
-            except:
-                self.gconf[k]=v
-        self.player = self.gconf['player']
-
-    @simple_debug
-    @inactivate_new_game_etc
-    def new_cb (self,*args):
-        if (self.gsd.grid and self.gsd.grid.is_changed() and not self.won):
-            try:
-                if dialog_extras.getBoolean(
-                    label=_("Save this game before starting new one?"),
-                    custom_yes=_("_Save game for later"),
-                    custom_no=_("_Abandon game"),
-                    ):
-                    self.save_game()
-                else:
-                    self.sudoku_tracker.abandon_game(self)
-            except dialog_extras.UserCancelledError:
-                # User cancelled new game
-                return
-        self.do_stop()
-        self.select_game()
-
-            
-    @simple_debug
-    def stop_game (self):
-       if (self.gsd.grid
-            and self.gsd.grid.is_changed()
-            and (not self.won)):
-            try:
-                if dialog_extras.getBoolean(label=_("Save game before closing?")):
-                    self.save_game(self)
-            except dialog_extras.UserCancelledError:
-                return
-            self.do_stop()
-            
-    def do_stop (self):
-        self.gsd.grid = None
-        self.tracker_ui.reset()
-        self.timer.reset_timer()
-        self.timer.start_timing()
-        self.history.clear()        
-        self.won = False
-        
-    @simple_debug
-    def resize_cb (self, widget, event):
-        self.gconf['width'] = event.width
-        self.gconf['height'] = event.height
-
-    @simple_debug
-    def quit_cb (self, *args):
-        # Offer a save...
-        #try:
-        if (self.gsd.grid
-            and self.gsd.grid.is_changed()
-            and (not self.won)):
-            #and dialog_extras.getBoolean(label=_("Save game before closing?"))):
-            self.save_game(self) # autosave...
-        #except dialog_extras.UserCancelledError:
-        #    return
-        if gtk.main_level() > 1:
-            # If we are in an embedded mainloop, that means that one
-            # of our "swallowed" dialogs is active, in which case we
-            # have to quit that mainloop before we can quit
-            # properly.
-            if self.swallower.running:
-                d = self.swallower.running
-                d.response(gtk.RESPONSE_DELETE_EVENT)
-            gtk.main_quit() # Quit the embedded mainloop
-            gobject.idle_add(self.quit_cb,100) # Call ourselves again
-                                               # to quit the main
-                                               # mainloop
-            return
-            #buttons = d.action_area.get_children()
-            #for b in buttons:
-            #    if d.get_response_for_widget(b) in [gtk.RESPONSE_CLOSE,gtk.RESPONSE_CANCEL]:
-            #        print 'clicking button',b
-            #        b.emit('clicked')
-            #        while gtk.events_pending():
-            #            print 'Take care of iters...'
-            #            gtk.main_iteration()
-            #        break
-        self.w.hide()
-        # make sure we really go away before doing our saving --
-        # otherwise we appear sluggish.
-        while gtk.events_pending():
-            gtk.main_iteration()
-        if self.won:
-            self.gconf['current_game']=''
-        if not self.won:
-            if not self.gsd.grid:
-                self.gconf['current_game']=''            
-        self.stop_worker_thread()
-        # allow KeyboardInterrupts, which calls quit_cb outside the main loop
-        try:
-            gtk.main_quit()
-        except RuntimeError, e:
-            pass
-
-    @simple_debug
-    @inactivate_new_game_etc
-    def enter_game_by_hand (self, *args):
-        self.stop_game()
-        self.gsd.change_grid(sudoku.InteractiveSudoku(),9)
-        for w in self.by_hand_widgets: w.show_all()
-        
-    @simple_debug
-    def save_handmade_grid (self, *args):
-        for w in self.by_hand_widgets: w.hide()
-        # this should make our active grid into our virgin grid
-        self.won = False
-        self.gsd.change_grid(self.gsd.grid,9)
-        self.sudoku_maker.names[self.gsd.grid.to_string()]=self.sudoku_maker.get_puzzle_name('Custom Puzzle')
-        self.history.clear()
-
-    @simple_debug
-    def cancel_handmade_grid (self, *args):
-        for w in self.by_hand_widgets: w.hide()
-
-    #def save_game_cb (self, *args):
-    #    try:
-    #        self.save_game(*args)
-    #    except dialog_extras.UserCancelledError:
-    #        pass
-
-    @simple_debug
-    def save_game (self, *args):
-	self.sudoku_tracker.save_game(self)
-
-    @simple_debug
-    def zoom_in_cb (self,*args):
-        self.gh.change_font_size(multiplier=1.1)
-        self.zoom = self.zoom * 1.1        
-
-    @simple_debug
-    def zoom_out_cb (self,*args):
-        self.gh.change_font_size(multiplier=0.9)
-        self.zoom = self.zoom * 0.9
-
-    def full_screen_cb (self, *args):
-        if not hasattr(self,'is_fullscreen'): self.is_fullscreen = False
-        if self.is_fullscreen:
-            self.w.unfullscreen()
-            self.is_fullscreen = False
-        else:
-            self.w.fullscreen()
-            self.is_fullscreen = True
-        
-    @simple_debug
-    def clear_cb (self,*args):        
-        clearer=Undo.UndoableObject(
-            lambda *args: self.cleared.append(self.gsd.reset_grid()), #action
-            lambda *args: [self.gsd.add_value_to_ui(*entry) for entry in self.cleared.pop()], #inverse
-            self.history #history
-            )
-        clearer.perform()
-
-    def clear_notes_cb (self, *args):
-        clearer = Undo.UndoableObject(
-            lambda *args: self.cleared_notes.append(self.gsd.clear_notes()), #action
-            # clear_notes returns a list of tuples indicating the cleared notes...
-            # (x,y,(top,bottom)) -- this is what we need for undoing
-            lambda *args: [self.gsd.__entries__[t[0],t[1]].set_notes(t[2]) for t in self.cleared_notes.pop()], #inverse
-            self.history
-            )
-        clearer.perform()
-
-    @simple_debug
-    def show_hint_cb (self, *args):
-        self.gsd.show_hint()
-
-    @simple_debug
-    def auto_hint_cb (self, action):
-        if action.get_active():
-            self.gsd.always_show_hints = True
-            self.gsd.update_all_hints()
-        else:
-            self.gsd.always_show_hints = False            
-            self.gsd.clear_hints()
-
-    @simple_debug
-    def impossible_implication_cb (self, action):
-        if action.get_active():
-            self.gsd.show_impossible_implications = True
-        else:
-            self.gsd.show_impossible_implications = False
-
-    @simple_debug
-    def auto_fill_cb (self, *args):
-        if not hasattr(self,'autofilled'): self.autofilled=[]
-        if not hasattr(self,'autofiller'):
-            self.autofiller = Undo.UndoableObject(
-                lambda *args: self.autofilled.append(self.gsd.auto_fill()),
-                lambda *args: [self.gsd.remove(entry[0],entry[1],do_removal=True) for entry in self.autofilled.pop()],
-                self.history
-                )
-        self.autofiller.perform()
-
-    @simple_debug
-    def auto_fill_current_square_cb (self, *args):
-        self.gsd.auto_fill_current_entry()
-
-    @simple_debug
-    def setup_tracker_interface (self):
-        self.trackers = {}        
-        self.tracker_ui = TrackerBox(self)
-        self.tracker_ui.show_all()
-        self.tracker_ui.hide()
-        self.game_box.add(self.tracker_ui)
-
-    @simple_debug
-    def set_tracker (self, n):
-        if self.gsd.trackers.has_key(n):
-            self.tracker_ui.select_tracker(n)
-            e = self.gsd.get_focused_entry()
-            if e:
-                if n==-1:
-                    for tid in self.gsd.trackers_for_point(e.x,e.y):
-                        self.gsd.remove_tracker(e.x,e.y,tid)
-                else:
-                    self.gsd.add_tracker(e.x,e.y,n)
-        else:
-            print 'No tracker ',n,'yet'
-        
-    @simple_debug
-    def tracker_toggle_cb (self, widg):
-        if widg.get_active():
-            #if len(self.tracker_ui.tracker_model)<=1:
-            #    self.tracker_ui.add_tracker()
-            self.tracker_ui.show_all()
-        else:
-            self.tracker_ui.hide()
-
-    @simple_debug
-    def toggle_toolbar_cb (self, widg):
-        if widg.get_active(): self.tb.show()
-        else: self.tb.hide()
-
-    def update_statusbar_cb (self, *args):
-        if not self.gsd.grid: return True
-        puzz = self.gsd.grid.virgin.to_string()
-        if (not hasattr(self,'current_puzzle_string') or
-            self.current_puzzle_string != puzz):
-            self.current_puzzle_diff = self.sudoku_maker.get_difficulty(puzz)
-	tot_string = _("Playing %(difficulty)s puzzle.")%{'difficulty':self.current_puzzle_diff.value_string()}
-        tot_string += " " + "(%1.2f)"%self.current_puzzle_diff.value
-        #if self.timer.tot_time or self.timer.tot_time_complete:
-        #    time_string = _("%s (%s active)")%(
-        #        self.timer.total_time_string(),
-        #        self.timer.active_time_string()
-        #        )
-        #    if not self.timer.__timing__:
-        #        time_string += " %s"%_('paused')
-        #    tot_string += " - " + time_string
-        #if self.gsd.hints and not self.gconf['always_show_hints']:
-        #    tot_string += " -  " +ngettext("%(n)s hint","%(n)s hints",
-        #                           self.gsd.hints)%{'n':self.gsd.hints}
-        #if self.gsd.auto_fills:
-        #    tot_string += "  " +ngettext("%(n)s auto-fill","%(n)s auto-fills",
-        #                            self.gsd.auto_fills)%{'n':self.gsd.auto_fills}
-        if not hasattr(self,'sbid'):
-            self.sbid = self.statusbar.get_context_id('game_info')
-        self.statusbar.pop(self.sbid)
-        self.statusbar.push(self.sbid,
-                            tot_string)
-        return True
-
-    def toggle_highlight_cb (self, widg):
-        if widg.get_active():
-            self.gsd.toggle_highlight(True)
-        else:
-            self.gsd.toggle_highlight(False)
-
-    @simple_debug
-    def show_info_cb (self, *args):
-        if not self.gsd.grid:
-            dialog_extras.show_message(parent=self.w,
-                                       title=_("Puzzle Information"),
-                                       label=_("There is no current puzzle.")
-                                       )
-            return
-        puzzle = self.gsd.grid.virgin.to_string()
-        diff = self.sudoku_maker.get_difficulty(puzzle)
-        information = _("Calculated difficulty: ")
-        information += diff.value_string()
-        information += " (%1.2f)"%diff.value
-        information += "\n"
-        information += _("Number of moves instantly fillable by elimination: ")
-        information += str(int(diff.instant_elimination_fillable))
-        information += "\n"
-        information += _("Number of moves instantly fillable by filling: ")
-        information += str(int(diff.instant_fill_fillable))
-        information += "\n"
-        information += _("Amount of trial-and-error required to solve: ")
-        information += str(len(diff.guesses))
-        dialog_extras.show_message(parent=self.w,
-                                   title=_("Puzzle Statistics"),
-                                   label=_("Puzzle Statistics"),
-                                   sublabel=information)
-
-    @simple_debug
-    def toggle_generator_cb (self, toggle):
-        if toggle.get_active():
-            self.start_worker_thread()
-        else:
-            self.stop_worker_thread()
-
-    @simple_debug
-    def show_high_scores_cb (self, *args):
-        hs=game_selector.HighScores(self.sudoku_tracker)
-        replay_game = hs.run_dialog()
-        if replay_game:
-            self.stop_game()
-            self.gsd.change_grid(replay_game,9)
-
-    @simple_debug
-    def autosave (self):
-        # this is called on a regular loop and will autosave if we
-        # have reason to...
-        if self.gsd.grid and self.gsd.grid.is_changed() and not self.won:
-            self.sudoku_tracker.save_game(self)
+# Ignore any exceptions writing to stdout using print statements
+class SafeStdout:
+    def __init__(self):
+        self.stdout = sys.stdout
     
-    def offer_to_load_autosaved_file (self):
-    	pass    
-
-    @simple_debug
-    def load_autosave (self, filename):
-        saver.unpickle_game(self,filename)
-        self.history.clear()
-        
-    #def toggle_autosave ():
+    def fileno(self):
+        return self.stdout.fileno()
 
-    @simple_debug
-    def show_about (self, *args):
-        about = gtk.AboutDialog()
-        about.set_name(APPNAME)
-        about.set_version(VERSION)
-        about.set_copyright(COPYRIGHT)
-	about.set_license(LICENSE[0] + '\n\n' + LICENSE[1] + '\n\n' +LICENSE[2])
-	about.set_wrap_license(True)
-        about.set_comments(DESCRIPTION)
-        about.set_authors(AUTHORS)
-        about.set_website(WEBSITE)
-        about.set_website_label(WEBSITE_LABEL)
-        about.set_logo_icon_name("gnome-sudoku")
-        about.set_translator_credits(_("translator-credits"))
-        about.connect("response", lambda d, r: d.destroy())
-        about.show()
-
-    @simple_debug
-    def show_help (self, *args):
-        #dialog_extras.show_faq(faq_file=os.path.join(BASE_DIR,_('FAQ')))
+    def write(self, data):
         try:
-            gnome.help_display('gnome-sudoku')
-        except gobject.GError, e:
-            # FIXME: This should create a pop-up dialog
-            print _('Unable to display help: %s') % str(e)
-
-    @simple_debug
-    def print_game (self, *args):
-        printing.print_sudokus([self.gsd])
-
-    @simple_debug
-    def print_multiple_games (self, *args):
-        gp=game_selector.GamePrinter(self.sudoku_tracker, self.gconf)
-        gp.run_dialog()
-
-    @simple_debug
-    def generate_puzzle_gui (self, *args):
-        sudoku_generator_gui.GameGenerator(self,self.gconf)
-
-class TrackerBox (gtk.VBox):
-
-    @simple_debug
-    def __init__ (self, main_ui):
-        
-        gtk.VBox.__init__(self)
-        self.glade = gtk.glade.XML(os.path.join(GLADE_DIR,'tracker.glade'))
-        self.main_ui = main_ui
-        self.vb = self.glade.get_widget('vbox1')
-        self.vb.unparent()
-        self.pack_start(self.vb,expand=True,fill=True)
-        self.setup_actions()
-        self.setup_tree()
-        self.show_all()
-
-    @simple_debug
-    def reset (self):
-        
-        for tree in self.tracker_model:
-            if tree[0]>-1:
-                self.tracker_model.remove(tree.iter)
-
-    @simple_debug
-    def setup_tree (self):
-        self.tracker_tree = self.glade.get_widget('treeview1')
-        self.tracker_model = gtk.ListStore(int,gtk.gdk.Pixbuf,str)
-        self.tracker_tree.set_model(self.tracker_model)
-        col1 = gtk.TreeViewColumn("",gtk.CellRendererPixbuf(),pixbuf=1)
-        col2 = gtk.TreeViewColumn("",gtk.CellRendererText(),text=2)
-        self.tracker_tree.append_column(col2)
-        self.tracker_tree.append_column(col1)
-        # Our initial row...
-        self.tracker_model.append([-1,None,_('No Tracker')])
-        self.tracker_tree.get_selection().connect('changed',self.selection_changed_cb)
-
-    @simple_debug
-    def setup_actions (self):
-        self.tracker_actions = gtk.ActionGroup('tracker_actions')        
-        self.tracker_actions.add_actions(
-            [('Clear',
-              gtk.STOCK_CLEAR,
-              _('_Clear Tracker'),
-              None,_('Clear all moves tracked by selected tracker.'),
-              self.clear_cb
-              ),
-             ('Keep',None,
-              _('_Clear Others'),
-              None,
-              _('Clear all moves not tracked by selected tracker.'),
-              self.keep_cb),
-             ]
-            )
-        for action,widget_name in [('Clear','ClearTrackerButton'),
-                                   ('Keep','KeepTrackerButton'),
-                                   ]:
-            a=self.tracker_actions.get_action(action)
-            a.connect_proxy(self.glade.get_widget(widget_name))
-        self.glade.get_widget('AddTrackerButton').connect('clicked',
-                                                          self.add_tracker)
-        # Default to insensitive (they only become sensitive once a tracker is added)
-        self.tracker_actions.set_sensitive(False)
-
-    @simple_debug
-    def add_tracker (self,*args):
-        #print 'Adding tracker!'
-        tracker_id = self.main_ui.gsd.create_tracker()
-        #print 'tracker_id = ',tracker_id
-        pb=image_extras.pixbuf_transform_color(
-            STOCK_PIXBUFS['tracks'],
-            (0,0,0),#white
-            self.main_ui.gsd.get_tracker_color(tracker_id),
-            )
-        # select our new tracker
-        self.tracker_tree.get_selection().select_iter(
-            self.tracker_model.append([tracker_id,
-                                  pb,
-                                  _("Tracker %s")%(tracker_id+1)]
-                                  )
-            )
-        
-    @simple_debug
-    def select_tracker (self, tracker_id):
-        for row in self.tracker_model:
-            if row[0]==tracker_id:
-                self.tracker_tree.get_selection().select_iter(row.iter)
-
-    @simple_debug
-    def selection_changed_cb (self, selection):
-        mod,itr = selection.get_selected()
-        if itr: selected_tracker_id = mod.get_value(itr,0)
-        else: selected_tracker_id=-1
-        # This should be cheap since we don't expect many trackers...
-        # We cycle through each row and toggle it off if it's not
-        # selected; on if it is selected
-        for row in self.tracker_model:
-            tid = row[0]
-            if tid != -1: # -1 == no tracker
-                self.main_ui.gsd.toggle_tracker(tid,tid==selected_tracker_id)
-        self.tracker_actions.set_sensitive(selected_tracker_id != -1)
-
-    @simple_debug
-    def clear_cb (self, action):
-        mod,itr=self.tracker_tree.get_selection().get_selected()
-        # This should only be called if there is an itr, but we'll
-        # double-check just in case.
-        if itr:
-            selected_tracker_id=mod.get_value(itr,0)
-            self.tracker_delete_tracks(selected_tracker_id)
-
-    @simple_debug
-    def keep_cb (self, action):
-        mod,itr=self.tracker_tree.get_selection().get_selected()
-        selected_tracker_id=mod.get_value(itr,0)
-        self.tracker_keep_tracks(selected_tracker_id)
-
-    @simple_debug
-    def tracker_delete_tracks (self, tracker_id):
-        clearer=Undo.UndoableObject(
-            lambda *args: self.main_ui.cleared.append(self.main_ui.gsd.delete_by_tracker(tracker_id)),
-            lambda *args: [self.main_ui.gsd.add_value_to_ui(*entry) for entry in self.main_ui.cleared.pop()],
-            self.main_ui.history)
-        clearer.perform()
-
-    @simple_debug
-    def tracker_keep_tracks (self, tracker_id):
-        clearer=Undo.UndoableObject(
-            lambda *args: self.main_ui.cleared.append(self.main_ui.gsd.delete_except_for_tracker(tracker_id)),
-            lambda *args: [self.main_ui.gsd.add_value_to_ui(*entry) for entry in self.main_ui.cleared.pop()],
-            self.main_ui.history)
-        clearer.perform()
+            self.stdout.write(data)
+        except:
+            pass
 
+sys.stdout = SafeStdout()
 
 def start_game ():
-    if options.debug: print 'Starting GNOME Sudoku in debug mode'
-
-    ##  You must call g_thread_init() before executing any other GLib
-    ##  functions in a threaded GLib program.
-    gobject.threads_init()
-
-    if options.profile:
-        options.profile = False
-        profile_me()
-        return
-
-    u = UI()
-    if not u.quit:
-        try:
-            gtk.main()        
-        except KeyboardInterrupt:
-            # properly quit on a keyboard interrupt...
-            u.quit_cb()
-
-def profile_me ():
-    print 'Profiling GNOME Sudoku'
-    import tempfile,os.path
-    import hotshot, hotshot.stats
-    pname = os.path.join(tempfile.gettempdir(),'GNOME_SUDOKU_HOTSHOT_PROFILE')
-    prof = hotshot.Profile(pname)
-    prof.runcall(start_game)
-    stats = hotshot.stats.load(pname)
-    stats.strip_dirs()
-    stats.sort_stats('time','calls').print_stats()    
-
-    
-        
-        
-if __name__ == '__main__':
-    import defaults
-    defaults.DATA_DIR == '/tmp/'; DATA_DIR=='/tmp/'
-    
-    
+	import main
+	main.start_game()

Added: trunk/gnome-sudoku/src/lib/main.py
==============================================================================
--- (empty file)
+++ trunk/gnome-sudoku/src/lib/main.py	Thu Jan 10 23:24:18 2008
@@ -0,0 +1,1034 @@
+try:
+    import pygtk
+    pygtk.require('2.0')
+except ImportError, err:
+    print ("PyGTK not found. Please make sure it is installed properly and referenced in your PYTHONPATH environment variable.")
+
+import gtk, gobject, gtk.glade
+import gnome, gnome.ui, pango
+import os, os.path
+from gtk_goodies import gconf_wrapper, Undo, dialog_extras, image_extras
+import gsudoku, sudoku, saver, sudoku_maker, printing, sudoku_generator_gui
+import game_selector
+import time, threading
+from gettext import gettext as _
+from gettext import ngettext
+from defaults import *
+from timer import ActiveTimer
+from simple_debug import simple_debug,options
+from dialog_swallower import SwappableArea
+
+icon_factory = gtk.IconFactory()
+STOCK_PIXBUFS = {}
+for filename,stock_id in [('footprints.png','tracks'),]:
+    pb = gtk.gdk.pixbuf_new_from_file(os.path.join(IMAGE_DIR,filename))
+    STOCK_PIXBUFS[stock_id]=pb
+    iconset = gtk.IconSet(pb)
+    icon_factory.add(stock_id,iconset)
+    icon_factory.add_default()
+
+gtk.stock_add([('tracks',
+                _('Track moves'),
+                0,0,""),])
+
+try:
+    STOCK_FULLSCREEN = gtk.STOCK_FULLSCREEN
+except:
+    STOCK_FULLSCREEN = _('Full Screen')
+
+def inactivate_new_game_etc (fun):
+    def _ (ui, *args, **kwargs):
+        paths = [
+            '/MenuBar/Game/New',
+            #'/MenuBar/Game/Open',
+            #'/MenuBar/Game/ByHand',
+            '/MenuBar/Game/Print',
+            '/MenuBar/Edit/Undo',
+            '/MenuBar/Edit/Redo',
+            '/MenuBar/Edit/Clear',
+            '/MenuBar/Edit/ClearNotes',
+            '/MenuBar/Tools/ShowPossible',
+            '/MenuBar/Tools/AutofillCurrentSquare',
+            '/MenuBar/Tools/Autofill',
+            '/MenuBar/Tools/AlwaysShowPossible',
+            '/MenuBar/Tools/ShowImpossibleImplications',
+            '/MenuBar/Tools/Tracker',
+            '/MenuBar/Game/PuzzleInfo',
+            #'/MenuBar/Game/HighScores',
+            ]
+        for p in paths:
+            action = ui.uimanager.get_action(p)
+            if not action: action = ui.uimanager.get_widget(p)
+            if not action: print 'No action at path',p
+            else: action.set_sensitive(False)
+        ret = fun(ui,*args,**kwargs)
+        for p in paths:
+            action = ui.uimanager.get_action(p)
+            if not action: action = ui.uimanager.get_widget(p)
+            if not action: print 'No action at path',p
+            else: action.set_sensitive(True)
+        return ret
+    return _
+
+class UI (gconf_wrapper.GConfWrapper):
+    ui='''<ui>
+    <menubar name="MenuBar">
+      <menu name="Game" action="Game">
+        <menuitem action="New"/>
+        <!--<menuitem action="ByHand"/>-->
+        <!--<menuitem action="Open"/>-->
+        <!--<separator/>
+        <menuitem action="Save"/>-->
+        <separator/>
+        <menuitem action="PuzzleInfo"/>
+        <!--<menuitem action="HighScores"/>-->
+        <separator/>        
+        <menuitem action="Print"/>
+        <menuitem action="PrintMany"/>
+        <separator/>
+        <menuitem action="Close"/>
+        <!--<menuitem action="Quit"/>-->
+      </menu>
+      <menu action="Edit">
+        <menuitem action="Undo"/>
+        <menuitem action="Redo"/>
+        <separator/>
+        <menuitem action="Clear"/>
+        <menuitem action="ClearNotes"/>        
+      </menu>
+      <menu action="View">
+        <menuitem action="FullScreen"/>
+        <separator/>
+        <menuitem action="ToggleToolbar"/>
+        <menuitem action="ToggleHighlight"/>        
+      </menu>
+      <menu action="Tools">
+         <menuitem action="ShowPossible"/>
+          <menuitem action="AutofillCurrentSquare"/>
+          <menuitem action="Autofill"/>
+          <separator/>
+          <menuitem action="AlwaysShowPossible"/>
+          <menuitem action="ShowImpossibleImplications"/>
+
+        <separator/>
+        <menuitem action="Generator"/>
+        <menuitem action="BackgroundGenerator"/>
+        <separator/>
+        <menuitem action="Tracker"/>
+        </menu>
+       <menu action="Help">
+         <menuitem action="ShowHelp"/>
+         <menuitem action="About"/>
+       </menu>
+     </menubar>
+     <toolbar name="Toolbar">
+      <!--<toolitem action="Quit"/>-->
+      <toolitem action="New"/>
+      <!--<toolitem action="Open"/>-->
+      <toolitem action="Print"/>
+      <!--<toolitem action="Save"/>-->
+      <!--<separator/>-->
+      <!--<toolitem action="Clear"/>      -->
+      <!--<toolitem action="ClearNotes"/>-->
+      <separator/>
+      <toolitem action="Undo"/>
+      <toolitem action="Redo"/>
+      <separator/>
+      <toolitem action="ShowPossible"/>
+      <!--<toolitem action="AlwaysShowPossible"/>-->
+      <toolitem action="AutofillCurrentSquare"/>      
+      <separator/>
+      <toolitem action="ToggleHighlight"/>
+      <!--<toolitem action="AlwaysShowPossible"/>-->
+      
+      <toolitem action="Tracker"/>
+     </toolbar>
+     </ui>'''
+
+    initial_prefs = {'group_size':9,
+                     'font_zoom':0,
+                     'zoom_on_resize':1,
+                     'always_show_hints':0,
+                     'player':os.environ.get('USERNAME',''),
+                     'difficulty':0.0,
+                     'minimum_number_of_new_puzzles':MIN_NEW_PUZZLES,
+                     'highlight':0,
+                     'bg_black':1,
+                     'bg_custom_color':'',
+                     'show_tracker':False,
+                     'width': 700,
+                     'height': 675,
+                     'auto_save_interval':60 # auto-save interval in seconds...
+                     #'show_notes':0
+                     }    
+
+    @simple_debug
+    def __init__ (self):
+        gconf_wrapper.GConfWrapper.__init__(self,
+                                            gconf_wrapper.GConf('gnome-sudoku')
+                                            )
+        self.setup_gui()
+        self.timer = ActiveTimer(self.w)
+        self.won = False
+        # add the accelerator group to our toplevel window
+        self.worker_connections=[]
+        self.timer.start_timing()
+        # setup sudoku maker...
+        self.sudoku_maker = sudoku_maker.SudokuMaker()
+        self.sudoku_tracker = saver.SudokuTracker()
+        #if not self.sudoku_tracker.playing:
+        #    self.main_actions.get_action('Open').set_sensitive(False)
+        #else:
+        #self.main_actions.get_action('Open').set_sensitive(True)
+        #if not self.sudoku_tracker.are_finished_games():
+        #    self.main_actions.get_action('HighScores').set_sensitive(False)
+        # select an easy puzzle...
+        #puz,d=self.sudoku_maker.get_new_puzzle(self.gconf['difficulty'])
+        #print 'Default to ',puz
+        #self.gsd.change_grid(puz, 9)
+        # generate puzzles while our use is working...        
+        self.show()
+        #print 'Select game!'
+        if self.select_game():
+            # If this return True, the user closed...
+            #print 'select game returned True - exit'
+            self.quit = True
+        else:
+            self.quit = False
+            # Generate puzzles in background...
+            if self.gconf['generate_puzzles_in_background']:
+                gobject.timeout_add(1000,lambda *args: self.start_worker_thread() and True)
+                
+
+    @inactivate_new_game_etc
+    def select_game (self):
+        self.tb.hide()
+        choice = game_selector.NewOrSavedGameSelector().run_swallowed_dialog(self.swallower)
+        #print "The user's choice is ",choice
+        if not choice:
+            return True
+        #print 'choice is ',choice
+        if choice[0] == game_selector.NewOrSavedGameSelector.NEW_GAME:
+            self.gsd.change_grid(choice[1],9)
+        if choice[0] == game_selector.NewOrSavedGameSelector.SAVED_GAME:
+            saver.open_game(self,choice[1])
+        if self.gconf['show_toolbar']: self.tb.show()
+
+    def show (self):
+        self.gsd.show()
+        self.w.show()
+
+    def setup_gui (self):
+        gnome.program_init('gnome-sudoku',VERSION, properties={gnome.PARAM_APP_DATADIR:APP_DATA_DIR})
+        self.initialize_prefs()
+        self.setup_main_window()
+        self.gsd = gsudoku.SudokuGameDisplay()
+        self.gsd.connect('puzzle-finished',self.you_win_callback)        
+        self.setup_color()
+        self.setup_actions()
+        self.setup_undo()
+        self.setup_autosave()
+        self.w.add_accel_group(self.uimanager.get_accel_group())
+        self.setup_main_boxes()        
+        self.setup_tracker_interface()
+        self.setup_toggles()        
+
+    def setup_main_window (self):
+        gtk.window_set_default_icon_name('gnome-sudoku')
+        self.w = gtk.Window()
+        self.w.set_default_size(self.gconf['width'], self.gconf['height'])
+        self.w.set_title(APPNAME_SHORT)
+        self.w.connect('configure-event',self.resize_cb)
+        self.w.connect('delete-event',self.quit_cb) 
+        self.uimanager = gtk.UIManager()
+
+    def setup_actions (self):
+        self.main_actions = gtk.ActionGroup('MainActions')        
+        self.main_actions.add_actions([
+            ('Game',None,_('_Game')),
+            ('New',gtk.STOCK_NEW,None,
+             '<Control>n',_('New game'),self.new_cb),
+            ('Print',gtk.STOCK_PRINT,None,
+             None,_('Print current game'),self.print_game),
+            ('PrintMany',gtk.STOCK_PRINT,_('Print _Multiple Sudokus'),
+             None,_('Print more than one sudoku at a time.'),self.print_multiple_games),
+            #('Quit',gtk.STOCK_QUIT,None,'<Control>q',
+            # 'Quit Sudoku game',self.quit_cb),
+            ('Close',gtk.STOCK_CLOSE,None,'<Control>w',
+             _('Close Sudoku'),self.quit_cb),
+            #('Save',gtk.STOCK_SAVE,_('_Save'),
+            # '<Control>s','Save game to play later.',
+            # self.save_game_cb),
+            #('ByHand',gtk.STOCK_EDIT,_('_Enter custom game'),
+            # None,_('Enter new puzzle by hand (use this to copy a puzzle from another source).'),
+            # self.enter_game_by_hand),
+            #('Open',gtk.STOCK_OPEN,_('Open game'),
+            # '<Control>o',_('Open a saved game from file.'),
+            # self.open_game),
+            ('Tools',None,_('_Tools')),
+            ('View',None,_('_View')),
+            ('ShowPossible',gtk.STOCK_DIALOG_INFO,_('_Hint'),
+             '<Control>h',
+             _('Show which numbers could go in the current square.'),
+             self.show_hint_cb),
+            ('AutofillCurrentSquare',gtk.STOCK_APPLY,_('_Fill'),'<Control>f',
+             _('Automatically fill in the current square if possible.'),
+             self.auto_fill_current_square_cb),
+            ('Autofill',gtk.STOCK_REFRESH,_('Fill _all squares'),'<Control>a',
+             _('Automatically fill in all squares for which there is only one valid value.'),
+             self.auto_fill_cb),
+            ('FullScreen',STOCK_FULLSCREEN,None,
+             'F11',None,self.full_screen_cb),
+            ('PuzzleInfo',gtk.STOCK_ABOUT,_('Puzzle _Statistics'),
+             None,_('Show statistics about current puzzle'),
+             self.show_info_cb),
+            ('Help',None,_('_Help'),
+             None,None,None),
+            ('About',gtk.STOCK_ABOUT,None,
+             None,None,self.show_about),
+            ('ShowHelp',gtk.STOCK_HELP, _('_Contents'),
+             'F1',None,self.show_help),
+            #('HighScores',None,_('High _Scores'),
+            # None,_('Show high scores or replay old games.'),
+            # self.show_high_scores_cb),
+            ])
+        self.main_actions.add_toggle_actions([
+            ('AlwaysShowPossible',
+             None,
+             _('_Always show hint'),
+             None,
+             _('Always show possible numbers in a square'),
+             self.auto_hint_cb),
+            ('ShowImpossibleImplications',
+             None,
+             _('Warn about _unfillable squares'),
+             None,
+             _('Warn about squares made unfillable by a move'),
+             self.impossible_implication_cb),
+            ('Tracker','tracks',_('_Track additions'),
+             '<Control>T',
+             _('Mark new additions in a separate color so you can keep track of them.'),
+             self.tracker_toggle_cb,False),
+            ('ToggleToolbar',None,_('Show _Toolbar'),None,None,self.toggle_toolbar_cb,True),
+            ('ToggleHighlight',gtk.STOCK_SELECT_COLOR,_('_Highlighter'),
+             None,_('Highlight the current row, column and box'),self.toggle_highlight_cb,False),
+            ('BackgroundGenerator',None,_('Generate new puzzles _while you play'),
+             None,
+             _('Generate new puzzles in the background while you play. This will automatically pause when the game goes into the background.'),
+             self.toggle_generator_cb, True),
+            ])
+        
+        self.edit_actions = gtk.ActionGroup('EditActions')
+        self.edit_actions.add_actions(
+            [('Edit',None,_('_Edit')),
+             ('Undo',gtk.STOCK_UNDO,_('_Undo'),'<Control>z',_('Undo last action')),
+             ('Redo',gtk.STOCK_REDO,_('_Redo'),'<Shift><Control>z',_('Redo last action')),
+             ('Clear',gtk.STOCK_CLEAR,_('_Clear'),'<Control>b',_("Clear entries you've filled in"),self.clear_cb),
+             ('ClearNotes',None,_('Clear _Notes'),None,_("Clear notes and hints"),self.clear_notes_cb),             
+             # Trackers...
+             ('Tracker%s',None,_('No Tracker'),'<Control>0',None,lambda *args: self.set_tracker(-1)),
+             ('Generator',None,_('_Generate new puzzles'),None,_('Generate new puzzles.'),
+              self.generate_puzzle_gui,),
+             ])
+        self.edit_actions.add_actions(
+            [('Tracker%s'%n,None,'Tracker _%s'%n,'<Control>%s'%n,None,lambda *args: self.set_tracker(n-1)) for
+             n in range(1,9)])
+        self.uimanager.insert_action_group(self.main_actions,0)
+        self.uimanager.insert_action_group(self.edit_actions,0)
+        self.uimanager.add_ui_from_string(self.ui)
+
+    def setup_undo (self):
+        self.cleared = [] # used for Undo memory
+        self.cleared_notes = [] # used for Undo memory
+        # Set up our UNDO stuff
+        undo_widg = self.edit_actions.get_action('Undo')
+        redo_widg = self.edit_actions.get_action('Redo')
+        self.history = Undo.UndoHistoryList(undo_widg,redo_widg)
+        for e in self.gsd.__entries__.values():
+            Undo.UndoableGenericWidget(e,self.history,
+                                       set_method='set_value_from_undo',
+                                       pre_change_signal='value-about-to-change'
+                                       )
+            Undo.UndoableGenericWidget(e,self.history,
+                                       set_method='set_notes',
+                                       get_method='get_note_text',
+                                       signal='notes-changed',
+                                       pre_change_signal='value-about-to-change',
+                                       )
+
+    def setup_color (self):
+        # setup background colors
+        if self.gconf['bg_custom_color']:
+            bgcol = self.gconf['bg_custom_color']
+        elif self.gconf['bg_black']:
+            bgcol = 'black'
+        else:
+            bgcol = None
+        if bgcol: self.gsd.set_bg_color(bgcol)            
+
+    def setup_autosave (self):
+        gobject.timeout_add(1000*(self.gconf['auto_save_interval'] or 60), # in seconds...
+                            self.autosave)
+
+    def setup_main_boxes (self):
+        self.vb = gtk.VBox()
+        # Add menu bar and toolbar...
+        mb = self.uimanager.get_widget('/MenuBar'); mb.show()
+        self.vb.pack_start(mb,fill=False,expand=False)
+        self.tb = self.uimanager.get_widget('/Toolbar')
+        self.vb.pack_start(self.tb,fill=False,expand=False)
+        self.main_area = gtk.HBox()
+        self.swallower = SwappableArea(self.main_area)
+        self.swallower.show()
+        self.vb.pack_start(self.swallower,True,padding=12)
+        self.main_area.pack_start(self.gsd,padding=6)
+        self.main_actions.set_visible(True)
+        self.game_box = gtk.VBox()
+        self.main_area.show()
+        self.vb.show()
+        self.game_box.show()
+        self.main_area.pack_start(self.game_box,False,padding=12)
+        self.statusbar = gtk.Statusbar(); self.statusbar.show()
+        gobject.timeout_add(500,self.update_statusbar_cb)        
+        self.vb.pack_end(self.statusbar,fill=False,expand=False)        
+        self.w.add(self.vb)
+
+    def setup_by_hand_area (self):
+        # Set up area for by-hand editing...
+        self.by_hand_label = gtk.Label()
+        self.by_hand_label.set_alignment(0,0)
+        self.by_hand_label.set_markup('<i>%s</i>'%_('Entering custom grid...'))
+        self.game_box.pack_start(self.by_hand_label,False,)#padding=12)        
+        self.by_hand_buttonbox = gtk.HButtonBox()
+        self.by_hand_buttonbox.set_spacing(12)
+        self.by_hand_save_button = gtk.Button(_('_Play game'))
+        self.by_hand_save_button.connect('clicked',self.save_handmade_grid)
+        self.by_hand_cancel_button = gtk.Button(stock=gtk.STOCK_CANCEL)
+        self.by_hand_cancel_button.connect('clicked',self.cancel_handmade_grid)
+        self.by_hand_buttonbox.add(self.by_hand_cancel_button)
+        self.by_hand_buttonbox.add(self.by_hand_save_button)
+        self.game_box.pack_start(self.by_hand_buttonbox,False,padding=18)
+        self.by_hand_widgets = [self.by_hand_label,self.by_hand_buttonbox]        
+
+    def setup_toggles (self):
+        # sync up toggles with gconf values...
+        map(lambda tpl: self.gconf_wrap_toggle(*tpl),
+            [('always_show_hints',
+              self.main_actions.get_action('AlwaysShowPossible')),
+             ('show_impossible_implications',
+              self.main_actions.get_action('ShowImpossibleImplications')),
+             ('generate_puzzles_in_background',
+              self.main_actions.get_action('BackgroundGenerator')),
+             ('show_toolbar',
+              self.main_actions.get_action('ToggleToolbar')),
+             ('highlight',
+              self.main_actions.get_action('ToggleHighlight')),
+             ('show_tracker',
+              self.main_actions.get_action('Tracker')),
+             ])        
+
+    @simple_debug
+    def start_worker_thread (self, *args):
+        n_new_puzzles = self.sudoku_maker.n_puzzles(new=True)
+        if n_new_puzzles < self.gconf['minimum_number_of_new_puzzles']:
+            #print 'Generate puzzles'
+            self.worker = threading.Thread(target=lambda *args: self.sudoku_maker.work(limit=5))
+            self.worker_connections = [
+                self.timer.connect('timing-started',self.sudoku_maker.resume),
+                self.timer.connect('timing-stopped',self.sudoku_maker.pause)
+                ]
+            self.worker.start()
+        #else:
+        #    print "Don't generate...",'We already have ',n_new_puzzles,'!'
+
+    @simple_debug
+    def stop_worker_thread (self, *args):
+        if hasattr(self,'worker'):
+            self.sudoku_maker.stop()
+            for c in self.worker_connections:
+                self.timer.disconnect(c)
+
+    @simple_debug
+    def you_win_callback (self,grid):
+        self.won = True
+        # increase difficulty for next time.
+        self.gconf['difficulty']=self.gconf['difficulty']+0.1
+        self.timer.finish_timing()
+        self.sudoku_tracker.finish_game(self)
+        sublabel = _("You completed the puzzle in %(totalTime)s (%(activeTime)s active)")%{'totalTime': self.timer.total_time_string(),
+        'activeTime': self.timer.active_time_string()
+                }
+        sublabel += "\n"
+        sublabel += ngettext("You got %(n)s hint","You got %(n)s hints",self.gsd.hints)%{'n':self.gsd.hints}
+        sublabel += "\n"
+        if self.gsd.impossible_hints:
+            sublabel += ngettext("You had %(n)s impossibility pointed out.",
+                                 "You had %(n)s impossibilities pointed out.",
+                                 self.gsd.impossible_hints)%{'n':self.gsd.impossible_hints}
+            sublabel += "\n"
+        if self.gsd.auto_fills:
+            sublabel += ngettext("You used the auto-fill %(n)s time",
+                                 "You used the auto-fill %(n)s times",
+                                 self.gsd.auto_fills)%{'n':self.gsd.auto_fills}
+        dialog_extras.show_message(_("You win!"),label=_("You win!"),
+                                   #icon=os.path.join(IMAGE_DIR,'winner2.png'),
+                                   sublabel=sublabel
+                                   )
+        # High scores is complicated and kind of unnecessary for now...
+        # If we reimplement, we should use the proper gnome-games system.
+        #hs = game_selector.HighScores(self.sudoku_tracker)
+        #hs.highlight_newest=True
+        #hs.run_swallowed_dialog(self.swallower)
+        #print 'Run high scores dialog!'
+        #hs.run_dialog()
+        #self.main_actions.get_action('HighScores').set_sensitive(True)
+        #self.gsd.blank_grid()
+        self.new_cb()
+
+    @simple_debug
+    def initialize_prefs (self):
+        for k,v in self.initial_prefs.items():
+            try:
+                self.gconf[k]
+            except:
+                self.gconf[k]=v
+        self.player = self.gconf['player']
+
+    @simple_debug
+    @inactivate_new_game_etc
+    def new_cb (self,*args):
+        if (self.gsd.grid and self.gsd.grid.is_changed() and not self.won):
+            try:
+                if dialog_extras.getBoolean(
+                    label=_("Save this game before starting new one?"),
+                    custom_yes=_("_Save game for later"),
+                    custom_no=_("_Abandon game"),
+                    ):
+                    self.save_game()
+                else:
+                    self.sudoku_tracker.abandon_game(self)
+            except dialog_extras.UserCancelledError:
+                # User cancelled new game
+                return
+        self.do_stop()
+        self.select_game()
+
+            
+    @simple_debug
+    def stop_game (self):
+       if (self.gsd.grid
+            and self.gsd.grid.is_changed()
+            and (not self.won)):
+            try:
+                if dialog_extras.getBoolean(label=_("Save game before closing?")):
+                    self.save_game(self)
+            except dialog_extras.UserCancelledError:
+                return
+            self.do_stop()
+            
+    def do_stop (self):
+        self.gsd.grid = None
+        self.tracker_ui.reset()
+        self.timer.reset_timer()
+        self.timer.start_timing()
+        self.history.clear()        
+        self.won = False
+        
+    @simple_debug
+    def resize_cb (self, widget, event):
+        self.gconf['width'] = event.width
+        self.gconf['height'] = event.height
+
+    @simple_debug
+    def quit_cb (self, *args):
+        # Offer a save...
+        #try:
+        if (self.gsd.grid
+            and self.gsd.grid.is_changed()
+            and (not self.won)):
+            #and dialog_extras.getBoolean(label=_("Save game before closing?"))):
+            self.save_game(self) # autosave...
+        #except dialog_extras.UserCancelledError:
+        #    return
+        if gtk.main_level() > 1:
+            # If we are in an embedded mainloop, that means that one
+            # of our "swallowed" dialogs is active, in which case we
+            # have to quit that mainloop before we can quit
+            # properly.
+            if self.swallower.running:
+                d = self.swallower.running
+                d.response(gtk.RESPONSE_DELETE_EVENT)
+            gtk.main_quit() # Quit the embedded mainloop
+            gobject.idle_add(self.quit_cb,100) # Call ourselves again
+                                               # to quit the main
+                                               # mainloop
+            return
+            #buttons = d.action_area.get_children()
+            #for b in buttons:
+            #    if d.get_response_for_widget(b) in [gtk.RESPONSE_CLOSE,gtk.RESPONSE_CANCEL]:
+            #        print 'clicking button',b
+            #        b.emit('clicked')
+            #        while gtk.events_pending():
+            #            print 'Take care of iters...'
+            #            gtk.main_iteration()
+            #        break
+        self.w.hide()
+        # make sure we really go away before doing our saving --
+        # otherwise we appear sluggish.
+        while gtk.events_pending():
+            gtk.main_iteration()
+        if self.won:
+            self.gconf['current_game']=''
+        if not self.won:
+            if not self.gsd.grid:
+                self.gconf['current_game']=''            
+        self.stop_worker_thread()
+        # allow KeyboardInterrupts, which calls quit_cb outside the main loop
+        try:
+            gtk.main_quit()
+        except RuntimeError, e:
+            pass
+
+    @simple_debug
+    @inactivate_new_game_etc
+    def enter_game_by_hand (self, *args):
+        self.stop_game()
+        self.gsd.change_grid(sudoku.InteractiveSudoku(),9)
+        for w in self.by_hand_widgets: w.show_all()
+        
+    @simple_debug
+    def save_handmade_grid (self, *args):
+        for w in self.by_hand_widgets: w.hide()
+        # this should make our active grid into our virgin grid
+        self.won = False
+        self.gsd.change_grid(self.gsd.grid,9)
+        self.sudoku_maker.names[self.gsd.grid.to_string()]=self.sudoku_maker.get_puzzle_name('Custom Puzzle')
+        self.history.clear()
+
+    @simple_debug
+    def cancel_handmade_grid (self, *args):
+        for w in self.by_hand_widgets: w.hide()
+
+    #def save_game_cb (self, *args):
+    #    try:
+    #        self.save_game(*args)
+    #    except dialog_extras.UserCancelledError:
+    #        pass
+
+    @simple_debug
+    def save_game (self, *args):
+	self.sudoku_tracker.save_game(self)
+
+    @simple_debug
+    def zoom_in_cb (self,*args):
+        self.gh.change_font_size(multiplier=1.1)
+        self.zoom = self.zoom * 1.1        
+
+    @simple_debug
+    def zoom_out_cb (self,*args):
+        self.gh.change_font_size(multiplier=0.9)
+        self.zoom = self.zoom * 0.9
+
+    def full_screen_cb (self, *args):
+        if not hasattr(self,'is_fullscreen'): self.is_fullscreen = False
+        if self.is_fullscreen:
+            self.w.unfullscreen()
+            self.is_fullscreen = False
+        else:
+            self.w.fullscreen()
+            self.is_fullscreen = True
+        
+    @simple_debug
+    def clear_cb (self,*args):        
+        clearer=Undo.UndoableObject(
+            lambda *args: self.cleared.append(self.gsd.reset_grid()), #action
+            lambda *args: [self.gsd.add_value_to_ui(*entry) for entry in self.cleared.pop()], #inverse
+            self.history #history
+            )
+        clearer.perform()
+
+    def clear_notes_cb (self, *args):
+        clearer = Undo.UndoableObject(
+            lambda *args: self.cleared_notes.append(self.gsd.clear_notes()), #action
+            # clear_notes returns a list of tuples indicating the cleared notes...
+            # (x,y,(top,bottom)) -- this is what we need for undoing
+            lambda *args: [self.gsd.__entries__[t[0],t[1]].set_notes(t[2]) for t in self.cleared_notes.pop()], #inverse
+            self.history
+            )
+        clearer.perform()
+
+    @simple_debug
+    def show_hint_cb (self, *args):
+        self.gsd.show_hint()
+
+    @simple_debug
+    def auto_hint_cb (self, action):
+        if action.get_active():
+            self.gsd.always_show_hints = True
+            self.gsd.update_all_hints()
+        else:
+            self.gsd.always_show_hints = False            
+            self.gsd.clear_hints()
+
+    @simple_debug
+    def impossible_implication_cb (self, action):
+        if action.get_active():
+            self.gsd.show_impossible_implications = True
+        else:
+            self.gsd.show_impossible_implications = False
+
+    @simple_debug
+    def auto_fill_cb (self, *args):
+        if not hasattr(self,'autofilled'): self.autofilled=[]
+        if not hasattr(self,'autofiller'):
+            self.autofiller = Undo.UndoableObject(
+                lambda *args: self.autofilled.append(self.gsd.auto_fill()),
+                lambda *args: [self.gsd.remove(entry[0],entry[1],do_removal=True) for entry in self.autofilled.pop()],
+                self.history
+                )
+        self.autofiller.perform()
+
+    @simple_debug
+    def auto_fill_current_square_cb (self, *args):
+        self.gsd.auto_fill_current_entry()
+
+    @simple_debug
+    def setup_tracker_interface (self):
+        self.trackers = {}        
+        self.tracker_ui = TrackerBox(self)
+        self.tracker_ui.show_all()
+        self.tracker_ui.hide()
+        self.game_box.add(self.tracker_ui)
+
+    @simple_debug
+    def set_tracker (self, n):
+        if self.gsd.trackers.has_key(n):
+            self.tracker_ui.select_tracker(n)
+            e = self.gsd.get_focused_entry()
+            if e:
+                if n==-1:
+                    for tid in self.gsd.trackers_for_point(e.x,e.y):
+                        self.gsd.remove_tracker(e.x,e.y,tid)
+                else:
+                    self.gsd.add_tracker(e.x,e.y,n)
+        else:
+            print 'No tracker ',n,'yet'
+        
+    @simple_debug
+    def tracker_toggle_cb (self, widg):
+        if widg.get_active():
+            #if len(self.tracker_ui.tracker_model)<=1:
+            #    self.tracker_ui.add_tracker()
+            self.tracker_ui.show_all()
+        else:
+            self.tracker_ui.hide()
+
+    @simple_debug
+    def toggle_toolbar_cb (self, widg):
+        if widg.get_active(): self.tb.show()
+        else: self.tb.hide()
+
+    def update_statusbar_cb (self, *args):
+        if not self.gsd.grid: return True
+        puzz = self.gsd.grid.virgin.to_string()
+        if (not hasattr(self,'current_puzzle_string') or
+            self.current_puzzle_string != puzz):
+            self.current_puzzle_diff = self.sudoku_maker.get_difficulty(puzz)
+	tot_string = _("Playing %(difficulty)s puzzle.")%{'difficulty':self.current_puzzle_diff.value_string()}
+        tot_string += " " + "(%1.2f)"%self.current_puzzle_diff.value
+        #if self.timer.tot_time or self.timer.tot_time_complete:
+        #    time_string = _("%s (%s active)")%(
+        #        self.timer.total_time_string(),
+        #        self.timer.active_time_string()
+        #        )
+        #    if not self.timer.__timing__:
+        #        time_string += " %s"%_('paused')
+        #    tot_string += " - " + time_string
+        #if self.gsd.hints and not self.gconf['always_show_hints']:
+        #    tot_string += " -  " +ngettext("%(n)s hint","%(n)s hints",
+        #                           self.gsd.hints)%{'n':self.gsd.hints}
+        #if self.gsd.auto_fills:
+        #    tot_string += "  " +ngettext("%(n)s auto-fill","%(n)s auto-fills",
+        #                            self.gsd.auto_fills)%{'n':self.gsd.auto_fills}
+        if not hasattr(self,'sbid'):
+            self.sbid = self.statusbar.get_context_id('game_info')
+        self.statusbar.pop(self.sbid)
+        self.statusbar.push(self.sbid,
+                            tot_string)
+        return True
+
+    def toggle_highlight_cb (self, widg):
+        if widg.get_active():
+            self.gsd.toggle_highlight(True)
+        else:
+            self.gsd.toggle_highlight(False)
+
+    @simple_debug
+    def show_info_cb (self, *args):
+        if not self.gsd.grid:
+            dialog_extras.show_message(parent=self.w,
+                                       title=_("Puzzle Information"),
+                                       label=_("There is no current puzzle.")
+                                       )
+            return
+        puzzle = self.gsd.grid.virgin.to_string()
+        diff = self.sudoku_maker.get_difficulty(puzzle)
+        information = _("Calculated difficulty: ")
+        information += diff.value_string()
+        information += " (%1.2f)"%diff.value
+        information += "\n"
+        information += _("Number of moves instantly fillable by elimination: ")
+        information += str(int(diff.instant_elimination_fillable))
+        information += "\n"
+        information += _("Number of moves instantly fillable by filling: ")
+        information += str(int(diff.instant_fill_fillable))
+        information += "\n"
+        information += _("Amount of trial-and-error required to solve: ")
+        information += str(len(diff.guesses))
+        dialog_extras.show_message(parent=self.w,
+                                   title=_("Puzzle Statistics"),
+                                   label=_("Puzzle Statistics"),
+                                   sublabel=information)
+
+    @simple_debug
+    def toggle_generator_cb (self, toggle):
+        if toggle.get_active():
+            self.start_worker_thread()
+        else:
+            self.stop_worker_thread()
+
+    @simple_debug
+    def show_high_scores_cb (self, *args):
+        hs=game_selector.HighScores(self.sudoku_tracker)
+        replay_game = hs.run_dialog()
+        if replay_game:
+            self.stop_game()
+            self.gsd.change_grid(replay_game,9)
+
+    @simple_debug
+    def autosave (self):
+        # this is called on a regular loop and will autosave if we
+        # have reason to...
+        if self.gsd.grid and self.gsd.grid.is_changed() and not self.won:
+            self.sudoku_tracker.save_game(self)
+    
+    def offer_to_load_autosaved_file (self):
+    	pass    
+
+    @simple_debug
+    def load_autosave (self, filename):
+        saver.unpickle_game(self,filename)
+        self.history.clear()
+        
+    #def toggle_autosave ():
+
+    @simple_debug
+    def show_about (self, *args):
+        about = gtk.AboutDialog()
+        about.set_name(APPNAME)
+        about.set_version(VERSION)
+        about.set_copyright(COPYRIGHT)
+	about.set_license(LICENSE[0] + '\n\n' + LICENSE[1] + '\n\n' +LICENSE[2])
+	about.set_wrap_license(True)
+        about.set_comments(DESCRIPTION)
+        about.set_authors(AUTHORS)
+        about.set_website(WEBSITE)
+        about.set_website_label(WEBSITE_LABEL)
+        about.set_logo_icon_name("gnome-sudoku")
+        about.set_translator_credits(_("translator-credits"))
+        about.connect("response", lambda d, r: d.destroy())
+        about.show()
+
+    @simple_debug
+    def show_help (self, *args):
+        #dialog_extras.show_faq(faq_file=os.path.join(BASE_DIR,_('FAQ')))
+        try:
+            gnome.help_display('gnome-sudoku')
+        except gobject.GError, e:
+            # FIXME: This should create a pop-up dialog
+            print _('Unable to display help: %s') % str(e)
+
+    @simple_debug
+    def print_game (self, *args):
+        printing.print_sudokus([self.gsd])
+
+    @simple_debug
+    def print_multiple_games (self, *args):
+        gp=game_selector.GamePrinter(self.sudoku_tracker, self.gconf)
+        gp.run_dialog()
+
+    @simple_debug
+    def generate_puzzle_gui (self, *args):
+        sudoku_generator_gui.GameGenerator(self,self.gconf)
+
+class TrackerBox (gtk.VBox):
+
+    @simple_debug
+    def __init__ (self, main_ui):
+        
+        gtk.VBox.__init__(self)
+        self.glade = gtk.glade.XML(os.path.join(GLADE_DIR,'tracker.glade'))
+        self.main_ui = main_ui
+        self.vb = self.glade.get_widget('vbox1')
+        self.vb.unparent()
+        self.pack_start(self.vb,expand=True,fill=True)
+        self.setup_actions()
+        self.setup_tree()
+        self.show_all()
+
+    @simple_debug
+    def reset (self):
+        
+        for tree in self.tracker_model:
+            if tree[0]>-1:
+                self.tracker_model.remove(tree.iter)
+
+    @simple_debug
+    def setup_tree (self):
+        self.tracker_tree = self.glade.get_widget('treeview1')
+        self.tracker_model = gtk.ListStore(int,gtk.gdk.Pixbuf,str)
+        self.tracker_tree.set_model(self.tracker_model)
+        col1 = gtk.TreeViewColumn("",gtk.CellRendererPixbuf(),pixbuf=1)
+        col2 = gtk.TreeViewColumn("",gtk.CellRendererText(),text=2)
+        self.tracker_tree.append_column(col2)
+        self.tracker_tree.append_column(col1)
+        # Our initial row...
+        self.tracker_model.append([-1,None,_('No Tracker')])
+        self.tracker_tree.get_selection().connect('changed',self.selection_changed_cb)
+
+    @simple_debug
+    def setup_actions (self):
+        self.tracker_actions = gtk.ActionGroup('tracker_actions')        
+        self.tracker_actions.add_actions(
+            [('Clear',
+              gtk.STOCK_CLEAR,
+              _('_Clear Tracker'),
+              None,_('Clear all moves tracked by selected tracker.'),
+              self.clear_cb
+              ),
+             ('Keep',None,
+              _('_Clear Others'),
+              None,
+              _('Clear all moves not tracked by selected tracker.'),
+              self.keep_cb),
+             ]
+            )
+        for action,widget_name in [('Clear','ClearTrackerButton'),
+                                   ('Keep','KeepTrackerButton'),
+                                   ]:
+            a=self.tracker_actions.get_action(action)
+            a.connect_proxy(self.glade.get_widget(widget_name))
+        self.glade.get_widget('AddTrackerButton').connect('clicked',
+                                                          self.add_tracker)
+        # Default to insensitive (they only become sensitive once a tracker is added)
+        self.tracker_actions.set_sensitive(False)
+
+    @simple_debug
+    def add_tracker (self,*args):
+        #print 'Adding tracker!'
+        tracker_id = self.main_ui.gsd.create_tracker()
+        #print 'tracker_id = ',tracker_id
+        pb=image_extras.pixbuf_transform_color(
+            STOCK_PIXBUFS['tracks'],
+            (0,0,0),#white
+            self.main_ui.gsd.get_tracker_color(tracker_id),
+            )
+        # select our new tracker
+        self.tracker_tree.get_selection().select_iter(
+            self.tracker_model.append([tracker_id,
+                                  pb,
+                                  _("Tracker %s")%(tracker_id+1)]
+                                  )
+            )
+        
+    @simple_debug
+    def select_tracker (self, tracker_id):
+        for row in self.tracker_model:
+            if row[0]==tracker_id:
+                self.tracker_tree.get_selection().select_iter(row.iter)
+
+    @simple_debug
+    def selection_changed_cb (self, selection):
+        mod,itr = selection.get_selected()
+        if itr: selected_tracker_id = mod.get_value(itr,0)
+        else: selected_tracker_id=-1
+        # This should be cheap since we don't expect many trackers...
+        # We cycle through each row and toggle it off if it's not
+        # selected; on if it is selected
+        for row in self.tracker_model:
+            tid = row[0]
+            if tid != -1: # -1 == no tracker
+                self.main_ui.gsd.toggle_tracker(tid,tid==selected_tracker_id)
+        self.tracker_actions.set_sensitive(selected_tracker_id != -1)
+
+    @simple_debug
+    def clear_cb (self, action):
+        mod,itr=self.tracker_tree.get_selection().get_selected()
+        # This should only be called if there is an itr, but we'll
+        # double-check just in case.
+        if itr:
+            selected_tracker_id=mod.get_value(itr,0)
+            self.tracker_delete_tracks(selected_tracker_id)
+
+    @simple_debug
+    def keep_cb (self, action):
+        mod,itr=self.tracker_tree.get_selection().get_selected()
+        selected_tracker_id=mod.get_value(itr,0)
+        self.tracker_keep_tracks(selected_tracker_id)
+
+    @simple_debug
+    def tracker_delete_tracks (self, tracker_id):
+        clearer=Undo.UndoableObject(
+            lambda *args: self.main_ui.cleared.append(self.main_ui.gsd.delete_by_tracker(tracker_id)),
+            lambda *args: [self.main_ui.gsd.add_value_to_ui(*entry) for entry in self.main_ui.cleared.pop()],
+            self.main_ui.history)
+        clearer.perform()
+
+    @simple_debug
+    def tracker_keep_tracks (self, tracker_id):
+        clearer=Undo.UndoableObject(
+            lambda *args: self.main_ui.cleared.append(self.main_ui.gsd.delete_except_for_tracker(tracker_id)),
+            lambda *args: [self.main_ui.gsd.add_value_to_ui(*entry) for entry in self.main_ui.cleared.pop()],
+            self.main_ui.history)
+        clearer.perform()
+
+
+def start_game ():
+    if options.debug: print 'Starting GNOME Sudoku in debug mode'
+
+    ##  You must call g_thread_init() before executing any other GLib
+    ##  functions in a threaded GLib program.
+    gobject.threads_init()
+
+    if options.profile:
+        options.profile = False
+        profile_me()
+        return
+
+    u = UI()
+    if not u.quit:
+        try:
+            gtk.main()        
+        except KeyboardInterrupt:
+            # properly quit on a keyboard interrupt...
+            u.quit_cb()
+
+def profile_me ():
+    print 'Profiling GNOME Sudoku'
+    import tempfile,os.path
+    import hotshot, hotshot.stats
+    pname = os.path.join(tempfile.gettempdir(),'GNOME_SUDOKU_HOTSHOT_PROFILE')
+    prof = hotshot.Profile(pname)
+    prof.runcall(start_game)
+    stats = hotshot.stats.load(pname)
+    stats.strip_dirs()
+    stats.sort_stats('time','calls').print_stats()    
+
+    
+        
+        
+if __name__ == '__main__':
+    import defaults
+    defaults.DATA_DIR == '/tmp/'; DATA_DIR=='/tmp/'
+    
+    



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]