Work on puzzle grid in player
authorHarishankar <v.harishankar@gmail.com>
Mon, 4 May 2020 09:23:30 +0000 (14:53 +0530)
committerHarishankar <v.harishankar@gmail.com>
Mon, 4 May 2020 09:23:30 +0000 (14:53 +0530)
The puzzle grid in the player now responds to user input for
moving currently selected row/col for up, down, left and right
keys

wordblox.c
wordblox.h
wordblox_player.c
wordblox_player.glade

index 8ab9891..0cbb804 100644 (file)
@@ -107,6 +107,8 @@ void do_set_clue_word (Puzzle *p, enum ORIENTATION orient)
        index = get_num ();
        printf (INPUT_CLUE);
        fgets (clue, MAX_CLUE_LENGTH, stdin);
+       if (strlen (clue) == 1)
+               return;
        char* cl = strtok (clue, "\n");
        
        bool res;
index 9a5c27c..8886fab 100644 (file)
@@ -11,8 +11,9 @@
 #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 {
@@ -36,6 +37,12 @@ enum ORIENTATION {
        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 */
@@ -133,7 +140,7 @@ void export_clues (Puzzle *p, const char *filename)
 /* 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)
        {
@@ -154,13 +161,15 @@ void export_grid_image  (Puzzle *p, const char *filename, bool answerkey)
                {
                        /* 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 */
@@ -171,22 +180,24 @@ void export_grid_image  (Puzzle *p, const char *filename, bool answerkey)
                                        {
                                                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);
                                }
                        }
                }
index 2904929..a3105d6 100644 (file)
 #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
@@ -41,9 +211,37 @@ gboolean on_puzzle_area_draw (GtkWidget *widget, cairo_t *cr, gpointer data)
                                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);
+                               }
+                               
+
                        }
                }
        }
@@ -52,6 +250,18 @@ gboolean on_puzzle_area_draw (GtkWidget *widget, cairo_t *cr, gpointer data)
 
 }
 
+/* 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)
 {
@@ -90,10 +300,16 @@ void on_menu_open_activate (GtkMenuItem *item, GtkDrawingArea* area)
                        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);
        }
        
@@ -140,6 +356,11 @@ int main (int argc, char *argv [])
        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);             
index 428cb8a..bc9f27a 100644 (file)
                       </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>