366cbfa886280aa8afa8b4147970aba784cf0f37
1 # Get A Clue (C) 2010 V. Harishankar
2 # Crossword puzzle maker program
3 # Licensed under the GNU GPL v3
5 # Main window class for GetAClue player
13 import crosswordpuzzle
16 # typing mode constants
20 def gtk_main_quit (self
, *args
):
23 # callback for menu item quit activated event
24 def on_quit_activate (self
, menuitem
):
26 dlg
= gtk
.MessageDialog (self
.window
, gtk
.DIALOG_MODAL
,
27 gtk
.MESSAGE_QUESTION
, gtk
.BUTTONS_YES_NO
,
28 "Puzzle is open. Are you sure you wish to quit?")
29 if dlg
.run () <> gtk
.RESPONSE_YES
:
36 # callback for puzzle grid mouse button release event
37 def on_puzzlegrid_button_press_event (self
, drawarea
, event
):
38 # set the focus on the puzzle grid
40 self
.window
.set_focus (drawarea
)
42 col
= int (event
.x
/ 30)
43 row
= int (event
.y
/ 30)
45 if col
< self
.puzzle
.cols
and row
< self
.puzzle
.rows
:
46 if (self
.puzzle
.data
[row
][col
].occupied_across
is True or
47 self
.puzzle
.data
[row
][col
].occupied_down
is True):
48 self
.selected_col
= col
49 self
.selected_row
= row
50 drawarea
.queue_draw ()
54 # moving the current selection in grid by one up or down
55 def move_selection_updown (self
, step
):
56 # increase or reduce the row by step until an occupied grid is found
59 last_occupied_row
= self
.selected_row
61 self
.selected_row
+= step
62 if self
.selected_row
< 0 or self
.selected_row
>= self
.puzzle
.rows
:
63 self
.selected_row
= last_occupied_row
65 if (self
.puzzle
.data
[self
.selected_row
][self
.selected_col
].occupied_across
is True
66 or self
.puzzle
.data
[self
.selected_row
][self
.selected_col
].occupied_down
is True):
69 # moving the current selection in grid by one across either way
70 def move_selection_across (self
, step
):
71 # increase or reduce the row by step until an occupied grid is found
74 last_occupied_col
= self
.selected_col
76 self
.selected_col
+= step
77 if self
.selected_col
< 0 or self
.selected_col
>= self
.puzzle
.cols
:
78 self
.selected_col
= last_occupied_col
80 if (self
.puzzle
.data
[self
.selected_row
][self
.selected_col
].occupied_across
is True
81 or self
.puzzle
.data
[self
.selected_row
][self
.selected_col
].occupied_down
is True):
84 # set the guessed character in the grid at selected location and move the
85 # selection across or down as the case may be
86 def set_guess (self
, guess_char
):
88 self
.puzzle
.data
[self
.selected_row
][self
.selected_col
].guess
= guess_char
.upper ()
90 if self
.typing_mode
== self
.ACROSS
:
91 # move by one character across but only if there is no block
93 old_col
= self
.selected_col
94 self
.move_selection_across (1)
95 if abs (self
.selected_col
- old_col
) > 1:
96 self
.selected_col
= old_col
99 # move by one character down but only if there is no block
101 old_row
= self
.selected_row
102 self
.move_selection_updown (1)
103 if abs (self
.selected_row
- old_row
) > 1:
104 self
.selected_row
= old_row
106 # delete the guessed char in the previous row/col depending on the input mode
107 # If input mode is ACROSS then delete guessed char at previous column else
109 def delete_prev_guess (self
):
111 if self
.typing_mode
== self
.ACROSS
:
112 # prevent deleting characters when there is a gap
113 old_sel_col
= self
.selected_col
114 self
.move_selection_across (-1)
115 # only if there is no block inbetween delete
116 if abs (self
.selected_col
- old_sel_col
) <= 1:
117 self
.puzzle
.data
[self
.selected_row
][self
.selected_col
].guess
= None
120 self
.selected_col
= old_sel_col
122 # callback for puzzle grid key release event
123 def on_puzzlegrid_key_press_event (self
, drawarea
, event
):
125 key
= gtk
.gdk
.keyval_name (event
.keyval
)
127 if event
.state
== gtk
.gdk
.SHIFT_MASK
and key
== "Up":
128 # reduce the row by 1 until you find an occupied grid and not a
130 self
.move_selection_updown (-1)
131 self
.typing_mode
= self
.DOWN
132 drawarea
.queue_draw ()
133 elif event
.state
== gtk
.gdk
.SHIFT_MASK
and key
== "Down":
134 # increase the row by 1 until you find an occupied grid and not a
136 self
.move_selection_updown (1)
137 self
.typing_mode
= self
.DOWN
138 drawarea
.queue_draw ()
139 elif event
.state
== gtk
.gdk
.SHIFT_MASK
and key
== "Right":
140 # increase the column by 1 until you find an occupied grid and not
142 self
.move_selection_across (1)
143 self
.typing_mode
= self
.ACROSS
144 drawarea
.queue_draw ()
145 elif event
.state
== gtk
.gdk
.SHIFT_MASK
and key
== "Left":
146 # decrease the column by 1 until you find an occupied grid and not
148 self
.move_selection_across (-1)
149 self
.typing_mode
= self
.ACROSS
150 drawarea
.queue_draw ()
151 # if it is A-Z or a-z then
152 elif len (key
) == 1 and key
.isalpha ():
154 drawarea
.queue_draw ()
155 # if it is the delete key then delete character at selected row/col
156 elif key
== "Delete":
157 self
.puzzle
.data
[self
.selected_row
][self
.selected_col
].guess
= None
158 drawarea
.queue_draw ()
159 # if it is backspace key then delete character at previous row/col
160 # depending on the input mode. If across editing mode, then delete
161 # at previous column else at previous row
162 elif key
== "BackSpace":
163 self
.delete_prev_guess ()
164 drawarea
.queue_draw ()
168 # puzzle grid focus in event
169 def on_puzzlegrid_focus_out_event (self
, drawarea
, event
):
171 col
= drawarea
.window
.get_colormap ().alloc_color (gtk
.gdk
.Color ("gray"))
172 drawarea
.window
.set_background (col
)
176 # puzzle grid focus out event
177 def on_puzzlegrid_focus_in_event (self
, drawarea
, event
):
179 col
= drawarea
.window
.get_colormap ().alloc_color (gtk
.gdk
.Color ("white"))
180 drawarea
.window
.set_background (col
)
183 # callback for drawing the puzzle grid
184 def on_puzzlegrid_expose_event (self
, drawarea
, event
):
185 # if puzzle is loaded
188 drawarea
.set_size_request (self
.puzzle
.cols
*30+2, self
.puzzle
.rows
*30+2)
190 #numlayout = gtk.PrintContext().create_pango_layout ()
191 #numlayout.set_font_description (pango.FontDescription ("Sans 8"))
192 ctx
= drawarea
.window
.cairo_create ()
193 ctx
.set_line_width (1.5)
195 # run through the grid
196 for row
in range (self
.puzzle
.rows
):
197 for col
in range (self
.puzzle
.cols
):
198 # (re)set foreground color
199 ctx
.set_source_rgb (0, 0, 0)
200 # if the area is not occupied
201 if (self
.puzzle
.data
[row
][col
].occupied_across
is False and
202 self
.puzzle
.data
[row
][col
].occupied_down
is False):
203 ctx
.rectangle (col
*30, row
*30, 30, 30)
206 # if selected row/column
207 if row
== self
.selected_row
and col
== self
.selected_col
:
208 ctx
.set_source_rgb (1, 1, 0)
209 ctx
.rectangle (col
*30,row
*30, 30, 30)
212 ctx
.set_source_rgb (1, 1, 1)
213 ctx
.rectangle (col
*30, row
*30, 30, 30)
215 ctx
.set_source_rgb (0, 0, 0)
216 ctx
.rectangle (col
*30, row
*30, 30, 30)
220 if self
.puzzle
.data
[row
][col
].numbered
<> 0:
221 ctx
.set_source_rgb (0, 0, 0)
222 ctx
.select_font_face ("Serif")
223 ctx
.set_font_size (10)
224 ctx
.move_to (col
*30+2, row
*30+10)
225 ctx
.show_text (str(self
.puzzle
.data
[row
][col
].numbered
))
227 # if there is a guessed character at the location
228 # and it is not revealed as a solution
229 if (self
.puzzle
.data
[row
][col
].guess
and
230 self
.puzzle
.data
[row
][col
].revealed
is False and
231 (self
.puzzle
.data
[row
][col
].occupied_across
is True
232 or self
.puzzle
.data
[row
][col
].occupied_down
is True)):
233 ctx
.set_source_rgb (0, 0, 0)
234 ctx
.select_font_face ("Serif", cairo
.FONT_SLANT_NORMAL
,
235 cairo
.FONT_WEIGHT_BOLD
)
236 ctx
.set_font_size (16)
237 ctx
.move_to (col
*30+10, row
*30+20)
238 ctx
.show_text (self
.puzzle
.data
[row
][col
].guess
)
242 # load clues to the list
243 def load_clues (self
):
244 # get the clues list store objects
245 across
= self
.ui
.get_object ("clues_across")
246 down
= self
.ui
.get_object ("clues_down")
250 # if puzzle is loaded
252 clues_across
= self
.puzzle
.get_clues_across ()
253 clues_down
= self
.puzzle
.get_clues_down ()
254 # insert the numbers and the clues for across
255 for word
, clue
in clues_across
:
256 across
.append ([str(self
.puzzle
.data
[word
[1]][word
[2]].numbered
),
258 # insert the numbers and the clues for down
259 for word
, clue
in clues_down
:
260 down
.append ([ str(self
.puzzle
.data
[word
[1]][word
[2]].numbered
),
263 def open_file (self
, file):
264 # try to open the file
267 self
.puzzle
= cPickle
.load (open (file, "rb"))
268 # assert that it is unfrozen otherwise raise frozen grid exception
269 self
.puzzle
.assert_frozen_grid ()
271 # set selected initial row and column to 0
272 self
.selected_row
= 0
273 self
.selected_col
= 0
274 # set the typing mode to default - across
275 self
.typing_mode
= self
.ACROSS
277 self
.window
.set_title ("GetAClue player - " + file)
280 # handle unpickling, and file errors
281 except (cPickle
.UnpicklingError
, IOError, OSError):
282 dlg
= gtk
.MessageDialog (self
.window
, gtk
.DIALOG_MODAL
,
283 gtk
.MESSAGE_ERROR
, gtk
.BUTTONS_CLOSE
,
284 "Invalid file. Cannot be loaded")
287 # if the puzzle has no words, then it cannot be played obviously
288 except crosswordpuzzle
.NoWordsException
:
290 dlg
= gtk
.MessageDialog (self
.window
, gtk
.DIALOG_MODAL
,
291 gtk
.MESSAGE_ERROR
, gtk
.BUTTONS_CLOSE
,
292 "Word grid has no words. Cannot play")
295 # if the puzzle is not frozen then it cannot be played
296 except crosswordpuzzle
.FrozenGridException
:
298 dlg
= gtk
.MessageDialog (self
.window
, gtk
.DIALOG_MODAL
,
299 gtk
.MESSAGE_ERROR
, gtk
.BUTTONS_CLOSE
,
300 "Word grid is not finalized/frozen. Cannot play")
304 def __init__ (self
, file_to_play
= None):
305 # load the user interface
306 self
.ui
= gtk
.Builder ()
307 self
.ui
.add_from_file ("playerwindow.glade")
310 self
.window
= self
.ui
.get_object ("mainwindow")
313 # set the cell renderer for tree views
314 cell
= gtk
.CellRendererText ()
315 tree_acol1
= self
.ui
.get_object ("tree_clues_across").get_column (0)
316 tree_acol2
= self
.ui
.get_object ("tree_clues_across").get_column (1)
317 tree_acol1
.pack_start (cell
)
318 tree_acol1
.add_attribute (cell
, "text", 0)
319 tree_acol2
.pack_start (cell
)
320 tree_acol2
.add_attribute (cell
, "text", 1)
322 tree_down
= self
.ui
.get_object ("tree_clues_down")
323 tree_dcol1
= self
.ui
.get_object ("tree_clues_down").get_column (0)
324 tree_dcol2
= self
.ui
.get_object ("tree_clues_down").get_column (1)
325 tree_dcol1
.pack_start (cell
)
326 tree_dcol1
.add_attribute (cell
, "text", 0)
327 tree_dcol2
.pack_start (cell
)
328 tree_dcol2
.add_attribute (cell
, "text", 1)
330 # connect the signals
331 self
.ui
.connect_signals (self
)
333 # set the puzzle to None
336 # open the file if it is set
338 self
.open_file (file_to_play
)