Reveal solution functionality completed
authorHarishankar <v.harishankar@gmail.com>
Wed, 6 May 2020 14:52:42 +0000 (20:22 +0530)
committerHarishankar <v.harishankar@gmail.com>
Wed, 6 May 2020 14:56:30 +0000 (20:26 +0530)
Now the "reveal solution" menu item will display the solution
if the correct password is entered. If no password is set the
solution will be revealed without any password.

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

index 1aec380..d5f4734 100644 (file)
@@ -41,7 +41,7 @@ columns (warning: existing file name may be overwritten)\n"
 
 /* for wordblox_player */
 #define ERROR_ICON "Unable to load icon!"
-#define ERROR_WINDOW "Error loading Window!"
+#define ERROR_WINDOW "Error loading Window Resource!"
 #define OPEN_FILE "Open File"
 #define UNFROZEN_GRID_PLAYER "Puzzle is not finalized/frozen and hence cannot\
  be played"
index e27d37c..1925573 100644 (file)
@@ -69,6 +69,7 @@ typedef struct {
        char char_ans[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE];
        int cur_row;
        int cur_col;
+       bool solution_revealed;
        enum ORIENTATION current_movement;
 } MainPlayerData;
 
@@ -861,7 +862,7 @@ void print_menu (enum COLOR fg, enum COLOR bg, const char* title,
                reset_color (); printf ("\n");
 }
 
