Added exception handling to opening file in player
[getaclue.git] / player_mainwindow.py
index 00be083..3ea5420 100644 (file)
@@ -13,36 +13,132 @@ import crosswordpuzzle
 
 class MainWindow:
        def gtk_main_quit (self, *args):
+               if self.puzzle:
+                       dlg = gtk.MessageDialog (self.window, gtk.DIALOG_MODAL,
+                                       gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO,
+                                       "Puzzle is open. Are you sure you wish to quit?")
+                       if dlg.run () == gtk.RESPONSE_NO:
+                               dlg.destroy ()
+                               return
+                       dlg.destroy ()
+
                gtk.main_quit ()
 
-       # drawing for puzzle grid
+       # callback for menu item quit activated event
+       def on_quit_activate (self, menuitem):
+               self.gtk_main_quit ()
+
+       # callback for puzzle grid mouse button release event
+       def on_puzzlegrid_button_press_event (self, drawarea, event):
+               self.window.set_focus (drawarea)
+               return True
+
+       # moving the current selection in grid by one up or down
+       def move_selection_updown (self, step):
+               # increase or reduce the row by step until an occupied grid is found
+               # black block
+               last_occupied_row = self.selected_row
+               while True:
+                       self.selected_row += step
+                       if self.selected_row < 0 or self.selected_row >= self.puzzle.rows:
+                               self.selected_row = last_occupied_row
+                               break
+                       if (self.puzzle.data[self.selected_row][self.selected_col].occupied_across is True
+                               or self.puzzle.data[self.selected_row][self.selected_col].occupied_down is True):
+                               break
+
+       # moving the current selection in grid by one across either way
+       def move_selection_across (self, step):
+               # increase or reduce the row by step until an occupied grid is found
+               # black block
+               last_occupied_col = self.selected_col
+               while True:
+                       self.selected_col += step
+                       if self.selected_col < 0 or self.selected_col >= self.puzzle.cols:
+                               self.selected_col = last_occupied_col
+                               break
+                       if (self.puzzle.data[self.selected_row][self.selected_col].occupied_across is True
+                               or self.puzzle.data[self.selected_row][self.selected_col].occupied_down is True):
+                               break
+
+       # callback for puzzle grid key release event
+       def on_puzzlegrid_key_press_event (self, drawarea, event):
+               key = gtk.gdk.keyval_name (event.keyval)
+
+               if event.state == gtk.gdk.SHIFT_MASK and key == "Up":
+                       # reduce the row by 1 until you find an occupied grid and not a
+                       # black block
+                       self.move_selection_updown (-1)
+                       drawarea.queue_draw ()
+               elif event.state == gtk.gdk.SHIFT_MASK and key == "Down":
+                       # increase the row by 1 until you find an occupied grid and not a
+                       # black block
+                       self.move_selection_updown (1)
+                       drawarea.queue_draw ()
+               elif event.state == gtk.gdk.SHIFT_MASK and key == "Right":
+                       # increase the column by 1 until you find an occupied grid and not
+                       # a black block
+                       self.move_selection_across (1)
+                       drawarea.queue_draw ()
+               elif event.state == gtk.gdk.SHIFT_MASK and key == "Left":
+                       # decrease the column by 1 until you find an occupied grid and not
+                       # a black block
+                       self.move_selection_across (-1)
+                       drawarea.queue_draw ()
+               return False
+
+       # puzzle grid focus in event
+       def on_puzzlegrid_focus_out_event (self, drawarea, event):
+               col = drawarea.window.get_colormap ().alloc_color (gtk.gdk.Color ("gray"))
+               drawarea.window.set_background (col)
+
+               return False
+
+       # puzzle grid focus out event
+       def on_puzzlegrid_focus_in_event (self, drawarea, event):
+               col = drawarea.window.get_colormap ().alloc_color (gtk.gdk.Color ("white"))
+               drawarea.window.set_background (col)
+               return False
+
+       # callback for drawing the puzzle grid
        def on_puzzlegrid_expose_event (self, drawarea, event):
                # if puzzle is loaded
                if self.puzzle:
                        # size the area
                        drawarea.set_size_request (self.puzzle.cols*30+2, self.puzzle.rows*30+2)
 
