-all: wordblox wordblox_player
+all: wordblah wordblah_player
-wordblox: wordblox.c wordblox.h constantstrings.h
- clang wordblox.c -lgd -lz -lcrypto -o wordblox
+wordblah: wordblah.c wordblah.h constantstrings.h
+ clang wordblah.c -lgd -lz -lcrypto -o wordblah
-wordblox_player: wordblox_player.c wordblox.h wordblox_resource.c wordblox.gresource.xml wordblox_player.glade constantstrings.h
- glib-compile-resources wordblox.gresource.xml --target wordblox_resource.c --generate-source
- clang -rdynamic -lz -lgd -lcrypto -o wordblox_player wordblox_player.c -Wall `pkg-config --cflags --libs gtk+-3.0`
+wordblah_player: wordblah_player.c wordblah.h wordblah.gresource.xml wordblah_player.glade constantstrings.h
+ glib-compile-resources wordblah.gresource.xml --target wordblah_resource.c --generate-source
+ clang -rdynamic -lz -lgd -lcrypto -o wordblah_player wordblah_player.c -Wall `pkg-config --cflags --libs gtk+-3.0`
#define USAGE_LINE_3 "new <nn> - create new puzzle with <nn> grid \
columns (warning: existing file name may be overwritten)\n"
-/* for wordblox_player */
+/* for wordblah_player */
#define ERROR_ICON "Unable to load icon!"
#define ERROR_WINDOW "Error loading Window Resource!"
#define OPEN_FILE "Open File"
/* about box info */
const char *AUTHOR[] = { "V. Harishankar", NULL};
const char *COPYRIGHT = "Copyright 2020 V.Harishankar";
-const char *COMMENTS = "A player for wordblox (crossword puzzles)";
-const char *PROGRAM_NAME = "Wordblox Player";
+const char *COMMENTS = "A player for wordblah (crossword puzzles)";
+const char *PROGRAM_NAME = "Wordblah Player";
const char *WEBSITE = "http://harishankar.org/";
const char *WEBSITE_LABEL = "Author's HomePage";
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+
+#include "wordblah.h"
+#include "constantstrings.h"
+
+/* export the clues to a text file */
+void do_export_clues (Puzzle *p)
+{
+ if (p->grid_frozen == false)
+ {
+ printf (UNFROZEN_GRID);
+ char ch = getchar ();
+ return;
+ }
+ char fname[256];
+ printf (INPUT_FILE);
+ fgets (fname, 256, stdin);
+ if (strlen(fname) == 1)
+ return;
+ char *filename = strtok (fname, "\n");
+
+ export_clues (p, filename);
+ printf (FILE_SAVED);
+ char ch = getchar ();
+}
+
+/* export the puzzle to a png file */
+void do_export_puzzle (Puzzle *p)
+{
+ if (p->grid_frozen == false)
+ {
+ printf (UNFROZEN_GRID);
+ char ch = getchar ();
+ return;
+ }
+ char fname[256];
+ printf (INPUT_FILE);
+ fgets (fname, 256, stdin);
+ if (strlen (fname) == 1)
+ return;
+ char* filename = strtok (fname, "\n");
+
+ printf (INPUT_EXPORT_ANSWERS);
+ char ans[3];
+ fgets (ans, 3, stdin);
+ bool solution;
+ solution = (toupper (ans[0]) == 'Y') ? true : false;
+
+ export_grid_image (p, filename, solution);
+ printf (FILE_SAVED);
+ char ch = getchar ();
+}
+
+/* reset the grid */
+void do_reset_puzzle (Puzzle *p)
+{
+ int grid_size = p->grid_size;
+ printf (INPUT_CONFIRM_RESET);
+ char conf[3];
+ fgets (conf, 3, stdin);
+ if (toupper (conf[0]) == 'Y')
+ init_puzzle (p, grid_size);
+
+ print_puzzle (p);
+ char ch = getchar ();
+}
+
+/* set the solution password for the puzzle */
+void do_set_solution_password (Puzzle *p)
+{
+ char* password;
+ password = getpass (INPUT_PASSWORD);
+ /* if empty reset the password to nothing */
+ if (strlen(password) == 0)
+ {
+ set_solution_password (p, "\0");
+ printf (SOLUTION_PASSWORD_RESET);
+ char ch = getchar ();
+ }
+ /* set the password */
+ else
+ {
+ set_solution_password (p, (const char* )password);
+ printf (PASSWORD_SET);
+ char ch = getchar ();
+ }
+}
+/* set the master (editing) password for the puzzle */
+void do_set_master_password (Puzzle *p)
+{
+ char* password;
+ password = getpass (INPUT_PASSWORD);
+ /* if empty reset the password to nothing */
+ if (strlen(password) == 0)
+ {
+ set_master_password (p, "\0");
+ printf (MASTER_PASSWORD_RESET);
+ char ch = getchar ();
+ }
+ /* set the password */
+ else
+ {
+ set_master_password (p, (const char* )password);
+ printf (PASSWORD_SET);
+ char ch = getchar ();
+ }
+}
+
+/* set clue for a word - only for frozen grid */
+void do_set_clue_word (Puzzle *p, enum ORIENTATION orient)
+{
+ print_puzzle (p);
+ if (p->grid_frozen == false)
+ {
+ printf (UNFROZEN_GRID);
+ char ch = getchar ();
+ return;
+ }
+ int index;
+ String clue;
+ printf (INPUT_INDEX);
+ index = get_num ();
+ printf (INPUT_CLUE);
+ fgets (clue, MAX_CLUE_LENGTH, stdin);
+ if (strlen (clue) == 1)
+ return;
+ char* cl = strtok (clue, "\n");
+
+ bool res;
+ res = set_clue (p, cl, index, orient);
+
+ if (res == false)
+ {
+ printf (NO_WORD_INDEX);
+ char ch = getchar ();
+ }
+}
+
+/* clear a cell in the grid */
+void do_clear_cell (Puzzle *p)
+{
+ print_puzzle (p);
+ if (p->grid_frozen == true)
+ {
+ printf (FROZEN_GRID);
+ char ch = getchar ();
+ return;
+ }
+ int row, col;
+ printf (INPUT_ROW);
+ row = get_num ();
+ printf (INPUT_COL);
+ col = get_num ();
+ if (row >= p->grid_size || col >= p->grid_size)
+ {
+ printf (EXCEED_GRID_SIZE);
+ char ch = getchar ();
+ return;
+ }
+ p->chars[row][col] = ' ';
+ print_puzzle (p);
+
+ char ch = getchar ();
+}
+
+/* add a down word to the grid */
+void do_add_down_word (Puzzle *p)
+{
+ print_puzzle (p);
+ if (p->grid_frozen == true)
+ {
+ printf (FROZEN_GRID);
+ char ch = getchar ();
+ return;
+ }
+ char wd[MAX_PUZZLE_SIZE];
+ int row, col;
+ printf (INPUT_WORD);
+ fgets (wd, MAX_PUZZLE_SIZE, stdin);
+ char *word = is_valid_word (wd);
+ if (word == NULL)
+ {
+ printf (INVALID_WORD);
+ char ch = getchar ();
+ return;
+ }
+ printf (INPUT_ROW);
+ row = get_num ();
+ printf (INPUT_COL);
+ col = get_num ();
+ if (row >= p->grid_size || col >= p->grid_size)
+ {
+ printf (EXCEED_GRID_SIZE);
+ char ch = getchar ();
+ return;
+ }
+ if (strlen (word) > (p->grid_size - row))
+ {
+ printf (WORD_TOO_LONG);
+ char ch = getchar ();
+ return;
+ }
+
+ for (int i = row; i < row + strlen(word); i ++)
+ p->chars[i][col] = toupper(word[i - row]);
+
+ print_puzzle (p);
+ char ch = getchar ();
+}
+
+/* add an across word to the grid */
+void do_add_across_word (Puzzle *p)
+{
+ print_puzzle (p);
+ if (p->grid_frozen == true)
+ {
+ printf (FROZEN_GRID);
+ char ch = getchar ();
+ return;
+ }
+ char wd[MAX_PUZZLE_SIZE];
+ int row, col;
+ printf (INPUT_WORD);
+ fgets (wd, MAX_PUZZLE_SIZE, stdin);
+ char *word = is_valid_word (wd);
+ if (word == NULL)
+ {
+ printf (INVALID_WORD);
+ char ch = getchar ();
+ return;
+ }
+ printf (INPUT_ROW);
+ row = get_num ();
+ printf (INPUT_COL);
+ col = get_num ();
+ if (row >= p->grid_size || col >= p->grid_size)
+ {
+ printf (EXCEED_GRID_SIZE);
+ char ch = getchar ();
+ return;
+ }
+
+ if (strlen (word) > (p->grid_size - col))
+ {
+ printf (WORD_TOO_LONG);
+ char ch = getchar ();
+ return;
+ }
+
+ for (int i = col; i < col+strlen (word); i ++)
+ p->chars[row][i] = toupper(word[i - col]);
+
+ print_puzzle (p);
+ char ch = getchar ();
+}
+/* confirm exit */
+bool do_confirm_exit ()
+{
+ printf (INPUT_CONFIRM_EXIT);
+ char res[3];
+ fgets (res, 3, stdin);
+ if (toupper(res[0]) == 'Y')
+ return true;
+ else
+ return false;
+}
+
+/* main loop for the puzzle editor */
+void puzzle_editor_loop (Puzzle *p, const char *filename)
+{
+ bool loop = true;
+ while (loop)
+ {
+ char puzzle_title[60];
+ sprintf (puzzle_title, "%s - %s", PUZZLE_MENU_TITLE, filename);
+ print_menu (WHITE, BLUE, puzzle_title, PUZZLE_EDIT_MENU, 15, 50);
+ printf (INPUT_CHOICE);
+ int ch = get_num ();
+ switch (ch)
+ {
+ case 1: print_puzzle (p);
+ char ch = getchar ();
+ break;
+ case 2: do_add_across_word (p);
+ break;
+ case 3: do_add_down_word (p);
+ break;
+ case 4: do_clear_cell (p);
+ break;
+ case 5: freeze_puzzle (p);
+ print_puzzle (p);
+ ch = getchar ();
+ break;
+ case 6: unfreeze_puzzle (p);
+ print_puzzle (p);
+ ch = getchar ();
+ break;
+ case 7: do_set_clue_word (p, ACROSS);
+ break;
+ case 8: do_set_clue_word (p, DOWN);
+ break;
+ case 9: save_puzzle (p, filename);
+ printf ("%s\n",FILE_SAVED);
+ ch = getchar ();
+ break;
+ case 10: do_set_master_password (p);
+ break;
+ case 11: do_set_solution_password (p);
+ break;
+ case 12: do_reset_puzzle (p);
+ break;
+ case 13: do_export_puzzle (p);
+ break;
+ case 14: do_export_clues (p);
+ break;
+ case 15: loop = ! do_confirm_exit ();
+ break;
+ }
+ }
+}
+
+/* open an existing puzzle */
+void do_open_puzzle (const char *filename)
+{
+ Puzzle p;
+ /* if no filename is provided, get it from command line */
+ if (filename == NULL)
+ {
+ printf (INPUT_FILE);
+ char fname[256];
+ fgets(fname, 256, stdin);
+ if (strlen (fname) == 1)
+ return;
+ filename = strtok (fname, "\n");
+ }
+
+ p = load_puzzle (filename);
+
+ if (strcmp (p.hashed_master_password, "\0") != 0)
+ {
+ char *passwd;
+ passwd = getpass (INPUT_PASSWORD);
+ if (strlen (passwd) == 0)
+ return;
+
+ if (verify_master_password (&p, (const char*) passwd))
+ puzzle_editor_loop (&p, filename);
+ else
+ {
+ printf (WRONG_PASSWORD);
+ char ch = getchar ();
+ }
+ }
+ else
+ puzzle_editor_loop (&p, filename);
+}
+
+/* create a new blank puzzle */
+void do_new_puzzle (char *filename, int size)
+{
+ Puzzle p;
+ /* if filename is not provided get it from command line */
+ if (filename == NULL)
+ {
+ printf (INPUT_FILE);
+ char fname[256];
+ fgets (fname, 256, stdin);
+ if (strlen (fname) == 1)
+ return;
+ filename = strtok (fname, "\n");
+ }
+ /* if no size is specified get it from command line */
+ if (size == -1)
+ {
+ printf (INPUT_GRID_SIZE);
+ size = get_num ();
+ }
+
+ if (size > MAX_PUZZLE_SIZE)
+ {
+ printf (EXCEED_MAX_GRID_SIZE);
+ char c = getchar ();
+ }
+ else
+ {
+ init_puzzle (&p, size);
+ puzzle_editor_loop (&p, filename);
+ }
+}
+
+/* The main loop of the program */
+int main_loop ()
+{
+ /* Print the main menu */
+ while (1)
+ {
+ print_menu (WHITE, BLUE, MAIN_MENU_TITLE, MAIN_MENU, 3, 50);
+ printf (INPUT_CHOICE);
+ int ch = get_num ();
+ switch (ch)
+ {
+ case 1: do_new_puzzle (NULL, -1);
+ break;
+ case 2: do_open_puzzle (NULL);
+ break;
+ case 3: exit (0);
+ }
+ }
+}
+
+int main (int argc, char* argv[])
+{
+ if (argc >= 2)
+ {
+ Puzzle p;
+ switch (argc)
+ {
+ case 2 : do_open_puzzle (argv[1]);
+ break;
+ case 4 : if (strcmp (argv[2], "new") == 0)
+ {
+ int grid_size = atoi (argv[3]);
+ do_new_puzzle (argv[1], grid_size);
+ break;
+ }
+ default: fprintf (stderr, USAGE_LINE_1, argv[0]);
+ fprintf (stderr, USAGE_LINE_2);
+ fprintf (stderr, USAGE_LINE_3);
+ exit (3);
+ }
+ }
+ return (main_loop ());
+}
--- /dev/null
+
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/harishankar/wordblah">
+ <file preprocess="xml-stripblanks">wordblah_player.glade</file>
+ <file>wordblah.svg</file>
+ </gresource>
+</gresources>
--- /dev/null
+#ifndef __WORDBLAH_H
+#define __WORDBLAH_H
+#define _XOPEN_SOURCE
+#include <unistd.h>
+#include <stdbool.h>
+#include <string.h>
+#include <ctype.h>
+#include <gd.h>
+#include <gdfontmb.h>
+#include <gdfontg.h>
+#include <zlib.h>
+#include <openssl/conf.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include "constantstrings.h"
+
+#define MAX_PUZZLE_SIZE 25
+#define MAX_CLUE_LENGTH 150
+#define GRID_PIXELS 37
+
+/* Enum to define terminal colours */
+enum COLOR {
+ BLACK = 0,
+ RED= 1,
+ GREEN=2,
+ YELLOW=3,
+ BLUE=4,
+ MAGENTA=5,
+ CYAN=6,
+ WHITE=7
+};
+
+enum ATTR {
+ NORMAL = 23,
+ BOLD=1
+};
+
+enum ORIENTATION {
+ ACROSS=1,
+ 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 */
+typedef struct {
+ char chars[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE];
+ int start_across_word[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE];
+ int start_down_word[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE];
+ String clue_across[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE];
+ String clue_down[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE];
+ int grid_size;
+ bool grid_frozen;
+ char hashed_master_password[256];
+ char hashed_solution_password[256];
+} Puzzle;
+
+/* The player data struct type - for the player app */
+typedef struct {
+ Puzzle puzzle;
+ char filename[65535];
+ bool is_loaded;
+ char char_ans[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE];
+ int cur_row;
+ int cur_col;
+ bool solution_revealed;
+ enum ORIENTATION current_movement;
+} MainPlayerData;
+
+/* compute the hash of a password */
+void digest_message(const unsigned char *message,
+ size_t message_len, unsigned char **digest, unsigned int *digest_len)
+{
+ EVP_MD_CTX *mdctx;
+
+ if((mdctx = EVP_MD_CTX_new()) == NULL)
+ goto err;
+
+ if(1 != EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL))
+ goto err;
+
+ if(1 != EVP_DigestUpdate(mdctx, message, message_len))
+ goto err;
+
+ if((*digest = (unsigned char *)
+ OPENSSL_malloc(EVP_MD_size(EVP_sha256()))) == NULL)
+ goto err;
+
+ if(1 != EVP_DigestFinal_ex(mdctx, *digest, digest_len))
+ goto err;
+
+ EVP_MD_CTX_free(mdctx);
+ return;
+err:
+ EVP_MD_CTX_free(mdctx);
+ ERR_print_errors_fp(stderr);
+ exit (2);
+}
+
+/* convert the hashed binary password to hexadecimal representation and
+ free the hashed binary password */
+void to_hexadecimal (char *hex, unsigned char *binary_pwd, unsigned int len)
+{
+ char buf[3];
+ /* keep reference to beginning of the hashed password */
+ unsigned char *binary_pw_begin = binary_pwd;
+ for (int i = 0; i < len; i ++)
+ {
+ sprintf (buf, "%02x", (*binary_pwd)&0xff);
+ strcat (hex, buf);
+ binary_pwd ++;
+ }
+ /* free the hashed password */
+ OPENSSL_free (binary_pw_begin);
+}
+
+/* get a number from the user */
+int get_num ()
+{
+ char s[5];
+ fgets (s, 5, stdin);
+ int n = atoi (s);
+ return n;
+}
+
+/* verify solution password */
+bool verify_solution_password (Puzzle *p, const char* password)
+{
+ /* no password set */
+ if (strcmp (p->hashed_solution_password, "\0") == 0)
+ return true;
+
+ /* hash the user input password and compare it with the stored password */
+ unsigned char* hashed_sol_password;
+ unsigned int len;
+ digest_message ((const unsigned char *)password, strlen(password),
+ &hashed_sol_password, &len);
+ char hashed_hex_pwd[256] = { (char) NULL };
+ to_hexadecimal (hashed_hex_pwd, hashed_sol_password, len);
+
+ if (strcmp (p->hashed_solution_password, hashed_hex_pwd) == 0)
+ return true;
+
+ return false;
+}
+
+
+/* verify master password */
+bool verify_master_password (Puzzle *p, const char* password)
+{
+ /* no password set */
+ if (strcmp (p->hashed_master_password, "\0") == 0)
+ return true;
+
+ /* hash the user input password and compare it with the stored password */
+ unsigned char* hashed_mas_password;
+ unsigned int len;
+ digest_message ((const unsigned char *)password, strlen(password),
+ &hashed_mas_password, &len);
+ char hashed_hex_pwd[256] = { (char) NULL };
+ to_hexadecimal (hashed_hex_pwd, hashed_mas_password, len);
+
+ if (strcmp (p->hashed_master_password, hashed_hex_pwd) == 0)
+ return true;
+
+ return false;
+}
+
+/* Set or reset solution password for puzzle */
+void set_solution_password (Puzzle *p, const char *password)
+{
+ /* if it is a null string, reset the password */
+ if (strcmp (password, "\0") == 0)
+ strcpy (p->hashed_solution_password, "\0");
+ else
+ {
+
+ unsigned char* hashedpwd;
+ unsigned int len;
+ digest_message ((const unsigned char *)password, strlen(password),
+ &hashedpwd, &len);
+ /* the hashedpwd contains binary data - we will convert it to
+ hexadecimal data and store in file */
+
+ to_hexadecimal (p->hashed_solution_password, hashedpwd, len);
+ }
+}
+
+/* Set or reset master password for puzzle */
+void set_master_password (Puzzle *p, const char *password)
+{
+ /* if it is a null string, reset the password */
+ if (strcmp (password, "\0") == 0)
+ strcpy (p->hashed_master_password, "\0");
+ else
+ {
+
+ unsigned char* hashedpwd;
+ unsigned int len;
+ digest_message ((const unsigned char *)password, strlen(password),
+ &hashedpwd, &len);
+ /* the hashedpwd contains binary data - we will convert it to
+ hexadecimal data and store in file */
+
+ to_hexadecimal (p->hashed_master_password, hashedpwd, len);
+ }
+}
+
+/* Output the clues to text file */
+void export_clues (Puzzle *p, const char *filename)
+{
+ FILE *outfile = fopen (filename, "w");
+ if (outfile == NULL)
+ {
+ fprintf (stderr, "%s\n", ERROR_WRITING_FILE);
+ exit (1);
+ }
+ /* first the across clues */
+ fprintf (outfile, "ACROSS CLUES\n");
+ for (int i = 0; i < p->grid_size; i ++)
+ {
+ for (int j = 0; j < p->grid_size; j ++)
+ {
+ if (p->start_across_word[i][j] != -1)
+ fprintf (outfile, "%d - %s\n", p->start_across_word[i][j],
+ p->clue_across[i][j]);
+ }
+ }
+ /* now the down clues */
+ fprintf (outfile, "DOWN CLUES\n");
+ for (int i = 0; i < p->grid_size; i ++)
+ {
+ for (int j = 0; j < p->grid_size; j ++)
+ {
+ if (p->start_down_word[i][j] != -1)
+ fprintf (outfile, "%d - %s\n", p->start_down_word[i][j],
+ p->clue_down[i][j]);
+ }
+ }
+ fclose (outfile);
+}
+
+/* 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 * GRID_PIXELS;
+ FILE * outfile = fopen (filename, "wb");
+ if (outfile == NULL)
+ {
+ fprintf (stderr, "%s\n", ERROR_WRITING_FILE);
+ exit (1);
+ }
+
+ gdImagePtr img = gdImageCreate (img_size, img_size);
+ gdImageColorAllocate (img, 255,255,255);
+ int black = gdImageColorAllocate (img, 0, 0, 0);
+ int blue = gdImageColorAllocate (img, 0, 0, 216);
+ gdFontPtr sm_fnt = gdFontGetMediumBold ();
+ gdFontPtr lg_fnt = gdFontGetGiant ();
+
+ for (int i = 0; i < p->grid_size; i ++)
+ {
+ for (int j = 0; j < p->grid_size; j++)
+ {
+ /* if it is a block, draw the black square */
+ if (p->chars[i][j] == '#')
+ 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*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 */
+ if (p->start_across_word[i][j] != -1 ||
+ p->start_down_word[i][j] != -1)
+ {
+ if (p->start_across_word[i][j] != -1)
+ {
+ char str[5];
+ sprintf (str, "%d", p->start_across_word[i][j]);
+ 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*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*GRID_PIXELS+15,
+ i*GRID_PIXELS+10, p->chars[i][j], black);
+ }
+ }
+ }
+ }
+
+ gdImagePng (img, outfile);
+ gdImageDestroy (img);
+ fclose (outfile);
+}
+
+/* Set the terminal colour */
+void set_color (enum COLOR fg, enum COLOR bg, enum ATTR at) {
+ printf ("\x1B[%d;%d;%dm", fg+30, bg+40, at);
+}
+
+/* Reset the terminal colour */
+void reset_color () {
+ printf ("\x1B[0m");
+}
+
+/* check if the prev row has a block or not */
+bool prev_row_block (Puzzle *p, int r, int c)
+{
+ if (r == 0)
+ return true;
+ if (p->chars[r-1][c] == '#')
+ return true;
+ return false;
+}
+
+/* check if the next row has a block or not */
+bool next_row_block (Puzzle *p, int r, int c)
+{
+ if (r == p->grid_size-1)
+ return true;
+ if (p->chars[r+1][c] == '#')
+ return true;
+ return false;
+}
+
+/* check if the prev col has a block or not */
+bool prev_col_block (Puzzle *p, int r, int c)
+{
+ if (c == 0)
+ return true;
+ if (p->chars[r][c-1] == '#')
+ return true;
+ return false;
+}
+
+/* check if the next col has a block or not */
+bool next_col_block (Puzzle *p, int r, int c)
+{
+ if (c == p->grid_size - 1)
+ return true;
+ if (p->chars[r][c+1] == '#')
+ return true;
+ return false;
+}
+
+/* check if previous row is blank or not */
+bool prev_row_blank (Puzzle *p, int r, int c)
+{
+ if (r == 0) return true;
+ if (p->chars[r-1][c] == ' ' || p->chars[r-1][c] == '#') return true;
+ return false;
+}
+/* check if next row is blank or not */
+bool next_row_blank (Puzzle *p, int r, int c)
+{
+ if (r == p->grid_size - 1) return true;
+ if (p->chars[r+1][c] == ' ' || p->chars[r+1][c] == '#') return true;
+ return false;
+}
+/* check if previous col is blank or not */
+bool prev_col_blank (Puzzle *p, int r, int c)
+{
+ if (c == 0) return true;
+ if (p->chars[r][c-1] == ' ' || p->chars[r][c-1] == '#') return true;
+ return false;
+}
+/* check if the next col is blank or not */
+bool next_col_blank (Puzzle *p, int r, int c)
+{
+ if (c == p->grid_size -1) return true;
+ if (p->chars[r][c+1] == ' ' || p->chars[r][c+1] == '#') return true;
+ return false;
+}
+
+/* set the current row/col to the beginning of word index (across or down) */
+void set_selection_to_word_start (MainPlayerData *app_data,
+ enum ORIENTATION orient, int word_index)
+{
+ for (int i = 0; i < app_data->puzzle.grid_size; i ++)
+ {
+ for (int j = 0; j < app_data->puzzle.grid_size; j ++)
+ {
+ if (orient == ACROSS &&
+ app_data->puzzle.start_across_word[i][j] == word_index)
+ {
+ app_data->current_movement = ACROSS;
+ app_data->cur_row = i;
+ app_data->cur_col = j;
+ break;
+ }
+ else if (orient == DOWN &&
+ app_data->puzzle.start_down_word[i][j] == word_index)
+ {
+ app_data->current_movement = DOWN;
+ app_data->cur_row = i;
+ app_data->cur_col = j;
+ break;
+ }
+ }
+ }
+}
+
+/* unfreeze the grid - make editing possible to change words */
+void unfreeze_puzzle (Puzzle *p)
+{
+ for (int i = 0; i < p->grid_size; i ++)
+ {
+ for (int j = 0; j < p->grid_size; j ++)
+ {
+ if (p->chars[i][j] == '#')
+ p->chars[i][j] = ' ';
+
+ p->start_across_word[i][j] = -1;
+ p->start_down_word[i][j] = -1;
+ }
+ }
+ p->grid_frozen = false;
+}
+
+/* freeze the grid - make editing impossible because it finalizes the
+ across and down words in the grid */
+void freeze_puzzle (Puzzle *p)
+{
+ int word_num = 1;
+ bool across_word_start, down_word_start;
+ for (int i = 0; i < p->grid_size; i ++)
+ {
+ for (int j = 0; j < p->grid_size; j++)
+ {
+ across_word_start = false;
+ down_word_start = false;
+ /* if it is a blank cell - cover it with a block */
+ if (p->chars[i][j] == ' ' || p->chars[i][j] == '#')
+ p->chars[i][j] = '#';
+ /* it is not a blank cell - check all possibilities */
+ else
+ {
+ bool prev_row = prev_row_blank (p, i, j);
+ bool next_row = next_row_blank (p, i, j);
+ bool prev_col = prev_col_blank (p, i, j);
+ bool next_col = next_col_blank (p, i, j);
+ if (prev_row && ! next_row)
+ down_word_start = true;
+ if (prev_col && ! next_col)
+ across_word_start = true;
+ }
+
+ if (across_word_start == true)
+ p->start_across_word[i][j] = word_num;
+ else
+ p->start_across_word[i][j] = -1;
+ if (down_word_start == true)
+ p->start_down_word[i][j] = word_num;
+ else
+ p->start_down_word[i][j] = -1;
+ if (across_word_start == true || down_word_start == true)
+ word_num ++;
+ }
+ }
+ p->grid_frozen = true;
+}
+
+/* reset the entire grid */
+void init_puzzle (Puzzle *p, int grid_size)
+{
+ p->grid_size = grid_size;
+ p->grid_frozen = false;
+ for (int i = 0; i < p->grid_size; i ++)
+ {
+ for (int j = 0; j < p->grid_size; j ++)
+ {
+ p->chars[i][j] = ' ';
+ p->start_across_word[i][j] = -1;
+ p->start_down_word[i][j] = -1;
+ strcpy (p->clue_across[i][j], "");
+ strcpy (p->clue_down[i][j], "");
+ }
+ }
+ strcpy (p->hashed_master_password, "\0");
+ strcpy (p->hashed_solution_password, "\0");
+
+}
+
+/* save the puzzle to a file */
+void save_puzzle (Puzzle *puzzle, const char* file)
+{
+ FILE *outfile;
+ /* First output the uncompressed contents to a temp file */
+ outfile = tmpfile ();
+ if (outfile == NULL)
+ {
+ fprintf (stderr, "%s\n", ERROR_WRITING_FILE);
+ exit (1);
+ }
+ /* grid size is the first field */
+ fprintf (outfile, "%d\n", puzzle->grid_size);
+ /* whether grid is frozen or not */
+ fprintf (outfile, "%d\n", puzzle->grid_frozen);
+ /* the hashed password */
+ fprintf (outfile, "%s\n", puzzle->hashed_master_password);
+ /* the hashed_solution_password */
+ fprintf (outfile, "%s\n", puzzle->hashed_solution_password);
+
+ /* First output the grid characters columns/rows */
+ for (int i = 0; i < puzzle->grid_size; i ++)
+ {
+ for (int j = 0; j < puzzle->grid_size; j ++)
+ fprintf (outfile, "%c", puzzle->chars[i][j]);
+ fprintf (outfile, "\n");
+ }
+
+ /* Next output the start across/down numbers */
+ for (int i = 0; i < puzzle->grid_size; i ++)
+ {
+ for (int j = 0; j < puzzle->grid_size; j++)
+ {
+ fprintf (outfile, "%d ", puzzle->start_across_word[i][j]);
+ fprintf (outfile, "%d ", puzzle->start_down_word[i][j]);
+ }
+ fprintf (outfile, "\n");
+ }
+
+ /* Output the across clues */
+ fprintf (outfile, "ACROSS\n");
+ /* Search the grid for across words */
+ for (int i = 0; i < puzzle->grid_size; i ++)
+ {
+ for (int j = 0; j < puzzle->grid_size; j++)
+ {
+ /* if it is an across word, then put the word index followed by
+ tab character (as separator) and the clue */
+ if (puzzle->start_across_word[i][j] != -1)
+ fprintf (outfile, "%d\t%s\n", puzzle->start_across_word[i][j],
+ puzzle->clue_across[i][j]);
+ }
+ }
+
+ /* Output the down clues */
+ fprintf (outfile, "DOWN\n");
+ /* Search the grid for down words */
+ for (int i = 0; i < puzzle->grid_size; i ++)
+ {
+ for (int j = 0; j < puzzle->grid_size; j++)
+ {
+ /* same as across word, put the word index followed by the tab
+ character and then the clue */
+ if (puzzle->start_down_word[i][j] != -1)
+ fprintf (outfile, "%d\t%s\n", puzzle->start_down_word[i][j],
+ puzzle->clue_down[i][j]);
+ }
+ }
+
+ /* Flush the buffer and rewind to beginning - to read and save into
+ gzip compressed file */
+ fflush (outfile);
+ fseek (outfile, 0, 0);
+
+ /* now compress the file and save it to destination file */
+ gzFile outdestfile = gzopen (file, "wb");
+ if (outdestfile == NULL)
+ {
+ fprintf (stderr, "%s\n", ERROR_WRITING_FILE);
+ fclose (outfile);
+ exit (1);
+ }
+ char buf[128];
+ int num = fread (buf, sizeof(char), sizeof(char)*128, outfile);
+ while (num > 0)
+ {
+ int res = gzwrite (outdestfile, buf, num*sizeof(char) );
+ if (res == 0)
+ {
+ fprintf (stderr, "%s %s\n", ERROR_WRITING_FILE, COMPRESSED);
+ fclose (outfile);
+ exit (1);
+ }
+ num = fread (buf, sizeof(char), sizeof(char)*128, outfile);
+ }
+ gzclose (outdestfile);
+ fclose (outfile);
+
+}
+
+/* read the puzzle from a file */
+Puzzle load_puzzle (const char* file)
+{
+ /* First open the GZip file */
+ gzFile insourcefile = gzopen (file, "rb");
+ if (insourcefile == NULL)
+ {
+ fprintf (stderr, "%s %s\n", ERROR_READING_FILE, COMPRESSED);
+ exit (1);
+ }
+ /* Open a temporary file to uncompress the contents */
+ FILE *infile = tmpfile ();
+ if (infile == NULL)
+ {
+ fprintf (stderr, "%s\n", ERROR_READING_FILE);
+ exit (1);
+ }
+ /* Put the uncompressed content to the temp file */
+ char buf[128];
+ int num = 0;
+ num = gzread (insourcefile, buf, 128);
+ while (num > 0)
+ {
+ int res = fwrite (buf, 1, num, infile);
+ if (res == 0)
+ {
+ fprintf (stderr, "%s\n", ERROR_READING_FILE);
+ fclose (infile);
+ gzclose (insourcefile);
+ exit (1);
+ }
+ num = gzread (insourcefile, buf, 128);
+ }
+ /* Close the gzip file */
+ gzclose (insourcefile);
+ /* Flush the temp file buffer and rewind to beginning */
+ fflush (infile);
+ fseek (infile, 0, 0);
+
+ /* Read the temporary file contents to the structure Puzzle */
+ Puzzle p;
+ char line[MAX_CLUE_LENGTH+10];
+ fgets (line, MAX_CLUE_LENGTH + 10, infile);
+ p.grid_size = atoi (line);
+ fgets (line, MAX_CLUE_LENGTH + 10, infile);
+ p.grid_frozen = atoi (line) == 0 ? false : true ;
+ fgets (line, MAX_CLUE_LENGTH + 10, infile);
+ if (strlen (line) != 1)
+ strcpy (p.hashed_master_password, strtok (line, "\n"));
+ else
+ strcpy (p.hashed_master_password, "\0");
+ fgets (line, MAX_CLUE_LENGTH + 10, infile);
+ if (strlen (line) != 1)
+ strcpy (p.hashed_solution_password, strtok (line, "\n"));
+ else
+ strcpy (p.hashed_solution_password, "\0");
+
+ /* read each character of the grid */
+ for (int i = 0; i < p.grid_size; i ++ )
+ {
+ fgets (line, MAX_CLUE_LENGTH + 10, infile);
+ for (int j = 0; j < p.grid_size; j ++)
+ p.chars[i][j] = line[j];
+ }
+ /* read the word numbers */
+ for (int i = 0; i < p.grid_size; i ++)
+ {
+ fgets (line, MAX_CLUE_LENGTH + 10, infile);
+ char *token = strtok (line, " ");
+ for (int j = 0; j < p.grid_size; j ++)
+ {
+ if (token != NULL)
+ p.start_across_word[i][j] = atoi (token);
+ token = strtok (NULL, " ");
+ if (token != NULL)
+ p.start_down_word[i][j] = atoi (token);
+ token = strtok (NULL, " ");
+ }
+ }
+ /* read the clues */
+ fgets (line, MAX_CLUE_LENGTH + 10, infile);
+
+ /* across clues */
+ char clues[100][MAX_CLUE_LENGTH];
+ int word_num[100];
+ int c = 0;
+ /* first read the across clues from file */
+ while (1)
+ {
+ fgets (line, MAX_CLUE_LENGTH + 10, infile);
+ /* if reached the end of across clues */
+ if (strcmp (line, "DOWN\n") == 0)
+ break;
+ word_num[c] = atoi (strtok (line, "\t"));
+ char *cl = strtok (NULL, "\n");
+ if (cl != NULL)
+ strcpy (clues[c], cl);
+ else
+ strcpy (clues[c], "\0");
+ c++;
+ }
+ /* set the clue to the correct cell in grid */
+ for (int i = 0; i < p.grid_size; i ++)
+ {
+ for (int j = 0; j < p.grid_size; j ++)
+ {
+ for (int r = 0; r < c; r ++)
+ if (p.start_across_word[i][j] == word_num[r])
+ strcpy (p.clue_across[i][j], clues[r]);
+ }
+ }
+
+ /* down clues */
+ c = 0;
+ while (fgets (line, MAX_CLUE_LENGTH + 10, infile))
+ {
+ word_num[c] = atoi (strtok (line, "\t"));
+ char* cl = strtok (NULL, "\n");
+ if (cl != NULL)
+ strcpy (clues[c], cl);
+ else
+ strcpy (clues[c], "\0");
+ c++;
+ }
+ for (int i = 0; i < p.grid_size; i ++)
+ {
+ for (int j = 0; j < p.grid_size; j ++)
+ {
+ for (int r = 0; r < c; r ++)
+ if (p.start_down_word[i][j] == word_num[r])
+ strcpy (p.clue_down[i][j], clues[r]);
+ }
+ }
+
+ fclose (infile);
+ return p;
+}
+
+/* display the puzzle */
+void print_puzzle (Puzzle *p)
+{
+ printf ("\n");
+ set_color (WHITE, CYAN, NORMAL);
+ printf (" ");
+ for (int i = 0; i < p->grid_size; i ++)
+ printf ("%3d", i);
+ reset_color ();
+ printf("\n");
+ for (int i = 0; i < p->grid_size; i ++)
+ {
+ set_color (WHITE, CYAN, NORMAL);
+ printf ("%3d ", i);
+ for (int j = 0; j < p->grid_size; j ++)
+ {
+ if (p->chars[i][j] == '#') {
+ set_color (WHITE, BLACK, NORMAL);
+ printf (" ");
+ }
+ else
+ {
+ if (p->start_across_word[i][j] != -1 ||
+ p->start_down_word[i][j] != -1)
+ {
+ set_color (BLUE, WHITE, NORMAL);
+ if (p->start_across_word[i][j] != -1)
+ printf ("%-2d", p->start_across_word[i][j]);
+ else
+ printf ("%-2d", p->start_down_word[i][j]);
+ }
+ else
+ {
+ set_color (BLACK, WHITE,NORMAL);
+ printf (" ");
+ }
+
+ set_color (BLACK, WHITE, BOLD);
+ printf ("%c", p->chars[i][j]);
+ }
+ reset_color ();
+ }
+ printf ("\n");
+ }
+ /* print the clues if set */
+ if (p->grid_frozen == true)
+ {
+ printf ("\x1B[1mACROSS - CLUES\x1B[0m\n");
+ for (int i = 0; i < p->grid_size; i ++)
+ {
+ for (int j = 0; j < p->grid_size; j ++)
+ {
+ if (p->start_across_word[i][j] != -1)
+ {
+ printf ("%d - %s; ", p->start_across_word[i][j],
+ p->clue_across[i][j]);
+ }
+ }
+ }
+ printf ("\n\x1B[1mDOWN - CLUES\x1B[0m\n");
+ for (int i = 0; i < p->grid_size; i ++)
+ {
+ for (int j = 0; j < p->grid_size; j ++)
+ {
+ if (p->start_down_word[i][j] != -1)
+ {
+ printf ("%d - %s; ", p->start_down_word[i][j],
+ p->clue_down[i][j]);
+ }
+ }
+ }
+ printf ("\n");
+ }
+}
+
+/* function to check if a word is valid or not */
+char* is_valid_word (char *word)
+{
+ if (word == NULL || strlen(word) == 0)
+ return NULL;
+ for (int i = 0; i < strlen (word) - 1; i ++)
+ if (! isalpha (word[i]))
+ return NULL;
+
+ return strtok (word, "\n");
+}
+
+
+/* function to set a clue for an across word */
+bool set_clue (Puzzle *p, String clue, int index, enum ORIENTATION order)
+{
+ for (int i = 0; i < p->grid_size; i ++)
+ {
+ for (int j = 0; j < p->grid_size; j ++)
+ {
+ if (order == ACROSS)
+ {
+ if (p->start_across_word[i][j] == index)
+ {
+ strcpy (p->clue_across[i][j], clue);
+ return true;
+ }
+ }
+ else if (order == DOWN)
+ {
+ if (p->start_down_word[i][j] == index)
+ {
+ strcpy (p->clue_down[i][j], clue);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+/* function to print a menu */
+void print_menu (enum COLOR fg, enum COLOR bg, const char* title,
+ char **items, int num_items, int padding)
+{
+ /* clear screen */
+ printf ("\e[1;1H\e[2J");
+ set_color (fg, bg, NORMAL);
+ printf ("\u2554");
+ for (int i = 0; i < padding; i ++)
+ printf ("\u2550");
+ printf ("\u2557");
+ reset_color (); printf ("\n");
+ set_color (fg, bg, NORMAL);
+ printf ("\u2551");
+ set_color (fg, bg, BOLD);
+ printf ("%-*s", padding, title);
+ reset_color ();
+ set_color (fg, bg, NORMAL);
+ printf ("\u2551");
+ reset_color (); printf ("\n");
+ set_color (fg, bg, NORMAL);
+ printf ("\u2560");
+ for (int i = 0; i < padding; i ++)
+ printf ("\u2550");
+ printf ("\u2563");
+ reset_color (); printf ("\n");
+ for (int i = 0; i < num_items; i ++)
+ {
+ set_color (fg, bg, NORMAL);
+ printf ("\u2551%-*s\u2551", padding, items[i]);
+ reset_color (); printf ("\n");
+ }
+ set_color (fg, bg, NORMAL);
+ printf ("\u255A");
+ for (int i = 0; i < padding; i ++)
+ printf ("\u2550");
+ printf ("\u255D");
+ reset_color (); printf ("\n");
+}
+
+/* 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);
+
+ 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 ++)
+ for (int j = 0; j < app_data->puzzle.grid_size; j ++)
+ app_data->char_ans[i][j] = ' ';
+
+}
+
+/* save the user grid to a file */
+void save_user_data (MainPlayerData *app_data, const char *filename)
+{
+ FILE *outfile;
+ outfile = fopen (filename, "wb");
+ if (outfile == NULL)
+ {
+ fprintf (stderr, ERROR_WRITING_FILE);
+ return;
+ }
+ fprintf (outfile, "%s\n", app_data->filename);
+ for (int i = 0; i < app_data->puzzle.grid_size; i ++)
+ {
+ for (int j = 0; j < app_data->puzzle.grid_size; j ++)
+ fprintf (outfile, "%c", app_data->char_ans[i][j]);
+ fprintf (outfile, "\n");
+ }
+
+ fclose (outfile);
+}
+
+/* load the user grid from a file */
+void load_user_data (MainPlayerData *app_data, const char *filename)
+{
+ FILE *infile;
+ infile = fopen (filename, "rb");
+ if (infile == NULL)
+ {
+ fprintf (stderr, "%s\n", ERROR_READING_FILE);
+ return;
+ }
+
+ char puzzle_file_name[65535];
+ fgets (puzzle_file_name, 65535, infile);
+ reset_player_data (app_data, strtok (puzzle_file_name, "\n"));
+
+ char line[MAX_PUZZLE_SIZE+10];
+ for (int i = 0; i < app_data->puzzle.grid_size; i ++)
+ {
+ fgets (line, MAX_PUZZLE_SIZE+10, infile);
+ for (int j = 0; j < app_data->puzzle.grid_size; j ++)
+ app_data->char_ans[i][j] = line[j];
+
+ }
+ fclose (infile);
+}
+
+/* in the player app, move the current selection index left or right */
+void move_current_col (MainPlayerData *app_data, 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;
+ }
+}
+
+/* in the player app move the current selection index up or down */
+void move_current_row (MainPlayerData *app_data, 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;
+ }
+}
+
+#endif
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="64px"
+ height="64px"
+ id="svg2985"
+ version="1.1"
+ inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"
+ sodipodi:docname="wordblox.svg"
+ inkscape:export-filename="/home/hari/Projects/GetAClue/resources/getaclue.png"
+ inkscape:export-xdpi="96"
+ inkscape:export-ydpi="96">
+ <defs
+ id="defs2987" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.6875"
+ inkscape:cx="373.4413"
+ inkscape:cy="-67.966563"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:document-units="px"
+ inkscape:grid-bbox="true"
+ inkscape:window-width="1366"
+ inkscape:window-height="704"
+ inkscape:window-x="0"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1" />
+ <metadata
+ id="metadata2990">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer">
+ <rect
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.50000000000000000;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="rect2993"
+ width="60.18182"
+ height="60.363636"
+ x="2.1818182"
+ y="2" />
+ <path
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.5;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ d="m 22.727273,2.5454545 0,59.6363635"
+ id="path2995"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.5;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ d="M 42.545455,2.3636367 42.545455,62"
+ id="path2995-2"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.5;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ d="M 2.5454545,22.363636 62,22.363636"
+ id="path3022"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.5;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ d="m 3.2727273,42.545455 58.9840237,0"
+ id="path3024"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.5;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ d="M 42.909095,11.727272 V 2 h 9.727272 9.727273 v 9.727272 9.727273 h -9.727273 -9.727272 z"
+ id="path3032"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.5;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ d="M 3.2727282,11.818181 V 2.5454545 h 9.7272728 9.727272 v 9.2727265 9.272727 H 13.000001 3.2727282 Z"
+ id="path3032-3"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.5;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ d="M 3.2727273,51.818181 V 42.545455 H 13 22.727272 v 9.272726 9.272727 H 13 3.2727273 Z"
+ id="path3032-3-7"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.5;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ d="M 42.545455,52.727273 V 43.454547 H 52.272728 62 V 52.727273 62 h -9.727272 -9.727273 z"
+ id="path3032-3-5"
+ inkscape:connector-curvature="0" />
+ </g>
+</svg>
--- /dev/null
+#include <gtk/gtk.h>
+
+#include "constantstrings.h"
+#include "wordblah_resource.c"
+#include "wordblah.h"
+
+GtkWidget *main_window;
+GtkListStore *across_store;
+GtkListStore *down_store;
+
+MainPlayerData app_data;
+
+/* update the clue items store */
+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);
+ }
+ }
+ }
+ }
+}
+
+/* slot for handling list down selection changed */
+gboolean on_down_list_selection_changed (GtkTreeSelection *sel,
+ GtkDrawingArea *area)
+{
+ GtkTreeIter iter;
+ GtkTreeModel* mod;
+
+ if (gtk_tree_selection_get_selected (sel, &mod, &iter))
+ {
+ guint sel_word_index;
+ gtk_tree_model_get (mod, &iter, 0, &sel_word_index, -1);
+ set_selection_to_word_start (&app_data, DOWN, sel_word_index);
+ }
+
+ 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);
+
+ return FALSE;
+
+}
+/* slot for handling list across selection changed */
+gboolean on_across_list_selection_changed (GtkTreeSelection *sel,
+ GtkDrawingArea *area)
+{
+ GtkTreeIter iter;
+ GtkTreeModel* mod;
+
+ if (gtk_tree_selection_get_selected (sel, &mod, &iter))
+ {
+ guint sel_word_index;
+ gtk_tree_model_get (mod, &iter, 0, &sel_word_index, -1);
+ set_selection_to_word_start (&app_data, ACROSS, sel_word_index);
+ }
+
+ 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);
+
+ return FALSE;
+
+}
+
+/* slot for handling mouse button event for puzzle drawing area */
+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;
+ int row = (event->y - 5) / GRID_PIXELS;
+
+ if (app_data.puzzle.chars[row][col] != '#')
+ {
+ if (row < app_data.puzzle.grid_size &&
+ col < app_data.puzzle.grid_size)
+ {
+ app_data.cur_row = row;
+ app_data.cur_col = col;
+
+ /* if it is a start of both across and down word, then
+ toggle the movement on clicking it */
+ if (app_data.puzzle.start_across_word[row][col] != -1 &&
+ app_data.puzzle.start_down_word[row][col] != -1)
+ {
+ if (app_data.current_movement == ACROSS)
+ app_data.current_movement = DOWN;
+ else
+ app_data.current_movement = ACROSS;
+ }
+ /* if it is only start of across word, make the current
+ movement across */
+ else if (app_data.puzzle.start_across_word[row][col] != -1)
+ app_data.current_movement = ACROSS;
+ /* else down movement */
+ else if (app_data.puzzle.start_down_word[row][col] != -1)
+ app_data.current_movement = DOWN;
+ }
+ }
+ }
+
+ 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 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)
+ {
+ /* 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);
+ app_data.current_movement = DOWN;
+ break;
+ case GDK_KEY_Up : move_current_row (&app_data, DIR_BACK);
+ app_data.current_movement = DOWN;
+ break;
+ case GDK_KEY_Right: move_current_col (&app_data, DIR_FORWARD);
+ app_data.current_movement = ACROSS;
+ break;
+ case GDK_KEY_Left : move_current_col (&app_data, DIR_BACK);
+ app_data.current_movement = ACROSS;
+ break;
+ case GDK_KEY_BackSpace:
+ case GDK_KEY_Delete:
+ case GDK_KEY_space:
+ app_data.char_ans[app_data.cur_row][app_data.cur_col] = ' ';
+ break;
+ case GDK_KEY_a :
+ case GDK_KEY_A :
+ app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'A';
+ break;
+ case GDK_KEY_b :
+ case GDK_KEY_B :
+ app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'B';
+ break;
+ case GDK_KEY_c :
+ case GDK_KEY_C :
+ app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'C';
+ break;
+ case GDK_KEY_d :
+ case GDK_KEY_D :
+ app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'D';
+ break;
+ case GDK_KEY_e :
+ case GDK_KEY_E :
+ app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'E';
+ break;
+ case GDK_KEY_f :
+ case GDK_KEY_F :
+ app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'F';
+ break;
+ case GDK_KEY_g :
+ case GDK_KEY_G :
+ app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'G';
+ break;
+ case GDK_KEY_h :
+ case GDK_KEY_H :
+ app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'H';
+ break;
+ case GDK_KEY_i :
+ case GDK_KEY_I :
+ app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'I';
+ break;
+ case GDK_KEY_j :
+ case GDK_KEY_J :
+ app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'J';
+ break;
+ case GDK_KEY_k :
+ case GDK_KEY_K :
+ app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'K';
+ break;
+ case GDK_KEY_l :
+ case GDK_KEY_L :
+ app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'L';
+ break;
+ case GDK_KEY_m :
+ case GDK_KEY_M :
+ app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'M';
+ break;
+ case GDK_KEY_n :
+ case GDK_KEY_N :
+ app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'N';
+ break;
+ case GDK_KEY_o :
+ case GDK_KEY_O :
+ app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'O';
+ break;
+ case GDK_KEY_p :
+ case GDK_KEY_P :
+ app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'P';
+ break;
+ case GDK_KEY_q :
+ case GDK_KEY_Q :
+ app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'Q';
+ break;
+ case GDK_KEY_r :
+ case GDK_KEY_R :
+ app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'R';
+ break;
+ case GDK_KEY_s :
+ case GDK_KEY_S :
+ app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'S';
+ break;
+ case GDK_KEY_t :
+ case GDK_KEY_T :
+ app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'T';
+ break;
+ case GDK_KEY_u :
+ case GDK_KEY_U :
+ app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'U';
+ break;
+ case GDK_KEY_v :
+ case GDK_KEY_V :
+ app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'V';
+ break;
+ case GDK_KEY_w :
+ case GDK_KEY_W :
+ app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'W';
+ break;
+ case GDK_KEY_x :
+ case GDK_KEY_X :
+ app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'X';
+ break;
+ case GDK_KEY_y :
+ case GDK_KEY_Y :
+ app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'Y';
+ break;
+ case GDK_KEY_z :
+ case GDK_KEY_Z :
+ app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'Z';
+ break;
+ default : return FALSE;
+
+ }
+ }
+
+ /* move the selection pointer to next row/col as per the current
+ movement orientation if it is not a key left right up or down */
+ if (event->keyval != GDK_KEY_Down && event->keyval != GDK_KEY_Up &&
+ event->keyval != GDK_KEY_Left && event->keyval != GDK_KEY_Right
+ && event->keyval != GDK_KEY_Delete)
+ {
+ /* if the current movement is an across movement */
+ if (app_data.current_movement == ACROSS)
+ {
+ /* if the next column is not blocking move the col */
+ if (event->keyval != GDK_KEY_BackSpace &&
+ next_col_block (&app_data.puzzle, app_data.cur_row,
+ app_data.cur_col) == false)
+ move_current_col (&app_data, DIR_FORWARD);
+ else if (event->keyval == GDK_KEY_BackSpace &&
+ prev_col_block (&app_data.puzzle, app_data.cur_row,
+ app_data.cur_col) == false)
+ move_current_col (&app_data, DIR_BACK);
+ }
+ /* current movement is a up/down movement */
+ else
+ {
+ if (event->keyval != GDK_KEY_BackSpace &&
+ next_row_block (&app_data.puzzle,
+ app_data.cur_row, app_data.cur_col) == false)
+ move_current_row (&app_data, DIR_FORWARD);
+ else if (event->keyval == GDK_KEY_BackSpace &&
+ prev_row_block (&app_data.puzzle,
+ app_data.cur_row, app_data.cur_col) == false)
+ move_current_row (&app_data, 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, 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 */
+ 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 ++)
+ {
+ /* 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;
+ 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;
+ }
+ }
+
+ 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
+ to black */
+ if (app_data.puzzle.chars[i][j] != '#')
+ gdk_cairo_set_source_rgba (cr, &colorback);
+
+ /* 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);
+ else
+ gdk_cairo_set_source_rgba (cr, &colorbacksel2);
+ }
+
+ 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);
+ }
+
+ /* 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);
+
+ cairo_set_font_size (cr, 16);
+ cairo_move_to (cr, j*GRID_PIXELS+GRID_PIXELS/2,
+ i*GRID_PIXELS+GRID_PIXELS-10);
+ char ctxt[3];
+ /* 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);
+
+ }
+ }
+ }
+
+ return FALSE;
+
+}
+
+/* slot for reveal solution menu */
+void on_menu_reveal_solution_activate (GtkMenuItem *item, GtkDrawingArea *area)
+{
+ /* if puzzle solution is password protected ask for the password */
+ if (strlen (app_data.puzzle.hashed_solution_password) > 0)
+ {
+ GtkBuilder *builder;
+ builder = gtk_builder_new ();
+
+ guint ret = gtk_builder_add_from_resource
+ (builder,
+ "/org/harishankar/wordblah/wordblah_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_solution_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 load grid state menu */
+void on_menu_load_grid_state_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 (main_window),
+ action,
+ "_Cancel",
+ GTK_RESPONSE_CANCEL,
+ "_Open",
+ GTK_RESPONSE_ACCEPT,
+ NULL
+ );
+
+ res = gtk_dialog_run (GTK_DIALOG (dialog));
+ if (res == GTK_RESPONSE_ACCEPT)
+ {
+ char *filename;
+ filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+ load_user_data (&app_data, filename);
+ g_free (filename);
+ }
+
+ gtk_widget_destroy (dialog);
+
+ 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);
+
+ update_clue_items ();
+}
+
+/* slot for save grid state menu */
+void on_menu_save_grid_state_activate (GtkMenuItem *item, gpointer *data)
+{
+ if (app_data.is_loaded == false)
+ return;
+
+ GtkWidget *dialog;
+ GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_SAVE;
+ gint res;
+ dialog = gtk_file_chooser_dialog_new (SAVE_FILE, GTK_WINDOW(main_window),
+ action,
+ "_Cancel",
+ GTK_RESPONSE_CANCEL,
+ "_Save",
+ GTK_RESPONSE_ACCEPT,
+ NULL);
+ res = gtk_dialog_run (GTK_DIALOG (dialog));
+ if (res == GTK_RESPONSE_ACCEPT)
+ {
+ char *filename;
+ filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+ save_user_data (&app_data, filename);
+ g_free (filename);
+ }
+
+ gtk_widget_destroy (dialog);
+}
+
+/* slot for exit menu */
+void on_menu_exit_activate (GtkMenuItem *item, gpointer data)
+{
+ gtk_main_quit ();
+}
+
+/* slot for open menu */
+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(main_window),
+ action,
+ "_Cancel",
+ GTK_RESPONSE_CANCEL,
+ "_Open",
+ GTK_RESPONSE_ACCEPT,
+ NULL);
+ res = gtk_dialog_run (GTK_DIALOG (dialog));
+ if (res == GTK_RESPONSE_ACCEPT)
+ {
+ char *filename;
+ filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog));
+ MainPlayerData temp;
+ reset_player_data (&temp, filename);
+
+ /* if the grid is not frozen then the game cannot be played */
+ if (temp.is_loaded == false)
+ {
+ GtkWidget *errordlg ;
+ errordlg = gtk_message_dialog_new (GTK_WINDOW(main_window),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE,
+ UNFROZEN_GRID_PLAYER);
+ gtk_dialog_run (GTK_DIALOG(errordlg));
+ gtk_widget_destroy (errordlg);
+ }
+ else
+ {
+ app_data = temp;
+ 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 ();
+ g_free (filename);
+ }
+
+ gtk_widget_destroy (dialog);
+}
+
+/* slot for about menu */
+void on_menu_about_activate (GtkMenuItem *item, gpointer data)
+{
+ const char *AUTHOR[] = {"V.Harishankar", NULL};
+ gtk_show_about_dialog (GTK_WINDOW(main_window), "authors",AUTHOR,
+ "program-name", PROGRAM_NAME,
+ "copyright", COPYRIGHT,
+ "comments", COMMENTS,
+ "website", WEBSITE,
+ "website-label", WEBSITE_LABEL,
+ "license-type", GTK_LICENSE_GPL_2_0,
+ "version", VERSION,
+ (char*)NULL);
+}
+
+int main (int argc, char *argv [])
+{
+ gtk_init (&argc, &argv);
+ GdkPixbuf *icon;
+ icon = gdk_pixbuf_new_from_resource
+ ("/org/harishankar/wordblah/wordblah.svg", NULL);
+ if (icon == NULL)
+ fprintf (stderr, ERROR_ICON);
+
+ GtkBuilder *builder;
+ builder = gtk_builder_new ();
+ guint ret = gtk_builder_add_from_resource (builder,
+ "/org/harishankar/wordblah/wordblah_player.glade", NULL);
+
+ app_data.is_loaded = false;
+
+ if (ret == 0)
+ {
+ fprintf (stderr, ERROR_WINDOW);
+ g_object_unref (builder);
+ return 1;
+ }
+ else
+ {
+ 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 (main_window != NULL)
+ {
+ gtk_window_set_default_icon (icon);
+
+ GtkWidget *draw_area = GTK_WIDGET (
+ gtk_builder_get_object(builder, "puzzle_area"));
+
+ /* make drawing area respond to mouse event */
+ gtk_widget_set_events(draw_area,
+ gtk_widget_get_events(draw_area)
+ | GDK_BUTTON_PRESS_MASK);
+
+ gtk_builder_connect_signals (builder, NULL);
+ g_object_unref (builder);
+ gtk_widget_show (main_window);
+ gtk_main ();
+ return 0;
+ }
+ else
+ {
+ g_object_unref (builder);
+ fprintf (stderr, ERROR_WINDOW);
+ return 1;
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.2 -->
+<interface>
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkListStore" id="store_across_clues">
+ <columns>
+ <!-- column-name ID -->
+ <column type="gint"/>
+ <!-- column-name Clue -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkListStore" id="store_down_clues">
+ <columns>
+ <!-- column-name ID -->
+ <column type="gint"/>
+ <!-- column-name Clue -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkWindow" id="main_window">
+ <property name="can_focus">False</property>
+ <property name="title" translatable="yes">Wordblah Player</property>
+ <property name="default_width">540</property>
+ <property name="default_height">400</property>
+ <signal name="destroy" handler="on_menu_exit_activate" swapped="no"/>
+ <child type="titlebar">
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkMenuBar" id="main_menu">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="menu_puzzle">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Puzzle</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="menu_open">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Open...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_menu_open_activate" object="puzzle_area" swapped="no"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparatorMenuItem">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menu_exit">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">E_xit</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_menu_exit_activate" swapped="no"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menu_grid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Grid</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <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_grid_state_activate" swapped="no"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menu_load_grid_state">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Load Grid State...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_menu_load_grid_state_activate" object="puzzle_area" swapped="no"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menu_view">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_View</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <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_reveal_solution_activate" object="puzzle_area" swapped="no"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menu_help">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Help</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="menu_about">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_About...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_menu_about_activate" swapped="no"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkPaned">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="width_request">80</property>
+ <property name="height_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkViewport">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hscroll_policy">natural</property>
+ <property name="vscroll_policy">natural</property>
+ <child>
+ <object class="GtkDrawingArea" id="puzzle_area">
+ <property name="visible">True</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="button-press-event" handler="on_puzzle_area_button_press_event" swapped="no"/>
+ <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>
+ <packing>
+ <property name="resize">True</property>
+ <property name="shrink">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkPaned">
+ <property name="visible">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">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">False</property>
+ <property name="model">store_across_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">
+ <signal name="changed" handler="on_across_list_selection_changed" object="puzzle_area" swapped="no"/>
+ </object>
+ </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>
+ <packing>
+ <property name="resize">True</property>
+ <property name="shrink">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">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">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">
+ <signal name="changed" handler="on_down_list_selection_changed" object="puzzle_area" swapped="no"/>
+ </object>
+ </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>
+ <packing>
+ <property name="resize">True</property>
+ <property name="shrink">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="resize">True</property>
+ <property name="shrink">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </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>
+++ /dev/null
-#include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
-#include <string.h>
-#include <ctype.h>
-#include <unistd.h>
-
-#include "wordblox.h"
-#include "constantstrings.h"
-
-/* export the clues to a text file */
-void do_export_clues (Puzzle *p)
-{
- if (p->grid_frozen == false)
- {
- printf (UNFROZEN_GRID);
- char ch = getchar ();
- return;
- }
- char fname[256];
- printf (INPUT_FILE);
- fgets (fname, 256, stdin);
- if (strlen(fname) == 1)
- return;
- char *filename = strtok (fname, "\n");
-
- export_clues (p, filename);
- printf (FILE_SAVED);
- char ch = getchar ();
-}
-
-/* export the puzzle to a png file */
-void do_export_puzzle (Puzzle *p)
-{
- if (p->grid_frozen == false)
- {
- printf (UNFROZEN_GRID);
- char ch = getchar ();
- return;
- }
- char fname[256];
- printf (INPUT_FILE);
- fgets (fname, 256, stdin);
- if (strlen (fname) == 1)
- return;
- char* filename = strtok (fname, "\n");
-
- printf (INPUT_EXPORT_ANSWERS);
- char ans[3];
- fgets (ans, 3, stdin);
- bool solution;
- solution = (toupper (ans[0]) == 'Y') ? true : false;
-
- export_grid_image (p, filename, solution);
- printf (FILE_SAVED);
- char ch = getchar ();
-}
-
-/* reset the grid */
-void do_reset_puzzle (Puzzle *p)
-{
- int grid_size = p->grid_size;
- printf (INPUT_CONFIRM_RESET);
- char conf[3];
- fgets (conf, 3, stdin);
- if (toupper (conf[0]) == 'Y')
- init_puzzle (p, grid_size);
-
- print_puzzle (p);
- char ch = getchar ();
-}
-
-/* set the solution password for the puzzle */
-void do_set_solution_password (Puzzle *p)
-{
- char* password;
- password = getpass (INPUT_PASSWORD);
- /* if empty reset the password to nothing */
- if (strlen(password) == 0)
- {
- set_solution_password (p, "\0");
- printf (SOLUTION_PASSWORD_RESET);
- char ch = getchar ();
- }
- /* set the password */
- else
- {
- set_solution_password (p, (const char* )password);
- printf (PASSWORD_SET);
- char ch = getchar ();
- }
-}
-/* set the master (editing) password for the puzzle */
-void do_set_master_password (Puzzle *p)
-{
- char* password;
- password = getpass (INPUT_PASSWORD);
- /* if empty reset the password to nothing */
- if (strlen(password) == 0)
- {
- set_master_password (p, "\0");
- printf (MASTER_PASSWORD_RESET);
- char ch = getchar ();
- }
- /* set the password */
- else
- {
- set_master_password (p, (const char* )password);
- printf (PASSWORD_SET);
- char ch = getchar ();
- }
-}
-
-/* set clue for a word - only for frozen grid */
-void do_set_clue_word (Puzzle *p, enum ORIENTATION orient)
-{
- print_puzzle (p);
- if (p->grid_frozen == false)
- {
- printf (UNFROZEN_GRID);
- char ch = getchar ();
- return;
- }
- int index;
- String clue;
- printf (INPUT_INDEX);
- index = get_num ();
- printf (INPUT_CLUE);
- fgets (clue, MAX_CLUE_LENGTH, stdin);
- if (strlen (clue) == 1)
- return;
- char* cl = strtok (clue, "\n");
-
- bool res;
- res = set_clue (p, cl, index, orient);
-
- if (res == false)
- {
- printf (NO_WORD_INDEX);
- char ch = getchar ();
- }
-}
-
-/* clear a cell in the grid */
-void do_clear_cell (Puzzle *p)
-{
- print_puzzle (p);
- if (p->grid_frozen == true)
- {
- printf (FROZEN_GRID);
- char ch = getchar ();
- return;
- }
- int row, col;
- printf (INPUT_ROW);
- row = get_num ();
- printf (INPUT_COL);
- col = get_num ();
- if (row >= p->grid_size || col >= p->grid_size)
- {
- printf (EXCEED_GRID_SIZE);
- char ch = getchar ();
- return;
- }
- p->chars[row][col] = ' ';
- print_puzzle (p);
-
- char ch = getchar ();
-}
-
-/* add a down word to the grid */
-void do_add_down_word (Puzzle *p)
-{
- print_puzzle (p);
- if (p->grid_frozen == true)
- {
- printf (FROZEN_GRID);
- char ch = getchar ();
- return;
- }
- char wd[MAX_PUZZLE_SIZE];
- int row, col;
- printf (INPUT_WORD);
- fgets (wd, MAX_PUZZLE_SIZE, stdin);
- char *word = is_valid_word (wd);
- if (word == NULL)
- {
- printf (INVALID_WORD);
- char ch = getchar ();
- return;
- }
- printf (INPUT_ROW);
- row = get_num ();
- printf (INPUT_COL);
- col = get_num ();
- if (row >= p->grid_size || col >= p->grid_size)
- {
- printf (EXCEED_GRID_SIZE);
- char ch = getchar ();
- return;
- }
- if (strlen (word) > (p->grid_size - row))
- {
- printf (WORD_TOO_LONG);
- char ch = getchar ();
- return;
- }
-
- for (int i = row; i < row + strlen(word); i ++)
- p->chars[i][col] = toupper(word[i - row]);
-
- print_puzzle (p);
- char ch = getchar ();
-}
-
-/* add an across word to the grid */
-void do_add_across_word (Puzzle *p)
-{
- print_puzzle (p);
- if (p->grid_frozen == true)
- {
- printf (FROZEN_GRID);
- char ch = getchar ();
- return;
- }
- char wd[MAX_PUZZLE_SIZE];
- int row, col;
- printf (INPUT_WORD);
- fgets (wd, MAX_PUZZLE_SIZE, stdin);
- char *word = is_valid_word (wd);
- if (word == NULL)
- {
- printf (INVALID_WORD);
- char ch = getchar ();
- return;
- }
- printf (INPUT_ROW);
- row = get_num ();
- printf (INPUT_COL);
- col = get_num ();
- if (row >= p->grid_size || col >= p->grid_size)
- {
- printf (EXCEED_GRID_SIZE);
- char ch = getchar ();
- return;
- }
-
- if (strlen (word) > (p->grid_size - col))
- {
- printf (WORD_TOO_LONG);
- char ch = getchar ();
- return;
- }
-
- for (int i = col; i < col+strlen (word); i ++)
- p->chars[row][i] = toupper(word[i - col]);
-
- print_puzzle (p);
- char ch = getchar ();
-}
-/* confirm exit */
-bool do_confirm_exit ()
-{
- printf (INPUT_CONFIRM_EXIT);
- char res[3];
- fgets (res, 3, stdin);
- if (toupper(res[0]) == 'Y')
- return true;
- else
- return false;
-}
-
-/* main loop for the puzzle editor */
-void puzzle_editor_loop (Puzzle *p, const char *filename)
-{
- bool loop = true;
- while (loop)
- {
- char puzzle_title[60];
- sprintf (puzzle_title, "%s - %s", PUZZLE_MENU_TITLE, filename);
- print_menu (WHITE, BLUE, puzzle_title, PUZZLE_EDIT_MENU, 15, 50);
- printf (INPUT_CHOICE);
- int ch = get_num ();
- switch (ch)
- {
- case 1: print_puzzle (p);
- char ch = getchar ();
- break;
- case 2: do_add_across_word (p);
- break;
- case 3: do_add_down_word (p);
- break;
- case 4: do_clear_cell (p);
- break;
- case 5: freeze_puzzle (p);
- print_puzzle (p);
- ch = getchar ();
- break;
- case 6: unfreeze_puzzle (p);
- print_puzzle (p);
- ch = getchar ();
- break;
- case 7: do_set_clue_word (p, ACROSS);
- break;
- case 8: do_set_clue_word (p, DOWN);
- break;
- case 9: save_puzzle (p, filename);
- printf ("%s\n",FILE_SAVED);
- ch = getchar ();
- break;
- case 10: do_set_master_password (p);
- break;
- case 11: do_set_solution_password (p);
- break;
- case 12: do_reset_puzzle (p);
- break;
- case 13: do_export_puzzle (p);
- break;
- case 14: do_export_clues (p);
- break;
- case 15: loop = ! do_confirm_exit ();
- break;
- }
- }
-}
-
-/* open an existing puzzle */
-void do_open_puzzle (const char *filename)
-{
- Puzzle p;
- /* if no filename is provided, get it from command line */
- if (filename == NULL)
- {
- printf (INPUT_FILE);
- char fname[256];
- fgets(fname, 256, stdin);
- if (strlen (fname) == 1)
- return;
- filename = strtok (fname, "\n");
- }
-
- p = load_puzzle (filename);
-
- if (strcmp (p.hashed_master_password, "\0") != 0)
- {
- char *passwd;
- passwd = getpass (INPUT_PASSWORD);
- if (strlen (passwd) == 0)
- return;
-
- if (verify_master_password (&p, (const char*) passwd))
- puzzle_editor_loop (&p, filename);
- else
- {
- printf (WRONG_PASSWORD);
- char ch = getchar ();
- }
- }
- else
- puzzle_editor_loop (&p, filename);
-}
-
-/* create a new blank puzzle */
-void do_new_puzzle (char *filename, int size)
-{
- Puzzle p;
- /* if filename is not provided get it from command line */
- if (filename == NULL)
- {
- printf (INPUT_FILE);
- char fname[256];
- fgets (fname, 256, stdin);
- if (strlen (fname) == 1)
- return;
- filename = strtok (fname, "\n");
- }
- /* if no size is specified get it from command line */
- if (size == -1)
- {
- printf (INPUT_GRID_SIZE);
- size = get_num ();
- }
-
- if (size > MAX_PUZZLE_SIZE)
- {
- printf (EXCEED_MAX_GRID_SIZE);
- char c = getchar ();
- }
- else
- {
- init_puzzle (&p, size);
- puzzle_editor_loop (&p, filename);
- }
-}
-
-/* The main loop of the program */
-int main_loop ()
-{
- /* Print the main menu */
- while (1)
- {
- print_menu (WHITE, BLUE, MAIN_MENU_TITLE, MAIN_MENU, 3, 50);
- printf (INPUT_CHOICE);
- int ch = get_num ();
- switch (ch)
- {
- case 1: do_new_puzzle (NULL, -1);
- break;
- case 2: do_open_puzzle (NULL);
- break;
- case 3: exit (0);
- }
- }
-}
-
-int main (int argc, char* argv[])
-{
- if (argc >= 2)
- {
- Puzzle p;
- switch (argc)
- {
- case 2 : do_open_puzzle (argv[1]);
- break;
- case 4 : if (strcmp (argv[2], "new") == 0)
- {
- int grid_size = atoi (argv[3]);
- do_new_puzzle (argv[1], grid_size);
- break;
- }
- default: fprintf (stderr, USAGE_LINE_1, argv[0]);
- fprintf (stderr, USAGE_LINE_2);
- fprintf (stderr, USAGE_LINE_3);
- exit (3);
- }
- }
- return (main_loop ());
-}
+++ /dev/null
-
-<?xml version="1.0" encoding="UTF-8"?>
-<gresources>
- <gresource prefix="/org/harishankar/wordblox">
- <file preprocess="xml-stripblanks">wordblox_player.glade</file>
- <file>wordblox.svg</file>
- </gresource>
-</gresources>
+++ /dev/null
-#ifndef __WORDBLOX_H
-#define __WORDBLOX_H
-#define _XOPEN_SOURCE
-#include <unistd.h>
-#include <stdbool.h>
-#include <string.h>
-#include <ctype.h>
-#include <gd.h>
-#include <gdfontmb.h>
-#include <gdfontg.h>
-#include <zlib.h>
-#include <openssl/conf.h>
-#include <openssl/evp.h>
-#include <openssl/err.h>
-#include "constantstrings.h"
-
-#define MAX_PUZZLE_SIZE 25
-#define MAX_CLUE_LENGTH 150
-#define GRID_PIXELS 37
-
-/* Enum to define terminal colours */
-enum COLOR {
- BLACK = 0,
- RED= 1,
- GREEN=2,
- YELLOW=3,
- BLUE=4,
- MAGENTA=5,
- CYAN=6,
- WHITE=7
-};
-
-enum ATTR {
- NORMAL = 23,
- BOLD=1
-};
-
-enum ORIENTATION {
- ACROSS=1,
- 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 */
-typedef struct {
- char chars[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE];
- int start_across_word[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE];
- int start_down_word[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE];
- String clue_across[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE];
- String clue_down[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE];
- int grid_size;
- bool grid_frozen;
- char hashed_master_password[256];
- char hashed_solution_password[256];
-} Puzzle;
-
-/* The player data struct type - for the player app */
-typedef struct {
- Puzzle puzzle;
- char filename[65535];
- bool is_loaded;
- char char_ans[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE];
- int cur_row;
- int cur_col;
- bool solution_revealed;
- enum ORIENTATION current_movement;
-} MainPlayerData;
-
-/* compute the hash of a password */
-void digest_message(const unsigned char *message,
- size_t message_len, unsigned char **digest, unsigned int *digest_len)
-{
- EVP_MD_CTX *mdctx;
-
- if((mdctx = EVP_MD_CTX_new()) == NULL)
- goto err;
-
- if(1 != EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL))
- goto err;
-
- if(1 != EVP_DigestUpdate(mdctx, message, message_len))
- goto err;
-
- if((*digest = (unsigned char *)
- OPENSSL_malloc(EVP_MD_size(EVP_sha256()))) == NULL)
- goto err;
-
- if(1 != EVP_DigestFinal_ex(mdctx, *digest, digest_len))
- goto err;
-
- EVP_MD_CTX_free(mdctx);
- return;
-err:
- EVP_MD_CTX_free(mdctx);
- ERR_print_errors_fp(stderr);
- exit (2);
-}
-
-/* convert the hashed binary password to hexadecimal representation and
- free the hashed binary password */
-void to_hexadecimal (char *hex, unsigned char *binary_pwd, unsigned int len)
-{
- char buf[3];
- /* keep reference to beginning of the hashed password */
- unsigned char *binary_pw_begin = binary_pwd;
- for (int i = 0; i < len; i ++)
- {
- sprintf (buf, "%02x", (*binary_pwd)&0xff);
- strcat (hex, buf);
- binary_pwd ++;
- }
- /* free the hashed password */
- OPENSSL_free (binary_pw_begin);
-}
-
-/* get a number from the user */
-int get_num ()
-{
- char s[5];
- fgets (s, 5, stdin);
- int n = atoi (s);
- return n;
-}
-
-/* verify solution password */
-bool verify_solution_password (Puzzle *p, const char* password)
-{
- /* no password set */
- if (strcmp (p->hashed_solution_password, "\0") == 0)
- return true;
-
- /* hash the user input password and compare it with the stored password */
- unsigned char* hashed_sol_password;
- unsigned int len;
- digest_message ((const unsigned char *)password, strlen(password),
- &hashed_sol_password, &len);
- char hashed_hex_pwd[256] = { (char) NULL };
- to_hexadecimal (hashed_hex_pwd, hashed_sol_password, len);
-
- if (strcmp (p->hashed_solution_password, hashed_hex_pwd) == 0)
- return true;
-
- return false;
-}
-
-
-/* verify master password */
-bool verify_master_password (Puzzle *p, const char* password)
-{
- /* no password set */
- if (strcmp (p->hashed_master_password, "\0") == 0)
- return true;
-
- /* hash the user input password and compare it with the stored password */
- unsigned char* hashed_mas_password;
- unsigned int len;
- digest_message ((const unsigned char *)password, strlen(password),
- &hashed_mas_password, &len);
- char hashed_hex_pwd[256] = { (char) NULL };
- to_hexadecimal (hashed_hex_pwd, hashed_mas_password, len);
-
- if (strcmp (p->hashed_master_password, hashed_hex_pwd) == 0)
- return true;
-
- return false;
-}
-
-/* Set or reset solution password for puzzle */
-void set_solution_password (Puzzle *p, const char *password)
-{
- /* if it is a null string, reset the password */
- if (strcmp (password, "\0") == 0)
- strcpy (p->hashed_solution_password, "\0");
- else
- {
-
- unsigned char* hashedpwd;
- unsigned int len;
- digest_message ((const unsigned char *)password, strlen(password),
- &hashedpwd, &len);
- /* the hashedpwd contains binary data - we will convert it to
- hexadecimal data and store in file */
-
- to_hexadecimal (p->hashed_solution_password, hashedpwd, len);
- }
-}
-
-/* Set or reset master password for puzzle */
-void set_master_password (Puzzle *p, const char *password)
-{
- /* if it is a null string, reset the password */
- if (strcmp (password, "\0") == 0)
- strcpy (p->hashed_master_password, "\0");
- else
- {
-
- unsigned char* hashedpwd;
- unsigned int len;
- digest_message ((const unsigned char *)password, strlen(password),
- &hashedpwd, &len);
- /* the hashedpwd contains binary data - we will convert it to
- hexadecimal data and store in file */
-
- to_hexadecimal (p->hashed_master_password, hashedpwd, len);
- }
-}
-
-/* Output the clues to text file */
-void export_clues (Puzzle *p, const char *filename)
-{
- FILE *outfile = fopen (filename, "w");
- if (outfile == NULL)
- {
- fprintf (stderr, "%s\n", ERROR_WRITING_FILE);
- exit (1);
- }
- /* first the across clues */
- fprintf (outfile, "ACROSS CLUES\n");
- for (int i = 0; i < p->grid_size; i ++)
- {
- for (int j = 0; j < p->grid_size; j ++)
- {
- if (p->start_across_word[i][j] != -1)
- fprintf (outfile, "%d - %s\n", p->start_across_word[i][j],
- p->clue_across[i][j]);
- }
- }
- /* now the down clues */
- fprintf (outfile, "DOWN CLUES\n");
- for (int i = 0; i < p->grid_size; i ++)
- {
- for (int j = 0; j < p->grid_size; j ++)
- {
- if (p->start_down_word[i][j] != -1)
- fprintf (outfile, "%d - %s\n", p->start_down_word[i][j],
- p->clue_down[i][j]);
- }
- }
- fclose (outfile);
-}
-
-/* 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 * GRID_PIXELS;
- FILE * outfile = fopen (filename, "wb");
- if (outfile == NULL)
- {
- fprintf (stderr, "%s\n", ERROR_WRITING_FILE);
- exit (1);
- }
-
- gdImagePtr img = gdImageCreate (img_size, img_size);
- gdImageColorAllocate (img, 255,255,255);
- int black = gdImageColorAllocate (img, 0, 0, 0);
- int blue = gdImageColorAllocate (img, 0, 0, 216);
- gdFontPtr sm_fnt = gdFontGetMediumBold ();
- gdFontPtr lg_fnt = gdFontGetGiant ();
-
- for (int i = 0; i < p->grid_size; i ++)
- {
- for (int j = 0; j < p->grid_size; j++)
- {
- /* if it is a block, draw the black square */
- if (p->chars[i][j] == '#')
- 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*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 */
- if (p->start_across_word[i][j] != -1 ||
- p->start_down_word[i][j] != -1)
- {
- if (p->start_across_word[i][j] != -1)
- {
- char str[5];
- sprintf (str, "%d", p->start_across_word[i][j]);
- 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*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*GRID_PIXELS+15,
- i*GRID_PIXELS+10, p->chars[i][j], black);
- }
- }
- }
- }
-
- gdImagePng (img, outfile);
- gdImageDestroy (img);
- fclose (outfile);
-}
-
-/* Set the terminal colour */
-void set_color (enum COLOR fg, enum COLOR bg, enum ATTR at) {
- printf ("\x1B[%d;%d;%dm", fg+30, bg+40, at);
-}
-
-/* Reset the terminal colour */
-void reset_color () {
- printf ("\x1B[0m");
-}
-
-/* check if the prev row has a block or not */
-bool prev_row_block (Puzzle *p, int r, int c)
-{
- if (r == 0)
- return true;
- if (p->chars[r-1][c] == '#')
- return true;
- return false;
-}
-
-/* check if the next row has a block or not */
-bool next_row_block (Puzzle *p, int r, int c)
-{
- if (r == p->grid_size-1)
- return true;
- if (p->chars[r+1][c] == '#')
- return true;
- return false;
-}
-
-/* check if the prev col has a block or not */
-bool prev_col_block (Puzzle *p, int r, int c)
-{
- if (c == 0)
- return true;
- if (p->chars[r][c-1] == '#')
- return true;
- return false;
-}
-
-/* check if the next col has a block or not */
-bool next_col_block (Puzzle *p, int r, int c)
-{
- if (c == p->grid_size - 1)
- return true;
- if (p->chars[r][c+1] == '#')
- return true;
- return false;
-}
-
-/* check if previous row is blank or not */
-bool prev_row_blank (Puzzle *p, int r, int c)
-{
- if (r == 0) return true;
- if (p->chars[r-1][c] == ' ' || p->chars[r-1][c] == '#') return true;
- return false;
-}
-/* check if next row is blank or not */
-bool next_row_blank (Puzzle *p, int r, int c)
-{
- if (r == p->grid_size - 1) return true;
- if (p->chars[r+1][c] == ' ' || p->chars[r+1][c] == '#') return true;
- return false;
-}
-/* check if previous col is blank or not */
-bool prev_col_blank (Puzzle *p, int r, int c)
-{
- if (c == 0) return true;
- if (p->chars[r][c-1] == ' ' || p->chars[r][c-1] == '#') return true;
- return false;
-}
-/* check if the next col is blank or not */
-bool next_col_blank (Puzzle *p, int r, int c)
-{
- if (c == p->grid_size -1) return true;
- if (p->chars[r][c+1] == ' ' || p->chars[r][c+1] == '#') return true;
- return false;
-}
-
-/* set the current row/col to the beginning of word index (across or down) */
-void set_selection_to_word_start (MainPlayerData *app_data,
- enum ORIENTATION orient, int word_index)
-{
- for (int i = 0; i < app_data->puzzle.grid_size; i ++)
- {
- for (int j = 0; j < app_data->puzzle.grid_size; j ++)
- {
- if (orient == ACROSS &&
- app_data->puzzle.start_across_word[i][j] == word_index)
- {
- app_data->current_movement = ACROSS;
- app_data->cur_row = i;
- app_data->cur_col = j;
- break;
- }
- else if (orient == DOWN &&
- app_data->puzzle.start_down_word[i][j] == word_index)
- {
- app_data->current_movement = DOWN;
- app_data->cur_row = i;
- app_data->cur_col = j;
- break;
- }
- }
- }
-}
-
-/* unfreeze the grid - make editing possible to change words */
-void unfreeze_puzzle (Puzzle *p)
-{
- for (int i = 0; i < p->grid_size; i ++)
- {
- for (int j = 0; j < p->grid_size; j ++)
- {
- if (p->chars[i][j] == '#')
- p->chars[i][j] = ' ';
-
- p->start_across_word[i][j] = -1;
- p->start_down_word[i][j] = -1;
- }
- }
- p->grid_frozen = false;
-}
-
-/* freeze the grid - make editing impossible because it finalizes the
- across and down words in the grid */
-void freeze_puzzle (Puzzle *p)
-{
- int word_num = 1;
- bool across_word_start, down_word_start;
- for (int i = 0; i < p->grid_size; i ++)
- {
- for (int j = 0; j < p->grid_size; j++)
- {
- across_word_start = false;
- down_word_start = false;
- /* if it is a blank cell - cover it with a block */
- if (p->chars[i][j] == ' ' || p->chars[i][j] == '#')
- p->chars[i][j] = '#';
- /* it is not a blank cell - check all possibilities */
- else
- {
- bool prev_row = prev_row_blank (p, i, j);
- bool next_row = next_row_blank (p, i, j);
- bool prev_col = prev_col_blank (p, i, j);
- bool next_col = next_col_blank (p, i, j);
- if (prev_row && ! next_row)
- down_word_start = true;
- if (prev_col && ! next_col)
- across_word_start = true;
- }
-
- if (across_word_start == true)
- p->start_across_word[i][j] = word_num;
- else
- p->start_across_word[i][j] = -1;
- if (down_word_start == true)
- p->start_down_word[i][j] = word_num;
- else
- p->start_down_word[i][j] = -1;
- if (across_word_start == true || down_word_start == true)
- word_num ++;
- }
- }
- p->grid_frozen = true;
-}
-
-/* reset the entire grid */
-void init_puzzle (Puzzle *p, int grid_size)
-{
- p->grid_size = grid_size;
- p->grid_frozen = false;
- for (int i = 0; i < p->grid_size; i ++)
- {
- for (int j = 0; j < p->grid_size; j ++)
- {
- p->chars[i][j] = ' ';
- p->start_across_word[i][j] = -1;
- p->start_down_word[i][j] = -1;
- strcpy (p->clue_across[i][j], "");
- strcpy (p->clue_down[i][j], "");
- }
- }
- strcpy (p->hashed_master_password, "\0");
- strcpy (p->hashed_solution_password, "\0");
-
-}
-
-/* save the puzzle to a file */
-void save_puzzle (Puzzle *puzzle, const char* file)
-{
- FILE *outfile;
- /* First output the uncompressed contents to a temp file */
- outfile = tmpfile ();
- if (outfile == NULL)
- {
- fprintf (stderr, "%s\n", ERROR_WRITING_FILE);
- exit (1);
- }
- /* grid size is the first field */
- fprintf (outfile, "%d\n", puzzle->grid_size);
- /* whether grid is frozen or not */
- fprintf (outfile, "%d\n", puzzle->grid_frozen);
- /* the hashed password */
- fprintf (outfile, "%s\n", puzzle->hashed_master_password);
- /* the hashed_solution_password */
- fprintf (outfile, "%s\n", puzzle->hashed_solution_password);
-
- /* First output the grid characters columns/rows */
- for (int i = 0; i < puzzle->grid_size; i ++)
- {
- for (int j = 0; j < puzzle->grid_size; j ++)
- fprintf (outfile, "%c", puzzle->chars[i][j]);
- fprintf (outfile, "\n");
- }
-
- /* Next output the start across/down numbers */
- for (int i = 0; i < puzzle->grid_size; i ++)
- {
- for (int j = 0; j < puzzle->grid_size; j++)
- {
- fprintf (outfile, "%d ", puzzle->start_across_word[i][j]);
- fprintf (outfile, "%d ", puzzle->start_down_word[i][j]);
- }
- fprintf (outfile, "\n");
- }
-
- /* Output the across clues */
- fprintf (outfile, "ACROSS\n");
- /* Search the grid for across words */
- for (int i = 0; i < puzzle->grid_size; i ++)
- {
- for (int j = 0; j < puzzle->grid_size; j++)
- {
- /* if it is an across word, then put the word index followed by
- tab character (as separator) and the clue */
- if (puzzle->start_across_word[i][j] != -1)
- fprintf (outfile, "%d\t%s\n", puzzle->start_across_word[i][j],
- puzzle->clue_across[i][j]);
- }
- }
-
- /* Output the down clues */
- fprintf (outfile, "DOWN\n");
- /* Search the grid for down words */
- for (int i = 0; i < puzzle->grid_size; i ++)
- {
- for (int j = 0; j < puzzle->grid_size; j++)
- {
- /* same as across word, put the word index followed by the tab
- character and then the clue */
- if (puzzle->start_down_word[i][j] != -1)
- fprintf (outfile, "%d\t%s\n", puzzle->start_down_word[i][j],
- puzzle->clue_down[i][j]);
- }
- }
-
- /* Flush the buffer and rewind to beginning - to read and save into
- gzip compressed file */
- fflush (outfile);
- fseek (outfile, 0, 0);
-
- /* now compress the file and save it to destination file */
- gzFile outdestfile = gzopen (file, "wb");
- if (outdestfile == NULL)
- {
- fprintf (stderr, "%s\n", ERROR_WRITING_FILE);
- fclose (outfile);
- exit (1);
- }
- char buf[128];
- int num = fread (buf, sizeof(char), sizeof(char)*128, outfile);
- while (num > 0)
- {
- int res = gzwrite (outdestfile, buf, num*sizeof(char) );
- if (res == 0)
- {
- fprintf (stderr, "%s %s\n", ERROR_WRITING_FILE, COMPRESSED);
- fclose (outfile);
- exit (1);
- }
- num = fread (buf, sizeof(char), sizeof(char)*128, outfile);
- }
- gzclose (outdestfile);
- fclose (outfile);
-
-}
-
-/* read the puzzle from a file */
-Puzzle load_puzzle (const char* file)
-{
- /* First open the GZip file */
- gzFile insourcefile = gzopen (file, "rb");
- if (insourcefile == NULL)
- {
- fprintf (stderr, "%s %s\n", ERROR_READING_FILE, COMPRESSED);
- exit (1);
- }
- /* Open a temporary file to uncompress the contents */
- FILE *infile = tmpfile ();
- if (infile == NULL)
- {
- fprintf (stderr, "%s\n", ERROR_READING_FILE);
- exit (1);
- }
- /* Put the uncompressed content to the temp file */
- char buf[128];
- int num = 0;
- num = gzread (insourcefile, buf, 128);
- while (num > 0)
- {
- int res = fwrite (buf, 1, num, infile);
- if (res == 0)
- {
- fprintf (stderr, "%s\n", ERROR_READING_FILE);
- fclose (infile);
- gzclose (insourcefile);
- exit (1);
- }
- num = gzread (insourcefile, buf, 128);
- }
- /* Close the gzip file */
- gzclose (insourcefile);
- /* Flush the temp file buffer and rewind to beginning */
- fflush (infile);
- fseek (infile, 0, 0);
-
- /* Read the temporary file contents to the structure Puzzle */
- Puzzle p;
- char line[MAX_CLUE_LENGTH+10];
- fgets (line, MAX_CLUE_LENGTH + 10, infile);
- p.grid_size = atoi (line);
- fgets (line, MAX_CLUE_LENGTH + 10, infile);
- p.grid_frozen = atoi (line) == 0 ? false : true ;
- fgets (line, MAX_CLUE_LENGTH + 10, infile);
- if (strlen (line) != 1)
- strcpy (p.hashed_master_password, strtok (line, "\n"));
- else
- strcpy (p.hashed_master_password, "\0");
- fgets (line, MAX_CLUE_LENGTH + 10, infile);
- if (strlen (line) != 1)
- strcpy (p.hashed_solution_password, strtok (line, "\n"));
- else
- strcpy (p.hashed_solution_password, "\0");
-
- /* read each character of the grid */
- for (int i = 0; i < p.grid_size; i ++ )
- {
- fgets (line, MAX_CLUE_LENGTH + 10, infile);
- for (int j = 0; j < p.grid_size; j ++)
- p.chars[i][j] = line[j];
- }
- /* read the word numbers */
- for (int i = 0; i < p.grid_size; i ++)
- {
- fgets (line, MAX_CLUE_LENGTH + 10, infile);
- char *token = strtok (line, " ");
- for (int j = 0; j < p.grid_size; j ++)
- {
- if (token != NULL)
- p.start_across_word[i][j] = atoi (token);
- token = strtok (NULL, " ");
- if (token != NULL)
- p.start_down_word[i][j] = atoi (token);
- token = strtok (NULL, " ");
- }
- }
- /* read the clues */
- fgets (line, MAX_CLUE_LENGTH + 10, infile);
-
- /* across clues */
- char clues[100][MAX_CLUE_LENGTH];
- int word_num[100];
- int c = 0;
- /* first read the across clues from file */
- while (1)
- {
- fgets (line, MAX_CLUE_LENGTH + 10, infile);
- /* if reached the end of across clues */
- if (strcmp (line, "DOWN\n") == 0)
- break;
- word_num[c] = atoi (strtok (line, "\t"));
- char *cl = strtok (NULL, "\n");
- if (cl != NULL)
- strcpy (clues[c], cl);
- else
- strcpy (clues[c], "\0");
- c++;
- }
- /* set the clue to the correct cell in grid */
- for (int i = 0; i < p.grid_size; i ++)
- {
- for (int j = 0; j < p.grid_size; j ++)
- {
- for (int r = 0; r < c; r ++)
- if (p.start_across_word[i][j] == word_num[r])
- strcpy (p.clue_across[i][j], clues[r]);
- }
- }
-
- /* down clues */
- c = 0;
- while (fgets (line, MAX_CLUE_LENGTH + 10, infile))
- {
- word_num[c] = atoi (strtok (line, "\t"));
- char* cl = strtok (NULL, "\n");
- if (cl != NULL)
- strcpy (clues[c], cl);
- else
- strcpy (clues[c], "\0");
- c++;
- }
- for (int i = 0; i < p.grid_size; i ++)
- {
- for (int j = 0; j < p.grid_size; j ++)
- {
- for (int r = 0; r < c; r ++)
- if (p.start_down_word[i][j] == word_num[r])
- strcpy (p.clue_down[i][j], clues[r]);
- }
- }
-
- fclose (infile);
- return p;
-}
-
-/* display the puzzle */
-void print_puzzle (Puzzle *p)
-{
- printf ("\n");
- set_color (WHITE, CYAN, NORMAL);
- printf (" ");
- for (int i = 0; i < p->grid_size; i ++)
- printf ("%3d", i);
- reset_color ();
- printf("\n");
- for (int i = 0; i < p->grid_size; i ++)
- {
- set_color (WHITE, CYAN, NORMAL);
- printf ("%3d ", i);
- for (int j = 0; j < p->grid_size; j ++)
- {
- if (p->chars[i][j] == '#') {
- set_color (WHITE, BLACK, NORMAL);
- printf (" ");
- }
- else
- {
- if (p->start_across_word[i][j] != -1 ||
- p->start_down_word[i][j] != -1)
- {
- set_color (BLUE, WHITE, NORMAL);
- if (p->start_across_word[i][j] != -1)
- printf ("%-2d", p->start_across_word[i][j]);
- else
- printf ("%-2d", p->start_down_word[i][j]);
- }
- else
- {
- set_color (BLACK, WHITE,NORMAL);
- printf (" ");
- }
-
- set_color (BLACK, WHITE, BOLD);
- printf ("%c", p->chars[i][j]);
- }
- reset_color ();
- }
- printf ("\n");
- }
- /* print the clues if set */
- if (p->grid_frozen == true)
- {
- printf ("\x1B[1mACROSS - CLUES\x1B[0m\n");
- for (int i = 0; i < p->grid_size; i ++)
- {
- for (int j = 0; j < p->grid_size; j ++)
- {
- if (p->start_across_word[i][j] != -1)
- {
- printf ("%d - %s; ", p->start_across_word[i][j],
- p->clue_across[i][j]);
- }
- }
- }
- printf ("\n\x1B[1mDOWN - CLUES\x1B[0m\n");
- for (int i = 0; i < p->grid_size; i ++)
- {
- for (int j = 0; j < p->grid_size; j ++)
- {
- if (p->start_down_word[i][j] != -1)
- {
- printf ("%d - %s; ", p->start_down_word[i][j],
- p->clue_down[i][j]);
- }
- }
- }
- printf ("\n");
- }
-}
-
-/* function to check if a word is valid or not */
-char* is_valid_word (char *word)
-{
- if (word == NULL || strlen(word) == 0)
- return NULL;
- for (int i = 0; i < strlen (word) - 1; i ++)
- if (! isalpha (word[i]))
- return NULL;
-
- return strtok (word, "\n");
-}
-
-
-/* function to set a clue for an across word */
-bool set_clue (Puzzle *p, String clue, int index, enum ORIENTATION order)
-{
- for (int i = 0; i < p->grid_size; i ++)
- {
- for (int j = 0; j < p->grid_size; j ++)
- {
- if (order == ACROSS)
- {
- if (p->start_across_word[i][j] == index)
- {
- strcpy (p->clue_across[i][j], clue);
- return true;
- }
- }
- else if (order == DOWN)
- {
- if (p->start_down_word[i][j] == index)
- {
- strcpy (p->clue_down[i][j], clue);
- return true;
- }
- }
- }
- }
- return false;
-}
-
-/* function to print a menu */
-void print_menu (enum COLOR fg, enum COLOR bg, const char* title,
- char **items, int num_items, int padding)
-{
- /* clear screen */
- printf ("\e[1;1H\e[2J");
- set_color (fg, bg, NORMAL);
- printf ("\u2554");
- for (int i = 0; i < padding; i ++)
- printf ("\u2550");
- printf ("\u2557");
- reset_color (); printf ("\n");
- set_color (fg, bg, NORMAL);
- printf ("\u2551");
- set_color (fg, bg, BOLD);
- printf ("%-*s", padding, title);
- reset_color ();
- set_color (fg, bg, NORMAL);
- printf ("\u2551");
- reset_color (); printf ("\n");
- set_color (fg, bg, NORMAL);
- printf ("\u2560");
- for (int i = 0; i < padding; i ++)
- printf ("\u2550");
- printf ("\u2563");
- reset_color (); printf ("\n");
- for (int i = 0; i < num_items; i ++)
- {
- set_color (fg, bg, NORMAL);
- printf ("\u2551%-*s\u2551", padding, items[i]);
- reset_color (); printf ("\n");
- }
- set_color (fg, bg, NORMAL);
- printf ("\u255A");
- for (int i = 0; i < padding; i ++)
- printf ("\u2550");
- printf ("\u255D");
- reset_color (); printf ("\n");
-}
-
-/* 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);
-
- 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 ++)
- for (int j = 0; j < app_data->puzzle.grid_size; j ++)
- app_data->char_ans[i][j] = ' ';
-
-}
-
-/* save the user grid to a file */
-void save_user_data (MainPlayerData *app_data, const char *filename)
-{
- FILE *outfile;
- outfile = fopen (filename, "wb");
- if (outfile == NULL)
- {
- fprintf (stderr, ERROR_WRITING_FILE);
- return;
- }
- fprintf (outfile, "%s\n", app_data->filename);
- for (int i = 0; i < app_data->puzzle.grid_size; i ++)
- {
- for (int j = 0; j < app_data->puzzle.grid_size; j ++)
- fprintf (outfile, "%c", app_data->char_ans[i][j]);
- fprintf (outfile, "\n");
- }
-
- fclose (outfile);
-}
-
-/* load the user grid from a file */
-void load_user_data (MainPlayerData *app_data, const char *filename)
-{
- FILE *infile;
- infile = fopen (filename, "rb");
- if (infile == NULL)
- {
- fprintf (stderr, "%s\n", ERROR_READING_FILE);
- return;
- }
-
- char puzzle_file_name[65535];
- fgets (puzzle_file_name, 65535, infile);
- reset_player_data (app_data, strtok (puzzle_file_name, "\n"));
-
- char line[MAX_PUZZLE_SIZE+10];
- for (int i = 0; i < app_data->puzzle.grid_size; i ++)
- {
- fgets (line, MAX_PUZZLE_SIZE+10, infile);
- for (int j = 0; j < app_data->puzzle.grid_size; j ++)
- app_data->char_ans[i][j] = line[j];
-
- }
- fclose (infile);
-}
-
-/* in the player app, move the current selection index left or right */
-void move_current_col (MainPlayerData *app_data, 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;
- }
-}
-
-/* in the player app move the current selection index up or down */
-void move_current_row (MainPlayerData *app_data, 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;
- }
-}
-
-#endif
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="64px"
- height="64px"
- id="svg2985"
- version="1.1"
- inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"
- sodipodi:docname="wordblox.svg"
- inkscape:export-filename="/home/hari/Projects/GetAClue/resources/getaclue.png"
- inkscape:export-xdpi="96"
- inkscape:export-ydpi="96">
- <defs
- id="defs2987" />
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="0.6875"
- inkscape:cx="373.4413"
- inkscape:cy="-67.966563"
- inkscape:current-layer="layer1"
- showgrid="true"
- inkscape:document-units="px"
- inkscape:grid-bbox="true"
- inkscape:window-width="1366"
- inkscape:window-height="704"
- inkscape:window-x="0"
- inkscape:window-y="27"
- inkscape:window-maximized="1" />
- <metadata
- id="metadata2990">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- id="layer1"
- inkscape:label="Layer 1"
- inkscape:groupmode="layer">
- <rect
- style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.50000000000000000;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
- id="rect2993"
- width="60.18182"
- height="60.363636"
- x="2.1818182"
- y="2" />
- <path
- style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.5;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
- d="m 22.727273,2.5454545 0,59.6363635"
- id="path2995"
- inkscape:connector-curvature="0" />
- <path
- style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.5;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
- d="M 42.545455,2.3636367 42.545455,62"
- id="path2995-2"
- inkscape:connector-curvature="0" />
- <path
- style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.5;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
- d="M 2.5454545,22.363636 62,22.363636"
- id="path3022"
- inkscape:connector-curvature="0" />
- <path
- style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.5;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
- d="m 3.2727273,42.545455 58.9840237,0"
- id="path3024"
- inkscape:connector-curvature="0" />
- <path
- style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.5;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
- d="M 42.909095,11.727272 V 2 h 9.727272 9.727273 v 9.727272 9.727273 h -9.727273 -9.727272 z"
- id="path3032"
- inkscape:connector-curvature="0" />
- <path
- style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.5;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
- d="M 3.2727282,11.818181 V 2.5454545 h 9.7272728 9.727272 v 9.2727265 9.272727 H 13.000001 3.2727282 Z"
- id="path3032-3"
- inkscape:connector-curvature="0" />
- <path
- style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.5;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
- d="M 3.2727273,51.818181 V 42.545455 H 13 22.727272 v 9.272726 9.272727 H 13 3.2727273 Z"
- id="path3032-3-7"
- inkscape:connector-curvature="0" />
- <path
- style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.5;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
- d="M 42.545455,52.727273 V 43.454547 H 52.272728 62 V 52.727273 62 h -9.727272 -9.727273 z"
- id="path3032-3-5"
- inkscape:connector-curvature="0" />
- </g>
-</svg>
+++ /dev/null
-#include <gtk/gtk.h>
-
-#include "constantstrings.h"
-#include "wordblox_resource.c"
-#include "wordblox.h"
-
-GtkWidget *main_window;
-GtkListStore *across_store;
-GtkListStore *down_store;
-
-MainPlayerData app_data;
-
-/* update the clue items store */
-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);
- }
- }
- }
- }
-}
-
-/* slot for handling list down selection changed */
-gboolean on_down_list_selection_changed (GtkTreeSelection *sel,
- GtkDrawingArea *area)
-{
- GtkTreeIter iter;
- GtkTreeModel* mod;
-
- if (gtk_tree_selection_get_selected (sel, &mod, &iter))
- {
- guint sel_word_index;
- gtk_tree_model_get (mod, &iter, 0, &sel_word_index, -1);
- set_selection_to_word_start (&app_data, DOWN, sel_word_index);
- }
-
- 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);
-
- return FALSE;
-
-}
-/* slot for handling list across selection changed */
-gboolean on_across_list_selection_changed (GtkTreeSelection *sel,
- GtkDrawingArea *area)
-{
- GtkTreeIter iter;
- GtkTreeModel* mod;
-
- if (gtk_tree_selection_get_selected (sel, &mod, &iter))
- {
- guint sel_word_index;
- gtk_tree_model_get (mod, &iter, 0, &sel_word_index, -1);
- set_selection_to_word_start (&app_data, ACROSS, sel_word_index);
- }
-
- 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);
-
- return FALSE;
-
-}
-
-/* slot for handling mouse button event for puzzle drawing area */
-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;
- int row = (event->y - 5) / GRID_PIXELS;
-
- if (app_data.puzzle.chars[row][col] != '#')
- {
- if (row < app_data.puzzle.grid_size &&
- col < app_data.puzzle.grid_size)
- {
- app_data.cur_row = row;
- app_data.cur_col = col;
-
- /* if it is a start of both across and down word, then
- toggle the movement on clicking it */
- if (app_data.puzzle.start_across_word[row][col] != -1 &&
- app_data.puzzle.start_down_word[row][col] != -1)
- {
- if (app_data.current_movement == ACROSS)
- app_data.current_movement = DOWN;
- else
- app_data.current_movement = ACROSS;
- }
- /* if it is only start of across word, make the current
- movement across */
- else if (app_data.puzzle.start_across_word[row][col] != -1)
- app_data.current_movement = ACROSS;
- /* else down movement */
- else if (app_data.puzzle.start_down_word[row][col] != -1)
- app_data.current_movement = DOWN;
- }
- }
- }
-
- 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 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)
- {
- /* 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);
- app_data.current_movement = DOWN;
- break;
- case GDK_KEY_Up : move_current_row (&app_data, DIR_BACK);
- app_data.current_movement = DOWN;
- break;
- case GDK_KEY_Right: move_current_col (&app_data, DIR_FORWARD);
- app_data.current_movement = ACROSS;
- break;
- case GDK_KEY_Left : move_current_col (&app_data, DIR_BACK);
- app_data.current_movement = ACROSS;
- break;
- case GDK_KEY_BackSpace:
- case GDK_KEY_Delete:
- case GDK_KEY_space:
- app_data.char_ans[app_data.cur_row][app_data.cur_col] = ' ';
- break;
- case GDK_KEY_a :
- case GDK_KEY_A :
- app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'A';
- break;
- case GDK_KEY_b :
- case GDK_KEY_B :
- app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'B';
- break;
- case GDK_KEY_c :
- case GDK_KEY_C :
- app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'C';
- break;
- case GDK_KEY_d :
- case GDK_KEY_D :
- app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'D';
- break;
- case GDK_KEY_e :
- case GDK_KEY_E :
- app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'E';
- break;
- case GDK_KEY_f :
- case GDK_KEY_F :
- app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'F';
- break;
- case GDK_KEY_g :
- case GDK_KEY_G :
- app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'G';
- break;
- case GDK_KEY_h :
- case GDK_KEY_H :
- app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'H';
- break;
- case GDK_KEY_i :
- case GDK_KEY_I :
- app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'I';
- break;
- case GDK_KEY_j :
- case GDK_KEY_J :
- app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'J';
- break;
- case GDK_KEY_k :
- case GDK_KEY_K :
- app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'K';
- break;
- case GDK_KEY_l :
- case GDK_KEY_L :
- app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'L';
- break;
- case GDK_KEY_m :
- case GDK_KEY_M :
- app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'M';
- break;
- case GDK_KEY_n :
- case GDK_KEY_N :
- app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'N';
- break;
- case GDK_KEY_o :
- case GDK_KEY_O :
- app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'O';
- break;
- case GDK_KEY_p :
- case GDK_KEY_P :
- app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'P';
- break;
- case GDK_KEY_q :
- case GDK_KEY_Q :
- app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'Q';
- break;
- case GDK_KEY_r :
- case GDK_KEY_R :
- app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'R';
- break;
- case GDK_KEY_s :
- case GDK_KEY_S :
- app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'S';
- break;
- case GDK_KEY_t :
- case GDK_KEY_T :
- app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'T';
- break;
- case GDK_KEY_u :
- case GDK_KEY_U :
- app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'U';
- break;
- case GDK_KEY_v :
- case GDK_KEY_V :
- app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'V';
- break;
- case GDK_KEY_w :
- case GDK_KEY_W :
- app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'W';
- break;
- case GDK_KEY_x :
- case GDK_KEY_X :
- app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'X';
- break;
- case GDK_KEY_y :
- case GDK_KEY_Y :
- app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'Y';
- break;
- case GDK_KEY_z :
- case GDK_KEY_Z :
- app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'Z';
- break;
- default : return FALSE;
-
- }
- }
-
- /* move the selection pointer to next row/col as per the current
- movement orientation if it is not a key left right up or down */
- if (event->keyval != GDK_KEY_Down && event->keyval != GDK_KEY_Up &&
- event->keyval != GDK_KEY_Left && event->keyval != GDK_KEY_Right
- && event->keyval != GDK_KEY_Delete)
- {
- /* if the current movement is an across movement */
- if (app_data.current_movement == ACROSS)
- {
- /* if the next column is not blocking move the col */
- if (event->keyval != GDK_KEY_BackSpace &&
- next_col_block (&app_data.puzzle, app_data.cur_row,
- app_data.cur_col) == false)
- move_current_col (&app_data, DIR_FORWARD);
- else if (event->keyval == GDK_KEY_BackSpace &&
- prev_col_block (&app_data.puzzle, app_data.cur_row,
- app_data.cur_col) == false)
- move_current_col (&app_data, DIR_BACK);
- }
- /* current movement is a up/down movement */
- else
- {
- if (event->keyval != GDK_KEY_BackSpace &&
- next_row_block (&app_data.puzzle,
- app_data.cur_row, app_data.cur_col) == false)
- move_current_row (&app_data, DIR_FORWARD);
- else if (event->keyval == GDK_KEY_BackSpace &&
- prev_row_block (&app_data.puzzle,
- app_data.cur_row, app_data.cur_col) == false)
- move_current_row (&app_data, 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, 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 */
- 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 ++)
- {
- /* 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;
- 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;
- }
- }
-
- 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
- to black */
- if (app_data.puzzle.chars[i][j] != '#')
- gdk_cairo_set_source_rgba (cr, &colorback);
-
- /* 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);
- else
- gdk_cairo_set_source_rgba (cr, &colorbacksel2);
- }
-
- 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);
- }
-
- /* 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);
-
- cairo_set_font_size (cr, 16);
- cairo_move_to (cr, j*GRID_PIXELS+GRID_PIXELS/2,
- i*GRID_PIXELS+GRID_PIXELS-10);
- char ctxt[3];
- /* 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);
-
- }
- }
- }
-
- return FALSE;
-
-}
-
-/* slot for reveal solution menu */
-void on_menu_reveal_solution_activate (GtkMenuItem *item, GtkDrawingArea *area)
-{
- /* if puzzle solution is password protected ask for the password */
- if (strlen (app_data.puzzle.hashed_solution_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_solution_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 load grid state menu */
-void on_menu_load_grid_state_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 (main_window),
- action,
- "_Cancel",
- GTK_RESPONSE_CANCEL,
- "_Open",
- GTK_RESPONSE_ACCEPT,
- NULL
- );
-
- res = gtk_dialog_run (GTK_DIALOG (dialog));
- if (res == GTK_RESPONSE_ACCEPT)
- {
- char *filename;
- filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
- load_user_data (&app_data, filename);
- g_free (filename);
- }
-
- gtk_widget_destroy (dialog);
-
- 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);
-
- update_clue_items ();
-}
-
-/* slot for save grid state menu */
-void on_menu_save_grid_state_activate (GtkMenuItem *item, gpointer *data)
-{
- if (app_data.is_loaded == false)
- return;
-
- GtkWidget *dialog;
- GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_SAVE;
- gint res;
- dialog = gtk_file_chooser_dialog_new (SAVE_FILE, GTK_WINDOW(main_window),
- action,
- "_Cancel",
- GTK_RESPONSE_CANCEL,
- "_Save",
- GTK_RESPONSE_ACCEPT,
- NULL);
- res = gtk_dialog_run (GTK_DIALOG (dialog));
- if (res == GTK_RESPONSE_ACCEPT)
- {
- char *filename;
- filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
- save_user_data (&app_data, filename);
- g_free (filename);
- }
-
- gtk_widget_destroy (dialog);
-}
-
-/* slot for exit menu */
-void on_menu_exit_activate (GtkMenuItem *item, gpointer data)
-{
- gtk_main_quit ();
-}
-
-/* slot for open menu */
-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(main_window),
- action,
- "_Cancel",
- GTK_RESPONSE_CANCEL,
- "_Open",
- GTK_RESPONSE_ACCEPT,
- NULL);
- res = gtk_dialog_run (GTK_DIALOG (dialog));
- if (res == GTK_RESPONSE_ACCEPT)
- {
- char *filename;
- filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog));
- MainPlayerData temp;
- reset_player_data (&temp, filename);
-
- /* if the grid is not frozen then the game cannot be played */
- if (temp.is_loaded == false)
- {
- GtkWidget *errordlg ;
- errordlg = gtk_message_dialog_new (GTK_WINDOW(main_window),
- GTK_DIALOG_DESTROY_WITH_PARENT,
- GTK_MESSAGE_ERROR,
- GTK_BUTTONS_CLOSE,
- UNFROZEN_GRID_PLAYER);
- gtk_dialog_run (GTK_DIALOG(errordlg));
- gtk_widget_destroy (errordlg);
- }
- else
- {
- app_data = temp;
- 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 ();
- g_free (filename);
- }
-
- gtk_widget_destroy (dialog);
-}
-
-/* slot for about menu */
-void on_menu_about_activate (GtkMenuItem *item, gpointer data)
-{
- const char *AUTHOR[] = {"V.Harishankar", NULL};
- gtk_show_about_dialog (GTK_WINDOW(main_window), "authors",AUTHOR,
- "program-name", PROGRAM_NAME,
- "copyright", COPYRIGHT,
- "comments", COMMENTS,
- "website", WEBSITE,
- "website-label", WEBSITE_LABEL,
- "license-type", GTK_LICENSE_GPL_2_0,
- "version", VERSION,
- (char*)NULL);
-}
-
-int main (int argc, char *argv [])
-{
- gtk_init (&argc, &argv);
- GdkPixbuf *icon;
- icon = gdk_pixbuf_new_from_resource
- ("/org/harishankar/wordblox/wordblox.svg", NULL);
- if (icon == NULL)
- fprintf (stderr, ERROR_ICON);
-
- GtkBuilder *builder;
- builder = gtk_builder_new ();
- guint ret = gtk_builder_add_from_resource (builder,
- "/org/harishankar/wordblox/wordblox_player.glade", NULL);
-
- app_data.is_loaded = false;
-
- if (ret == 0)
- {
- fprintf (stderr, ERROR_WINDOW);
- g_object_unref (builder);
- return 1;
- }
- else
- {
- 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 (main_window != NULL)
- {
- gtk_window_set_default_icon (icon);
-
- GtkWidget *draw_area = GTK_WIDGET (
- gtk_builder_get_object(builder, "puzzle_area"));
-
- /* make drawing area respond to mouse event */
- gtk_widget_set_events(draw_area,
- gtk_widget_get_events(draw_area)
- | GDK_BUTTON_PRESS_MASK);
-
- gtk_builder_connect_signals (builder, NULL);
- g_object_unref (builder);
- gtk_widget_show (main_window);
- gtk_main ();
- return 0;
- }
- else
- {
- g_object_unref (builder);
- fprintf (stderr, ERROR_WINDOW);
- return 1;
- }
- }
-}
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.22.2 -->
-<interface>
- <requires lib="gtk+" version="3.20"/>
- <object class="GtkListStore" id="store_across_clues">
- <columns>
- <!-- column-name ID -->
- <column type="gint"/>
- <!-- column-name Clue -->
- <column type="gchararray"/>
- </columns>
- </object>
- <object class="GtkListStore" id="store_down_clues">
- <columns>
- <!-- column-name ID -->
- <column type="gint"/>
- <!-- column-name Clue -->
- <column type="gchararray"/>
- </columns>
- </object>
- <object class="GtkWindow" id="main_window">
- <property name="can_focus">False</property>
- <property name="title" translatable="yes">Wordblox Player</property>
- <property name="default_width">540</property>
- <property name="default_height">400</property>
- <signal name="destroy" handler="on_menu_exit_activate" swapped="no"/>
- <child type="titlebar">
- <placeholder/>
- </child>
- <child>
- <object class="GtkBox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="orientation">vertical</property>
- <child>
- <object class="GtkMenuBar" id="main_menu">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <object class="GtkMenuItem" id="menu_puzzle">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">_Puzzle</property>
- <property name="use_underline">True</property>
- <child type="submenu">
- <object class="GtkMenu">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <object class="GtkMenuItem" id="menu_open">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">_Open...</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="on_menu_open_activate" object="puzzle_area" swapped="no"/>
- </object>
- </child>
- <child>
- <object class="GtkSeparatorMenuItem">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- </object>
- </child>
- <child>
- <object class="GtkMenuItem" id="menu_exit">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">E_xit</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="on_menu_exit_activate" swapped="no"/>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="GtkMenuItem" id="menu_grid">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">_Grid</property>
- <property name="use_underline">True</property>
- <child type="submenu">
- <object class="GtkMenu">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <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_grid_state_activate" swapped="no"/>
- </object>
- </child>
- <child>
- <object class="GtkMenuItem" id="menu_load_grid_state">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">_Load Grid State...</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="on_menu_load_grid_state_activate" object="puzzle_area" swapped="no"/>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="GtkMenuItem" id="menu_view">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">_View</property>
- <property name="use_underline">True</property>
- <child type="submenu">
- <object class="GtkMenu">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <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_reveal_solution_activate" object="puzzle_area" swapped="no"/>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="GtkMenuItem" id="menu_help">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">_Help</property>
- <property name="use_underline">True</property>
- <child type="submenu">
- <object class="GtkMenu">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <object class="GtkMenuItem" id="menu_about">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">_About...</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="on_menu_about_activate" swapped="no"/>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkPaned">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <child>
- <object class="GtkScrolledWindow">
- <property name="width_request">80</property>
- <property name="height_request">80</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="shadow_type">in</property>
- <child>
- <object class="GtkViewport">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="hscroll_policy">natural</property>
- <property name="vscroll_policy">natural</property>
- <child>
- <object class="GtkDrawingArea" id="puzzle_area">
- <property name="visible">True</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="button-press-event" handler="on_puzzle_area_button_press_event" swapped="no"/>
- <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>
- <packing>
- <property name="resize">True</property>
- <property name="shrink">True</property>
- </packing>
- </child>
- <child>
- <object class="GtkPaned">
- <property name="visible">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">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">False</property>
- <property name="model">store_across_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">
- <signal name="changed" handler="on_across_list_selection_changed" object="puzzle_area" swapped="no"/>
- </object>
- </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>
- <packing>
- <property name="resize">True</property>
- <property name="shrink">True</property>
- </packing>
- </child>
- <child>
- <object class="GtkScrolledWindow">
- <property name="visible">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">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">
- <signal name="changed" handler="on_down_list_selection_changed" object="puzzle_area" swapped="no"/>
- </object>
- </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>
- <packing>
- <property name="resize">True</property>
- <property name="shrink">True</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="resize">True</property>
- <property name="shrink">True</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </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>