#include <zlib.h>
#include "constantstrings.h"
-#define MAX_PUZZLE_SIZE 20
+#define MAX_PUZZLE_SIZE 25
#define MAX_CLUE_LENGTH 150
+#define GRID_PIXELS 37
/* Enum to define terminal colours */
enum COLOR {
DOWN=2
};
+/* for use with the player */
+enum DIRECTION {
+ DIR_FORWARD = 1,
+ DIR_BACK = -1
+};
+
typedef char String[MAX_CLUE_LENGTH];
/* The main puzzle struct type */
/* Output the grid to image - if answerkey is true export filled grid */
void export_grid_image (Puzzle *p, const char *filename, bool answerkey)
{
- int img_size = p->grid_size * 40;
+ int img_size = p->grid_size * GRID_PIXELS;
FILE * outfile = fopen (filename, "wb");
if (outfile == NULL)
{
{
/* if it is a block, draw the black square */
if (p->chars[i][j] == '#')
- gdImageFilledRectangle (img, j*40, i*40, j*40+40,
- i*40+40,black);
+ gdImageFilledRectangle (img, j*GRID_PIXELS, i*GRID_PIXELS,
+ j*GRID_PIXELS+GRID_PIXELS,
+ i*GRID_PIXELS+GRID_PIXELS,black);
else
{
/* draw a regular square */
- gdImageRectangle (img, j*40, i*40, j*40+40,
- i*40+40, black);
+ gdImageRectangle (img, j*GRID_PIXELS, i*GRID_PIXELS,
+ j*GRID_PIXELS+GRID_PIXELS,
+ i*GRID_PIXELS+GRID_PIXELS, black);
/* print the numers, if it is either start across word or
a down word */
{
char str[5];
sprintf (str, "%d", p->start_across_word[i][j]);
- gdImageString (img, sm_fnt, j*40+2, i*40+2,
- (unsigned char *)str, blue);
+ gdImageString (img, sm_fnt, j*GRID_PIXELS+2,
+ i*GRID_PIXELS+2,
+ (unsigned char *)str, blue);
}
else
{
char str[5];
sprintf (str, "%d", p->start_down_word[i][j]);
- gdImageString (img, sm_fnt, j*40+2, i*40+2,
- (unsigned char *)str, blue);
+ gdImageString (img, sm_fnt, j*GRID_PIXELS+2,
+ i*GRID_PIXELS+2,
+ (unsigned char *)str, blue);
}
}
/* if answerkey is true, draw the character in the cell */
if (answerkey)
{
- gdImageChar (img, lg_fnt, j*40+15, i*40+15,
- p->chars[i][j], black);
+ gdImageChar (img, lg_fnt, j*GRID_PIXELS+15,
+ i*GRID_PIXELS+10, p->chars[i][j], black);
}
}
}
#include "wordblox.h"
GtkWidget *window;
+GtkListStore *across_store;
+GtkListStore *down_store;
struct MainAppData {
Puzzle puzzle;
char filename[65535];
bool is_loaded;
+ int cur_row;
+ int cur_col;
} app_data;
+/* update the clue items */
+void update_clue_items ()
+{
+ gtk_list_store_clear (across_store);
+ gtk_list_store_clear (down_store);
+
+ /* if the puzzle is loaded */
+ if (app_data.is_loaded == true)
+ {
+ /* iterate through the puzzle grid and gte the clues */
+ for (int i = 0; i < app_data.puzzle.grid_size; i ++)
+ {
+ for (int j = 0; j < app_data.puzzle.grid_size; j ++)
+ {
+ /* if it is the start of an across word */
+ if (app_data.puzzle.start_across_word[i][j] != -1)
+ {
+ GtkTreeIter iter;
+
+ gtk_list_store_append (across_store, &iter);
+ gtk_list_store_set (across_store, &iter, 0,
+ app_data.puzzle.start_across_word[i][j],
+ 1, app_data.puzzle.clue_across[i][j],
+ -1);
+ }
+ /* if it is the start of a down word */
+ if (app_data.puzzle.start_down_word[i][j] != -1)
+ {
+ GtkTreeIter iter;
+ gtk_list_store_append (down_store, &iter);
+ gtk_list_store_set (down_store, &iter, 0,
+ app_data.puzzle.start_down_word[i][j],
+ 1, app_data.puzzle.clue_down[i][j],
+ -1);
+ }
+ }
+ }
+ }
+}
+
+/* move the current selection index left or right */
+void move_current_col (enum DIRECTION dir)
+{
+ int r = app_data.cur_row;
+ int c = app_data.cur_col;
+ if (dir == DIR_FORWARD)
+ {
+ c ++;
+ while (c < app_data.puzzle.grid_size)
+ {
+ if (app_data.puzzle.chars[r][c] == '#')
+ c ++;
+ else
+ break;
+ }
+ if (c < app_data.puzzle.grid_size)
+ app_data.cur_col = c;
+ }
+ else
+ {
+ c --;
+ while (c >= 0)
+ {
+ if (app_data.puzzle.chars[r][c] == '#')
+ c --;
+ else
+ break;
+ }
+ if (c >= 0)
+ app_data.cur_col = c;
+ }
+}
+
+/* move the current selection index up or down */
+void move_current_row (enum DIRECTION dir)
+{
+ int r = app_data.cur_row;
+ int c = app_data.cur_col;
+ if (dir == DIR_FORWARD)
+ {
+ r ++;
+ while (r < app_data.puzzle.grid_size)
+ {
+ if (app_data.puzzle.chars[r][c] == '#')
+ r ++;
+ else
+ break;
+ }
+ if (r < app_data.puzzle.grid_size)
+ app_data.cur_row = r;
+ }
+ else
+ {
+ r --;
+ while (r >= 0)
+ {
+ if (app_data.puzzle.chars[r][c] == '#')
+ r --;
+ else
+ break;
+ }
+ if (r >= 0)
+ app_data.cur_row = r;
+ }
+}
+
+
+/* slot for handling key press event for puzzle drawing area */
+gboolean on_puzzle_area_key_press_event (GtkWidget *widget,
+ GdkEventKey *event, gpointer data)
+{
+ /* respond to key events only if the puzzle is loaded */
+ if (app_data.is_loaded == true)
+ {
+ switch (event->keyval)
+ {
+ case GDK_KEY_Down : move_current_row (DIR_FORWARD);
+ gtk_widget_queue_draw_area (widget,
+ 0, 0,
+ app_data.puzzle.grid_size*GRID_PIXELS+10,
+ app_data.puzzle.grid_size*GRID_PIXELS+10);
+ break;
+ case GDK_KEY_Up : move_current_row (DIR_BACK);
+ gtk_widget_queue_draw_area (widget,
+ 0, 0,
+ app_data.puzzle.grid_size*GRID_PIXELS+10,
+ app_data.puzzle.grid_size*GRID_PIXELS+10);
+ break;
+ case GDK_KEY_Right: move_current_col (DIR_FORWARD);
+ gtk_widget_queue_draw_area (widget,
+ 0, 0,
+ app_data.puzzle.grid_size*GRID_PIXELS+10,
+ app_data.puzzle.grid_size*GRID_PIXELS+10);
+ break;
+ case GDK_KEY_Left : move_current_col (DIR_BACK);
+ gtk_widget_queue_draw_area (widget,
+ 0, 0,
+ app_data.puzzle.grid_size*GRID_PIXELS+10,
+ app_data.puzzle.grid_size*GRID_PIXELS+10);
+ }
+ }
+
+ return FALSE;
+}
+
/* slot for drawing the puzzle */
gboolean on_puzzle_area_draw (GtkWidget *widget, cairo_t *cr, gpointer data)
{
/* if a puzzle is loaded */
if (app_data.is_loaded == true)
{
- GdkRGBA colorfore, colorback;
+ GdkRGBA colorfore, colorback, colorblue, colorbacksel;
gdk_rgba_parse (&colorfore, "#000000");
gdk_rgba_parse (&colorback, "#ffffff");
+ gdk_rgba_parse (&colorblue, "#0000dd");
+ gdk_rgba_parse (&colorbacksel, "#ffffaa");
cairo_set_line_width (cr, 3);
/* set the size of the drawing area */
- gtk_widget_set_size_request (widget, app_data.puzzle.grid_size*30+5,
- app_data.puzzle.grid_size*30+5);
+ gtk_widget_set_size_request (widget,
+ app_data.puzzle.grid_size*GRID_PIXELS+10,
+ app_data.puzzle.grid_size*GRID_PIXELS+10);
/* Draw the grid */
for (int i = 0; i < app_data.puzzle.grid_size; i ++)
{
for (int j = 0; j < app_data.puzzle.grid_size; j ++)
{
- cairo_rectangle (cr, j*30+5, i*30+5, 30, 30);
+ /* if it is the current selection or if -1 is the current
+ selection then let the current selection be the first word */
+ if (app_data.cur_col == -1 && app_data.cur_row == -1)
+ {
+ if (app_data.puzzle.start_across_word[i][j] == 1)
+ {
+ app_data.cur_row = i;
+ app_data.cur_col = j;
+ }
+ else if (app_data.puzzle.start_down_word[i][j] == 1)
+ {
+ app_data.cur_row = i;
+ app_data.cur_col = j;
+ }
+ }
+
gdk_cairo_set_source_rgba (cr, &colorfore);
+ cairo_rectangle (cr, j*GRID_PIXELS+5, i*GRID_PIXELS+5,
+ GRID_PIXELS, GRID_PIXELS);
+
cairo_stroke (cr);
/* if it is not a blank grid then set the background color
if (app_data.puzzle.chars[i][j] != '#')
gdk_cairo_set_source_rgba (cr, &colorback);
- cairo_rectangle (cr, j*30+5, i*30+5, 30, 30);
-
+ /* if it is a current selection then set the background
+ to yellow */
+ if (app_data.cur_row == i && app_data.cur_col == j)
+ gdk_cairo_set_source_rgba (cr, &colorbacksel);
+
+ cairo_rectangle (cr, j*GRID_PIXELS+5, i*GRID_PIXELS+5,
+ GRID_PIXELS, GRID_PIXELS);
cairo_fill (cr);
+
+ /* draw the word number if it is the start of a word */
+ if (app_data.puzzle.start_across_word[i][j] != -1 ||
+ app_data.puzzle.start_down_word[i][j] != -1)
+ {
+ int num;
+ if (app_data.puzzle.start_across_word[i][j] != -1)
+ num = app_data.puzzle.start_across_word[i][j];
+ else
+ num = app_data.puzzle.start_down_word[i][j];
+
+ gdk_cairo_set_source_rgba (cr, &colorblue);
+ cairo_select_font_face (cr, "sans serif",
+ CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ cairo_set_font_size (cr, 11);
+ cairo_move_to (cr, j*GRID_PIXELS+7, i*GRID_PIXELS+15);
+ char cnum[3];
+ sprintf (cnum, "%d", num);
+ cairo_show_text (cr, (const char*)cnum);
+ }
+
+
}
}
}
}
+/* slot for reveal solution menu */
+void on_menu_reveal_solution_activate (GtkMenuItem *item, gpointer *data)
+{
+ /* TODO */
+}
+
+/* slot for save grid state menu */
+void on_menu_save_grid_state_activate (GtkMenuItem *item, gpointer *data)
+{
+ /* TODO */
+}
+
/* slot for exit menu */
void on_menu_exit_activate (GtkMenuItem *item, gpointer data)
{
gtk_dialog_run (GTK_DIALOG(errordlg));
gtk_widget_destroy (errordlg);
app_data.is_loaded = false;
+ app_data.cur_col = -1;
+ app_data.cur_row = -1;
+ }
+ else
+ {
+ gtk_widget_queue_draw_area (GTK_WIDGET (area), 0, 0,
+ app_data.puzzle.grid_size*30+10,
+ app_data.puzzle.grid_size*30+10);
+ update_clue_items ();
}
-
- gtk_widget_queue_draw_area (GTK_WIDGET (area), 0, 0, 305, 305);
-
g_free (filename);
}
else
{
window = GTK_WIDGET (gtk_builder_get_object (builder, "main_window") );
+ across_store = GTK_LIST_STORE (gtk_builder_get_object
+ (builder, "store_across_clues"));
+ down_store = GTK_LIST_STORE (gtk_builder_get_object
+ (builder, "store_down_clues"));
+
if (window != NULL)
{
gtk_window_set_default_icon (icon);
</object>
</child>
<child>
- <object class="GtkMenuItem" id="menu_save">
+ <object class="GtkMenuItem" id="menu_save_grid_state">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Save Grid State...</property>
<property name="use_underline">True</property>
- <signal name="activate" handler="on_menu_save_activate" swapped="no"/>
+ <signal name="activate" handler="on_menu_save_grid_state_activate" swapped="no"/>
</object>
</child>
<child>
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
- <object class="GtkMenuItem" id="menu_view_answers">
+ <object class="GtkMenuItem" id="menu_reveal_solution">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Reveal Solution...</property>
<property name="use_underline">True</property>
- <signal name="activate" handler="on_menu_view_answers_activate" swapped="no"/>
+ <signal name="activate" handler="on_menu_reveal_solution_activate" swapped="no"/>
</object>
</child>
</object>
<child>
<object class="GtkDrawingArea" id="puzzle_area">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can_focus">True</property>
+ <property name="has_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
<signal name="draw" handler="on_puzzle_area_draw" swapped="no"/>
+ <signal name="key-press-event" handler="on_puzzle_area_key_press_event" swapped="no"/>
</object>
</child>
</object>
<child>
<object class="GtkPaned">
<property name="visible">True</property>
- <property name="can_focus">True</property>
+ <property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
- <property name="can_focus">True</property>
+ <property name="can_focus">False</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkTreeView" id="list_across_clues">
<property name="visible">True</property>
- <property name="can_focus">True</property>
+ <property name="can_focus">False</property>
<property name="model">store_across_clues</property>
<property name="enable_search">False</property>
<property name="search_column">0</property>
<child internal-child="selection">
<object class="GtkTreeSelection"/>
</child>
+ <child>
+ <object class="GtkTreeViewColumn" id="id">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">Id</property>
+ <child>
+ <object class="GtkCellRendererText"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="ClueAcross">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">Clues Across</property>
+ <child>
+ <object class="GtkCellRendererText"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ <attribute name="placeholder-text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
</object>
</child>
</object>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
- <property name="can_focus">True</property>
+ <property name="can_focus">False</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkTreeView" id="list_down_clues">
<property name="visible">True</property>
- <property name="can_focus">True</property>
+ <property name="can_focus">False</property>
<property name="model">store_down_clues</property>
+ <property name="enable_search">False</property>
<property name="search_column">0</property>
+ <property name="show_expanders">False</property>
<child internal-child="selection">
<object class="GtkTreeSelection"/>
</child>
+ <child>
+ <object class="GtkTreeViewColumn" id="ID">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">Id</property>
+ <child>
+ <object class="GtkCellRendererText"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="ClueDown">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">Clues Down</property>
+ <child>
+ <object class="GtkCellRendererText"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
</object>
</child>
</object>