3 #include "constantstrings.h"
4 #include "wordblah_resource.c"
7 GtkWidget
*main_window
;
8 GtkListStore
*across_store
;
9 GtkListStore
*down_store
;
11 MainPlayerData app_data
;
13 /* update the clue items store */
14 void update_clue_items ()
16 gtk_list_store_clear (across_store
);
17 gtk_list_store_clear (down_store
);
19 /* if the puzzle is loaded */
20 if (app_data
.is_loaded
== true)
22 /* iterate through the puzzle grid and gte the clues */
23 for (int i
= 0; i
< app_data
.puzzle
.grid_size
; i
++)
25 for (int j
= 0; j
< app_data
.puzzle
.grid_size
; j
++)
27 /* if it is the start of an across word */
28 if (app_data
.puzzle
.start_across_word
[i
][j
] != -1)
32 gtk_list_store_append (across_store
, &iter
);
33 gtk_list_store_set (across_store
, &iter
, 0,
34 app_data
.puzzle
.start_across_word
[i
][j
],
35 1, app_data
.puzzle
.clue_across
[i
][j
],
38 /* if it is the start of a down word */
39 if (app_data
.puzzle
.start_down_word
[i
][j
] != -1)
42 gtk_list_store_append (down_store
, &iter
);
43 gtk_list_store_set (down_store
, &iter
, 0,
44 app_data
.puzzle
.start_down_word
[i
][j
],
45 1, app_data
.puzzle
.clue_down
[i
][j
],
53 /* slot for handling list down selection changed */
54 gboolean
on_down_list_selection_changed (GtkTreeSelection
*sel
,
60 if (gtk_tree_selection_get_selected (sel
, &mod
, &iter
) == TRUE
)
63 gtk_tree_model_get (mod
, &iter
, 0, &sel_word_index
, -1);
64 set_selection_to_word_start (&app_data
, DOWN
, sel_word_index
);
67 gtk_widget_queue_draw_area (GTK_WIDGET(area
), 0, 0,
68 app_data
.puzzle
.grid_size
*GRID_PIXELS
+10,
69 app_data
.puzzle
.grid_size
*GRID_PIXELS
+10);
74 /* slot for handling list across selection changed */
75 gboolean
on_across_list_selection_changed (GtkTreeSelection
*sel
,
81 if (gtk_tree_selection_get_selected (sel
, &mod
, &iter
) == TRUE
)
84 gtk_tree_model_get (mod
, &iter
, 0, &sel_word_index
, -1);
85 set_selection_to_word_start (&app_data
, ACROSS
, sel_word_index
);
88 gtk_widget_queue_draw_area (GTK_WIDGET(area
), 0, 0,
89 app_data
.puzzle
.grid_size
*GRID_PIXELS
+10,
90 app_data
.puzzle
.grid_size
*GRID_PIXELS
+10);
96 /* slot for handling mouse button event for puzzle drawing area */
97 gboolean
on_puzzle_area_button_press_event (GtkWidget
*widget
,
98 GdkEventButton
*event
, gpointer data
)
100 /* if it is solution mode, then don't do anything but reset it to
102 if (app_data
.solution_revealed
== true)
104 app_data
.solution_revealed
= false;
105 gtk_widget_queue_draw_area (widget
, 0, 0,
106 app_data
.puzzle
.grid_size
*GRID_PIXELS
+10,
107 app_data
.puzzle
.grid_size
*GRID_PIXELS
+10);
111 /* Depending on the type of button handle the movement */
112 if (event
->type
== GDK_BUTTON_PRESS
&& event
->button
== 1)
114 int col
= (event
->x
- 5) / GRID_PIXELS
;
115 int row
= (event
->y
- 5) / GRID_PIXELS
;
117 if (app_data
.puzzle
.chars
[row
][col
] != '#')
119 if (row
< app_data
.puzzle
.grid_size
&&
120 col
< app_data
.puzzle
.grid_size
)
122 app_data
.cur_row
= row
;
123 app_data
.cur_col
= col
;
125 /* if it is a start of both across and down word, then
126 toggle the movement on clicking it */
127 if (app_data
.puzzle
.start_across_word
[row
][col
] != -1 &&
128 app_data
.puzzle
.start_down_word
[row
][col
] != -1)
130 if (app_data
.current_movement
== ACROSS
)
131 app_data
.current_movement
= DOWN
;
133 app_data
.current_movement
= ACROSS
;
135 /* if it is only start of across word, make the current
137 else if (app_data
.puzzle
.start_across_word
[row
][col
] != -1)
138 app_data
.current_movement
= ACROSS
;
139 /* else down movement */
140 else if (app_data
.puzzle
.start_down_word
[row
][col
] != -1)
141 app_data
.current_movement
= DOWN
;
146 gtk_widget_queue_draw_area (widget
, 0, 0,
147 app_data
.puzzle
.grid_size
*GRID_PIXELS
+10,
148 app_data
.puzzle
.grid_size
*GRID_PIXELS
+10);
153 /* slot for handling key press event for puzzle drawing area */
154 gboolean
on_puzzle_area_key_press_event (GtkWidget
*widget
,
155 GdkEventKey
*event
, gpointer data
)
157 /* respond to key events only if the puzzle is loaded */
158 if (app_data
.is_loaded
== true)
160 /* if the solution is revealed, don't respond to key events except
161 to return to the non-solution mode */
162 if (app_data
.solution_revealed
== true)
164 app_data
.solution_revealed
= false;
165 gtk_widget_queue_draw_area (widget
, 0, 0,
166 app_data
.puzzle
.grid_size
*GRID_PIXELS
+10,
167 app_data
.puzzle
.grid_size
*GRID_PIXELS
+10);
171 switch (event
->keyval
)
173 case GDK_KEY_Down
: move_current_row (&app_data
, DIR_FORWARD
);
174 app_data
.current_movement
= DOWN
;
176 case GDK_KEY_Up
: move_current_row (&app_data
, DIR_BACK
);
177 app_data
.current_movement
= DOWN
;
179 case GDK_KEY_Right
: move_current_col (&app_data
, DIR_FORWARD
);
180 app_data
.current_movement
= ACROSS
;
182 case GDK_KEY_Left
: move_current_col (&app_data
, DIR_BACK
);
183 app_data
.current_movement
= ACROSS
;
185 case GDK_KEY_BackSpace
:
188 app_data
.char_ans
[app_data
.cur_row
][app_data
.cur_col
] = ' ';
192 app_data
.char_ans
[app_data
.cur_row
][app_data
.cur_col
] = 'A';
196 app_data
.char_ans
[app_data
.cur_row
][app_data
.cur_col
] = 'B';
200 app_data
.char_ans
[app_data
.cur_row
][app_data
.cur_col
] = 'C';
204 app_data
.char_ans
[app_data
.cur_row
][app_data
.cur_col
] = 'D';
208 app_data
.char_ans
[app_data
.cur_row
][app_data
.cur_col
] = 'E';
212 app_data
.char_ans
[app_data
.cur_row
][app_data
.cur_col
] = 'F';
216 app_data
.char_ans
[app_data
.cur_row
][app_data
.cur_col
] = 'G';
220 app_data
.char_ans
[app_data
.cur_row
][app_data
.cur_col
] = 'H';
224 app_data
.char_ans
[app_data
.cur_row
][app_data
.cur_col
] = 'I';
228 app_data
.char_ans
[app_data
.cur_row
][app_data
.cur_col
] = 'J';
232 app_data
.char_ans
[app_data
.cur_row
][app_data
.cur_col
] = 'K';
236 app_data
.char_ans
[app_data
.cur_row
][app_data
.cur_col
] = 'L';
240 app_data
.char_ans
[app_data
.cur_row
][app_data
.cur_col
] = 'M';
244 app_data
.char_ans
[app_data
.cur_row
][app_data
.cur_col
] = 'N';
248 app_data
.char_ans
[app_data
.cur_row
][app_data
.cur_col
] = 'O';
252 app_data
.char_ans
[app_data
.cur_row
][app_data
.cur_col
] = 'P';
256 app_data
.char_ans
[app_data
.cur_row
][app_data
.cur_col
] = 'Q';
260 app_data
.char_ans
[app_data
.cur_row
][app_data
.cur_col
] = 'R';
264 app_data
.char_ans
[app_data
.cur_row
][app_data
.cur_col
] = 'S';
268 app_data
.char_ans
[app_data
.cur_row
][app_data
.cur_col
] = 'T';
272 app_data
.char_ans
[app_data
.cur_row
][app_data
.cur_col
] = 'U';
276 app_data
.char_ans
[app_data
.cur_row
][app_data
.cur_col
] = 'V';
280 app_data
.char_ans
[app_data
.cur_row
][app_data
.cur_col
] = 'W';
284 app_data
.char_ans
[app_data
.cur_row
][app_data
.cur_col
] = 'X';
288 app_data
.char_ans
[app_data
.cur_row
][app_data
.cur_col
] = 'Y';
292 app_data
.char_ans
[app_data
.cur_row
][app_data
.cur_col
] = 'Z';
294 default : return FALSE
;
299 /* move the selection pointer to next row/col as per the current
300 movement orientation if it is not a key left right up or down */
301 if (event
->keyval
!= GDK_KEY_Down
&& event
->keyval
!= GDK_KEY_Up
&&
302 event
->keyval
!= GDK_KEY_Left
&& event
->keyval
!= GDK_KEY_Right
303 && event
->keyval
!= GDK_KEY_Delete
)
305 /* if the current movement is an across movement */
306 if (app_data
.current_movement
== ACROSS
)
308 /* if the next column is not blocking move the col */
309 if (event
->keyval
!= GDK_KEY_BackSpace
&&
310 next_col_block (&app_data
.puzzle
, app_data
.cur_row
,
311 app_data
.cur_col
) == false)
312 move_current_col (&app_data
, DIR_FORWARD
);
313 else if (event
->keyval
== GDK_KEY_BackSpace
&&
314 prev_col_block (&app_data
.puzzle
, app_data
.cur_row
,
315 app_data
.cur_col
) == false)
316 move_current_col (&app_data
, DIR_BACK
);
318 /* current movement is a up/down movement */
321 if (event
->keyval
!= GDK_KEY_BackSpace
&&
322 next_row_block (&app_data
.puzzle
,
323 app_data
.cur_row
, app_data
.cur_col
) == false)
324 move_current_row (&app_data
, DIR_FORWARD
);
325 else if (event
->keyval
== GDK_KEY_BackSpace
&&
326 prev_row_block (&app_data
.puzzle
,
327 app_data
.cur_row
, app_data
.cur_col
) == false)
328 move_current_row (&app_data
, DIR_BACK
);
333 gtk_widget_queue_draw_area (widget
, 0, 0,
334 app_data
.puzzle
.grid_size
*GRID_PIXELS
+10,
335 app_data
.puzzle
.grid_size
*GRID_PIXELS
+10);
340 /* slot for drawing the puzzle */
341 gboolean
on_puzzle_area_draw (GtkWidget
*widget
, cairo_t
*cr
, gpointer data
)
343 /* if a puzzle is loaded */
344 if (app_data
.is_loaded
== true)
346 GdkRGBA colorfore
, colorback
, colorblue
, colorbacksel1
,
347 colorbacksel2
, colorsolution
;
348 gdk_rgba_parse (&colorfore
, "#000000");
349 gdk_rgba_parse (&colorback
, "#ffffff");
350 gdk_rgba_parse (&colorblue
, "#0000dd");
351 gdk_rgba_parse (&colorbacksel1
, "#ffffaa");
352 gdk_rgba_parse (&colorbacksel2
, "#aaffff");
353 gdk_rgba_parse (&colorsolution
, "#990099");
354 cairo_set_line_width (cr
, 3);
356 /* set the size of the drawing area */
357 gtk_widget_set_size_request (widget
,
358 app_data
.puzzle
.grid_size
*GRID_PIXELS
+10,
359 app_data
.puzzle
.grid_size
*GRID_PIXELS
+10);
362 for (int i
= 0; i
< app_data
.puzzle
.grid_size
; i
++)
364 for (int j
= 0; j
< app_data
.puzzle
.grid_size
; j
++)
366 /* if it is the current selection or if -1 is the current
367 selection then let the current selection be the first word */
368 if (app_data
.cur_col
== -1 && app_data
.cur_row
== -1)
370 if (app_data
.puzzle
.start_across_word
[i
][j
] == 1)
372 app_data
.cur_row
= i
;
373 app_data
.cur_col
= j
;
374 app_data
.current_movement
= ACROSS
;
376 else if (app_data
.puzzle
.start_down_word
[i
][j
] == 1)
378 app_data
.cur_row
= i
;
379 app_data
.cur_col
= j
;
380 app_data
.current_movement
= DOWN
;
384 gdk_cairo_set_source_rgba (cr
, &colorfore
);
385 cairo_rectangle (cr
, j
*GRID_PIXELS
+5, i
*GRID_PIXELS
+5,
386 GRID_PIXELS
, GRID_PIXELS
);
390 /* if it is not a blank grid then set the background color
392 if (app_data
.puzzle
.chars
[i
][j
] != '#')
393 gdk_cairo_set_source_rgba (cr
, &colorback
);
395 /* if it is a current selection and solution reveal mode is not
396 set then then set the background depending on whether across
397 or down movement mode is current */
398 if (app_data
.solution_revealed
== false &&
399 app_data
.cur_row
== i
&& app_data
.cur_col
== j
)
401 if (app_data
.current_movement
== ACROSS
)
402 gdk_cairo_set_source_rgba (cr
, &colorbacksel1
);
404 gdk_cairo_set_source_rgba (cr
, &colorbacksel2
);
407 cairo_rectangle (cr
, j
*GRID_PIXELS
+5, i
*GRID_PIXELS
+5,
408 GRID_PIXELS
, GRID_PIXELS
);
411 /* draw the word number if it is the start of a word */
412 if (app_data
.puzzle
.start_across_word
[i
][j
] != -1 ||
413 app_data
.puzzle
.start_down_word
[i
][j
] != -1)
416 if (app_data
.puzzle
.start_across_word
[i
][j
] != -1)
417 num
= app_data
.puzzle
.start_across_word
[i
][j
];
419 num
= app_data
.puzzle
.start_down_word
[i
][j
];
421 gdk_cairo_set_source_rgba (cr
, &colorblue
);
422 cairo_select_font_face (cr
, "sans serif",
423 CAIRO_FONT_SLANT_NORMAL
,
424 CAIRO_FONT_WEIGHT_NORMAL
);
425 cairo_set_font_size (cr
, 11);
426 cairo_move_to (cr
, j
*GRID_PIXELS
+7, i
*GRID_PIXELS
+15);
428 sprintf (cnum
, "%d", num
);
429 cairo_show_text (cr
, (const char*)cnum
);
432 /* if solution is not revealed set the color to normal
433 or set it to solution color */
434 if (app_data
.solution_revealed
== false)
435 gdk_cairo_set_source_rgba (cr
, &colorfore
);
437 gdk_cairo_set_source_rgba (cr
, &colorsolution
);
439 cairo_select_font_face (cr
, "sans serif",
440 CAIRO_FONT_SLANT_NORMAL
,
441 CAIRO_FONT_WEIGHT_BOLD
);
443 cairo_set_font_size (cr
, 16);
444 cairo_move_to (cr
, j
*GRID_PIXELS
+GRID_PIXELS
/2,
445 i
*GRID_PIXELS
+GRID_PIXELS
-10);
447 /* if it is solution mode reveal the answer or else the
449 if (app_data
.solution_revealed
== false)
450 sprintf (ctxt
, "%c", app_data
.char_ans
[i
][j
]);
452 if (app_data
.puzzle
.chars
[i
][j
] != '#')
453 sprintf (ctxt
, "%c", app_data
.puzzle
.chars
[i
][j
]);
455 sprintf (ctxt
, "%c", ' ');
456 cairo_show_text (cr
, (const char*) ctxt
);
466 /* slot for reveal solution menu */
467 void on_menu_reveal_solution_activate (GtkMenuItem
*item
, GtkDrawingArea
*area
)
469 /* if puzzle solution is password protected ask for the password */
470 if (strlen (app_data
.puzzle
.hashed_solution_password
) > 0)
473 builder
= gtk_builder_new ();
475 guint ret
= gtk_builder_add_from_resource
477 "/org/harishankar/wordblah/wordblah_player.glade",
481 fprintf (stderr
, ERROR_WINDOW
);
482 g_object_unref (builder
);
486 GtkWidget
*password_dialog
= GTK_WIDGET (gtk_builder_get_object
487 (builder
, "password_dialog"));
488 GtkWidget
*password_text
= GTK_WIDGET (gtk_builder_get_object
489 (builder
, "password_text"));
491 if (password_dialog
== NULL
)
493 fprintf (stderr
, ERROR_WINDOW
);
494 g_object_unref (builder
);
497 gtk_window_set_transient_for (GTK_WINDOW(password_dialog
),
498 GTK_WINDOW(main_window
));
499 gtk_dialog_set_default_response (GTK_DIALOG(password_dialog
),
500 GTK_RESPONSE_ACCEPT
);
501 gint res
= gtk_dialog_run (GTK_DIALOG (password_dialog
));
502 if (res
== GTK_RESPONSE_ACCEPT
)
504 const gchar
*user_pwd
= gtk_entry_get_text
505 (GTK_ENTRY(password_text
));
506 /* if password is correct */
507 if (verify_solution_password (&app_data
.puzzle
, user_pwd
) == true)
508 app_data
.solution_revealed
= true;
509 /* password is incorrect */
512 app_data
.solution_revealed
= false;
513 GtkWidget
*errordlg
;
514 errordlg
= gtk_message_dialog_new (GTK_WINDOW(main_window
),
515 GTK_DIALOG_DESTROY_WITH_PARENT
,
519 gtk_dialog_run (GTK_DIALOG(errordlg
));
520 gtk_widget_destroy (errordlg
);
524 gtk_widget_destroy (password_text
);
525 gtk_widget_destroy (password_dialog
);
526 g_object_unref (builder
);
529 app_data
.solution_revealed
= true;
531 gtk_widget_queue_draw_area (GTK_WIDGET(area
), 0, 0,
532 app_data
.puzzle
.grid_size
*GRID_PIXELS
+10,
533 app_data
.puzzle
.grid_size
*GRID_PIXELS
+10);
537 /* slot for load grid state menu */
538 void on_menu_load_grid_state_activate (GtkMenuItem
*item
, GtkDrawingArea
*area
)
541 GtkFileChooserAction action
= GTK_FILE_CHOOSER_ACTION_OPEN
;
543 dialog
= gtk_file_chooser_dialog_new (OPEN_FILE
, GTK_WINDOW (main_window
),
552 res
= gtk_dialog_run (GTK_DIALOG (dialog
));
553 if (res
== GTK_RESPONSE_ACCEPT
)
556 filename
= gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog
));
558 load_user_data (&temp
, filename
);
559 /* if the puzzle file is invalid or the grid is frozen */
560 if (temp
.puzzle
.grid_size
== 0 ||
561 temp
.puzzle
.grid_frozen
== false)
565 if (temp
.puzzle
.grid_size
== 0)
566 strcpy (message
, INVALID_PUZZLE
);
568 strcpy (message
, UNFROZEN_GRID_PLAYER
);
570 errordlg
= gtk_message_dialog_new (GTK_WINDOW(main_window
),
571 GTK_DIALOG_DESTROY_WITH_PARENT
,
575 gtk_dialog_run (GTK_DIALOG(errordlg
));
576 gtk_widget_destroy (errordlg
);
584 gtk_widget_destroy (dialog
);
586 gtk_widget_queue_draw_area (GTK_WIDGET(area
), 0, 0,
587 app_data
.puzzle
.grid_size
*GRID_PIXELS
+10,
588 app_data
.puzzle
.grid_size
*GRID_PIXELS
+10);
590 update_clue_items ();
593 /* slot for save grid state menu */
594 void on_menu_save_grid_state_activate (GtkMenuItem
*item
, gpointer
*data
)
596 if (app_data
.is_loaded
== false)
600 GtkFileChooserAction action
= GTK_FILE_CHOOSER_ACTION_SAVE
;
602 dialog
= gtk_file_chooser_dialog_new (SAVE_FILE
, GTK_WINDOW(main_window
),
609 res
= gtk_dialog_run (GTK_DIALOG (dialog
));
610 if (res
== GTK_RESPONSE_ACCEPT
)
613 filename
= gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog
));
614 save_user_data (&app_data
, filename
);
618 gtk_widget_destroy (dialog
);
621 /* slot for exit menu */
622 void on_menu_exit_activate (GtkMenuItem
*item
, gpointer data
)
627 /* slot for open menu */
628 void on_menu_open_activate (GtkMenuItem
*item
, GtkDrawingArea
* area
)
631 GtkFileChooserAction action
= GTK_FILE_CHOOSER_ACTION_OPEN
;
633 dialog
= gtk_file_chooser_dialog_new (OPEN_FILE
, GTK_WINDOW(main_window
),
640 res
= gtk_dialog_run (GTK_DIALOG (dialog
));
641 if (res
== GTK_RESPONSE_ACCEPT
)
644 filename
= gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog
));
646 reset_player_data (&temp
, filename
);
648 /* if the puzzle is either an invalid puzzle file or grid is not frozen
649 then the game cannot be played */
650 if (temp
.puzzle
.grid_size
== 0 || temp
.is_loaded
== false)
652 GtkWidget
*errordlg
;
654 if (temp
.puzzle
.grid_size
== 0)
655 strcpy (message
, INVALID_PUZZLE
);
657 strcpy (message
, UNFROZEN_GRID_PLAYER
);
659 errordlg
= gtk_message_dialog_new (GTK_WINDOW(main_window
),
660 GTK_DIALOG_DESTROY_WITH_PARENT
,
664 gtk_dialog_run (GTK_DIALOG(errordlg
));
665 gtk_widget_destroy (errordlg
);
670 gtk_widget_queue_draw_area (GTK_WIDGET (area
), 0, 0,
671 app_data
.puzzle
.grid_size
*30+10,
672 app_data
.puzzle
.grid_size
*30+10);
675 update_clue_items ();
679 gtk_widget_destroy (dialog
);
682 /* slot for about menu */
683 void on_menu_about_activate (GtkMenuItem
*item
, gpointer data
)
685 const char *AUTHOR
[] = {"V.Harishankar", NULL
};
686 gtk_show_about_dialog (GTK_WINDOW(main_window
), "authors",AUTHOR
,
687 "program-name", PROGRAM_NAME
,
688 "copyright", COPYRIGHT
,
689 "comments", COMMENTS
,
691 "website-label", WEBSITE_LABEL
,
692 "license-type", GTK_LICENSE_CUSTOM
,
693 "license", LICENSE_BSD
,
698 int main (int argc
, char *argv
[])
700 gtk_init (&argc
, &argv
);
702 icon
= gdk_pixbuf_new_from_resource
703 ("/org/harishankar/wordblah/wordblah.svg", NULL
);
705 fprintf (stderr
, ERROR_ICON
);
708 builder
= gtk_builder_new ();
709 guint ret
= gtk_builder_add_from_resource (builder
,
710 "/org/harishankar/wordblah/wordblah_player.glade", NULL
);
712 app_data
.is_loaded
= false;
716 fprintf (stderr
, ERROR_WINDOW
);
717 g_object_unref (builder
);
722 main_window
= GTK_WIDGET (gtk_builder_get_object (builder
,
725 across_store
= GTK_LIST_STORE (gtk_builder_get_object
726 (builder
, "store_across_clues"));
727 down_store
= GTK_LIST_STORE (gtk_builder_get_object
728 (builder
, "store_down_clues"));
730 if (main_window
!= NULL
)
732 gtk_window_set_default_icon (icon
);
734 GtkWidget
*draw_area
= GTK_WIDGET (
735 gtk_builder_get_object(builder
, "puzzle_area"));
737 /* make drawing area respond to mouse event */
738 gtk_widget_set_events(draw_area
,
739 gtk_widget_get_events(draw_area
)
740 | GDK_BUTTON_PRESS_MASK
);
742 gtk_builder_connect_signals (builder
, NULL
);
743 g_object_unref (builder
);
744 gtk_widget_show (main_window
);
750 g_object_unref (builder
);
751 fprintf (stderr
, ERROR_WINDOW
);