From a0e5db4497c0827879e0817e9b65586dc729352d Mon Sep 17 00:00:00 2001 From: David Hampton Date: Sun, 18 Mar 2007 17:37:53 +0000 Subject: [PATCH] Add GUID numbers to check formats. Detect duplicate check format files (by guid) and show a warning dialog to the user. Remember the users selected check format by GUID and not by index. Add text clipping when using GnomePrint. git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@15734 57a11ea4-9604-0410-9ed3-97b8803252fd --- checks/deluxe.chk | 5 +- checks/gnomeprint/deluxe.chk | 5 +- checks/gnomeprint/quicken.chk | 5 +- checks/gnomeprint/quicken_wallet.chk | 5 +- checks/quicken.chk | 5 +- checks/quicken_wallet.chk | 5 +- src/gnome/dialog-print-check.c | 266 ++++++++++++++++++++++----- 7 files changed, 236 insertions(+), 60 deletions(-) diff --git a/checks/deluxe.chk b/checks/deluxe.chk index 529132b9a0..f5159b2893 100644 --- a/checks/deluxe.chk +++ b/checks/deluxe.chk @@ -1,9 +1,10 @@ [Top] +Guid = 54a0fef9-0b7e-462b-8af3-a5f4f328f7bc Title = Deluxe(tm) Personal Checks US-Letter Rotation = -90.0 Translation = 4;492 -Show_Grid = 0 -Show_Boxes = 0 +Show_Grid = false +Show_Boxes = false [Check Items] Type_1 = PAYEE diff --git a/checks/gnomeprint/deluxe.chk b/checks/gnomeprint/deluxe.chk index bc1be76026..21d23b9e89 100644 --- a/checks/gnomeprint/deluxe.chk +++ b/checks/gnomeprint/deluxe.chk @@ -1,9 +1,10 @@ [Top] +Guid = 54a0fef9-0b7e-462b-8af3-a5f4f328f7bc Title = Deluxe(tm) Personal Checks US-Letter Rotation = 90 Translation = 232;300 -Show_Grid = 0 -Show_Boxes = 0 +Show_Grid = false +Show_Boxes = false [Check Items] Type_1 = PAYEE diff --git a/checks/gnomeprint/quicken.chk b/checks/gnomeprint/quicken.chk index f7e4e412f8..51a41f8539 100644 --- a/checks/gnomeprint/quicken.chk +++ b/checks/gnomeprint/quicken.chk @@ -1,9 +1,10 @@ [Top] +Guid = 67b144d1-96a5-48d5-9337-0e1083bbf229 Title = Quicken/QuickBooks (tm) US-Letter Rotation = 0.0 Translation = 0.0;0.0 -Show_Grid = 0 -Show_Boxes = 0 +Show_Grid = false +Show_Boxes = false [Check Positions] Height = 252.0 diff --git a/checks/gnomeprint/quicken_wallet.chk b/checks/gnomeprint/quicken_wallet.chk index 5cb1621dfd..bfc18e196e 100644 --- a/checks/gnomeprint/quicken_wallet.chk +++ b/checks/gnomeprint/quicken_wallet.chk @@ -1,9 +1,10 @@ [Top] +Guid = 617da3b6-21d8-4340-af07-6a4f38bdeb3f Title = Quicken(tm) Wallet Checks w/ side stub Rotation = 0.0 Translation = 0.0;0.0 -Show_Grid = 0 -Show_Boxes = 0 +Show_Grid = false +Show_Boxes = false [Check Positions] Height = 204.0 diff --git a/checks/quicken.chk b/checks/quicken.chk index 0029ac1f8f..efbe159aef 100644 --- a/checks/quicken.chk +++ b/checks/quicken.chk @@ -1,9 +1,10 @@ [Top] +Guid = 67b144d1-96a5-48d5-9337-0e1083bbf229 Title = Quicken/QuickBooks (tm) US-Letter Rotation = 0.0 Translation = 0.0;4.0 -Show_Grid = 0 -Show_Boxes = 0 +Show_Grid = false +Show_Boxes = false [Check Positions] Height = 252.0 diff --git a/checks/quicken_wallet.chk b/checks/quicken_wallet.chk index 828289c748..9a49a0e1f2 100644 --- a/checks/quicken_wallet.chk +++ b/checks/quicken_wallet.chk @@ -1,9 +1,10 @@ [Top] +Guid = 617da3b6-21d8-4340-af07-6a4f38bdeb3f Title = Quicken(tm) Wallet Checks w/ side stub Rotation = 0.0 Translation = 0.0;4.0 -Show_Grid = 0 -Show_Boxes = 0 +Show_Grid = false +Show_Boxes = false [Check Positions] Height = 204.0 diff --git a/src/gnome/dialog-print-check.c b/src/gnome/dialog-print-check.c index f9e54c91b3..c666f31f25 100644 --- a/src/gnome/dialog-print-check.c +++ b/src/gnome/dialog-print-check.c @@ -55,6 +55,7 @@ #define G_LOG_DOMAIN "gnc.printing.checks" #define GCONF_SECTION "dialogs/print_checks" +#define KEY_CHECK_FORMAT_GUID "check_format_guid" #define KEY_CHECK_FORMAT "check_format" #define KEY_CHECK_POSITION "check_position" #define KEY_DATE_FORMAT_USER "date_format_custom" @@ -78,6 +79,7 @@ #define KF_GROUP_TOP "Top" #define KF_GROUP_POS "Check Positions" #define KF_GROUP_ITEMS "Check Items" +#define KF_KEY_GUID "Guid" #define KF_KEY_TITLE "Title" #define KF_KEY_ROTATION "Rotation" #define KF_KEY_TRANSLATION "Translation" @@ -92,10 +94,25 @@ #define KF_KEY_TEXT "Text" #define KF_KEY_FILENAME "Filename" +/**< This enum specifies the columns used in the check format combobox. */ +typedef enum format_combo_col_t { + COL_NAME = 0, /**< This column holds a copy of the check + * format name and is what is displayed to + * the user in the combobox. It is NULL for + * separator lines. */ + COL_DATA, /**< This column holds a pointer to the check + * format data read in from a file. It is + * NULL for the custom check format and for + * separator lines. */ + COL_SEP, /**< This column contains the value TRUE if + * this enry specifies a separator line. */ +} format_combo_col; + #if USE_GTKPRINT # define GncPrintContext GtkPrintContext #else # define GncPrintContext GnomePrintContext +# define GNOMEPRINT_CLIP_EXTRA 2 #endif @@ -103,9 +120,6 @@ void gnc_ui_print_check_response_cb(GtkDialog * dialog, gint response, PrintCheckDialog * pcd); void gnc_print_check_format_changed(GtkComboBox *widget, PrintCheckDialog * pcd); void gnc_print_check_position_changed(GtkComboBox *widget, PrintCheckDialog * pcd); -static void gnc_ui_print_save_dialog(PrintCheckDialog * pcd); -static void gnc_ui_print_restore_dialog(PrintCheckDialog * pcd); -void gnc_ui_print_restore_dialog(PrintCheckDialog * pcd); void gnc_print_check_save_button_clicked(GtkButton *button, PrintCheckDialog *pcd); @@ -171,6 +185,13 @@ typedef struct _check_item { * description in a text file. */ typedef struct _check_format { + gchar *guid; /**< Unique identifier for this format. */ + + const gchar *group; /**< The group where this format was found. */ + + gchar *filename; /**< The name of the file from which this data + * was read. */ + gchar *title; /**< Title of this check format. Displayed to * user in the dialog box. */ @@ -227,11 +248,44 @@ struct _print_check_dialog { gchar *format_string; - GSList *formats_list; check_format_t *selected_format; }; +/**< This function walks ths list of available check formats looking for a + * specific format as specified by guid number. If found, a pointer to the + * check format is returned to the caller. Additionally, if the caller passed + * a pointer to a GtkTreeIter, then the iter for that entry will also be + * returned. */ +static check_format_t * +find_existing_format (GtkListStore *store, gchar *guid, GtkTreeIter *iter_out) +{ + GtkTreeIter iter; + check_format_t *format; + + g_return_val_if_fail(store, NULL); + g_return_val_if_fail(guid, NULL); + + if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) + return NULL; + + do { + gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, + COL_DATA, &format, -1); + if (format == NULL) + continue; + if (strcmp(format->guid, guid) != 0) + continue; + + if (iter_out) + *iter_out = iter; + return format; + } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter)); + + return NULL; +} + + static void save_float_pair (const char *section, const char *key, double a, double b) { @@ -263,12 +317,20 @@ get_float_pair (const char *section, const char *key, double *a, double *b) static void gnc_ui_print_save_dialog(PrintCheckDialog * pcd) { + GtkTreeModel *model; + GtkTreeIter iter; + check_format_t *check; const gchar *format; gint active; /* Options page */ - active = gtk_combo_box_get_active(GTK_COMBO_BOX(pcd->format_combobox)); - gnc_gconf_set_int(GCONF_SECTION, KEY_CHECK_FORMAT, active, NULL); + if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(pcd->format_combobox), + &iter)) { + model = gtk_combo_box_get_model(GTK_COMBO_BOX(pcd->format_combobox)); + gtk_tree_model_get(model, &iter, COL_DATA, &check, -1); + gnc_gconf_set_string(GCONF_SECTION, KEY_CHECK_FORMAT_GUID, + check ? check->guid : "custom", NULL); + } active = gtk_combo_box_get_active(GTK_COMBO_BOX(pcd->position_combobox)); gnc_gconf_set_int(GCONF_SECTION, KEY_CHECK_POSITION, active, NULL); active = gnc_date_format_get_format (GNC_DATE_FORMAT(pcd->date_format)); @@ -306,16 +368,29 @@ gnc_ui_print_save_dialog(PrintCheckDialog * pcd) gnc_gconf_set_int(GCONF_SECTION, KEY_CUSTOM_UNITS, active, NULL); } -void +static void gnc_ui_print_restore_dialog(PrintCheckDialog * pcd) { - gchar *format; + GtkTreeModel *model; + GtkTreeIter iter; + gchar *format, *guid; gdouble x, y; gint active; /* Options page */ - active = gnc_gconf_get_int(GCONF_SECTION, KEY_CHECK_FORMAT, NULL); - gtk_combo_box_set_active(GTK_COMBO_BOX(pcd->format_combobox), active); + guid = gnc_gconf_get_string(GCONF_SECTION, KEY_CHECK_FORMAT_GUID, NULL); + if (guid == NULL) { + active = gnc_gconf_get_int(GCONF_SECTION, KEY_CHECK_FORMAT, NULL); + gtk_combo_box_set_active(GTK_COMBO_BOX(pcd->format_combobox), active); + } else if (strcmp(guid, "custom") == 0) { + gtk_combo_box_set_active(GTK_COMBO_BOX(pcd->format_combobox), + pcd->format_max - 1); + } else { + model = gtk_combo_box_get_model(GTK_COMBO_BOX(pcd->format_combobox)); + if (find_existing_format(GTK_LIST_STORE(model), guid, &iter)) { + gtk_combo_box_set_active_iter(GTK_COMBO_BOX(pcd->format_combobox), &iter); + } + } active = gnc_gconf_get_int(GCONF_SECTION, KEY_CHECK_POSITION, NULL); gtk_combo_box_set_active(GTK_COMBO_BOX(pcd->position_combobox), active); active = gnc_gconf_get_int(GCONF_SECTION, KEY_DATE_FORMAT, NULL); @@ -422,10 +497,15 @@ pcd_save_custom_data(PrintCheckDialog *pcd, gchar *filename) GtkWidget *dialog; gdouble multip; gint i = 0; + GUID guid; + char buf[GUID_ENCODING_LENGTH+1]; multip = pcd_get_custom_multip(pcd); key_file = g_key_file_new(); + guid_new(&guid); + g_key_file_set_string(key_file, KF_GROUP_TOP, KF_KEY_GUID, + guid_to_string_buff(&guid, buf)); g_key_file_set_string(key_file, KF_GROUP_TOP, KF_KEY_TITLE, _("Custom Check")); g_key_file_set_boolean(key_file, KF_GROUP_TOP, KF_KEY_SHOW_GRID, FALSE); @@ -777,10 +857,24 @@ format_read_general_info(const gchar * file, GKeyFile * key_file, check_format_t * format) { GError *error = NULL; + gchar **parts; gchar *value; double *dd; gsize dd_len; + value = g_key_file_get_string(key_file, KF_GROUP_TOP, KF_KEY_GUID, &error); + if (error) { + g_warning("Check file %s, group %s, key %s, error: %s", + file, KF_GROUP_TOP, KF_KEY_GUID, error->message); + g_error_free(error); + return FALSE; + } + parts = g_strsplit(value, "-", -1); + format->guid = g_strjoinv("", parts); + g_strfreev(parts); + g_debug("Check file %s, group %s, key %s, value: %s", + file, KF_GROUP_TOP, KF_KEY_GUID, format->guid); + format->title = g_key_file_get_string(key_file, KF_GROUP_TOP, KF_KEY_TITLE, &error); if (!error) { @@ -882,6 +976,8 @@ format_read_general_info(const gchar * file, static void free_check_format(check_format_t * data) { + g_free(data->guid); + g_free(data->filename); g_free(data->title); g_free(data->font); g_slist_foreach(data->positions, (GFunc) free_check_position, NULL); @@ -895,8 +991,8 @@ free_check_format(check_format_t * data) /** Read a single check format file and append the resulting format to the * list of all known formats. This function calls other functions to read * each section of the data file. */ -static void -read_one_check_format(PrintCheckDialog * pcd, +static check_format_t * +read_one_check_format(PrintCheckDialog * pcd, const gchar *groupname, const gchar * dirname, const gchar * file) { gchar *pathname; @@ -908,10 +1004,12 @@ read_one_check_format(PrintCheckDialog * pcd, g_free(pathname); if (!key_file) { g_warning("Check file %s, cannot load file", file); - return; + return NULL; } format = g_new0(check_format_t, 1); + format->group = groupname; + format->filename = g_strdup(file); if (format_read_general_info(file, key_file, format)) { format->positions = format_read_multicheck_info(file, key_file, format); format->items = format_read_item_placement(file, key_file, format); @@ -921,10 +1019,10 @@ read_one_check_format(PrintCheckDialog * pcd, if ((NULL == format->title) || (NULL == format->items)) { g_warning("Check file %s, no items read. Dropping file.", file); free_check_format(format); - return; + return NULL; } - pcd->formats_list = g_slist_append(pcd->formats_list, format); + return format; } @@ -932,20 +1030,59 @@ read_one_check_format(PrintCheckDialog * pcd, * then calling a helper function to read and parse the check format withing * the file. */ static void -read_one_check_directory(PrintCheckDialog * pcd, const gchar * dirname) +read_one_check_directory(PrintCheckDialog * pcd, GtkListStore *store, + const gchar *groupname, const gchar * dirname) { + check_format_t *format, *existing; GDir *dir; const gchar *filename; + GtkTreeIter iter; + GtkWidget *dialog; + gboolean found = FALSE; dir = g_dir_open(dirname, 0, NULL); - if (dir) { - while ((filename = g_dir_read_name(dir)) != NULL) { - if (g_str_has_prefix(filename, "#")) - continue; - if (g_str_has_suffix(filename, ".chk")) - read_one_check_format(pcd, dirname, filename); + if (dir == NULL) + return; + + while ((filename = g_dir_read_name(dir)) != NULL) { + if (g_str_has_prefix(filename, "#")) + continue; + if (!g_str_has_suffix(filename, ".chk")) + continue; + + format = read_one_check_format(pcd, groupname, dirname, filename); + if (NULL == format) + continue; + + existing = find_existing_format(store, format->guid, NULL); + if (existing) { + dialog = gtk_message_dialog_new + (GTK_WINDOW(pcd->dialog), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, + _("There is a duplicate check format file.")); + gtk_message_dialog_format_secondary_text + (GTK_MESSAGE_DIALOG(dialog), + _("The guids in the %s check format file '%s' and " + "the %s check format file '%s' match."), + existing->group, existing->filename, + format->group, format->filename); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + } else { + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, COL_NAME, format->title, + COL_DATA, format, -1); + found = TRUE; } - g_dir_close(dir); + } + g_dir_close(dir); + + /* If any files were added to the list, add a separator between + * this group and the next. */ + if (found) { + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, COL_SEP, TRUE, -1); } } @@ -954,22 +1091,32 @@ read_one_check_directory(PrintCheckDialog * pcd, const gchar * dirname) * for check files, and then looks in the user's .gnucash directory for any * custom check files. */ static void -read_formats(PrintCheckDialog * pcd) +read_formats(PrintCheckDialog * pcd, GtkListStore *store) { gchar *dirname, *pkgdatadir; pkgdatadir = gnc_path_get_pkgdatadir(); dirname = g_build_filename(pkgdatadir, CHECK_FMT_DIR, (char *)NULL); - read_one_check_directory(pcd, dirname); + read_one_check_directory(pcd, store, _("application"), dirname); g_free(dirname); g_free(pkgdatadir); dirname = gnc_build_dotgnucash_path(CHECK_FMT_DIR); - read_one_check_directory(pcd, dirname); + read_one_check_directory(pcd, store, _("user"), dirname); g_free(dirname); } +static gboolean +format_is_a_separator (GtkTreeModel *model, GtkTreeIter *iter, gpointer data) +{ + gboolean separator; + + gtk_tree_model_get(model, iter, COL_SEP, &separator, -1); + return separator; +} + + /********************************************************************\ * gnc_ui_print_check_dialog_create * make a new print check dialog and wait for it. @@ -983,8 +1130,8 @@ gnc_ui_print_check_dialog_create(GncPluginPageRegister *plugin_page, GladeXML *xml; GtkWidget *table; GtkWindow *window; - GSList *elem; GtkListStore *store; + GtkTreeIter iter; pcd = g_new0(PrintCheckDialog, 1); pcd->plugin_page = plugin_page; @@ -996,8 +1143,6 @@ gnc_ui_print_check_dialog_create(GncPluginPageRegister *plugin_page, pcd->xml = xml; pcd->dialog = glade_xml_get_widget (xml, "Print Check Dialog"); - read_formats(pcd); - /* now pick out the relevant child widgets */ pcd->format_combobox = glade_xml_get_widget (xml, "check_format_combobox"); pcd->position_combobox = glade_xml_get_widget (xml, "check_position_combobox"); @@ -1034,15 +1179,15 @@ gnc_ui_print_check_dialog_create(GncPluginPageRegister *plugin_page, gtk_table_attach_defaults(GTK_TABLE(table), pcd->date_format, 1, 3, 2, 7); /* Update the combo boxes bases on the available check formats */ - store = gtk_list_store_new (1, G_TYPE_STRING); + store = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_BOOLEAN); + read_formats(pcd, store); + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, COL_NAME, _("Custom"), -1); + pcd->format_max = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store),NULL); gtk_combo_box_set_model(GTK_COMBO_BOX(pcd->format_combobox), GTK_TREE_MODEL(store)); - for (elem = pcd->formats_list; elem; elem = g_slist_next(elem)) { - gtk_combo_box_append_text(GTK_COMBO_BOX(pcd->format_combobox), - ((check_format_t*)elem->data)->title); - } - gtk_combo_box_append_text(GTK_COMBO_BOX(pcd->format_combobox), _("Custom")); - pcd->format_max = g_slist_length(pcd->formats_list); /* -1 for 0 base, +1 for custom entry*/ + gtk_combo_box_set_row_separator_func(GTK_COMBO_BOX(pcd->format_combobox), + format_is_a_separator, NULL, NULL); #if USE_GTKPRINT gtk_widget_destroy(glade_xml_get_widget (xml, "lower_left")); @@ -1139,7 +1284,7 @@ draw_grid(GncPrintContext * context, gint width, gint height) cairo_restore(cr); g_object_unref(layout); #else - gnome_print_gsave(context); + gnome_print_grestore(context); #endif } @@ -1197,9 +1342,30 @@ draw_text(GncPrintContext * context, const gchar * text, check_item_t * data, g_object_unref(layout); return width; #else + gdouble x0, x1, y0, y1; + + /* Clip text to the enclosing rectangle */ + gnome_print_gsave(context); + if (data->w && data->h) { + g_debug("Text clip rectangle, coords %f,%f, size %f,%f", + data->x, data->y - data->h, data->w, data->h); + x0 = data->x - GNOMEPRINT_CLIP_EXTRA; + x1 = data->x + data->w + GNOMEPRINT_CLIP_EXTRA; + y0 = data->y - GNOMEPRINT_CLIP_EXTRA; + y1 = data->y + data->h + GNOMEPRINT_CLIP_EXTRA; + gnome_print_moveto(context, x0, y0); + gnome_print_lineto(context, x0, y1); + gnome_print_lineto(context, x1, y1); + gnome_print_lineto(context, x1, y0); + gnome_print_lineto(context, x0, y0); + gnome_print_clip(context); + } + + /* Draw the text */ g_debug("Text move to %f,%f, print '%s'", data->x, data->y, text); gnome_print_moveto(context, data->x, data->y); gnome_print_show(context, text); + gnome_print_grestore(context); return 0.0; #endif } @@ -1480,7 +1646,7 @@ draw_page_items(GncPrintContext * context, #endif default: - text = g_strdup_printf("(unknown check field %d)", item->type); + text = g_strdup_printf("(unknown check field, type %d)", item->type); draw_text(context, text, item, default_desc); g_free(text); } @@ -1811,25 +1977,31 @@ void gnc_print_check_format_changed (GtkComboBox *widget, PrintCheckDialog * pcd) { - GtkListStore *store; + GtkListStore *p_store; + GtkTreeModel *f_model; + GtkTreeIter f_iter; gboolean sensitive; - gint fnum, pnum; + gint pnum; check_format_t *format; + gboolean separator; GSList *elem; - fnum = gtk_combo_box_get_active(GTK_COMBO_BOX(pcd->format_combobox)); - if (-1 == fnum) + if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(pcd->format_combobox), &f_iter)) + return; + f_model = gtk_combo_box_get_model(GTK_COMBO_BOX(pcd->format_combobox)); + gtk_tree_model_get(f_model, &f_iter, COL_DATA, &format, COL_SEP, &separator, -1); + if (separator) return; + pnum = gtk_combo_box_get_active(GTK_COMBO_BOX(pcd->position_combobox)); /* Update the positions combobox */ - format = g_slist_nth_data(pcd->formats_list, fnum); pcd->selected_format = format; g_signal_handlers_block_by_func(pcd->position_combobox, gnc_print_check_position_changed, pcd); - store = gtk_list_store_new (1, G_TYPE_STRING); + p_store = gtk_list_store_new (1, G_TYPE_STRING); gtk_combo_box_set_model(GTK_COMBO_BOX(pcd->position_combobox), - GTK_TREE_MODEL(store)); + GTK_TREE_MODEL(p_store)); if (format) { pcd->position_max = g_slist_length(format->positions); /* -1 for 0 base, +1 for custom entry */ for (elem = format->positions; elem; elem = g_slist_next(elem)) { @@ -1849,7 +2021,7 @@ gnc_print_check_format_changed (GtkComboBox *widget, gtk_widget_set_sensitive(GTK_WIDGET(pcd->position_combobox), sensitive); /* Update the custom page */ - sensitive = (fnum == pcd->format_max); + sensitive = (!separator && !format); gtk_container_foreach(GTK_CONTAINER(pcd->custom_table), gnc_print_check_set_sensitive, GINT_TO_POINTER(sensitive)); @@ -1882,7 +2054,5 @@ gnc_ui_print_check_response_cb(GtkDialog * dialog, gtk_widget_destroy(pcd->dialog); g_object_unref(pcd->xml); - g_slist_foreach(pcd->formats_list, (GFunc)free_check_format, NULL); - g_slist_free(pcd->formats_list); g_free(pcd); }