-/* reset the player data, from the new file */
+/* reset the player data, loading from the puzzle file */
 void reset_player_data (MainPlayerData *app_data, const char *filename)
 {
        app_data->puzzle = load_puzzle (filename);
@@ -869,6 +870,7 @@ void reset_player_data (MainPlayerData *app_data, const char *filename)
        app_data->is_loaded = app_data->puzzle.grid_frozen;
        app_data->cur_col = -1;
        app_data->cur_row = -1;
+       app_data->solution_revealed = false;
        strcpy (app_data->filename, filename);
        /* reset the answer keys */
        for (int i = 0; i < app_data->puzzle.grid_size; i ++)
index 8e7299b..605d9f5 100644 (file)
@@ -4,7 +4,7 @@
 #include "wordblox_resource.c"
 #include "wordblox.h"
 
-GtkWidget *window; 
+GtkWidget *main_window; 
 GtkListStore *across_store;
 GtkListStore *down_store;
 
@@ -97,6 +97,18 @@ gboolean on_across_list_selection_changed (GtkTreeSelection *sel,
 gboolean on_puzzle_area_button_press_event (GtkWidget *widget, 
                                                                        GdkEventButton *event, gpointer data)
 {
+       /* if it is solution mode, then don't do anything but reset it to 
+          non solution mode */
+       if (app_data.solution_revealed == true)
+       {
+               app_data.solution_revealed = false;
+               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;
+       }
+       
+       /* Depending on the type of button handle the movement */
        if (event->type == GDK_BUTTON_PRESS && event->button == 1)
        {
                int col = (event->x - 5) / GRID_PIXELS;
@@ -145,6 +157,17 @@ gboolean on_puzzle_area_key_press_event (GtkWidget *widget,
        /* respond to key events only if the puzzle is loaded */
        if (app_data.is_loaded == true)
        {
+               /* if the solution is revealed, don't respond to key events except
+                  to return to the non-solution mode */
+               if (app_data.solution_revealed == true)
+               {
+                       app_data.solution_revealed = false;
+                       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;
+               }
+               
                switch (event->keyval)
                {
                        case GDK_KEY_Down : move_current_row (&app_data, DIR_FORWARD);
@@ -320,12 +343,14 @@ 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, colorblue, colorbacksel1, colorbacksel2;
+               GdkRGBA colorfore, colorback, colorblue, colorbacksel1, 
+                               colorbacksel2, colorsolution;
                gdk_rgba_parse (&colorfore, "#000000"); 
                gdk_rgba_parse (&colorback, "#ffffff");
                gdk_rgba_parse (&colorblue, "#0000dd");
                gdk_rgba_parse (&colorbacksel1, "#ffffaa");
                gdk_rgba_parse (&colorbacksel2, "#aaffff");
+               gdk_rgba_parse (&colorsolution, "#990099");
                cairo_set_line_width (cr, 3);
                
                /* set the size of the drawing area */
@@ -346,11 +371,13 @@ gboolean on_puzzle_area_draw (GtkWidget *widget, cairo_t *cr, gpointer data)
                                        {
                                                app_data.cur_row = i;
                                                app_data.cur_col = j;
+                                               app_data.current_movement = ACROSS;
                                        }
                                        else if (app_data.puzzle.start_down_word[i][j] == 1)
                                        {
                                                app_data.cur_row = i; 
                                                app_data.cur_col = j;
+                                               app_data.current_movement = DOWN;
                                        }
                                }
                        
@@ -365,9 +392,11 @@ 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);
                                
-                               /* if it is a current selection then set the background 
-                               to yellow */
-                               if (app_data.cur_row == i && app_data.cur_col == j)
+                               /* if it is a current selection and solution reveal mode is not 
+                                  set then then set the background depending on whether across
+                                  or down movement mode is current */
+                               if (app_data.solution_revealed == false && 
+                                               app_data.cur_row == i && app_data.cur_col == j)
                                {
                                        if (app_data.current_movement == ACROSS)
                                                gdk_cairo_set_source_rgba (cr, &colorbacksel1);
@@ -400,8 +429,13 @@ gboolean on_puzzle_area_draw (GtkWidget *widget, cairo_t *cr, gpointer data)
                                        cairo_show_text (cr, (const char*)cnum);
                                }
                                
-                               /* draw the answer if it is there */
-                               gdk_cairo_set_source_rgba (cr, &colorfore);     
+                               /* if solution is not revealed set the color to normal
+                                  or set it to solution color */
+                               if (app_data.solution_revealed == false)
+                                       gdk_cairo_set_source_rgba (cr, &colorfore);
+                               else
+                                       gdk_cairo_set_source_rgba (cr, &colorsolution);
+                                       
                                cairo_select_font_face (cr, "sans serif", 
                                                                                CAIRO_FONT_SLANT_NORMAL,
                                                                                CAIRO_FONT_WEIGHT_BOLD);
@@ -410,7 +444,15 @@ gboolean on_puzzle_area_draw (GtkWidget *widget, cairo_t *cr, gpointer data)
                                cairo_move_to (cr, j*GRID_PIXELS+GRID_PIXELS/2, 
                                                                i*GRID_PIXELS+GRID_PIXELS-10);
                                char ctxt[3];
-                               sprintf (ctxt, "%c", app_data.char_ans[i][j]);
+                               /* if it is solution mode reveal the answer or else the 
+                                  user input */
+                                 if (app_data.solution_revealed == false)
+                                       sprintf (ctxt, "%c", app_data.char_ans[i][j]);
+                                 else
+                                       if (app_data.puzzle.chars[i][j] != '#')
+                                               sprintf (ctxt, "%c", app_data.puzzle.chars[i][j]);
+                                       else
+                                               sprintf (ctxt, "%c", ' ');
                                cairo_show_text (cr, (const char*) ctxt);
 
                        }
@@ -422,9 +464,74 @@ 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)
+void on_menu_reveal_solution_activate (GtkMenuItem *item, GtkDrawingArea *area)
 {
-       /* TODO */
+       /* if puzzle is password protected ask for the password */
+       if (strlen (app_data.puzzle.hashed_password) > 0)
+       {
+               GtkBuilder *builder;
+               builder = gtk_builder_new ();
+               
+               guint ret = gtk_builder_add_from_resource 
+                                               (builder, 
+                                                       "/org/harishankar/wordblox/wordblox_player.glade", 
+                                                       NULL);          
+               if (ret == 0)
+               {
+                       fprintf (stderr, ERROR_WINDOW);
+                       g_object_unref (builder);
+                       return;
+               }
+                                               
+               GtkWidget *password_dialog = GTK_WIDGET (gtk_builder_get_object 
+                                                                               (builder, "password_dialog"));
+               GtkWidget *password_text = GTK_WIDGET (gtk_builder_get_object 
+                                                                               (builder, "password_text"));
+                                                                               
+               if (password_dialog == NULL)
+               {
+                       fprintf (stderr, ERROR_WINDOW);
+                       g_object_unref (builder);
+                       return;
+               }
+               gtk_window_set_transient_for (GTK_WINDOW(password_dialog), 
+                                                                       GTK_WINDOW(main_window));
+               gtk_dialog_set_default_response (GTK_DIALOG(password_dialog), 
+               GTK_RESPONSE_ACCEPT);
+               gint res = gtk_dialog_run (GTK_DIALOG (password_dialog));
+               if (res == GTK_RESPONSE_ACCEPT)
+               {
+                       const gchar *user_pwd = gtk_entry_get_text 
+                                                                                       (GTK_ENTRY(password_text));
+                       /* if password is correct */
+                       if (verify_password (&app_data.puzzle, user_pwd) == true)
+                               app_data.solution_revealed = true;
+                       /* password is incorrect */
+                       else
+                       {
+                               app_data.solution_revealed = false;
+                               GtkWidget *errordlg ;
+                               errordlg = gtk_message_dialog_new (GTK_WINDOW(main_window), 
+                                                                                               GTK_DIALOG_DESTROY_WITH_PARENT,
+                                                                                               GTK_MESSAGE_ERROR,
+                                                                                               GTK_BUTTONS_CLOSE,
+                                                                                               WRONG_PASSWORD);
+                               gtk_dialog_run (GTK_DIALOG(errordlg));
+                               gtk_widget_destroy (errordlg);
+                       }
+               }
+               
+               gtk_widget_destroy (password_text);
+               gtk_widget_destroy (password_dialog);
+               g_object_unref (builder);
+                       }
+       else
+               app_data.solution_revealed = true;
+
+       gtk_widget_queue_draw_area (GTK_WIDGET(area), 0, 0,  
+                               app_data.puzzle.grid_size*GRID_PIXELS+10, 
+                               app_data.puzzle.grid_size*GRID_PIXELS+10);
+
 }
 
 /* slot for save grid state menu */
@@ -445,7 +552,8 @@ void on_menu_open_activate (GtkMenuItem *item, GtkDrawingArea* area)
        GtkWidget *dialog;
        GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN;
        gint res;
-       dialog = gtk_file_chooser_dialog_new (OPEN_FILE, GTK_WINDOW(window), action, 
+       dialog = gtk_file_chooser_dialog_new (OPEN_FILE, GTK_WINDOW(main_window),
+                                                                                        action, 
                                                                                        "_Cancel", 
                                                                                        GTK_RESPONSE_CANCEL, 
                                                                                        "_Open",
@@ -463,7 +571,7 @@ void on_menu_open_activate (GtkMenuItem *item, GtkDrawingArea* area)
                if (temp.is_loaded == false)
                {
                        GtkWidget *errordlg ;
-                       errordlg = gtk_message_dialog_new (GTK_WINDOW(window), 
+                       errordlg = gtk_message_dialog_new (GTK_WINDOW(main_window), 
                                                                                                GTK_DIALOG_DESTROY_WITH_PARENT,
                                                                                                GTK_MESSAGE_ERROR,
                                                                                                GTK_BUTTONS_CLOSE,
@@ -490,7 +598,7 @@ void on_menu_open_activate (GtkMenuItem *item, GtkDrawingArea* area)
 void on_menu_about_activate (GtkMenuItem *item, gpointer data)
 {      
        const char *AUTHOR[] =  {"V.Harishankar", NULL};
-       gtk_show_about_dialog (GTK_WINDOW(window), "authors",AUTHOR, 
+       gtk_show_about_dialog (GTK_WINDOW(main_window), "authors",AUTHOR, 
                                                        "program-name", PROGRAM_NAME,
                                                        "copyright", COPYRIGHT,
                                                        "comments", COMMENTS,
@@ -525,13 +633,15 @@ int main (int argc, char *argv [])
        }
        else 
        {
-               window = GTK_WIDGET (gtk_builder_get_object (builder, "main_window") );
+               main_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)
+               if (main_window != NULL)
                {
                        gtk_window_set_default_icon (icon);
                        
@@ -545,7 +655,7 @@ int main (int argc, char *argv [])
                
                        gtk_builder_connect_signals (builder, NULL);
                        g_object_unref (builder);
-                       gtk_widget_show (window);
+                       gtk_widget_show (main_window);
                        gtk_main ();
                        return 0;
                }
index 3f30d95..7142360 100644 (file)
@@ -99,7 +99,7 @@
                         <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_reveal_solution_activate" swapped="no"/>
+                        <signal name="activate" handler="on_menu_reveal_solution_activate" object="puzzle_area" swapped="no"/>
                       </object>
                     </child>
                   </object>
       </object>
     </child>
   </object>
+  <object class="GtkDialog" id="password_dialog">
+    <property name="can_focus">False</property>
+    <property name="title" translatable="yes">Password</property>
+    <property name="resizable">False</property>
+    <property name="hide_titlebar_when_maximized">True</property>
+    <property name="type_hint">dialog</property>
+    <property name="transient_for">main_window</property>
+    <child type="titlebar">
+      <placeholder/>
+    </child>
+    <child internal-child="vbox">
+      <object class="GtkBox">
+        <property name="can_focus">False</property>
+        <property name="margin_left">10</property>
+        <property name="margin_right">10</property>
+        <property name="margin_top">10</property>
+        <property name="margin_bottom">10</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox">
+            <property name="can_focus">False</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="button1">
+                <property name="label">gtk-cancel</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_stock">True</property>
+                <property name="always_show_image">True</property>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="button2">
+                <property name="label">gtk-ok</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_stock">True</property>
+                <property name="always_show_image">True</property>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="label" translatable="yes">Password for revealing solution</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkEntry" id="password_text">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="max_length">256</property>
+            <property name="visibility">False</property>
+            <property name="invisible_char">●</property>
+            <property name="activates_default">True</property>
+            <property name="input_purpose">password</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">3</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="-6">button1</action-widget>
+      <action-widget response="-3">button2</action-widget>
+    </action-widgets>
+  </object>
 </interface>