Added exception handling to opening file in player
[getaclue.git] / player_mainwindow.py
1 # Get A Clue (C) 2010 V. Harishankar
2 # Crossword puzzle maker program
3 # Licensed under the GNU GPL v3
4
5 # Main window class for GetAClue player
6
7 import cPickle
8 import pygtk
9 pygtk.require20 ()
10 import gtk
11 import pango
12 import crosswordpuzzle
13
14 class MainWindow:
15 def gtk_main_quit (self, *args):
16 if self.puzzle:
17 dlg = gtk.MessageDialog (self.window, gtk.DIALOG_MODAL,
18 gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO,
19 "Puzzle is open. Are you sure you wish to quit?")
20 if dlg.run () == gtk.RESPONSE_NO:
21 dlg.destroy ()
22 return
23 dlg.destroy ()
24
25 gtk.main_quit ()
26
27 # callback for menu item quit activated event
28 def on_quit_activate (self, menuitem):
29 self.gtk_main_quit ()
30
31 # callback for puzzle grid mouse button release event
32 def on_puzzlegrid_button_press_event (self, drawarea, event):
33 self.window.set_focus (drawarea)
34 return True
35
36 # moving the current selection in grid by one up or down
37 def move_selection_updown (self, step):
38 # increase or reduce the row by step until an occupied grid is found
39 # black block
40 last_occupied_row = self.selected_row
41 while True:
42 self.selected_row += step
43 if self.selected_row < 0 or self.selected_row >= self.puzzle.rows:
44 self.selected_row = last_occupied_row
45 break
46 if (self.puzzle.data[self.selected_row][self.selected_col].occupied_across is True
47 or self.puzzle.data[self.selected_row][self.selected_col].occupied_down is True):
48 break
49
50 # moving the current selection in grid by one across either way
51 def move_selection_across (self, step):
52 # increase or reduce the row by step until an occupied grid is found
53 # black block
54 last_occupied_col = self.selected_col
55 while True:
56 self.selected_col += step
57 if self.selected_col < 0 or self.selected_col >= self.puzzle.cols:
58 self.selected_col = last_occupied_col
59 break
60 if (self.puzzle.data[self.selected_row][self.selected_col].occupied_across is True
61 or self.puzzle.data[self.selected_row][self.selected_col].occupied_down is True):
62 break
63
64 # callback for puzzle grid key release event
65 def on_puzzlegrid_key_press_event (self, drawarea, event):
66 key = gtk.gdk.keyval_name (event.keyval)
67
68 if event.state == gtk.gdk.SHIFT_MASK and key == "Up":
69 # reduce the row by 1 until you find an occupied grid and not a
70 # black block
71 self.move_selection_updown (-1)
72 drawarea.queue_draw ()
73 elif event.state == gtk.gdk.SHIFT_MASK and key == "Down":
74 # increase the row by 1 until you find an occupied grid and not a
75 # black block
76 self.move_selection_updown (1)
77 drawarea.queue_draw ()
78 elif event.state == gtk.gdk.SHIFT_MASK and key == "Right":
79 # increase the column by 1 until you find an occupied grid and not
80 # a black block
81 self.move_selection_across (1)
82 drawarea.queue_draw ()
83 elif event.state == gtk.gdk.SHIFT_MASK and key == "Left":
84 # decrease the column by 1 until you find an occupied grid and not
85 # a black block
86 self.move_selection_across (-1)
87 drawarea.queue_draw ()
88 return False
89
90 # puzzle grid focus in event
91 def on_puzzlegrid_focus_out_event (self, drawarea, event):
92 col = drawarea.window.get_colormap ().alloc_color (gtk.gdk.Color ("gray"))
93 drawarea.window.set_background (col)
94
95 return False
96
97 # puzzle grid focus out event
98 def on_puzzlegrid_focus_in_event (self, drawarea, event):
99 col = drawarea.window.get_colormap ().alloc_color (gtk.gdk.Color ("white"))
100 drawarea.window.set_background (col)
101 return False
102
103 # callback for drawing the puzzle grid
104 def on_puzzlegrid_expose_event (self, drawarea, event):
105 # if puzzle is loaded
106 if self.puzzle:
107 # size the area
108 drawarea.set_size_request (self.puzzle.cols*30+2, self.puzzle.rows*30+2)
109
110 #numlayout = gtk.PrintContext().create_pango_layout ()
111 #numlayout.set_font_description (pango.FontDescription ("Sans 8"))
112 ctx = drawarea.window.cairo_create ()
113 ctx.set_line_width (1.5)
114
115 # run through the grid
116 for row in range (self.puzzle.rows):
117 for col in range (self.puzzle.cols):
118 # (re)set foreground color
119 ctx.set_source_rgb (0, 0, 0)
120 # if the area is not occupied
121 if (self.puzzle.data[row][col].occupied_across is False and
122 self.puzzle.data[row][col].occupied_down is False):
123 ctx.rectangle (col*30, row*30, 30, 30)
124 ctx.fill ()
125 else:
126 # if selected row/column
127 if row == self.selected_row and col == self.selected_col:
128 ctx.set_source_rgb (1, 1, 0.6)
129 ctx.rectangle (col*30,row*30, 30, 30)
130 ctx.fill ()
131 else:
132 ctx.set_source_rgb (1, 1, 1)
133 ctx.rectangle (col*30, row*30, 30, 30)
134 ctx.fill ()
135 ctx.set_source_rgb (0, 0, 0)
136 ctx.rectangle (col*30, row*30, 30, 30)
137 ctx.stroke ()
138
139 # if numbered
140 if self.puzzle.data[row][col].numbered <> 0:
141 ctx.set_source_rgb (0, 0, 0)
142 ctx.select_font_face ("Sans 7")
143 ctx.move_to (col*30+2, row*30+10)
144 ctx.show_text (str(self.puzzle.data[row][col].numbered))
145
146 return False
147
148 def load_clues (self):
149 # get the clues list store objects
150 across = self.ui.get_object ("clues_across")
151 down = self.ui.get_object ("clues_down")
152 across.clear ()
153 down.clear ()
154
155 # if puzzle is loaded
156 if self.puzzle:
157 clues_across = self.puzzle.get_clues_across ()
158 clues_down = self.puzzle.get_clues_down ()
159 # insert the numbers and the clues for across
160 for word, clue in clues_across:
161 across.append ([str(self.puzzle.data[word[1]][word[2]].numbered),
162 clue])
163 # insert the numbers and the clues for down
164 for word, clue in clues_down:
165 down.append ([ str(self.puzzle.data[word[1]][word[2]].numbered),
166 clue])
167
168 def open_file (self, file):
169 # try to open the file
170 try:
171 # load the puzzle
172 self.puzzle = cPickle.load (open (file, "rb"))
173 # assert that it is unfrozen otherwise raise frozen grid exception
174 self.puzzle.assert_frozen_grid ()
175
176 # set selected initial row and column to 0
177 self.selected_row = 0
178 self.selected_col = 0
179 self.window.set_title ("GetAClue player - " + file)
180 # load the clues
181 self.load_clues ()
182 # handle unpickling, and file errors
183 except (cPickle.UnpicklingError, IOError, OSError):
184 dlg = gtk.MessageDialog (self.window, gtk.DIALOG_MODAL,
185 gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE,
186 "Invalid file. Cannot be loaded")
187 dlg.run ()
188 dlg.destroy ()
189 # if the puzzle has no words, then it cannot be played obviously
190 except crosswordpuzzle.NoWordsException:
191 self.puzzle = None
192 dlg = gtk.MessageDialog (self.window, gtk.DIALOG_MODAL,
193 gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE,
194 "Word grid has no words. Cannot play")
195 dlg.run ()
196 dlg.destroy ()
197 # if the puzzle is not frozen then it cannot be played
198 except crosswordpuzzle.FrozenGridException:
199 self.puzzle = None
200 dlg = gtk.MessageDialog (self.window, gtk.DIALOG_MODAL,
201 gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE,
202 "Word grid is not finalized/frozen. Cannot play")
203 dlg.run ()
204 dlg.destroy ()
205
206 def __init__ (self, file_to_play = None):
207 # load the user interface
208 self.ui = gtk.Builder ()
209 self.ui.add_from_file ("playerwindow.glade")
210
211 # window object
212 self.window = self.ui.get_object ("mainwindow")
213 self.window.show ()
214
215 # set the cell renderer
216 cell = gtk.CellRendererText ()
217 tree_acol1 = self.ui.get_object ("tree_clues_across").get_column (0)
218 tree_acol2 = self.ui.get_object ("tree_clues_across").get_column (1)
219 tree_acol1.pack_start (cell)
220 tree_acol1.add_attribute (cell, "text", 0)
221 tree_acol2.pack_start (cell)
222 tree_acol2.add_attribute (cell, "text", 1)
223
224 tree_down = self.ui.get_object ("tree_clues_down")
225 tree_dcol1 = self.ui.get_object ("tree_clues_down").get_column (0)
226 tree_dcol2 = self.ui.get_object ("tree_clues_down").get_column (1)
227 tree_dcol1.pack_start (cell)
228 tree_dcol1.add_attribute (cell, "text", 0)
229 tree_dcol2.pack_start (cell)
230 tree_dcol2.add_attribute (cell, "text", 1)
231
232 # connect the signals
233 self.ui.connect_signals (self)
234
235 # set the puzzle to None
236 self.puzzle = None
237
238 # open the file if it is set
239 if file_to_play:
240 self.open_file (file_to_play)
241
242 gtk.main ()
243
244