-                       # set background color to white
-                       col = drawarea.window.get_colormap ().alloc_color (gtk.gdk.Color ("white"))
-                       drawarea.window.set_background (col)
-
                        #numlayout = gtk.PrintContext().create_pango_layout ()
                        #numlayout.set_font_description (pango.FontDescription ("Sans 8"))
                        ctx = drawarea.window.cairo_create ()
                        ctx.set_line_width (1.5)
 
+                       # run through the grid
                        for row in range (self.puzzle.rows):
                                for col in range (self.puzzle.cols):
+                                       # (re)set foreground color
+                                       ctx.set_source_rgb (0, 0, 0)
                                        # if the area is not occupied
                                        if (self.puzzle.data[row][col].occupied_across is False and
                                                self.puzzle.data[row][col].occupied_down is False):
                                                ctx.rectangle (col*30, row*30, 30, 30)
                                                ctx.fill ()
                                        else:
-                                               ctx.rectangle (col*30, row*30, 30, 30)
-                                               ctx.stroke ()
+                                               # if selected row/column
+                                               if row == self.selected_row and col == self.selected_col:
+                                                       ctx.set_source_rgb (1, 1, 0.6)
+                                                       ctx.rectangle (col*30,row*30, 30, 30)
+                                                       ctx.fill ()
+                                               else:
+                                                       ctx.set_source_rgb (1, 1, 1)
+                                                       ctx.rectangle (col*30, row*30, 30, 30)
+                                                       ctx.fill ()
+                                                       ctx.set_source_rgb (0, 0, 0)
+                                                       ctx.rectangle (col*30, row*30, 30, 30)
+                                                       ctx.stroke ()
+
                                                # if numbered
                                                if self.puzzle.data[row][col].numbered <> 0:
+                                                       ctx.set_source_rgb (0, 0, 0)
                                                        ctx.select_font_face ("Sans 7")
                                                        ctx.move_to (col*30+2, row*30+10)
                                                        ctx.show_text (str(self.puzzle.data[row][col].numbered))
@@ -70,9 +166,42 @@ class MainWindow:
                                                        clue])
 
        def open_file (self, file):
-               self.puzzle = cPickle.load (open (file, "rb"))
-               self.window.set_title ("GetAClue player - " + file)
-               self.load_clues ()
+               # try to open the file
+               try:
+                       # load the puzzle
+                       self.puzzle = cPickle.load (open (file, "rb"))
+                       # assert that it is unfrozen otherwise raise frozen grid exception
+                       self.puzzle.assert_frozen_grid ()
+
+                       # set selected initial row and column to 0
+                       self.selected_row = 0
+                       self.selected_col = 0
+                       self.window.set_title ("GetAClue player - " + file)
+                       # load the clues
+                       self.load_clues ()
+               # handle unpickling, and file errors
+               except (cPickle.UnpicklingError, IOError, OSError):
+                       dlg = gtk.MessageDialog (self.window, gtk.DIALOG_MODAL,
+                               gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE,
+                               "Invalid file. Cannot be loaded")
+                       dlg.run ()
+                       dlg.destroy ()
+               # if the puzzle has no words, then it cannot be played obviously
+               except crosswordpuzzle.NoWordsException:
+                       self.puzzle = None
+                       dlg = gtk.MessageDialog (self.window, gtk.DIALOG_MODAL,
+                               gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE,
+                               "Word grid has no words. Cannot play")
+                       dlg.run ()
+                       dlg.destroy ()
+               # if the puzzle is not frozen then it cannot be played
+               except crosswordpuzzle.FrozenGridException:
+                       self.puzzle = None
+                       dlg = gtk.MessageDialog (self.window, gtk.DIALOG_MODAL,
+                               gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE,
+                               "Word grid is not finalized/frozen. Cannot play")
+                       dlg.run ()
+                       dlg.destroy ()
 
        def __init__ (self, file_to_play = None):
                # load the user interface