GnuCash  4.8a-132-gcdaeb421d+
dialog-print-check.c
Go to the documentation of this file.
1 /********************************************************************\
2  * dialog-print-check.c : dialog to control check printing. *
3  * Copyright (C) 2000 Bill Gribble <grib@billgribble.com> *
4  * Copyright (C) 2006,2007 David Hampton <hampton@employees.org> *
5  * *
6  * This program is free software; you can redistribute it and/or *
7  * modify it under the terms of the GNU General Public License as *
8  * published by the Free Software Foundation; either version 2 of *
9  * the License, or (at your option) any later version. *
10  * *
11  * This program is distributed in the hope that it will be useful, *
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14  * GNU General Public License for more details. *
15  * *
16  * You should have received a copy of the GNU General Public License*
17  * along with this program; if not, contact: *
18  * *
19  * Free Software Foundation Voice: +1-617-542-5942 *
20  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
21  * Boston, MA 02110-1301, USA gnu@gnu.org *
22 \********************************************************************/
23 
31 #include <config.h>
32 
33 #include <gtk/gtk.h>
34 #include <glib/gi18n.h>
35 #include <stdio.h>
36 #include <locale.h>
37 #include <math.h>
38 
39 #include "qof.h"
40 #include "gnc-date.h"
41 #include "gnc-prefs.h"
42 #include "gnc-numeric.h"
44 #include "dialog-print-check.h"
45 #include "dialog-utils.h"
46 #include "print-session.h"
47 #include "gnc-ui.h"
48 #include "gnc-date-format.h"
49 #include "gnc-ui-util.h"
50 #include "gnc-path.h"
51 #include "gnc-filepath-utils.h"
52 #include "gnc-gkeyfile-utils.h"
53 
54 #include "gnc-engine.h"
55 #include "engine-helpers.h"
56 #include "Split.h"
57 #include "Transaction.h"
58 
59 #undef G_LOG_DOMAIN
60 #define G_LOG_DOMAIN "gnc.printing.checks"
61 
62 /* This static indicates the debugging module that this .o belongs to.
63  */
64 G_GNUC_UNUSED static QofLogModule log_module = "gnc.printing.checks";
65 
66 #define GNC_PREFS_GROUP "dialogs.checkprinting"
67 #define GNC_PREF_CHECK_FORMAT_GUID "check-format-guid"
68 #define GNC_PREF_CHECK_POSITION "check-position"
69 #define GNC_PREF_FIRST_PAGE_COUNT "first-page-count"
70 #define GNC_PREF_DATE_FORMAT_USER "date-format-user"
71 #define GNC_PREF_CUSTOM_PAYEE "custom-payee"
72 #define GNC_PREF_CUSTOM_DATE "custom-date"
73 #define GNC_PREF_CUSTOM_WORDS "custom-amount-words"
74 #define GNC_PREF_CUSTOM_NUMBER "custom-amount-number"
75 #define GNC_PREF_CUSTOM_ADDRESS "custom-address"
76 #define GNC_PREF_CUSTOM_NOTES "custom-notes"
77 #define GNC_PREF_CUSTOM_MEMO "custom-memo"
78 #define GNC_PREF_CUSTOM_TRANSLATION "custom-translation"
79 #define GNC_PREF_CUSTOM_ROTATION "custom-rotation"
80 #define GNC_PREF_CUSTOM_UNITS "custom-units"
81 #define GNC_PREF_PRINT_DATE_FMT "print-date-format"
82 #define GNC_PREF_DEFAULT_FONT "default-font"
83 #define GNC_PREF_BLOCKING_CHARS "blocking-chars"
84 #define GNC_PREF_SPLITS_AMOUNT "splits-amount"
85 #define GNC_PREF_SPLITS_MEMO "splits-memo"
86 #define GNC_PREF_SPLITS_ACCOUNT "splits-account"
87 
88 
89 #define DEFAULT_FONT "sans 12"
90 #define CHECK_FMT_DIR "checks"
91 #define CHECK_NAME_EXTENSION ".chk"
92 #define DEGREES_TO_RADIANS (G_PI / 180.0)
93 
94 #define BLOCKING_CHAR_OFF 0
95 #define BLOCKING_CHAR_ON 1
96 
97 #define KF_GROUP_TOP "Top"
98 #define KF_GROUP_POS "Check Positions"
99 #define KF_GROUP_ITEMS "Check Items"
100 #define KF_KEY_GUID "Guid"
101 #define KF_KEY_TITLE "Title"
102 #define KF_KEY_ROTATION "Rotation"
103 #define KF_KEY_TRANSLATION "Translation"
104 #define KF_KEY_FONT "Font"
105 #define KF_KEY_ALIGN "Align"
106 #define KF_KEY_BLOCKING "Blocking_Chars"
107 #define KF_KEY_SHOW_GRID "Show_Grid"
108 #define KF_KEY_SHOW_BOXES "Show_Boxes"
109 #define KF_KEY_NAMES "Names"
110 #define KF_KEY_HEIGHT "Height"
111 #define KF_KEY_TYPE "Type"
112 #define KF_KEY_COORDS "Coords"
113 #define KF_KEY_TEXT "Text"
114 #define KF_KEY_FILENAME "Filename"
115 #define KF_KEY_DATE_FORMAT "DateFormat"
116 #define KF_KEY_SPLITS_AMOUNT "SplitsAmount"
117 #define KF_KEY_SPLITS_MEMO "SplitsMemo"
118 #define KF_KEY_SPLITS_ACCOUNT "SplitsAccount"
119 
120 /* This enum specifies the columns used in the check format combobox.
121  */
122 typedef enum format_combo_col_t
123 {
124  COL_NAME = 0,
135 
136 void gnc_ui_print_check_response_cb (GtkDialog *dialog, gint response, PrintCheckDialog *pcd);
137 void gnc_print_check_format_changed (GtkComboBox *widget, PrintCheckDialog *pcd);
138 void gnc_print_check_position_changed (GtkComboBox *widget, PrintCheckDialog *pcd);
139 void gnc_print_check_save_button_clicked (GtkButton *button, PrintCheckDialog *pcd);
140 void gnc_check_format_title_changed (GtkEditable *editable, GtkWidget *ok_button);
141 
142 static void initialize_format_combobox (PrintCheckDialog *pcd);
143 gchar* get_check_address (PrintCheckDialog *pcd);
144 static gboolean check_format_has_address (PrintCheckDialog *pcd);
145 gchar* get_check_splits_amount (PrintCheckDialog *pcd);
146 gchar* get_check_splits_memo (PrintCheckDialog *pcd);
147 gchar* get_check_splits_account (PrintCheckDialog *pcd);
148 
149 /* This enum defines the types of items that gnucash knows how to
150  * print on checks. Most refer to specific fields from a gnucash
151  * transaction and split, but some are generic items unrelated to
152  * gnucash.
153  */
154 #define ENUM_CHECK_ITEM_TYPE(_) \
155  _(NONE,) \
156  _(PAYEE,) \
157  _(DATE,) \
158  _(NOTES,) \
159  _(CHECK_NUMBER,) \
160  \
161  _(MEMO,) \
162  _(ACTION,) \
163  _(AMOUNT_NUMBER,) \
164  _(AMOUNT_WORDS,) \
165  \
166  _(TEXT,) \
167  _(ADDRESS,) \
168  _(DATE_FORMAT,) \
169  _(SPLITS_AMOUNT,) \
170  _(SPLITS_MEMO,) \
171  _(SPLITS_ACCOUNT,) \
172  _(PICTURE,)
173 
174 DEFINE_ENUM(CheckItemType, ENUM_CHECK_ITEM_TYPE)
175 FROM_STRING_DEC(CheckItemType, ENUM_CHECK_ITEM_TYPE)
176 FROM_STRING_FUNC(CheckItemType, ENUM_CHECK_ITEM_TYPE)
177 AS_STRING_DEC(CheckItemType, ENUM_CHECK_ITEM_TYPE)
178 AS_STRING_FUNC(CheckItemType, ENUM_CHECK_ITEM_TYPE)
179 
180 /* This data structure describes a single item printed on a check.
181  * It is built from a description in a text file.
182  */
183 typedef struct _check_item
184 {
185 
186  CheckItemType type;
188  gdouble x, y;
193  gdouble w, h;
198  gchar *filename;
201  gchar *text;
204  gchar *font;
208  gboolean blocking;
213  gboolean print_date_format;
217  PangoAlignment align;
220 } check_item_t;
221 
222 /* This data structure describes an entire page of checks. Depending
223  * upon the check format, the page may contain multiple checks or
224  * only a single check. The data structure is built from a
225  * description in a text file.
226  */
227 typedef struct _check_format
228 {
230  gchar *guid;
232  const gchar *group;
234  gchar *filename;
237  gchar *title;
240  gboolean blocking;
243  gboolean print_date_format;
246  gboolean show_grid;
248  gboolean show_boxes;
251  gdouble rotation;
253  gdouble trans_x;
255  gdouble trans_y;
257  gchar *font;
259  gdouble height;
261  GSList *positions;
263  GSList *items;
265 
266 
267 /* This data structure is used to manage the print check dialog, and
268  * the overall check printing process. It contains pointers to many
269  * of the widgets in the dialog, pointers to the check descriptions
270  * that have been read, and also contains the data from the gnucash
271  * transaction/split that is to be printed.
272  */
273 struct _print_check_dialog
274 {
275  GtkBuilder *builder;
276  GtkWidget *dialog;
277  GtkWindow *caller_window;
278 
279  Split *split;
280  GList *splits;
281 
282  GtkWidget *format_combobox;
283  gint format_max;
284  GtkWidget *position_combobox;
285  gint position_max;
286  GtkSpinButton *first_page_count;
287  GtkWidget *custom_table;
288  GtkSpinButton *payee_x, *payee_y;
289  GtkSpinButton *date_x, *date_y;
290  GtkSpinButton *words_x, *words_y;
291  GtkSpinButton *number_x, *number_y;
292  GtkSpinButton *address_x, *address_y;
293  GtkSpinButton *notes_x, *notes_y;
294  GtkSpinButton *memo_x, *memo_y;
295  GtkSpinButton *splits_amount_x, *splits_amount_y;
296  GtkSpinButton *splits_memo_x, *splits_memo_y;
297  GtkSpinButton *splits_account_x, *splits_account_y;
298  GtkSpinButton *translation_x, *translation_y;
299  GtkSpinButton *check_rotation;
300  GtkWidget *translation_label;
301 
302  GtkWidget *units_combobox;
303 
304  GtkWidget *date_format;
305 
306  GtkWidget *check_address_name;
307  GtkWidget *check_address_1;
308  GtkWidget *check_address_2;
309  GtkWidget *check_address_3;
310  GtkWidget *check_address_4;
311 
312  gchar *default_font;
313 
314  check_format_t *selected_format;
315 };
316 
317 
318 /* This function walks ths list of available check formats looking for a
319  * specific format as specified by guid number. If found, a pointer to it is
320  * returned to the caller. Additionally, if the caller passed a pointer to a
321  * GtkTreeIter, then the iter for that entry will also be returned.
322  */
323 static check_format_t *
324 find_existing_format (GtkListStore *store, gchar *guid, GtkTreeIter *iter_out)
325 {
326  GtkTreeIter iter;
327  check_format_t *format;
328 
329  g_return_val_if_fail(store, NULL);
330  g_return_val_if_fail(guid, NULL);
331 
332  if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter))
333  return NULL;
334 
335  do
336  {
337  gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
338  COL_DATA, &format, -1);
339  if (format == NULL)
340  continue;
341  if (strcmp(format->guid, guid) != 0)
342  continue;
343 
344  if (iter_out)
345  *iter_out = iter;
346  return format;
347  }
348  while (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter));
349 
350  return NULL;
351 }
352 
353 
354 /* This function returns a string containing the check address in a five-line
355  * format.
356  *
357  * Note that the string needs to be freed with g_free.
358  */
359 gchar *
360 get_check_address( PrintCheckDialog *pcd)
361 {
362  gchar *address;
363  address = g_strconcat(gtk_entry_get_text(GTK_ENTRY(pcd->check_address_name)), "\n",
364  gtk_entry_get_text(GTK_ENTRY(pcd->check_address_1)), "\n",
365  gtk_entry_get_text(GTK_ENTRY(pcd->check_address_2)), "\n",
366  gtk_entry_get_text(GTK_ENTRY(pcd->check_address_3)), "\n",
367  gtk_entry_get_text(GTK_ENTRY(pcd->check_address_4)),
368  NULL);
369  return address;
370 }
371 
372 
374 
376 /* This function formats the splits amounts for printing.
377  */
378 gchar *
379 get_check_splits_amount(PrintCheckDialog *pcd)
380 {
381  gchar* amount = NULL;
382  Transaction *trans;
383  GList *node;
384  SplitList* s_list;
385 
386  trans = xaccSplitGetParent(pcd->split);
387  s_list = xaccTransGetSplitList(trans);
388  if ( !s_list ) return NULL;
389 
390  amount = g_strconcat("", NULL);
391  node = s_list;
392  while ( node )
393  {
394  Split *split = node->data;
395  /* Include all splits except the main split for the check */
396  if (split != pcd->split)
397  {
398  const gchar* split_amount;
399  gchar* amt_temp;
400  split_amount = xaccPrintAmount(xaccSplitGetAmount(split), gnc_split_amount_print_info(split, TRUE));
401  amt_temp = amount;
402  if (amount && *amount)
403  amount = g_strconcat(amt_temp, "\n", split_amount, NULL);
404  else
405  amount = g_strconcat(amt_temp, split_amount, NULL);
406  g_free(amt_temp);
407  }
408  node = node->next;
409  }
410  return amount;
411 }
412 
413 
414 /* This function formats the splits memo fields for printing.
415  */
416 gchar *
417 get_check_splits_memo(PrintCheckDialog *pcd)
418 {
419  gchar* memo = NULL;
420  const gchar* split_memo;
421  Transaction *trans;
422  GList *node;
423  SplitList* s_list;
424 
425  trans = xaccSplitGetParent(pcd->split);
426  s_list = xaccTransGetSplitList(trans);
427  if ( !s_list ) return NULL;
428 
429  memo = g_strconcat("", NULL);
430  node = s_list;
431  while ( node )
432  {
433  Split *split = node->data;
434  /* Include all splits except the main split for the check */
435  if (split != pcd->split)
436  {
437  gchar* memo_temp;
438  split_memo = xaccSplitGetMemo(split);
439  memo_temp = memo;
440  if (memo && *memo)
441  memo = g_strconcat(memo_temp, "\n", split_memo, NULL);
442  else
443  memo = g_strconcat(memo_temp, split_memo, NULL);
444  g_free(memo_temp);
445  }
446  node = node->next;
447  }
448  return memo;
449 }
450 
451 
452 /* This function formats the splits accounts for printing.
453  */
454 gchar *
455 get_check_splits_account(PrintCheckDialog *pcd)
456 {
457  gchar* account = NULL;
458  Transaction *trans;
459  GList *node;
460  SplitList* s_list;
461 
462  trans = xaccSplitGetParent(pcd->split);
463  s_list = xaccTransGetSplitList(trans);
464  if ( !s_list ) return NULL;
465 
466  account = g_strconcat("", NULL);
467  node = s_list;
468  while ( node )
469  {
470  Split *split = node->data;
471  /* Include all splits except the main split for the check */
472  if (split != pcd->split)
473  {
474  gchar* account_temp;
475  const gchar* aName = NULL;
476  Account *pAccount;
477  pAccount = xaccSplitGetAccount(split);
478  aName = gnc_get_account_name_for_register(pAccount);
479  account_temp = account;
480  if (account && *account)
481  account = g_strconcat(account_temp, "\n", aName, NULL);
482  else
483  account = g_strconcat(account_temp, aName, NULL);
484  g_free(account_temp);
485  }
486  node = node->next;
487  }
488  return account;
489 }
491 
492 
493 /* This function determines if an address item is present in the check format.
494  */
495 static gboolean
496 check_format_has_address ( PrintCheckDialog *pcd )
497 {
498  /* check format for an ADDRESS item */
499  check_item_t *item = NULL;
500  GSList *elem;
501  check_format_t *format = NULL;
502 
503  if ( !pcd ) return FALSE;
504 
505  /* If we're printing more than one check no addresses are allowed */
506  if (g_list_length(pcd->splits) != 1)
507  return FALSE;
508 
509  /* if format is NULL, then the custom format is being used
510  * which has an ADDRESS item by definition */
511  format = pcd->selected_format;
512  if ( !format ) return TRUE;
513 
514  for (elem = pcd->selected_format->items; elem; elem = g_slist_next(elem))
515  {
516  item = elem->data;
517  if ( item->type == ADDRESS ) return TRUE;
518  }
519  return FALSE;
520 }
521 
522 
523 static void
524 gnc_ui_print_save_dialog(PrintCheckDialog *pcd)
525 {
526  GtkTreeModel *model;
527  GtkTreeIter iter;
528  check_format_t *check;
529  const gchar *format;
530  gint active;
531 
532  /* Options page */
533  if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(pcd->format_combobox),
534  &iter))
535  {
536  model = gtk_combo_box_get_model(GTK_COMBO_BOX(pcd->format_combobox));
537  gtk_tree_model_get(model, &iter, COL_DATA, &check, -1);
538  gnc_prefs_set_string (GNC_PREFS_GROUP, GNC_PREF_CHECK_FORMAT_GUID,
539  check ? check->guid : "custom");
540  }
541  active = gtk_combo_box_get_active(GTK_COMBO_BOX(pcd->position_combobox));
542  gnc_prefs_set_int(GNC_PREFS_GROUP, GNC_PREF_CHECK_POSITION, active);
543  active = gtk_spin_button_get_value_as_int(pcd->first_page_count);
544  gnc_prefs_set_int(GNC_PREFS_GROUP, GNC_PREF_FIRST_PAGE_COUNT, active);
545  active = gnc_date_format_get_format (GNC_DATE_FORMAT(pcd->date_format));
546  gnc_prefs_set_int(GNC_PREFS_GROUP, GNC_PREF_DATE_FORMAT, active);
547  if (active == QOF_DATE_FORMAT_CUSTOM)
548  {
549  format = gnc_date_format_get_custom (GNC_DATE_FORMAT(pcd->date_format));
550  gnc_prefs_set_string (GNC_PREFS_GROUP, GNC_PREF_DATE_FORMAT_USER, format);
551  }
552  else
553  {
554  gnc_prefs_reset (GNC_PREFS_GROUP, GNC_PREF_DATE_FORMAT_USER);
555  }
556 
557  /* Custom format page */
558  gnc_prefs_set_coords(GNC_PREFS_GROUP, GNC_PREF_CUSTOM_PAYEE,
559  gtk_spin_button_get_value(pcd->payee_x),
560  gtk_spin_button_get_value(pcd->payee_y));
561  gnc_prefs_set_coords(GNC_PREFS_GROUP, GNC_PREF_CUSTOM_DATE,
562  gtk_spin_button_get_value(pcd->date_x),
563  gtk_spin_button_get_value(pcd->date_y));
564  gnc_prefs_set_coords(GNC_PREFS_GROUP, GNC_PREF_CUSTOM_WORDS,
565  gtk_spin_button_get_value(pcd->words_x),
566  gtk_spin_button_get_value(pcd->words_y));
567  gnc_prefs_set_coords(GNC_PREFS_GROUP, GNC_PREF_CUSTOM_NUMBER,
568  gtk_spin_button_get_value(pcd->number_x),
569  gtk_spin_button_get_value(pcd->number_y));
570  gnc_prefs_set_coords(GNC_PREFS_GROUP, GNC_PREF_CUSTOM_NOTES,
571  gtk_spin_button_get_value(pcd->notes_x),
572  gtk_spin_button_get_value(pcd->notes_y));
573  gnc_prefs_set_coords(GNC_PREFS_GROUP, GNC_PREF_CUSTOM_MEMO,
574  gtk_spin_button_get_value(pcd->memo_x),
575  gtk_spin_button_get_value(pcd->memo_y));
576  gnc_prefs_set_coords(GNC_PREFS_GROUP, GNC_PREF_CUSTOM_ADDRESS,
577  gtk_spin_button_get_value(pcd->address_x),
578  gtk_spin_button_get_value(pcd->address_y));
579  gnc_prefs_set_coords(GNC_PREFS_GROUP, GNC_PREF_SPLITS_AMOUNT,
580  gtk_spin_button_get_value(pcd->splits_amount_x),
581  gtk_spin_button_get_value(pcd->splits_amount_y));
582  gnc_prefs_set_coords(GNC_PREFS_GROUP, GNC_PREF_SPLITS_MEMO,
583  gtk_spin_button_get_value(pcd->splits_memo_x),
584  gtk_spin_button_get_value(pcd->splits_memo_y));
585  gnc_prefs_set_coords(GNC_PREFS_GROUP, GNC_PREF_SPLITS_ACCOUNT,
586  gtk_spin_button_get_value(pcd->splits_account_x),
587  gtk_spin_button_get_value(pcd->splits_account_y));
588  gnc_prefs_set_coords(GNC_PREFS_GROUP, GNC_PREF_CUSTOM_TRANSLATION,
589  gtk_spin_button_get_value(pcd->translation_x),
590  gtk_spin_button_get_value(pcd->translation_y));
591  gnc_prefs_set_float(GNC_PREFS_GROUP, GNC_PREF_CUSTOM_ROTATION,
592  gtk_spin_button_get_value(pcd->check_rotation));
593  active = gtk_combo_box_get_active(GTK_COMBO_BOX(pcd->units_combobox));
594  gnc_prefs_set_int(GNC_PREFS_GROUP, GNC_PREF_CUSTOM_UNITS, active);
595 }
596 
597 
598 static void
599 gnc_ui_print_restore_dialog(PrintCheckDialog *pcd)
600 {
601  GtkTreeModel *model;
602  GtkTreeIter iter;
603  gchar *format, *guid;
604  gdouble x, y;
605  gint active;
606 
607  /* Options page */
608  guid = gnc_prefs_get_string (GNC_PREFS_GROUP, GNC_PREF_CHECK_FORMAT_GUID);
609  if (guid == NULL)
610  gtk_combo_box_set_active(GTK_COMBO_BOX(pcd->format_combobox), 0);
611  else if (strcmp(guid, "custom") == 0)
612  {
613  gtk_combo_box_set_active(GTK_COMBO_BOX(pcd->format_combobox),
614  pcd->format_max - 1);
615  g_free (guid);
616  }
617  else
618  {
619  model = gtk_combo_box_get_model(GTK_COMBO_BOX(pcd->format_combobox));
620  if (find_existing_format(GTK_LIST_STORE(model), guid, &iter))
621  {
622  gtk_combo_box_set_active_iter(GTK_COMBO_BOX(pcd->format_combobox), &iter);
623  }
624  else
625  {
626  gtk_combo_box_set_active(GTK_COMBO_BOX(pcd->format_combobox), 0);
627  }
628  g_free (guid);
629  }
630 
631  active = gnc_prefs_get_int(GNC_PREFS_GROUP, GNC_PREF_CHECK_POSITION);
632 
633  /* If the check format used last time no longer exists, then the saved check
634  position may be invalid. If so set it to the first position. */
635  if (active < 0 || active > pcd->position_max)
636  active = 0;
637  gtk_combo_box_set_active(GTK_COMBO_BOX(pcd->position_combobox), active);
638  active = gnc_prefs_get_int(GNC_PREFS_GROUP, GNC_PREF_FIRST_PAGE_COUNT);
639  gtk_spin_button_set_value(pcd->first_page_count, (gdouble) active);
640  active = gnc_prefs_get_int(GNC_PREFS_GROUP, GNC_PREF_DATE_FORMAT);
641  gnc_date_format_set_format(GNC_DATE_FORMAT(pcd->date_format), active);
642  if (active == QOF_DATE_FORMAT_CUSTOM)
643  {
644  format = gnc_prefs_get_string (GNC_PREFS_GROUP, GNC_PREF_DATE_FORMAT_USER);
645  if (format)
646  {
647  gnc_date_format_set_custom(GNC_DATE_FORMAT(pcd->date_format), format);
648  g_free(format);
649  }
650  }
651 
652  /* Custom format page */
653  gnc_prefs_get_coords(GNC_PREFS_GROUP, GNC_PREF_CUSTOM_PAYEE, &x, &y);
654  gtk_spin_button_set_value(pcd->payee_x, x);
655  gtk_spin_button_set_value(pcd->payee_y, y);
656 
657  gnc_prefs_get_coords(GNC_PREFS_GROUP, GNC_PREF_CUSTOM_DATE, &x, &y);
658  gtk_spin_button_set_value(pcd->date_x, x);
659  gtk_spin_button_set_value(pcd->date_y, y);
660  gnc_prefs_get_coords(GNC_PREFS_GROUP, GNC_PREF_CUSTOM_WORDS, &x, &y);
661  gtk_spin_button_set_value(pcd->words_x, x);
662  gtk_spin_button_set_value(pcd->words_y, y);
663  gnc_prefs_get_coords(GNC_PREFS_GROUP, GNC_PREF_CUSTOM_NUMBER, &x, &y);
664  gtk_spin_button_set_value(pcd->number_x, x);
665  gtk_spin_button_set_value(pcd->number_y, y);
666  gnc_prefs_get_coords(GNC_PREFS_GROUP, GNC_PREF_CUSTOM_ADDRESS, &x, &y);
667  gtk_spin_button_set_value(pcd->address_x, x);
668  gtk_spin_button_set_value(pcd->address_y, y);
669  gnc_prefs_get_coords(GNC_PREFS_GROUP, GNC_PREF_CUSTOM_NOTES, &x, &y);
670  gtk_spin_button_set_value(pcd->notes_x, x);
671  gtk_spin_button_set_value(pcd->notes_y, y);
672  gnc_prefs_get_coords(GNC_PREFS_GROUP, GNC_PREF_CUSTOM_MEMO, &x, &y);
673  gtk_spin_button_set_value(pcd->memo_x, x);
674  gtk_spin_button_set_value(pcd->memo_y, y);
675  gnc_prefs_get_coords(GNC_PREFS_GROUP, GNC_PREF_SPLITS_AMOUNT, &x, &y);
676  gtk_spin_button_set_value(pcd->splits_amount_x, x);
677  gtk_spin_button_set_value(pcd->splits_amount_y, y);
678  gnc_prefs_get_coords(GNC_PREFS_GROUP, GNC_PREF_SPLITS_MEMO, &x, &y);
679  gtk_spin_button_set_value(pcd->splits_memo_x, x);
680  gtk_spin_button_set_value(pcd->splits_memo_y, y);
681  gnc_prefs_get_coords(GNC_PREFS_GROUP, GNC_PREF_SPLITS_ACCOUNT, &x, &y);
682  gtk_spin_button_set_value(pcd->splits_account_x, x);
683  gtk_spin_button_set_value(pcd->splits_account_y, y);
684  gnc_prefs_get_coords(GNC_PREFS_GROUP, GNC_PREF_CUSTOM_TRANSLATION, &x, &y);
685  gtk_spin_button_set_value(pcd->translation_x, x);
686  gtk_spin_button_set_value(pcd->translation_y, y);
687  x = gnc_prefs_get_float(GNC_PREFS_GROUP, GNC_PREF_CUSTOM_ROTATION);
688  gtk_spin_button_set_value(pcd->check_rotation, x);
689  active = gnc_prefs_get_int(GNC_PREFS_GROUP, GNC_PREF_CUSTOM_UNITS);
690  gtk_combo_box_set_active(GTK_COMBO_BOX(pcd->units_combobox), active);
691 }
692 
693 
694 static gdouble
695 pcd_get_custom_multip(PrintCheckDialog *pcd)
696 {
697  gint selected;
698 
699  selected = gtk_combo_box_get_active(GTK_COMBO_BOX(pcd->units_combobox));
700  switch (selected)
701  {
702  default:
703  return 72.0; /* inches */
704  case 1:
705  return 28.346; /* cm */
706  case 2:
707  return 2.8346; /* mm */
708  case 3:
709  return 1.0; /* points */
710  }
711 }
712 
713 
714 /* This function saves a coordinate pair into a check description file. It
715  * extracts the values from the spin buttons, adjusts them according to the
716  * unit multiplier (inches, pixels, etc), then adds them to the gKeyFile.
717  */
718 static void
719 pcd_key_file_save_xy (GKeyFile *key_file, const gchar *group_name,
720  const gchar *key_name, gdouble multip,
721  GtkSpinButton *spin0, GtkSpinButton *spin1)
722 {
723  gdouble dd[2];
724 
725  dd[0] = multip * gtk_spin_button_get_value(spin0);
726  dd[1] = multip * gtk_spin_button_get_value(spin1);
727 
728  /* Clip the numbers to three decimal places. */
729  dd[0] = round(dd[0] * 1000) / 1000;
730  dd[1] = round(dd[1] * 1000) / 1000;
731  g_key_file_set_double_list(key_file, group_name, key_name, dd, 2);
732 }
733 
734 
735 /* This function saves the information about a single printed item into a
736  * check description file. It uses a helper function to extracts and save the
737  * item coordinates.
738  */
739 static void
740 pcd_key_file_save_item_xy (GKeyFile *key_file, int index,
741  CheckItemType type, gdouble multip,
742  GtkSpinButton *spin0, GtkSpinButton *spin1)
743 {
744  gchar *key;
745  key = g_strdup_printf("Type_%d", index);
746  g_key_file_set_string(key_file, KF_GROUP_ITEMS, key,
747  CheckItemTypeasString(type));
748  g_free(key);
749  key = g_strdup_printf("Coords_%d", index);
750  pcd_key_file_save_xy(key_file, KF_GROUP_ITEMS, key, multip, spin0, spin1);
751  g_free(key);
752 }
753 
754 
755 /* This function saves all of the information from the custom check dialog
756  * into a check description file.
757  */
758 static void
759 pcd_save_custom_data(PrintCheckDialog *pcd, const gchar *title)
760 {
761  GKeyFile *key_file;
762  GError *error = NULL;
763  GtkWidget *dialog;
764  gdouble multip;
765  gint i = 1;
766  GncGUID guid;
767  char buf[GUID_ENCODING_LENGTH+1];
768  gchar *filename, *pathname;
769 
770  multip = pcd_get_custom_multip(pcd);
771 
772  key_file = g_key_file_new();
773  guid_replace(&guid);
774  guid_to_string_buff(&guid, buf);
775  g_key_file_set_string(key_file, KF_GROUP_TOP, KF_KEY_GUID, buf);
776  g_key_file_set_string(key_file, KF_GROUP_TOP, KF_KEY_TITLE, title);
777  g_key_file_set_boolean(key_file, KF_GROUP_TOP, KF_KEY_SHOW_GRID, FALSE);
778  g_key_file_set_boolean(key_file, KF_GROUP_TOP, KF_KEY_SHOW_BOXES, FALSE);
779  g_key_file_set_double(key_file, KF_GROUP_TOP, KF_KEY_ROTATION,
780  gtk_spin_button_get_value(pcd->check_rotation));
781  pcd_key_file_save_xy(key_file, KF_GROUP_TOP, KF_KEY_TRANSLATION, multip,
782  pcd->translation_x, pcd->translation_y);
783 
784  pcd_key_file_save_item_xy(key_file, i++, PAYEE, multip,
785  pcd->payee_x, pcd->payee_y);
786  pcd_key_file_save_item_xy(key_file, i++, DATE, multip,
787  pcd->date_x, pcd->date_y);
788  pcd_key_file_save_item_xy(key_file, i++, AMOUNT_WORDS, multip,
789  pcd->words_x, pcd->words_y);
790  pcd_key_file_save_item_xy(key_file, i++, AMOUNT_NUMBER, multip,
791  pcd->number_x, pcd->number_y);
792  pcd_key_file_save_item_xy(key_file, i++, ADDRESS, multip,
793  pcd->address_x, pcd->address_y);
794  pcd_key_file_save_item_xy(key_file, i++, NOTES, multip,
795  pcd->notes_x, pcd->notes_y);
796  pcd_key_file_save_item_xy(key_file, i++, MEMO, multip,
797  pcd->memo_x, pcd->memo_y);
798  pcd_key_file_save_item_xy(key_file, i++, SPLITS_AMOUNT, multip,
799  pcd->splits_amount_x, pcd->splits_amount_y);
800  pcd_key_file_save_item_xy(key_file, i++, SPLITS_MEMO, multip,
801  pcd->splits_memo_x, pcd->splits_memo_y);
802  pcd_key_file_save_item_xy(key_file, i++, SPLITS_ACCOUNT, multip,
803  pcd->splits_account_x, pcd->splits_account_y);
804 
805  filename = g_strconcat(title, CHECK_NAME_EXTENSION, NULL);
806  pathname = g_build_filename(gnc_userdata_dir(), CHECK_FMT_DIR,
807  filename, NULL);
808 
809  if (gnc_key_file_save_to_file(pathname, key_file, &error))
810  {
811  if (!gnc_prefs_get_bool(GNC_PREFS_GROUP, GNC_PREF_PRINT_DATE_FMT))
812  /* Reload the format combo box and reselect the "custom" entry */
813  initialize_format_combobox(pcd);
814 
815  gtk_combo_box_set_active(GTK_COMBO_BOX(pcd->format_combobox),
816  pcd->format_max - 1);
817  }
818  else
819  {
820  dialog = gtk_message_dialog_new(GTK_WINDOW(pcd->dialog),
821  GTK_DIALOG_DESTROY_WITH_PARENT,
822  GTK_MESSAGE_ERROR,
823  GTK_BUTTONS_CLOSE, "%s",
824  _("Cannot save check format file."));
825  gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
826  _("Cannot open file %s"),
827  _(error->message));
828  gtk_dialog_run(GTK_DIALOG(dialog));
829  gtk_widget_destroy(dialog);
830  g_error_free(error);
831  }
832  g_free(pathname);
833  g_free(filename);
834 }
835 
836 
837 /* This function makes the OK button active iff a title has been entered.
838  */
839 void
840 gnc_check_format_title_changed (GtkEditable *editable, GtkWidget *ok_button)
841 {
842  const gchar *text;
843  gboolean sensitive;
844 
845  text = gtk_entry_get_text(GTK_ENTRY(editable));
846  sensitive = text && *text;
847  gtk_widget_set_sensitive(ok_button, sensitive);
848 }
849 
850 
851 /* This function is called when the user clicks the "save format" button in
852  * the check printing dialog. It presents another dialog to the user to get
853  * the filename for saving the data.
854  */
855 void
856 gnc_print_check_save_button_clicked(GtkButton *unused, PrintCheckDialog *pcd)
857 {
858  GtkWidget *dialog, *entry, *button;
859  GtkBuilder *builder;
860  gchar *title;
861 
862  builder = gtk_builder_new();
863  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "format_title_dialog");
864 
865  /* Get a title for the new check format. */
866  dialog = GTK_WIDGET(gtk_builder_get_object (builder, "format_title_dialog"));
867  entry = GTK_WIDGET(gtk_builder_get_object (builder, "format_title"));
868  button = GTK_WIDGET(gtk_builder_get_object (builder, "ok_button"));
869  gnc_check_format_title_changed(GTK_EDITABLE(entry), button);
870  gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, pcd);
871 
872  gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(pcd->dialog));
873  if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_OK)
874  {
875  gtk_widget_destroy(dialog);
876  g_object_unref(G_OBJECT(builder));
877  return;
878  }
879 
880  title = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
881  gtk_widget_destroy (dialog);
882 
883  g_object_unref(G_OBJECT(builder));
884 
885  pcd_save_custom_data(pcd, title);
886  g_free(title);
887 }
888 
889 
890 /* This function is an auxiliary debugging function for converting an array of
891  * doubles into a printable string.
892  */
893 static gchar *
894 doubles_to_string(gdouble *dd, gint len)
895 {
896  GString *str;
897  gint i;
898 
899  str = g_string_new_len(NULL, 50);
900  for (i = 0; i < len; i++)
901  g_string_append_printf(str, "%f ", dd[i]);
902  return g_string_free(str, FALSE);
903 }
904 
905 
906 /* This function reads in the information describing the placement for each
907  * item to be printed on a check. This information is all relative to the
908  * upper left hand corner of a "check". See the format_read_multicheck_info()
909  * function for determining if there are multiple checks on a single page of
910  * paper. This data is build into a linked list and saved as part of the check
911  * format information. These items will be printed in the same order they are
912  * read, meaning that items listed later in the date file can be printed over
913  * top of items that appear earlier in the file.
914  */
915 static GSList *
916 format_read_item_placement(const gchar *file,
917  GKeyFile *key_file, check_format_t *format)
918 {
919  check_item_t *data = NULL;
920  GError *error = NULL;
921  GSList *list = NULL;
922  gchar *key, *value, *name;
923  int item_num;
924  gboolean bval;
925  gdouble *dd;
926  gsize dd_len;
927 
928  /* Read until failure. */
929  for (item_num = 1;; item_num++)
930  {
931 
932  /* Create the per-item data structure */
933  data = g_new0(check_item_t, 1);
934  if (NULL == data)
935  return list;
936 
937  /* Get the item type */
938  key = g_strdup_printf("%s_%d", KF_KEY_TYPE, item_num);
939  value = g_key_file_get_string(key_file, KF_GROUP_ITEMS, key, &error);
940  if (error)
941  {
942  if ((error->domain == G_KEY_FILE_ERROR)
943  && (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND))
944  {
945  /* This is the expected exit from this function. */
946  goto cleanup;
947  }
948  goto failed;
949  }
950  DEBUG("Check file %s, group %s, key %s, value: %s",
951  file, KF_GROUP_ITEMS, key, value);
952  g_free(key);
953 
954  /* Convert the type from a string to an enum, ignoring case. */
955  name = g_utf8_strup(value, -1);
956  data->type = CheckItemTypefromString(name);
957  g_free(name);
958  g_free(value);
959 
960 
961  /* Get the item location */
962  key = g_strdup_printf("%s_%d", KF_KEY_COORDS, item_num);
963  dd = g_key_file_get_double_list(key_file, KF_GROUP_ITEMS,
964  key, &dd_len, &error);
965  if (error)
966  goto failed;
967  value = doubles_to_string(dd, dd_len);
968  DEBUG("Check file %s, group %s, key %s, length %"G_GSIZE_FORMAT"; values: %s",
969  file, KF_GROUP_ITEMS, key, dd_len, value);
970  g_free(value);
971 
972  /* Must have "x;y" or "x;y;w;h". */
973  switch (dd_len)
974  {
975  case 4:
976  data->w = dd[2];
977  data->h = dd[3];
978  /* fall through */
979  case 2:
980  data->x = dd[0];
981  data->y = dd[1];
982  break;
983  default:
984  g_warning
985  ("Check file %s, group %s, key %s, error: 2 or 4 values only",
986  file, KF_GROUP_ITEMS, key);
987  goto cleanup;
988  }
989  g_free(dd);
990  g_free(key);
991 
992  /* Any text item can specify:
993  * a font FONT_n
994  * an alignment if a width was provided for the item ALIGN_n
995  * blocking chars flag BLOCKING_CHARS_n
996  * These values are optional and do not cause a failure if they are missing. */
997 
998  if (data->type != PICTURE)
999  {
1000  key = g_strdup_printf("%s_%d", KF_KEY_FONT, item_num);
1001  data->font =
1002  g_key_file_get_string(key_file, KF_GROUP_ITEMS, key, &error);
1003  if (!error)
1004  {
1005  DEBUG("Check file %s, group %s, key %s, value: %s",
1006  file, KF_GROUP_ITEMS, key, data->font);
1007  }
1008  else
1009  {
1010  if (!((error->domain == G_KEY_FILE_ERROR)
1011  && (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND)))
1012  g_warning("Check file %s, group %s, key %s, error: %s",
1013  file, KF_GROUP_ITEMS, key, error->message);
1014  g_clear_error(&error);
1015  }
1016  g_free(key);
1017 
1018  key = g_strdup_printf("%s_%d", KF_KEY_ALIGN, item_num);
1019  value =
1020  g_key_file_get_string(key_file, KF_GROUP_ITEMS, key, &error);
1021  if (!error)
1022  {
1023  DEBUG("Check file %s, group %s, key %s, value: %s",
1024  file, KF_GROUP_ITEMS, key, value);
1025  name = g_utf8_strdown(value, -1);
1026  if (strcmp(name, "right") == 0)
1027  data->align = PANGO_ALIGN_RIGHT;
1028  else if (strcmp(name, "center") == 0)
1029  data->align = PANGO_ALIGN_CENTER;
1030  else
1031  data->align = PANGO_ALIGN_LEFT;
1032  g_free(name);
1033  g_free(value);
1034  }
1035  else
1036  {
1037  if (!((error->domain == G_KEY_FILE_ERROR)
1038  && (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND)))
1039  g_warning("Check file %s, group %s, key %s, error: %s",
1040  file, KF_GROUP_ITEMS, key, error->message);
1041  data->align = PANGO_ALIGN_LEFT;
1042  g_clear_error(&error);
1043  }
1044  g_free(key);
1045 
1046  key = g_strdup_printf("%s_%d", KF_KEY_BLOCKING, item_num);
1047  bval =
1048  g_key_file_get_boolean(key_file, KF_GROUP_ITEMS, key, &error);
1049  if (!error)
1050  {
1051  DEBUG("Check file %s, group %s, key %s, value: %d",
1052  file, KF_GROUP_ITEMS, key, bval);
1053  data->blocking = bval;
1054  }
1055  else
1056  {
1057  if (!((error->domain == G_KEY_FILE_ERROR)
1058  && (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND)))
1059  g_warning("Check file %s, group %s, key %s, error: %s",
1060  file, KF_GROUP_ITEMS, key, error->message);
1061  data->blocking = format->blocking;
1062  g_clear_error(&error);
1063  }
1064  g_free(key);
1065  }
1066  /* Get any extra data for specific items. */
1067  switch (data->type)
1068  {
1069  case PICTURE:
1070  key = g_strdup_printf("%s_%d", KF_KEY_FILENAME, item_num);
1071  data->filename =
1072  g_key_file_get_string(key_file, KF_GROUP_ITEMS, key,
1073  &error);
1074  if (error)
1075  goto failed;
1076  DEBUG("Check file %s, group %s, key %s, value: %s",
1077  file, KF_GROUP_ITEMS, key, data->filename);
1078  g_free(key);
1079  break;
1080  case TEXT:
1081  key = g_strdup_printf("%s_%d", KF_KEY_TEXT, item_num);
1082  data->text =
1083  g_key_file_get_string(key_file, KF_GROUP_ITEMS, key,
1084  &error);
1085  if (error)
1086  goto failed;
1087  DEBUG("Check file %s, group %s, key %s, value: %s",
1088  file, KF_GROUP_ITEMS, key, data->text);
1089  g_free(key);
1090  break;
1091  case DATE:
1092  /* no error if the date_format is not present */
1093  key = g_strdup_printf("%s_%d", KF_KEY_DATE_FORMAT, item_num);
1094  bval = g_key_file_get_boolean(key_file, KF_GROUP_ITEMS, key, &error);
1095  if (!error)
1096  {
1097  DEBUG("Check file %s, group %s, key %s, value: %d",
1098  file, KF_GROUP_ITEMS, key, bval);
1099  data->print_date_format = bval;
1100  }
1101  else
1102  {
1103  if (!((error->domain == G_KEY_FILE_ERROR)
1104  && (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND)))
1105  g_warning("Check file %s, group %s, key %s, error: %s",
1106  file, KF_GROUP_ITEMS, key, error->message);
1107  data->print_date_format = format->print_date_format;
1108  g_clear_error(&error);
1109  }
1110  g_free(key);
1111  break;
1112  default:
1113  break;
1114  }
1115 
1116  list = g_slist_append(list, data);
1117  data = NULL;
1118  }
1119 
1120  /* Should never be reached. */
1121  return list;
1122 
1123 failed:
1124  g_warning("Check file %s, group %s, key %s, error: %s",
1125  file, KF_GROUP_ITEMS, key, error->message);
1126 cleanup:
1127  if (error)
1128  g_error_free(error);
1129  if (data)
1130  g_free(data);
1131  if (key)
1132  g_free(key);
1133  return list;
1134 }
1135 
1136 
1137 /* Free the data describing the placement of a single item on a check.
1138  */
1139 static void
1140 format_free_item_placement(check_item_t *data)
1141 {
1142  if (data->font)
1143  g_free(data->font);
1144  if (data->text)
1145  g_free(data->text);
1146  if (data->filename)
1147  g_free(data->filename);
1148  g_free(data);
1149 }
1150 
1151 
1152 /* Read the information describing whether a page contains multiple checks or
1153  * a single check. If there are multiple checks on a page, this functions
1154  * builds a linked list of the position names and their offsets (from the
1155  * upper left corner of the page).
1156  */
1157 static GSList *
1158 format_read_multicheck_info(const gchar *file,
1159  GKeyFile *key_file, check_format_t *format)
1160 {
1161  GError *error = NULL;
1162  GSList *list = NULL;
1163  gchar *key, **names;
1164  gsize length;
1165  gint i;
1166 
1167  key = g_strdup_printf("%s", KF_KEY_HEIGHT);
1168  format->height = g_key_file_get_double(key_file, KF_GROUP_POS, key, &error);
1169  if (error)
1170  {
1171  if ((error->domain == G_KEY_FILE_ERROR)
1172  && ((error->code == G_KEY_FILE_ERROR_GROUP_NOT_FOUND)
1173  || (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND)))
1174  {
1175  g_clear_error(&error);
1176  format->height = 0.0;
1177  }
1178  else
1179  {
1180  g_warning("Check file %s, error reading group %s, key %s: %s",
1181  file, KF_GROUP_POS, key, error->message);
1182  g_free(key);
1183  return NULL;
1184  }
1185  }
1186 
1187  names = g_key_file_get_string_list(key_file, KF_GROUP_POS, KF_KEY_NAMES,
1188  &length, &error);
1189  if (error)
1190  {
1191  if ((error->domain == G_KEY_FILE_ERROR)
1192  && ((error->code == G_KEY_FILE_ERROR_GROUP_NOT_FOUND)
1193  || (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND)))
1194  {
1195  /* This is the expected exit from this function. */
1196  g_free(key);
1197  return NULL;
1198  }
1199  g_warning("Check file %s, error reading group %s, key %s: %s",
1200  file, KF_GROUP_POS, key, error->message);
1201  g_free(key);
1202  return list;
1203  }
1204 
1205  for (i = 0; i < length; i++)
1206  list = g_slist_append(list, g_strdup(names[i]));
1207 
1208  g_strfreev(names);
1209  return list;
1210 }
1211 
1212 
1213 /* Free the data describing the placement of multiple checks on a page.
1214  */
1215 static void
1216 free_check_position(gchar *name)
1217 {
1218  g_free(name);
1219 }
1220 
1221 
1222 /* Read the information describing the general layout of a page of checks.
1223  * All items in this section are optional except or the name of the check
1224  * style.
1225  */
1226 static gboolean
1227 format_read_general_info(const gchar *file,
1228  GKeyFile *key_file, check_format_t *format)
1229 {
1230  GError *error = NULL;
1231  gchar **parts;
1232  gchar *value;
1233  double *dd;
1234  gsize dd_len;
1235 
1236  value = g_key_file_get_string(key_file, KF_GROUP_TOP, KF_KEY_GUID, &error);
1237  if (error)
1238  {
1239  g_warning("Check file %s, group %s, key %s, error: %s",
1240  file, KF_GROUP_TOP, KF_KEY_GUID, error->message);
1241  g_error_free(error);
1242  return FALSE;
1243  }
1244  parts = g_strsplit(value, "-", -1);
1245  format->guid = g_strjoinv("", parts);
1246  g_strfreev(parts);
1247  DEBUG("Check file %s, group %s, key %s, value: %s",
1248  file, KF_GROUP_TOP, KF_KEY_GUID, format->guid);
1249 
1250  format->title =
1251  g_key_file_get_string(key_file, KF_GROUP_TOP, KF_KEY_TITLE, &error);
1252  if (!error)
1253  {
1254  DEBUG("Check file %s, group %s, key %s, value: %s",
1255  file, KF_GROUP_TOP, KF_KEY_TITLE, format->title);
1256  }
1257  else
1258  {
1259  g_warning("Check file %s, group %s, key %s, error: %s",
1260  file, KF_GROUP_TOP, KF_KEY_TITLE, error->message);
1261  return FALSE;
1262  }
1263 
1264  format->blocking =
1265  g_key_file_get_boolean(key_file, KF_GROUP_TOP, KF_KEY_BLOCKING,
1266  &error);
1267  if (!error)
1268  {
1269  DEBUG("Check file %s, group %s, key %s, value: %d",
1270  file, KF_GROUP_TOP, KF_KEY_BLOCKING, format->blocking);
1271  }
1272  else
1273  {
1274  if (!((error->domain == G_KEY_FILE_ERROR)
1275  && (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND)))
1276  g_warning("Check file %s, group %s, key %s, error: %s",
1277  file, KF_GROUP_TOP, KF_KEY_BLOCKING, error->message);
1278  if ( gnc_prefs_get_bool(GNC_PREFS_GROUP, GNC_PREF_BLOCKING_CHARS) )
1279  {
1280  format->blocking = TRUE;
1281  }
1282  else
1283  {
1284  format->blocking = FALSE;
1285  }
1286  g_clear_error(&error);
1287  }
1288 
1289  format->print_date_format =
1290  g_key_file_get_boolean(key_file, KF_GROUP_TOP, KF_KEY_DATE_FORMAT,
1291  &error);
1292  if (!error)
1293  {
1294  DEBUG("Check file %s, group %s, key %s, value: %d",
1295  file, KF_GROUP_TOP, KF_KEY_DATE_FORMAT, format->print_date_format);
1296  }
1297  else
1298  {
1299  if (!((error->domain == G_KEY_FILE_ERROR)
1300  && (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND)))
1301  g_warning("Check file %s, group %s, key %s, error: %s",
1302  file, KF_GROUP_TOP, KF_KEY_DATE_FORMAT, error->message);
1303  if ( gnc_prefs_get_bool(GNC_PREFS_GROUP, GNC_PREF_PRINT_DATE_FMT) )
1304  {
1305  format->print_date_format = TRUE;
1306  }
1307  else
1308  {
1309  format->print_date_format = FALSE;
1310  }
1311  g_clear_error(&error);
1312  }
1313 
1314  format->show_grid =
1315  g_key_file_get_boolean(key_file, KF_GROUP_TOP, KF_KEY_SHOW_GRID,
1316  &error);
1317  if (!error)
1318  {
1319  DEBUG("Check file %s, group %s, key %s, value: %d",
1320  file, KF_GROUP_TOP, KF_KEY_SHOW_GRID, format->show_grid);
1321  }
1322  else
1323  {
1324  if (!((error->domain == G_KEY_FILE_ERROR)
1325  && (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND)))
1326  g_warning("Check file %s, group %s, key %s, error: %s",
1327  file, KF_GROUP_TOP, KF_KEY_SHOW_GRID, error->message);
1328  format->show_grid = FALSE;
1329  g_clear_error(&error);
1330  }
1331 
1332  format->show_boxes =
1333  g_key_file_get_boolean(key_file, KF_GROUP_TOP, KF_KEY_SHOW_BOXES,
1334  &error);
1335  if (!error)
1336  {
1337  DEBUG("Check file %s, group %s, key %s, value: %d",
1338  file, KF_GROUP_TOP, KF_KEY_SHOW_BOXES, format->show_boxes);
1339  }
1340  else
1341  {
1342  if (!((error->domain == G_KEY_FILE_ERROR)
1343  && (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND)))
1344  g_warning("Check file %s, group %s, key %s, error: %s",
1345  file, KF_GROUP_TOP, KF_KEY_SHOW_BOXES, error->message);
1346  format->show_boxes = FALSE;
1347  g_clear_error(&error);
1348  }
1349 
1350  format->font =
1351  g_key_file_get_string(key_file, KF_GROUP_TOP, KF_KEY_FONT, &error);
1352  if (!error)
1353  {
1354  DEBUG("Check file %s, group %s, key %s, value: %s",
1355  file, KF_GROUP_TOP, KF_KEY_FONT, format->font);
1356  }
1357  else
1358  {
1359  if (!((error->domain == G_KEY_FILE_ERROR)
1360  && (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND)))
1361  g_warning("Check file %s, group %s, key %s, error: %s",
1362  file, KF_GROUP_TOP, KF_KEY_FONT, error->message);
1363  g_clear_error(&error);
1364  }
1365 
1366  format->rotation =
1367  g_key_file_get_double(key_file, KF_GROUP_TOP, KF_KEY_ROTATION, &error);
1368  if (!error)
1369  {
1370  DEBUG("Check file %s, group %s, key %s, value: %f",
1371  file, KF_GROUP_TOP, KF_KEY_ROTATION, format->rotation);
1372  }
1373  else
1374  {
1375  if (!((error->domain == G_KEY_FILE_ERROR)
1376  && (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND)))
1377  g_warning("Check file %s, group %s, key %s, error: %s",
1378  file, KF_GROUP_TOP, KF_KEY_ROTATION, error->message);
1379  format->rotation = 0.0;
1380  g_clear_error(&error);
1381  }
1382 
1383  dd = g_key_file_get_double_list(key_file, KF_GROUP_TOP, KF_KEY_TRANSLATION,
1384  &dd_len, &error);
1385  if (!error)
1386  {
1387  value = doubles_to_string(dd, dd_len);
1388  DEBUG("Check file %s, group %s, key %s, length %"G_GSIZE_FORMAT"; values: %s",
1389  file, KF_GROUP_TOP, KF_KEY_TRANSLATION, dd_len, value);
1390  g_free(value);
1391 
1392  if (dd_len == 2)
1393  {
1394  format->trans_x = dd[0];
1395  format->trans_y = dd[1];
1396  }
1397  else
1398  {
1399  g_warning("Check file %s, error top %s, key %s: 2 values only",
1400  file, KF_GROUP_TOP, KF_KEY_TRANSLATION);
1401  }
1402  g_free(dd);
1403  }
1404  else
1405  {
1406  if (!((error->domain == G_KEY_FILE_ERROR)
1407  && (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND)))
1408  g_warning("Check file %s, group top %s, key %s: %s",
1409  file, KF_GROUP_ITEMS, KF_KEY_TRANSLATION, error->message);
1410  g_clear_error(&error);
1411  }
1412 
1413  return TRUE;
1414 }
1415 
1416 
1417 /* Free all of the information describing a page of checks.
1418  */
1419 static void
1420 free_check_format(check_format_t *data)
1421 {
1422  g_return_if_fail(data);
1423  g_free(data->guid);
1424  g_free(data->filename);
1425  g_free(data->title);
1426  g_free(data->font);
1427  g_slist_foreach(data->positions, (GFunc) free_check_position, NULL);
1428  g_slist_free(data->positions);
1429  g_slist_foreach(data->items, (GFunc) format_free_item_placement, NULL);
1430  g_slist_free(data->items);
1431  g_free(data);
1432 }
1433 
1434 
1435 /* Read a single check format file and append the resulting format to the
1436  * list of all known formats. This function calls other functions to read
1437  * each section of the data file.
1438  */
1439 static check_format_t *
1440 read_one_check_format(PrintCheckDialog *pcd, const gchar *groupname,
1441  const gchar *dirname, const gchar *file)
1442 {
1443  gchar *pathname;
1444  GKeyFile *key_file;
1445  check_format_t *format;
1446 
1447  pathname = g_build_filename(dirname, file, (char *)NULL);
1448  key_file = gnc_key_file_load_from_file(pathname, FALSE, FALSE, NULL);
1449  g_free(pathname);
1450  if (!key_file)
1451  {
1452  g_warning("Check file %s, cannot load file", file);
1453  return NULL;
1454  }
1455 
1456  format = g_new0(check_format_t, 1);
1457  format->group = groupname;
1458  format->filename = g_strdup(file);
1459  if (format_read_general_info(file, key_file, format))
1460  {
1461  format->positions = format_read_multicheck_info(file, key_file, format);
1462  format->items = format_read_item_placement(file, key_file, format);
1463  }
1464 
1465  g_key_file_free(key_file);
1466  if ((NULL == format->title) || (NULL == format->items))
1467  {
1468  g_warning("Check file %s, no items read. Dropping file.", file);
1469  free_check_format(format);
1470  return NULL;
1471  }
1472 
1473  return format;
1474 }
1475 
1476 
1477 /* Iterate over a single check directory, throwing out any backup files and
1478  * then calling a helper function to read and parse the check format within
1479  * the file.
1480  */
1481 static void
1482 read_one_check_directory(PrintCheckDialog *pcd, GtkListStore *store,
1483  const gchar *groupname, const gchar *dirname)
1484 {
1485  check_format_t *format = NULL, *existing;
1486  GDir *dir;
1487  const gchar *filename;
1488  GtkTreeIter iter;
1489  GtkWidget *dialog;
1490  gboolean found = FALSE;
1491 
1492  dir = g_dir_open(dirname, 0, NULL);
1493  if (dir == NULL)
1494  return;
1495 
1496  while ((filename = g_dir_read_name(dir)) != NULL)
1497  {
1498  if (g_str_has_prefix(filename, "#"))
1499  continue;
1500  if (!g_str_has_suffix(filename, ".chk"))
1501  continue;
1502 
1503  format = read_one_check_format(pcd, groupname, dirname, filename);
1504  if (NULL == format)
1505  continue;
1506 
1507  existing = find_existing_format(store, format->guid, NULL);
1508  if (existing)
1509  {
1510  dialog = gtk_message_dialog_new
1511  (GTK_WINDOW(pcd->dialog),
1512  GTK_DIALOG_DESTROY_WITH_PARENT,
1513  GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s",
1514  _("There is a duplicate check format file."));
1515  gtk_message_dialog_format_secondary_text
1516  (GTK_MESSAGE_DIALOG(dialog),
1517  /* Translators:
1518  %1$s is the type of the first check format
1519  (user defined or application defined);
1520  %2$s is the filename of that format;
1521  %3$s the type of the other check format; and
1522  %4$s the filename of that other format. */
1523  _("The GUIDs in the %s check format file '%s' and "
1524  "the %s check format file '%s' match."),
1525  existing->group, existing->filename,
1526  format->group, format->filename);
1527  gtk_dialog_run(GTK_DIALOG(dialog));
1528  gtk_widget_destroy(dialog);
1529  free_check_format (format);
1530  }
1531  else
1532  {
1533  gtk_list_store_append(store, &iter);
1534  gtk_list_store_set(store, &iter, COL_NAME, format->title,
1535  COL_DATA, format, -1);
1536  found = TRUE;
1537  }
1538  }
1539  g_dir_close(dir);
1540 
1541  /* If any files were added to the list, add a separator between
1542  * this group and the next. */
1543  if (found)
1544  {
1545  gtk_list_store_append(store, &iter);
1546  gtk_list_store_set(store, &iter, COL_SEP, TRUE, -1);
1547  }
1548 }
1549 
1550 
1551 /* Read all check formats. This function first looks in the system directory
1552  * for check files, and then looks in the user's .gnucash directory for any
1553  * custom check files.
1554  */
1555 static void
1556 read_formats(PrintCheckDialog *pcd, GtkListStore *store)
1557 {
1558  gchar *dirname, *pkgdatadir;
1559 
1560  pkgdatadir = gnc_path_get_pkgdatadir();
1561  dirname = g_build_filename(pkgdatadir, CHECK_FMT_DIR, (char *)NULL);
1562  /* Translators: This is a directory name. It may be presented to
1563  * the user to indicate that some data file was defined by the
1564  * gnucash application. */
1565  read_one_check_directory(pcd, store, _("application"), dirname);
1566  g_free(dirname);
1567  g_free(pkgdatadir);
1568 
1569  dirname = gnc_build_userdata_path(CHECK_FMT_DIR);
1570  /* Translators: This is a directory name. It may be presented to
1571  * the user to indicate that some data file was defined by a
1572  * user herself. */
1573  read_one_check_directory(pcd, store, _("user"), dirname);
1574  g_free(dirname);
1575 }
1576 
1577 
1578 static gboolean
1579 format_is_a_separator (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1580 {
1581  gboolean separator;
1582 
1583  gtk_tree_model_get(model, iter, COL_SEP, &separator, -1);
1584  return separator;
1585 }
1586 
1587 
1588 static void
1589 initialize_format_combobox (PrintCheckDialog *pcd)
1590 {
1591  GtkListStore *store;
1592  GtkTreeIter iter;
1593 
1594  store = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_BOOLEAN);
1595  read_formats(pcd, store);
1596  gtk_list_store_append(store, &iter);
1597  gtk_list_store_set(store, &iter, COL_NAME, _("Custom"), -1);
1598  pcd->format_max = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), NULL);
1599  gtk_combo_box_set_model(GTK_COMBO_BOX(pcd->format_combobox),
1600  GTK_TREE_MODEL(store));
1601  gtk_combo_box_set_row_separator_func(GTK_COMBO_BOX(pcd->format_combobox),
1602  format_is_a_separator, NULL, NULL);
1603  g_object_unref (store);
1604 }
1605 
1606 
1607 /*****************************************************
1608  * gnc_ui_print_check_dialog_create *
1609  * make a new print check dialog and wait for it. *
1610  *****************************************************/
1611 void
1612 gnc_ui_print_check_dialog_create(GtkWidget *parent,
1613  GList *splits)
1614 {
1615  PrintCheckDialog *pcd;
1616  GtkBuilder *builder;
1617  GtkWidget *table;
1618  gchar *font;
1619  Transaction *trans = NULL;
1620 
1621  pcd = g_new0(PrintCheckDialog, 1);
1622  pcd->caller_window = GTK_WINDOW(parent);
1623  pcd->splits = g_list_copy(splits);
1624 
1625  builder = gtk_builder_new();
1626  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment1");
1627  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment2");
1628  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment3");
1629  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment4");
1630  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment5");
1631  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment6");
1632  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment7");
1633  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment8");
1634  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment9");
1635  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment10");
1636  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment11");
1637  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment12");
1638  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment13");
1639  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment14");
1640  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment15");
1641  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment16");
1642  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment17");
1643  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment18");
1644  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment19");
1645  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment20");
1646  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment21");
1647  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment22");
1648  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment23");
1649  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment24");
1650  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "liststore1");
1651  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "liststore2");
1652  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "liststore3");
1653  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "print_check_dialog");
1654 
1655  gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, pcd);
1656 
1657  pcd->builder = builder;
1658  pcd->dialog = GTK_WIDGET(gtk_builder_get_object (builder, "print_check_dialog"));
1659 
1660  // Set the name for this dialog so it can be easily manipulated with css
1661  gtk_widget_set_name (GTK_WIDGET(pcd->dialog), "gnc-id-print-check");
1662 
1663  /* now pick out the relevant child widgets */
1664  pcd->format_combobox = GTK_WIDGET(gtk_builder_get_object (builder, "check_format_combobox"));
1665  pcd->position_combobox = GTK_WIDGET(gtk_builder_get_object (builder, "check_position_combobox"));
1666  pcd->first_page_count = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "first_page_count_entry"));
1667 
1668  pcd->custom_table = GTK_WIDGET(gtk_builder_get_object (builder, "custom_table"));
1669  pcd->payee_x = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "payee_x_entry"));
1670  pcd->payee_y = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "payee_y_entry"));
1671  pcd->date_x = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "date_x_entry"));
1672  pcd->date_y = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "date_y_entry"));
1673  pcd->words_x = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "amount_words_x_entry"));
1674  pcd->words_y = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "amount_words_y_entry"));
1675  pcd->number_x = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "amount_numbers_x_entry"));
1676  pcd->number_y = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "amount_numbers_y_entry"));
1677  pcd->notes_x = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "notes_x_entry"));
1678  pcd->notes_y = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "notes_y_entry"));
1679  pcd->memo_x = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "memo_x_entry"));
1680  pcd->memo_y = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "memo_y_entry"));
1681  pcd->address_x = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "address_x_entry"));
1682  pcd->address_y = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "address_y_entry"));
1683  pcd->splits_amount_x = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "splits_amount_x_entry"));
1684  pcd->splits_amount_y = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "splits_amount_y_entry"));
1685  pcd->splits_memo_x = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "splits_memo_x_entry"));
1686  pcd->splits_memo_y = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "splits_memo_y_entry"));
1687  pcd->splits_account_x = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "splits_account_x_entry"));
1688  pcd->splits_account_y = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "splits_account_y_entry"));
1689  pcd->translation_x = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "translation_x_entry"));
1690  pcd->translation_y = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "translation_y_entry"));
1691  pcd->translation_label = GTK_WIDGET(gtk_builder_get_object (builder, "translation_label"));
1692  pcd->check_rotation = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "check_rotation_entry"));
1693  pcd->units_combobox = GTK_WIDGET(gtk_builder_get_object (builder, "units_combobox"));
1694 
1695  gtk_window_set_transient_for(GTK_WINDOW(pcd->dialog), pcd->caller_window);
1696 
1697  /* Create and attach the date-format chooser */
1698  table = GTK_WIDGET(gtk_builder_get_object (builder, "options_table"));
1699  pcd->date_format = gnc_date_format_new_without_label();
1700  gtk_grid_attach (GTK_GRID(table), pcd->date_format, 1, 4, 1, 1);
1701 
1702  /* Default font (set in preferences) */
1703  font = gnc_prefs_get_string(GNC_PREFS_GROUP, GNC_PREF_DEFAULT_FONT);
1704  pcd->default_font = font ? font : g_strdup(DEFAULT_FONT);
1705 
1706  /* Update the combo boxes bases on the available check formats */
1707  initialize_format_combobox(pcd);
1708 
1709  /* address */
1710  pcd->check_address_name = GTK_WIDGET(gtk_builder_get_object (builder, "check_address_name"));
1711  pcd->check_address_1 = GTK_WIDGET(gtk_builder_get_object (builder, "check_address_1"));
1712  pcd->check_address_2 = GTK_WIDGET(gtk_builder_get_object (builder, "check_address_2"));
1713  pcd->check_address_3 = GTK_WIDGET(gtk_builder_get_object (builder, "check_address_3"));
1714  pcd->check_address_4 = GTK_WIDGET(gtk_builder_get_object (builder, "check_address_4"));
1715  /* fill in any available address data */
1716  /* Can't access business objects e.g. Customer,Vendor,Employee because
1717  * it would create build problems */
1718  if (g_list_length(pcd->splits) == 1)
1719  {
1720  GncOwner txn_owner;
1721 
1722  trans = xaccSplitGetParent((Split *)(pcd->splits->data));
1723  if (gncOwnerGetOwnerFromTxn (trans, &txn_owner))
1724  {
1725  GncOwner owner;
1726  gncOwnerCopy (gncOwnerGetEndOwner (&txn_owner), &owner);
1727 
1728  /* Got a business owner, get the address */
1729  gtk_entry_set_text(GTK_ENTRY(pcd->check_address_name), gncOwnerGetName(&owner));
1730  gtk_entry_set_text(GTK_ENTRY(pcd->check_address_1), gncAddressGetAddr1 (gncOwnerGetAddr(&owner)));
1731  gtk_entry_set_text(GTK_ENTRY(pcd->check_address_2), gncAddressGetAddr2 (gncOwnerGetAddr(&owner)));
1732  gtk_entry_set_text(GTK_ENTRY(pcd->check_address_3), gncAddressGetAddr3 (gncOwnerGetAddr(&owner)));
1733  gtk_entry_set_text(GTK_ENTRY(pcd->check_address_4), gncAddressGetAddr4 (gncOwnerGetAddr(&owner)));
1734  }
1735  }
1736 
1737  /* Use transaction description as address name if no better address has been found */
1738  if ( trans && (0 == gtk_entry_get_text_length (GTK_ENTRY(pcd->check_address_name))) )
1739  gtk_entry_set_text(GTK_ENTRY(pcd->check_address_name), xaccTransGetDescription(trans));
1740 
1741  gtk_widget_destroy(GTK_WIDGET(gtk_builder_get_object (builder, "lower_left")));
1742 
1743  gnc_ui_print_restore_dialog(pcd);
1744  gnc_restore_window_size(GNC_PREFS_GROUP, GTK_WINDOW(pcd->dialog), GTK_WINDOW (parent));
1745 
1746  g_object_unref(G_OBJECT(builder));
1747  gtk_widget_show_all(pcd->dialog);
1748 }
1749 
1750 
1751 /**************************************
1752  * Print check contents to the page. *
1753  **************************************/
1754 
1755 /* Draw a grid pattern on the page to be printed. This grid is helpful when
1756  * figuring out the offsets for where to print various items on the page.
1757  */
1758 static void
1759 draw_grid(GtkPrintContext *context, gint width, gint height, const gchar *font)
1760 {
1761  const double dash_pattern[2] = { 1.0, 5.0 };
1762  PangoFontDescription *desc;
1763  PangoLayout *layout;
1764  cairo_t *cr;
1765  gchar *text;
1766  gint i;
1767 
1768  /* Initialize for printing text */
1769  layout = gtk_print_context_create_pango_layout(context);
1770  desc = pango_font_description_from_string(font);
1771  pango_layout_set_font_description(layout, desc);
1772  pango_font_description_free(desc);
1773  pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT);
1774  pango_layout_set_width(layout, -1);
1775 
1776  /* Set up the line to draw with. */
1777  cr = gtk_print_context_get_cairo_context(context);
1778  cairo_save(cr);
1779  cairo_set_line_width(cr, 1.0);
1780  cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
1781  cairo_set_dash(cr, dash_pattern, 2, 0);
1782 
1783  /* Draw horizontal lines */
1784  for (i = -200; i < (height + 200); i += 50)
1785  {
1786  text = g_strdup_printf("%d", (int)i);
1787  cairo_move_to(cr, -200, i);
1788  cairo_line_to(cr, width + 200, i);
1789  cairo_stroke(cr);
1790  pango_layout_set_text(layout, text, -1);
1791  cairo_move_to(cr, 0, i);
1792  pango_cairo_show_layout(cr, layout);
1793  g_free(text);
1794  }
1795 
1796  /* Draw vertical lines */
1797  for (i = -200; i < (width + 200); i += 50)
1798  {
1799  text = g_strdup_printf("%d", (int)i);
1800  cairo_move_to(cr, i, -200);
1801  cairo_line_to(cr, i, height + 200);
1802  cairo_stroke(cr);
1803  pango_layout_set_text(layout, text, -1);
1804  cairo_move_to(cr, i, 0);
1805  pango_cairo_show_layout(cr, layout);
1806  g_free(text);
1807  }
1808 
1809  /* Clean up after ourselves */
1810  cairo_restore(cr);
1811  g_object_unref(layout);
1812 }
1813 
1814 
1815 /* Print a single line of text to the printed page. If a width and height
1816  * are specified, the line will be wrapped at the specified width, and the
1817  * resulting text will be clipped if it does not fit in the space
1818  * available.
1819  */
1820 static gdouble
1821 draw_text(GtkPrintContext *context, const gchar *text, check_item_t *data,
1822  PangoFontDescription *default_desc)
1823 {
1824  PangoFontDescription *desc;
1825  PangoLayout *layout;
1826  cairo_t *cr;
1827  gint layout_height, layout_width;
1828  gdouble width, height;
1829  gchar *new_text;
1830 
1831  if ((NULL == text) || (strlen(text) == 0))
1832  return 0.0;
1833 
1834  /* Initialize for printing text */
1835  layout = gtk_print_context_create_pango_layout(context);
1836  if (data->font)
1837  {
1838  desc = pango_font_description_from_string(data->font);
1839  pango_layout_set_font_description(layout, desc);
1840  pango_font_description_free(desc);
1841  }
1842  else
1843  {
1844  pango_layout_set_font_description(layout, default_desc);
1845  }
1846  pango_layout_set_alignment(layout,
1847  data->w ? data->align : PANGO_ALIGN_LEFT);
1848  pango_layout_set_width(layout, data->w ? data->w * PANGO_SCALE : -1);
1849  pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
1850  if ( data->blocking )
1851  {
1852  new_text = g_strdup_printf("***%s***", text);
1853  pango_layout_set_text(layout, new_text, -1);
1854  g_free(new_text);
1855  }
1856  else
1857  {
1858  pango_layout_set_text(layout, text, -1);
1859  }
1860  pango_layout_get_size(layout, &layout_width, &layout_height);
1861  width = (gdouble) layout_width / PANGO_SCALE;
1862  height = (gdouble) layout_height / PANGO_SCALE;
1863 
1864  cr = gtk_print_context_get_cairo_context(context);
1865  cairo_save(cr);
1866 
1867  /* Clip text to the enclosing rectangle */
1868  if (data->w && data->h)
1869  {
1870  DEBUG("Text clip rectangle, coords %f,%f, size %f,%f",
1871  data->x, data->y - data->h, data->w, data->h);
1872  cairo_rectangle(cr, data->x, data->y - data->h, data->w, data->h);
1873  cairo_clip_preserve(cr);
1874  }
1875 
1876  /* Draw the text */
1877  DEBUG("Text move to %f,%f, print '%s'", data->x, data->y,
1878  text ? text : "(null)");
1879  cairo_move_to(cr, data->x, data->y - height);
1880  pango_cairo_show_layout(cr, layout);
1881 
1882  /* Clean up after ourselves */
1883  cairo_restore(cr);
1884  g_object_unref(layout);
1885  return width;
1886 
1887 }
1888 
1889 
1890 /* Find and load the specified image. If the specified filename isn't an
1891  * absolute path name, this code will also look in the gnucash system check
1892  * format directory, and then in the user's private check format
1893  * directory.
1894  *
1895  * NOTE: The gtk_image_new_from_file() function never fails. If it can't
1896  * find the specified file, it returns the "broken image" icon. This function
1897  * takes advantage of that.
1898 */
1899 static GtkWidget *
1900 read_image (const gchar *filename)
1901 {
1902  GtkWidget *image;
1903  gchar *pkgdatadir, *dirname, *tmp_name;
1904 
1905  if (g_path_is_absolute(filename))
1906  return gtk_image_new_from_file(filename);
1907 
1908  pkgdatadir = gnc_path_get_pkgdatadir();
1909  tmp_name = g_build_filename(pkgdatadir, CHECK_FMT_DIR, filename, (char *)NULL);
1910  if (!g_file_test(tmp_name, G_FILE_TEST_EXISTS))
1911  {
1912  g_free(tmp_name);
1913  dirname = gnc_build_userdata_path(CHECK_FMT_DIR);
1914  tmp_name = g_build_filename(dirname, filename, (char *)NULL);
1915  g_free(dirname);
1916  }
1917  image = gtk_image_new_from_file(tmp_name);
1918  g_free(tmp_name);
1919  return image;
1920 }
1921 
1922 
1923 /* Print a single image to the printed page. This picture will be scaled
1924  * down to fit in the specified size rectangle. Scaling is done with the
1925  * proportions locked 1:1 so as not to distort the image.
1926  */
1927 static void
1928 draw_picture(GtkPrintContext *context, check_item_t *data)
1929 {
1930  cairo_t *cr;
1931  GdkPixbuf *pixbuf, *scaled_pixbuf;
1932  GtkImage *image;
1933  gint pix_w, pix_h;
1934  gdouble scale_w, scale_h, scale;
1935 
1936  cr = gtk_print_context_get_cairo_context(context);
1937  cairo_save(cr);
1938 
1939  /* Get the picture. */
1940  image = GTK_IMAGE(read_image(data->filename));
1941  pixbuf = gtk_image_get_pixbuf(image);
1942  if (pixbuf)
1943  {
1944  g_object_ref(pixbuf);
1945  }
1946  else
1947  {
1948  GtkIconTheme *def_theme = gtk_icon_theme_get_default ();
1949 
1950  g_warning("Filename '%s' cannot be read or understood.",
1951  data->filename);
1952 
1953  pixbuf = gtk_icon_theme_load_icon (def_theme,
1954  "image-missing",
1955  16,
1956  GTK_ICON_LOOKUP_USE_BUILTIN,
1957  NULL);
1958  }
1959  pix_w = gdk_pixbuf_get_width(pixbuf);
1960  pix_h = gdk_pixbuf_get_height(pixbuf);
1961 
1962  /* Draw the enclosing rectangle */
1963  if (data->w && data->h)
1964  {
1965  cairo_rectangle(cr, data->x, data->y - data->h, data->w, data->h);
1966  DEBUG("Picture clip rectangle, user coords %f,%f, user size %f,%f",
1967  data->x, data->y - data->h, data->w, data->h);
1968  }
1969  else
1970  {
1971  cairo_rectangle(cr, data->x, data->y - pix_h, pix_w, pix_h);
1972  DEBUG("Picture clip rectangle, user coords %f,%f, pic size %d,%d",
1973  data->x, data->y - data->h, pix_w, pix_h);
1974  }
1975  cairo_clip_preserve(cr);
1976 
1977  /* Scale down to fit. Never scale up. */
1978  scale_w = scale_h = 1;
1979  if (data->w && (pix_w > data->w))
1980  scale_w = data->w / pix_w;
1981  if (data->h && (pix_h > data->h))
1982  scale_h = data->h / pix_h;
1983  scale = MIN(scale_w, scale_h);
1984 
1985  if (scale != 1)
1986  {
1987  scaled_pixbuf = gdk_pixbuf_scale_simple(pixbuf, pix_w * scale,
1988  pix_h * scale,
1989  GDK_INTERP_BILINEAR);
1990  pix_h = gdk_pixbuf_get_height(scaled_pixbuf);
1991  gdk_cairo_set_source_pixbuf(cr, scaled_pixbuf, data->x,
1992  data->y - pix_h);
1993 
1994  g_object_unref(scaled_pixbuf);
1995  }
1996  else
1997  {
1998  gdk_cairo_set_source_pixbuf(cr, pixbuf, data->x, data->y - pix_h);
1999  }
2000  g_object_unref(pixbuf);
2001  cairo_paint(cr);
2002 
2003  /* Clean up after ourselves */
2004  cairo_restore(cr);
2005  gtk_widget_destroy(GTK_WIDGET(image));
2006 }
2007 
2008 
2009 #define DATE_FMT_HEIGHT 8
2010 #define DATE_FMT_SLOP 2
2011 
2012 /* There is a new Canadian requirement that all software that prints the date
2013  * on a check must also print the format of that date underneath using a 6-8
2014  * point font. This function implements that requirement. It requires the
2015  * font description used in printing the date so that it can print in the same
2016  * font using a smaller point size. It also requires width of the printed
2017  * date as an argument, allowing it to center the format string under the
2018  * date.
2019  *
2020  * Note: This code only prints a date if the user has explicitly requested it
2021  * via a preference setting. This is because gnucash has no way of
2022  * knowing if the user's checks already have a date format printed on them.
2023  */
2024 static void
2025 draw_date_format(GtkPrintContext *context, const gchar *date_format,
2026  check_item_t *data, PangoFontDescription *default_desc,
2027  gdouble width)
2028 {
2029  PangoFontDescription *date_desc;
2030  check_item_t date_item;
2031  gchar *text = NULL, *expanded = NULL;
2032  const gchar *c;
2033  GString *cdn_fmt;
2034 
2035  setlocale(LC_ALL, NULL);
2036  if ( !data->print_date_format ) return;
2037 
2038  date_desc = pango_font_description_copy_static(default_desc);
2039  pango_font_description_set_size(date_desc, DATE_FMT_HEIGHT * PANGO_SCALE);
2040  date_item = *data;
2041  date_item.y += (DATE_FMT_HEIGHT + DATE_FMT_SLOP);
2042  date_item.w = width;
2043  date_item.h = DATE_FMT_HEIGHT + DATE_FMT_SLOP;
2044  date_item.align = PANGO_ALIGN_CENTER;
2045 
2046  /* This is a date format string. It should only contain ascii. */
2047  cdn_fmt = g_string_new_len(NULL, 50);
2048  for (c = date_format; c && *c; )
2049  {
2050  if ((c[0] != '%') || (c[1] == '\0'))
2051  {
2052  c += 1;
2053  continue;
2054  }
2055  switch (c[1])
2056  {
2057  case 'F':
2058  cdn_fmt = g_string_append(cdn_fmt, "YYYYMMDD");
2059  break;
2060  case 'Y':
2061  cdn_fmt = g_string_append(cdn_fmt, "YYYY");
2062  break;
2063  case 'y':
2064  cdn_fmt = g_string_append(cdn_fmt, "YY");
2065  break;
2066  case 'm':
2067  cdn_fmt = g_string_append(cdn_fmt, "MM");
2068  break;
2069  case 'd':
2070  case 'e':
2071  cdn_fmt = g_string_append(cdn_fmt, "DD");
2072  break;
2073  case 'x':
2074  expanded = g_strdup_printf("%s%s",
2076  c + 2);
2077  c = expanded;
2078  continue;
2079  default:
2080  break;
2081  }
2082  c += 2;
2083  }
2084 
2085  text = g_string_free(cdn_fmt, FALSE);
2086  draw_text(context, text, &date_item, date_desc);
2087  g_free(text);
2088  if (expanded)
2089  g_free(expanded);
2090  pango_font_description_free(date_desc);
2091 }
2092 
2093 
2094 /* Print each of the items that in the description of a single check. This
2095  * function uses helper functions to print text based and picture based items.
2096  */
2097 static void
2098 draw_page_items(GtkPrintContext *context,
2099  check_format_t *format, gpointer user_data)
2100 {
2101  PrintCheckDialog *pcd = (PrintCheckDialog *) user_data;
2102  PangoFontDescription *default_desc;
2103  Transaction *trans;
2104  gnc_numeric amount;
2105  GNCPrintAmountInfo info;
2106  const gchar *date_format;
2107  gchar *text = NULL, buf[100];
2108  GSList *elem;
2109  check_item_t *item;
2110  gdouble width;
2111  gchar *address = NULL;
2112 
2113  trans = xaccSplitGetParent(pcd->split);
2114  /* This was valid when the check printing dialog was instantiated. */
2115  g_return_if_fail(trans);
2116  amount = gnc_numeric_abs(xaccSplitGetAmount(pcd->split));
2117 
2118  if (format->font)
2119  default_desc = pango_font_description_from_string(format->font);
2120  else
2121  default_desc = pango_font_description_from_string(pcd->default_font);
2122 
2123  /* Now put the actual data onto the page. */
2124  for (elem = format->items; elem; elem = g_slist_next(elem))
2125  {
2126  item = elem->data;
2127 
2128  switch (item->type)
2129  {
2130  case DATE:
2131  {
2132  GDate date;
2133  g_date_clear (&date, 1);
2134  gnc_gdate_set_time64 (&date, xaccTransGetDate(trans));
2135  date_format =
2136  gnc_date_format_get_custom(GNC_DATE_FORMAT
2137  (pcd->date_format));
2138  g_date_strftime(buf, 100, date_format, &date);
2139  width = draw_text(context, buf, item, default_desc);
2140  draw_date_format(context, date_format, item, default_desc, width);
2141  break;
2142  }
2143 
2144  case PAYEE:
2145  draw_text(context, xaccTransGetDescription(trans), item, default_desc);
2146  break;
2147 
2148  case NOTES:
2149  draw_text(context, xaccTransGetNotes(trans), item, default_desc);
2150  break;
2151 
2152  case MEMO:
2153  draw_text(context, xaccSplitGetMemo(pcd->split), item, default_desc);
2154  break;
2155 
2156  case ACTION:
2157  draw_text(context, gnc_get_action_num(trans, pcd->split), item,
2158  default_desc);
2159  break;
2160 
2161  case CHECK_NUMBER:
2162  draw_text(context, gnc_get_num_action(trans, pcd->split), item,
2163  default_desc);
2164  break;
2165 
2166  case AMOUNT_NUMBER:
2167  info = gnc_default_print_info(FALSE);
2168  draw_text(context, xaccPrintAmount(amount, info),
2169  item, default_desc);
2170  break;
2171 
2172  case AMOUNT_WORDS:
2173  text = numeric_to_words(amount);
2174  draw_text(context, text, item, default_desc);
2175  g_free(text);
2176  break;
2177 
2178  case TEXT:
2179  draw_text(context, item->text, item, default_desc);
2180  break;
2181 
2182  case ADDRESS:
2183  address = get_check_address(pcd);
2184  draw_text(context, address, item, default_desc);
2185  g_free(address);
2186  break;
2187 
2188  case SPLITS_AMOUNT:
2189  text = get_check_splits_amount(pcd);
2190  draw_text(context, text, item, default_desc);
2191  g_free(text);
2192  break;
2193 
2194  case SPLITS_MEMO:
2195  text = get_check_splits_memo(pcd);
2196  draw_text(context, text, item, default_desc);
2197  g_free(text);
2198  break;
2199 
2200  case SPLITS_ACCOUNT:
2201  text = get_check_splits_account(pcd);
2202  draw_text(context, text, item, default_desc);
2203  g_free(text);
2204  break;
2205 
2206  case PICTURE:
2207  draw_picture(context, item);
2208  break;
2209 
2210  default:
2211  text = g_strdup_printf("(unknown check field, type %d)", item->type);
2212  draw_text(context, text, item, default_desc);
2213  g_free(text);
2214  break;
2215  }
2216  }
2217 
2218  pango_font_description_free(default_desc);
2219 }
2220 
2221 
2222 /* Print each of the items that in the description of a single check. This
2223  * function uses helper functions to print text based and picture based items.
2224  */
2225 static void
2226 draw_page_boxes(GtkPrintContext *context,
2227  check_format_t *format, gpointer user_data)
2228 {
2229  cairo_t *cr;
2230  GSList *elem;
2231  check_item_t *item;
2232 
2233  cr = gtk_print_context_get_cairo_context(context);
2234 
2235  /* Now put the actual data onto the page. */
2236  for (elem = format->items; elem; elem = g_slist_next(elem))
2237  {
2238  item = elem->data;
2239  if (!item->w || !item->h)
2240  continue;
2241  cairo_rectangle(cr, item->x, item->y - item->h, item->w, item->h);
2242  cairo_stroke(cr);
2243  }
2244 }
2245 
2246 
2247 /* Print an entire page based upon the layout in a check description file. This
2248  * function takes care of translating/rotating the page, calling the function to
2249  * print the grid pattern (if requested), and calls a helper function to print
2250  * all check items.
2251  */
2252 static void
2253 draw_check_format(GtkPrintContext *context, gint position,
2254  check_format_t *format, gpointer user_data)
2255 {
2256  PrintCheckDialog *pcd = (PrintCheckDialog *) user_data;
2257  gdouble x, y, r, multip;
2258  cairo_t *cr = gtk_print_context_get_cairo_context(context);
2259 
2260  /* Translate all subsequent check items if required. */
2261  if ((position > 0) && (position < pcd->position_max))
2262  {
2263  /* Standard positioning is used.
2264  * Note that the first check on the page (position 0) doesn't
2265  * need to be moved (hence the test for position > 0 above. */
2266  cairo_translate(cr, 0, format->height); /* Translation is relative to previous
2267  check translation, not to page border ! */
2268  DEBUG("Position %d translated by %f relative to previous check (pre-defined)", position, format->height);
2269  DEBUG(" by %f relative to page (pre-defined)", position * format->height);
2270  }
2271  else if (position == pcd->position_max)
2272  {
2273  /* Custom positioning is used. */
2274  multip = pcd_get_custom_multip(pcd);
2275  x = multip * gtk_spin_button_get_value(pcd->translation_x);
2276  y = multip * gtk_spin_button_get_value(pcd->translation_y);
2277  cairo_translate(cr, x, y);
2278  DEBUG("Position translated by %f,%f (custom)", x, y);
2279  r = gtk_spin_button_get_value(pcd->check_rotation);
2280  cairo_rotate(cr, r * DEGREES_TO_RADIANS);
2281  DEBUG("Position rotated by %f degrees (custom)", r);
2282  }
2283 
2284  /* Draw layout boxes if requested. Also useful when determining check
2285  * layouts. */
2286  if (format->show_boxes)
2287  draw_page_boxes(context, format, user_data);
2288 
2289  /* Draw the actual check data. */
2290  draw_page_items(context, format, user_data);
2291 }
2292 
2293 
2294 static void
2295 draw_check_custom(GtkPrintContext *context, gpointer user_data)
2296 {
2297  PrintCheckDialog *pcd = (PrintCheckDialog *) user_data;
2298  GNCPrintAmountInfo info;
2299  PangoFontDescription *desc;
2300  Transaction *trans;
2301  gnc_numeric amount;
2302  cairo_t *cr;
2303  const gchar *date_format;
2304  gchar *text = NULL, buf[100];
2305  check_item_t item = { 0 };
2306  gdouble x, y, multip, degrees;
2307  GDate date;
2308  gchar *address;
2309 
2310  trans = xaccSplitGetParent(pcd->split);
2311  /* This was valid when the check printing dialog was instantiated. */
2312  g_return_if_fail(trans);
2313 
2314  desc = pango_font_description_from_string(pcd->default_font);
2315 
2316  multip = pcd_get_custom_multip(pcd);
2317  degrees = gtk_spin_button_get_value(pcd->check_rotation);
2318  cr = gtk_print_context_get_cairo_context(context);
2319  cairo_rotate(cr, degrees * DEGREES_TO_RADIANS);
2320  DEBUG("Page rotated by %f degrees", degrees);
2321 
2322  x = multip * gtk_spin_button_get_value(pcd->translation_x);
2323  y = multip * gtk_spin_button_get_value(pcd->translation_y);
2324  cairo_translate(cr, x, y);
2325  DEBUG("Page translated by %f,%f", x, y);
2326 
2327  item.x = multip * gtk_spin_button_get_value(pcd->payee_x);
2328  item.y = multip * gtk_spin_button_get_value(pcd->payee_y);
2329  draw_text(context, xaccTransGetDescription(trans), &item, desc);
2330 
2331  item.x = multip * gtk_spin_button_get_value(pcd->date_x);
2332  item.y = multip * gtk_spin_button_get_value(pcd->date_y);
2333  g_date_clear (&date, 1);
2334  gnc_gdate_set_time64 (&date, xaccTransGetDate(trans));
2335  date_format = gnc_date_format_get_custom(GNC_DATE_FORMAT(pcd->date_format));
2336  g_date_strftime(buf, 100, date_format, &date);
2337  draw_text(context, buf, &item, desc);
2338 
2339  item.x = multip * gtk_spin_button_get_value(pcd->number_x);
2340  item.y = multip * gtk_spin_button_get_value(pcd->number_y);
2341  info = gnc_default_print_info(FALSE);
2342  amount = gnc_numeric_abs(xaccSplitGetAmount(pcd->split));
2343  draw_text(context, xaccPrintAmount(amount, info), &item, desc);
2344 
2345  item.x = multip * gtk_spin_button_get_value(pcd->words_x);
2346  item.y = multip * gtk_spin_button_get_value(pcd->words_y);
2347  text = numeric_to_words(amount);
2348  draw_text(context, text, &item, desc);
2349  g_free(text);
2350 
2351  item.x = multip * gtk_spin_button_get_value(pcd->address_x);
2352  item.y = multip * gtk_spin_button_get_value(pcd->address_y);
2353  address = get_check_address(pcd);
2354  draw_text(context, address, &item, desc);
2355  g_free(address);
2356 
2357  item.x = multip * gtk_spin_button_get_value(pcd->splits_amount_x);
2358  item.y = multip * gtk_spin_button_get_value(pcd->splits_amount_y);
2359  text = get_check_splits_amount(pcd);
2360  draw_text(context, text, &item, desc);
2361  g_free(text);
2362 
2363  item.x = multip * gtk_spin_button_get_value(pcd->splits_memo_x);
2364  item.y = multip * gtk_spin_button_get_value(pcd->splits_memo_y);
2365  text = get_check_splits_memo(pcd);
2366  draw_text(context, text, &item, desc);
2367  g_free(text);
2368 
2369  item.x = multip * gtk_spin_button_get_value(pcd->splits_account_x);
2370  item.y = multip * gtk_spin_button_get_value(pcd->splits_account_y);
2371  text = get_check_splits_account(pcd);
2372  draw_text(context, text, &item, desc);
2373  g_free(text);
2374 
2375  item.x = multip * gtk_spin_button_get_value(pcd->notes_x);
2376  item.y = multip * gtk_spin_button_get_value(pcd->notes_y);
2377  draw_text(context, xaccTransGetNotes(trans), &item, desc);
2378 
2379  item.x = multip * gtk_spin_button_get_value(pcd->memo_x);
2380  item.y = multip * gtk_spin_button_get_value(pcd->memo_y);
2381  draw_text(context, xaccSplitGetMemo(pcd->split), &item, desc);
2382 
2383  pango_font_description_free(desc);
2384 }
2385 
2386 
2387 /* Print a page of checks. This takes into account the number of checks to print,
2388  * the number of checks on a page, and the starting check position on the page.
2389  * This function is called once by the GtkPrint code once for each page to be printed.
2390  */
2391 static void
2392 draw_page(GtkPrintOperation *operation,
2393  GtkPrintContext *context, gint page_nr, gpointer user_data)
2394 {
2395  PrintCheckDialog *pcd = (PrintCheckDialog *) user_data;
2396  check_format_t *format;
2397  cairo_t *cr = gtk_print_context_get_cairo_context(context);
2398 
2399  format = pcd->selected_format;
2400  if (format)
2401  {
2402  gint first_check, last_check;
2403  gint first_page_count;
2404  guint check_count = g_list_length(pcd->splits);
2405  gint check_number;
2406  gint position = gtk_combo_box_get_active(GTK_COMBO_BOX(pcd->position_combobox));
2407  gint last_blank_check_pos;
2408  gint checks_per_page;
2409  GList *next_split;
2410 
2411  if (position == pcd->position_max)
2412  {
2413  /* Custom position, one check per page */
2414  checks_per_page = 1;
2415  first_page_count = 1;
2416  }
2417  else
2418  {
2419  checks_per_page = pcd->position_max;
2420  first_page_count = gtk_spin_button_get_value_as_int(pcd->first_page_count);
2421  }
2422 
2423  if (page_nr == 0)
2424  {
2425  first_check = 0;
2426  last_check = first_page_count - 1;
2427  next_split = pcd->splits;
2428  }
2429  else
2430  {
2431  first_check = first_page_count + (page_nr - 1) * checks_per_page;
2432  last_check = MIN(check_count - 1, first_check + checks_per_page - 1);
2433  next_split = g_list_nth(pcd->splits, first_check);
2434  /* If position is not "custom" reset it to top */
2435  if (position < pcd->position_max)
2436  position = 0;
2437  }
2438 
2439  /* Do page level translations/rotations */
2440  cairo_translate(cr, format->trans_x, format->trans_y);
2441  DEBUG("Page translated by %f,%f", format->trans_x, format->trans_y);
2442  cairo_rotate(cr, format->rotation * DEGREES_TO_RADIANS);
2443  DEBUG("Page rotated by %f degrees", format->rotation);
2444 
2445  /* The grid is useful when determining check layouts */
2446  if (format->show_grid)
2447  {
2448  draw_grid(context,
2449  gtk_print_context_get_width(context),
2450  gtk_print_context_get_height(context),
2451  pcd->default_font);
2452  }
2453 
2454  last_blank_check_pos = position - 1;
2455  /* Optionally skip blank check positions if */
2456  if ((page_nr == 0) /* on first page AND */
2457  && (last_blank_check_pos > 0) /* there's more than one blank check */
2458  && (position < pcd->position_max)) /* but don't skip for custom positioning */
2459  cairo_translate(cr, 0, format->height * last_blank_check_pos);
2460 
2461  for (check_number = first_check; check_number <= last_check;
2462  check_number++, position++)
2463  {
2464  pcd->split = (Split *) next_split->data;
2465  next_split = g_list_next(next_split);
2466  draw_check_format(context, position, format, user_data);
2467  }
2468  }
2469  else
2470  {
2471  /* Custom check format */
2472  pcd->split = (Split *) g_list_nth_data(pcd->splits, page_nr);
2473  g_return_if_fail(pcd->split);
2474  draw_check_custom(context, user_data);
2475  }
2476 }
2477 
2478 
2479 /* Compute the number of pages required to complete this print operation.
2480  * This function is called once by the GtkPrint code to determine the number
2481  * of pages required to complete the print operation.
2482  */
2483 static void
2484 begin_print(GtkPrintOperation *operation,
2485  GtkPrintContext *context, gpointer user_data)
2486 {
2487  PrintCheckDialog *pcd = (PrintCheckDialog *) user_data;
2488  guint check_count = g_list_length(pcd->splits);
2489  gint pages;
2490  gint position = gtk_combo_box_get_active(GTK_COMBO_BOX(pcd->position_combobox));
2491 
2492  if (pcd->selected_format /* User selected a format other than custom */
2493  && pcd->position_max > 1 /* The format has more than one check per page
2494  (position_max is equivalent to custom
2495  positioning, and there need to be at least two
2496  other check defined positions (0 and 1), so
2497  custom positioning should be at least
2498  at position 2, i.e. >1) */
2499  && position < pcd->position_max) /* User chose a check position other
2500  then custom (which is always at
2501  position_max in the list) */
2502  {
2503  gint first_page_count, remaining_count;
2504 
2505  first_page_count = gtk_spin_button_get_value_as_int(pcd->first_page_count);
2506  remaining_count = check_count - first_page_count;
2507  pages = 1 /* First page, will have first_page_count checks */
2508  + remaining_count / pcd->position_max;
2509  /* Subsequent pages with all positions filled */
2510  if ((remaining_count % pcd->position_max) > 0)
2511  pages++; /* Last page, not all positions are filled. Needs to be added
2512  separately because integer division rounds towards 0 and
2513  would omit the last checks if they didn't fill a full page */
2514  }
2515  else
2516  pages = check_count;
2517  gtk_print_operation_set_n_pages(operation, pages);
2518 }
2519 
2520 
2521 /************************************
2522  * gnc_ui_print_check_dialog_ok_cb *
2523  ************************************/
2524 static void
2525 gnc_ui_print_check_dialog_ok_cb(PrintCheckDialog *pcd)
2526 {
2527  GtkPrintOperation *print;
2528  GtkPrintOperationResult res;
2529 
2530  print = gtk_print_operation_new();
2531 
2532  gnc_print_operation_init(print, "GnuCash-Checks");
2533  gtk_print_operation_set_unit(print, GTK_UNIT_POINTS);
2534  gtk_print_operation_set_use_full_page(print, TRUE);
2535  g_signal_connect(print, "begin_print", G_CALLBACK(begin_print), pcd);
2536  g_signal_connect(print, "draw_page", G_CALLBACK(draw_page), pcd);
2537 
2538  res = gtk_print_operation_run(print,
2539  GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
2540  pcd->caller_window, NULL);
2541 
2542  if (res == GTK_PRINT_OPERATION_RESULT_APPLY)
2544 
2545  g_object_unref(print);
2546 }
2547 
2548 
2549 static void
2550 gnc_print_check_set_sensitive (GtkWidget *widget, gpointer data)
2551 {
2552  gboolean sensitive;
2553  if (GTK_IS_LABEL(widget) || GTK_IS_SEPARATOR(widget))
2554  return;
2555  sensitive = GPOINTER_TO_INT(data);
2556  gtk_widget_set_sensitive(widget, sensitive);
2557 }
2558 
2559 
2560 void
2561 gnc_print_check_format_changed (GtkComboBox *widget,
2562  PrintCheckDialog *pcd)
2563 {
2564  GtkListStore *p_store;
2565  GtkTreeModel *f_model;
2566  GtkTreeIter f_iter, iter;
2567  gboolean sensitive;
2568  gint pnum;
2569  check_format_t *format;
2570  gboolean separator;
2571  GSList *elem;
2572 
2573  if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(pcd->format_combobox), &f_iter))
2574  return;
2575  f_model = gtk_combo_box_get_model(GTK_COMBO_BOX(pcd->format_combobox));
2576  gtk_tree_model_get(f_model, &f_iter, COL_DATA, &format, COL_SEP, &separator, -1);
2577  if (separator)
2578  return;
2579 
2580  pnum = gtk_combo_box_get_active(GTK_COMBO_BOX(pcd->position_combobox));
2581 
2582  /* Update the positions combobox */
2583  pcd->selected_format = format;
2584  p_store = gtk_list_store_new (1, G_TYPE_STRING);
2585  gtk_combo_box_set_model(GTK_COMBO_BOX(pcd->position_combobox),
2586  GTK_TREE_MODEL(p_store));
2587  if (format)
2588  {
2589  if (format->positions)
2590  {
2591  pcd->position_max = g_slist_length(format->positions); /* -1 for 0 base, +1 for custom entry */
2592  for (elem = format->positions; elem; elem = g_slist_next(elem))
2593  {
2594  gtk_list_store_append(GTK_LIST_STORE(p_store), &iter);
2595  gtk_list_store_set (GTK_LIST_STORE(p_store), &iter, 0, elem->data, -1);
2596  }
2597  }
2598  else
2599  {
2600  /* Invent a "Top" position if format has no positions */
2601  pcd->position_max = 1;
2602  gtk_list_store_append(GTK_LIST_STORE(p_store), &iter);
2603  gtk_list_store_set (GTK_LIST_STORE(p_store), &iter, 0, _("Top"), -1);
2604  }
2605  }
2606  else
2607  {
2608  pcd->position_max = 0;
2609  }
2610  gtk_list_store_append(GTK_LIST_STORE(p_store), &iter);
2611  gtk_list_store_set (GTK_LIST_STORE(p_store), &iter, 0, _("Custom"), -1);
2612  g_object_unref (p_store);
2613 
2614  /* If there's only one thing in the position combobox, make it insensitive */
2615  sensitive = (pcd->position_max > 0);
2616  gtk_widget_set_sensitive(GTK_WIDGET(pcd->position_combobox), sensitive);
2617 
2618  /* Update the custom page, this must be done before setting the active
2619  entry in the position combo box since gnc_print_check_position_changed
2620  will adjust these settings in some cases. */
2621  sensitive = (!separator && !format);
2622  gtk_container_foreach(GTK_CONTAINER(pcd->custom_table),
2623  gnc_print_check_set_sensitive,
2624  GINT_TO_POINTER(sensitive));
2625 
2626  /* Set the active entry in the position combo box, this will trigger a
2627  call to gnc_print_check_position_changed */
2628  pnum = MAX(MIN(pnum, pcd->position_max), 0);
2629  gtk_combo_box_set_active(GTK_COMBO_BOX(pcd->position_combobox), pnum);
2630 
2631  /* Update address fields */
2632  sensitive = check_format_has_address(pcd);
2633  gtk_widget_set_sensitive(pcd->check_address_name, sensitive);
2634  gtk_widget_set_sensitive(pcd->check_address_1, sensitive);
2635  gtk_widget_set_sensitive(pcd->check_address_2, sensitive);
2636  gtk_widget_set_sensitive(pcd->check_address_3, sensitive);
2637  gtk_widget_set_sensitive(pcd->check_address_4, sensitive);
2638 }
2639 
2640 
2641 void
2642 gnc_print_check_position_changed (GtkComboBox *widget,
2643  PrintCheckDialog *pcd)
2644 {
2645  gboolean sensitive;
2646  gint pnum;
2647  guint check_count;
2648  gint first_page_max, first_page_min, first_page_value;
2649 
2650  pnum = gtk_combo_box_get_active(GTK_COMBO_BOX(pcd->position_combobox));
2651 
2652  /* Make the translation and rotation fields active if the position is "custom" */
2653  sensitive = pnum == pcd->position_max;
2654  gtk_widget_set_sensitive(GTK_WIDGET(pcd->translation_x), sensitive);
2655  gtk_widget_set_sensitive(GTK_WIDGET(pcd->translation_y), sensitive);
2656  gtk_widget_set_sensitive(GTK_WIDGET(pcd->check_rotation), sensitive);
2657  gtk_widget_set_sensitive(GTK_WIDGET(pcd->units_combobox), sensitive);
2658 
2659  /* Set up the first page check count spin box */
2660  check_count = g_list_length(pcd->splits);
2661  first_page_max = MAX(1, MIN(pcd->position_max - pnum, check_count));
2662  first_page_min = 1;
2663  pnum = gtk_spin_button_get_value_as_int(pcd->first_page_count);
2664  first_page_value = MAX(MIN(pnum, first_page_max), first_page_min);
2665  gtk_spin_button_set_range(pcd->first_page_count, (gdouble)first_page_min, (gdouble)first_page_max);
2666  gtk_spin_button_set_value(pcd->first_page_count, (gdouble)first_page_value);
2667  sensitive = first_page_max > 1;
2668  gtk_widget_set_sensitive(GTK_WIDGET(pcd->first_page_count), sensitive);
2669 }
2670 
2671 
2672 void
2673 gnc_ui_print_check_response_cb(GtkDialog *dialog,
2674  gint response,
2675  PrintCheckDialog *pcd)
2676 {
2677  switch (response)
2678  {
2679  case GTK_RESPONSE_HELP:
2680  gnc_gnome_help (GTK_WINDOW(dialog), HF_HELP, HL_PRINTCHECK);
2681  return;
2682 
2683  case GTK_RESPONSE_OK:
2684  gnc_ui_print_check_dialog_ok_cb(pcd);
2685  gnc_ui_print_save_dialog(pcd);
2686  gnc_save_window_size(GNC_PREFS_GROUP, GTK_WINDOW(dialog));
2687  break;
2688 
2689  case GTK_RESPONSE_CANCEL:
2690  gnc_save_window_size(GNC_PREFS_GROUP, GTK_WINDOW(dialog));
2691  break;
2692  }
2693 
2694  gtk_widget_destroy(pcd->dialog);
2695  g_free(pcd->default_font);
2696  g_list_free(pcd->splits);
2697  g_free(pcd);
2698 }
void guid_replace(GncGUID *guid)
Generate a new guid.
Definition: guid.cpp:144
Used by the check printing code.
Definition: gnc-date.h:136
gchar * filename
The name of the file from which this data was read.
GSList * positions
Names of the checks on the page.
gdouble trans_x
Move entire page left by this amount.
time64 xaccTransGetDate(const Transaction *trans)
Retrieve the posted date of the transaction.
gchar * gnc_prefs_get_string(const gchar *group, const gchar *pref_name)
Get a string value from the preferences backend.
Date and Time handling routines.
gboolean gncOwnerGetOwnerFromTxn(Transaction *txn, GncOwner *owner)
Convenience function to get the owner from a transaction.
Definition: gncOwner.c:675
gchar * gnc_build_userdata_path(const gchar *filename)
Make a path to filename in the user&#39;s gnucash data directory.
GSList * items
List of items printed on each check.
utility functions for the GnuCash UI
gboolean blocking
Default for printing blocking characters for this page of checks.
An exact-rational-number library for gnucash.
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
gboolean show_grid
Print a grid pattern on the page.
gboolean gnc_prefs_set_int(const gchar *group, const gchar *pref_name, gint value)
Store an integer value into the preferences backend.
Definition: gnc-prefs.c:288
const char * xaccPrintAmount(gnc_numeric val, GNCPrintAmountInfo info)
Make a string representation of a gnc_numeric.
Definition: gnc-ui-util.c:1854
gboolean show_boxes
Print boxes when width and height are known.
GKeyFile helper routines.
void gnc_print_operation_save_print_settings(GtkPrintOperation *op)
Retrieve the print settings from the GtkPrintOperation op and save them in a static variable...
Definition: print-session.c:47
format_combo_col
gdouble rotation
Rotate the entire page by this amount.
void gnc_prefs_reset(const gchar *group, const gchar *pref_name)
Reset a preference to its default value in the preferences backend.
Definition: gnc-prefs.c:361
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
API for Transactions and Splits (journal entries)
const gchar * group
The group where this format was found.
gchar * guid
Unique identifier for this format.
gchar * guid_to_string_buff(const GncGUID *guid, gchar *str)
The guid_to_string_buff() routine puts a null-terminated string encoding of the id into the memory po...
Definition: guid.cpp:174
gboolean gnc_prefs_set_string(const gchar *group, const gchar *pref_name, const gchar *value)
Store a string into the preferences backend.
Definition: gnc-prefs.c:319
const gchar * gnc_userdata_dir(void)
Ensure that the user&#39;s configuration directory exists and is minimally populated. ...
gint gnc_prefs_get_int(const gchar *group, const gchar *pref_name)
Get an integer value from the preferences backend.
gboolean gnc_key_file_save_to_file(const gchar *filename, GKeyFile *key_file, GError **error)
Write a key/value file from memory to disk.
const char * xaccTransGetNotes(const Transaction *trans)
Gets the transaction Notes.
gchar * gnc_get_account_name_for_register(const Account *account)
Get either the full name of the account or the simple name, depending on the configuration parameter ...
Definition: gnc-ui-util.c:579
GList SplitList
GList of Split.
Definition: gnc-engine.h:211
Functions providing a register page for the GnuCash UI.
void gnc_print_operation_init(GtkPrintOperation *op, const gchar *jobname)
If print settings have been saved by gnc_print_operation_save_print_settings(), then set them on the ...
Definition: print-session.c:59
This column holds a pointer to the check format data read in from a file.
void gnc_gnome_help(GtkWindow *parent, const char *file_name, const char *anchor)
Launch the systems default help browser, gnome&#39;s yelp for linux, and open to a given link within a gi...
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
Definition: guid.h:84
gboolean gnc_prefs_set_float(const gchar *group, const gchar *pref_name, gdouble value)
Store a float value into the preferences backend.
Definition: gnc-prefs.c:308
gboolean gnc_prefs_set_coords(const gchar *group, const gchar *pref_name, gdouble x, gdouble y)
Store coordinates into the preferences backend.
Definition: gnc-prefs.c:341
void gnc_prefs_get_coords(const gchar *group, const gchar *pref_name, gdouble *x, gdouble *y)
Get a pair of coordinates from the preferences backend.
gchar * font
Default font for this page of checks.
const char * xaccTransGetDescription(const Transaction *trans)
Gets the transaction Description.
gnc_numeric gnc_numeric_abs(gnc_numeric a)
Returns a newly created gnc_numeric that is the absolute value of the given gnc_numeric value...
All type declarations for the whole Gnucash engine.
gdouble height
Height of one check on a page.
Generic api to store and retrieve preferences.
const GncOwner * gncOwnerGetEndOwner(const GncOwner *owner)
Get the "parent" Owner or GncGUID thereof.
Definition: gncOwner.c:573
void gnc_gdate_set_time64(GDate *gd, time64 time)
Set a GDate to a time64.
Definition: gnc-date.cpp:1247
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
Definition: gmock-Split.cpp:53
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
gboolean print_date_format
Default for printing date format characters for this page of checks.
const char * xaccSplitGetMemo(const Split *split)
Returns the memo string.
Definition: gmock-Split.cpp:99
FROM_STRING_DEC(CheckItemType, ENUM_CHECK_ITEM_TYPE)
File path resolution utility functions.
Take from locale information.
Definition: gnc-date.h:134
This column contains the value TRUE if this entry specifies a separator line.
GKeyFile * gnc_key_file_load_from_file(const gchar *filename, gboolean ignore_error, gboolean return_empty_struct, GError **caller_error)
Open and read a key/value file from disk into memory.
API for Transactions and Splits (journal entries)
const gchar * qof_date_format_get_string(QofDateFormat df)
This function returns a strftime formatting string for printing an all numeric date (e...
Definition: gnc-date.cpp:509
The type used to store guids in C.
Definition: guid.h:75
gdouble trans_y
Move entire page down by this amount.
SplitList * xaccTransGetSplitList(const Transaction *trans)
The xaccTransGetSplitList() method returns a GList of the splits in a transaction.
gchar * title
Title of this check format.
This column holds a copy of the check format name and is what is displayed to the user in the combobo...
gdouble gnc_prefs_get_float(const gchar *group, const gchar *pref_name)
Get an float value from the preferences backend.
gnc_numeric xaccSplitGetAmount(const Split *split)
Returns the amount of the split in the account&#39;s commodity.
Definition: gmock-Split.cpp:69