GnuCash  4.12-74-g36b33262ad+
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 the 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 && *guid))
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  }
616  else
617  {
618  model = gtk_combo_box_get_model(GTK_COMBO_BOX(pcd->format_combobox));
619  if (find_existing_format(GTK_LIST_STORE(model), guid, &iter))
620  {
621  gtk_combo_box_set_active_iter(GTK_COMBO_BOX(pcd->format_combobox), &iter);
622  }
623  else
624  {
625  gtk_combo_box_set_active(GTK_COMBO_BOX(pcd->format_combobox), 0);
626  }
627  }
628  g_free (guid);
629 
630  active = gnc_prefs_get_int(GNC_PREFS_GROUP, GNC_PREF_CHECK_POSITION);
631 
632  /* If the check format used last time no longer exists, then the saved check
633  position may be invalid. If so set it to the first position. */
634  if (active < 0 || active > pcd->position_max)
635  active = 0;
636  gtk_combo_box_set_active(GTK_COMBO_BOX(pcd->position_combobox), active);
637  active = gnc_prefs_get_int(GNC_PREFS_GROUP, GNC_PREF_FIRST_PAGE_COUNT);
638  gtk_spin_button_set_value(pcd->first_page_count, (gdouble) active);
639  active = gnc_prefs_get_int(GNC_PREFS_GROUP, GNC_PREF_DATE_FORMAT);
640  gnc_date_format_set_format(GNC_DATE_FORMAT(pcd->date_format), active);
641  if (active == QOF_DATE_FORMAT_CUSTOM)
642  {
643  format = gnc_prefs_get_string (GNC_PREFS_GROUP, GNC_PREF_DATE_FORMAT_USER);
644  if (format && *format)
645  {
646  gnc_date_format_set_custom(GNC_DATE_FORMAT(pcd->date_format), format);
647  g_free(format);
648  }
649  }
650 
651  /* Custom format page */
652  gnc_prefs_get_coords(GNC_PREFS_GROUP, GNC_PREF_CUSTOM_PAYEE, &x, &y);
653  gtk_spin_button_set_value(pcd->payee_x, x);
654  gtk_spin_button_set_value(pcd->payee_y, y);
655 
656  gnc_prefs_get_coords(GNC_PREFS_GROUP, GNC_PREF_CUSTOM_DATE, &x, &y);
657  gtk_spin_button_set_value(pcd->date_x, x);
658  gtk_spin_button_set_value(pcd->date_y, y);
659  gnc_prefs_get_coords(GNC_PREFS_GROUP, GNC_PREF_CUSTOM_WORDS, &x, &y);
660  gtk_spin_button_set_value(pcd->words_x, x);
661  gtk_spin_button_set_value(pcd->words_y, y);
662  gnc_prefs_get_coords(GNC_PREFS_GROUP, GNC_PREF_CUSTOM_NUMBER, &x, &y);
663  gtk_spin_button_set_value(pcd->number_x, x);
664  gtk_spin_button_set_value(pcd->number_y, y);
665  gnc_prefs_get_coords(GNC_PREFS_GROUP, GNC_PREF_CUSTOM_ADDRESS, &x, &y);
666  gtk_spin_button_set_value(pcd->address_x, x);
667  gtk_spin_button_set_value(pcd->address_y, y);
668  gnc_prefs_get_coords(GNC_PREFS_GROUP, GNC_PREF_CUSTOM_NOTES, &x, &y);
669  gtk_spin_button_set_value(pcd->notes_x, x);
670  gtk_spin_button_set_value(pcd->notes_y, y);
671  gnc_prefs_get_coords(GNC_PREFS_GROUP, GNC_PREF_CUSTOM_MEMO, &x, &y);
672  gtk_spin_button_set_value(pcd->memo_x, x);
673  gtk_spin_button_set_value(pcd->memo_y, y);
674  gnc_prefs_get_coords(GNC_PREFS_GROUP, GNC_PREF_SPLITS_AMOUNT, &x, &y);
675  gtk_spin_button_set_value(pcd->splits_amount_x, x);
676  gtk_spin_button_set_value(pcd->splits_amount_y, y);
677  gnc_prefs_get_coords(GNC_PREFS_GROUP, GNC_PREF_SPLITS_MEMO, &x, &y);
678  gtk_spin_button_set_value(pcd->splits_memo_x, x);
679  gtk_spin_button_set_value(pcd->splits_memo_y, y);
680  gnc_prefs_get_coords(GNC_PREFS_GROUP, GNC_PREF_SPLITS_ACCOUNT, &x, &y);
681  gtk_spin_button_set_value(pcd->splits_account_x, x);
682  gtk_spin_button_set_value(pcd->splits_account_y, y);
683  gnc_prefs_get_coords(GNC_PREFS_GROUP, GNC_PREF_CUSTOM_TRANSLATION, &x, &y);
684  gtk_spin_button_set_value(pcd->translation_x, x);
685  gtk_spin_button_set_value(pcd->translation_y, y);
686  x = gnc_prefs_get_float(GNC_PREFS_GROUP, GNC_PREF_CUSTOM_ROTATION);
687  gtk_spin_button_set_value(pcd->check_rotation, x);
688  active = gnc_prefs_get_int(GNC_PREFS_GROUP, GNC_PREF_CUSTOM_UNITS);
689  gtk_combo_box_set_active(GTK_COMBO_BOX(pcd->units_combobox), active);
690 }
691 
692 
693 static gdouble
694 pcd_get_custom_multip(PrintCheckDialog *pcd)
695 {
696  gint selected;
697 
698  selected = gtk_combo_box_get_active(GTK_COMBO_BOX(pcd->units_combobox));
699  switch (selected)
700  {
701  default:
702  return 72.0; /* inches */
703  case 1:
704  return 28.346; /* cm */
705  case 2:
706  return 2.8346; /* mm */
707  case 3:
708  return 1.0; /* points */
709  }
710 }
711 
712 
713 /* This function saves a coordinate pair into a check description file. It
714  * extracts the values from the spin buttons, adjusts them according to the
715  * unit multiplier (inches, pixels, etc), then adds them to the gKeyFile.
716  */
717 static void
718 pcd_key_file_save_xy (GKeyFile *key_file, const gchar *group_name,
719  const gchar *key_name, gdouble multip,
720  GtkSpinButton *spin0, GtkSpinButton *spin1)
721 {
722  gdouble dd[2];
723 
724  dd[0] = multip * gtk_spin_button_get_value(spin0);
725  dd[1] = multip * gtk_spin_button_get_value(spin1);
726 
727  /* Clip the numbers to three decimal places. */
728  dd[0] = round(dd[0] * 1000) / 1000;
729  dd[1] = round(dd[1] * 1000) / 1000;
730  g_key_file_set_double_list(key_file, group_name, key_name, dd, 2);
731 }
732 
733 
734 /* This function saves the information about a single printed item into a
735  * check description file. It uses a helper function to extracts and save the
736  * item coordinates.
737  */
738 static void
739 pcd_key_file_save_item_xy (GKeyFile *key_file, int index,
740  CheckItemType type, gdouble multip,
741  GtkSpinButton *spin0, GtkSpinButton *spin1)
742 {
743  gchar *key;
744  key = g_strdup_printf("Type_%d", index);
745  g_key_file_set_string(key_file, KF_GROUP_ITEMS, key,
746  CheckItemTypeasString(type));
747  g_free(key);
748  key = g_strdup_printf("Coords_%d", index);
749  pcd_key_file_save_xy(key_file, KF_GROUP_ITEMS, key, multip, spin0, spin1);
750  g_free(key);
751 }
752 
753 
754 /* This function saves all of the information from the custom check dialog
755  * into a check description file.
756  */
757 static void
758 pcd_save_custom_data(PrintCheckDialog *pcd, const gchar *title)
759 {
760  GKeyFile *key_file;
761  GError *error = NULL;
762  GtkWidget *dialog;
763  gdouble multip;
764  gint i = 1;
765  GncGUID guid;
766  char buf[GUID_ENCODING_LENGTH+1];
767  gchar *filename, *pathname;
768 
769  multip = pcd_get_custom_multip(pcd);
770 
771  key_file = g_key_file_new();
772  guid_replace(&guid);
773  guid_to_string_buff(&guid, buf);
774  g_key_file_set_string(key_file, KF_GROUP_TOP, KF_KEY_GUID, buf);
775  g_key_file_set_string(key_file, KF_GROUP_TOP, KF_KEY_TITLE, title);
776  g_key_file_set_boolean(key_file, KF_GROUP_TOP, KF_KEY_SHOW_GRID, FALSE);
777  g_key_file_set_boolean(key_file, KF_GROUP_TOP, KF_KEY_SHOW_BOXES, FALSE);
778  g_key_file_set_double(key_file, KF_GROUP_TOP, KF_KEY_ROTATION,
779  gtk_spin_button_get_value(pcd->check_rotation));
780  pcd_key_file_save_xy(key_file, KF_GROUP_TOP, KF_KEY_TRANSLATION, multip,
781  pcd->translation_x, pcd->translation_y);
782 
783  pcd_key_file_save_item_xy(key_file, i++, PAYEE, multip,
784  pcd->payee_x, pcd->payee_y);
785  pcd_key_file_save_item_xy(key_file, i++, DATE, multip,
786  pcd->date_x, pcd->date_y);
787  pcd_key_file_save_item_xy(key_file, i++, AMOUNT_WORDS, multip,
788  pcd->words_x, pcd->words_y);
789  pcd_key_file_save_item_xy(key_file, i++, AMOUNT_NUMBER, multip,
790  pcd->number_x, pcd->number_y);
791  pcd_key_file_save_item_xy(key_file, i++, ADDRESS, multip,
792  pcd->address_x, pcd->address_y);
793  pcd_key_file_save_item_xy(key_file, i++, NOTES, multip,
794  pcd->notes_x, pcd->notes_y);
795  pcd_key_file_save_item_xy(key_file, i++, MEMO, multip,
796  pcd->memo_x, pcd->memo_y);
797  pcd_key_file_save_item_xy(key_file, i++, SPLITS_AMOUNT, multip,
798  pcd->splits_amount_x, pcd->splits_amount_y);
799  pcd_key_file_save_item_xy(key_file, i++, SPLITS_MEMO, multip,
800  pcd->splits_memo_x, pcd->splits_memo_y);
801  pcd_key_file_save_item_xy(key_file, i++, SPLITS_ACCOUNT, multip,
802  pcd->splits_account_x, pcd->splits_account_y);
803 
804  filename = g_strconcat(title, CHECK_NAME_EXTENSION, NULL);
805  pathname = g_build_filename(gnc_userdata_dir(), CHECK_FMT_DIR,
806  filename, NULL);
807 
808  if (gnc_key_file_save_to_file(pathname, key_file, &error))
809  {
810  if (!gnc_prefs_get_bool(GNC_PREFS_GROUP, GNC_PREF_PRINT_DATE_FMT))
811  /* Reload the format combo box and reselect the "custom" entry */
812  initialize_format_combobox(pcd);
813 
814  gtk_combo_box_set_active(GTK_COMBO_BOX(pcd->format_combobox),
815  pcd->format_max - 1);
816  }
817  else
818  {
819  dialog = gtk_message_dialog_new(GTK_WINDOW(pcd->dialog),
820  GTK_DIALOG_DESTROY_WITH_PARENT,
821  GTK_MESSAGE_ERROR,
822  GTK_BUTTONS_CLOSE, "%s",
823  _("Cannot save check format file."));
824  gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
825  _("Cannot open file %s"),
826  _(error->message));
827  gtk_dialog_run(GTK_DIALOG(dialog));
828  gtk_widget_destroy(dialog);
829  g_error_free(error);
830  }
831  g_free(pathname);
832  g_free(filename);
833 }
834 
835 
836 /* This function makes the OK button active iff a title has been entered.
837  */
838 void
839 gnc_check_format_title_changed (GtkEditable *editable, GtkWidget *ok_button)
840 {
841  const gchar *text;
842  gboolean sensitive;
843 
844  text = gtk_entry_get_text(GTK_ENTRY(editable));
845  sensitive = text && *text;
846  gtk_widget_set_sensitive(ok_button, sensitive);
847 }
848 
849 
850 /* This function is called when the user clicks the "save format" button in
851  * the check printing dialog. It presents another dialog to the user to get
852  * the filename for saving the data.
853  */
854 void
855 gnc_print_check_save_button_clicked(GtkButton *unused, PrintCheckDialog *pcd)
856 {
857  GtkWidget *dialog, *entry, *button;
858  GtkBuilder *builder;
859  gchar *title;
860 
861  builder = gtk_builder_new();
862  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "format_title_dialog");
863 
864  /* Get a title for the new check format. */
865  dialog = GTK_WIDGET(gtk_builder_get_object (builder, "format_title_dialog"));
866  entry = GTK_WIDGET(gtk_builder_get_object (builder, "format_title"));
867  button = GTK_WIDGET(gtk_builder_get_object (builder, "ok_button"));
868  gnc_check_format_title_changed(GTK_EDITABLE(entry), button);
869  gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, pcd);
870 
871  gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(pcd->dialog));
872  if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_OK)
873  {
874  gtk_widget_destroy(dialog);
875  g_object_unref(G_OBJECT(builder));
876  return;
877  }
878 
879  title = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
880  gtk_widget_destroy (dialog);
881 
882  g_object_unref(G_OBJECT(builder));
883 
884  pcd_save_custom_data(pcd, title);
885  g_free(title);
886 }
887 
888 
889 /* This function is an auxiliary debugging function for converting an array of
890  * doubles into a printable string.
891  */
892 static gchar *
893 doubles_to_string(gdouble *dd, gint len)
894 {
895  GString *str;
896  gint i;
897 
898  str = g_string_new_len(NULL, 50);
899  for (i = 0; i < len; i++)
900  g_string_append_printf(str, "%f ", dd[i]);
901  return g_string_free(str, FALSE);
902 }
903 
904 
905 /* This function reads in the information describing the placement for each
906  * item to be printed on a check. This information is all relative to the
907  * upper left hand corner of a "check". See the format_read_multicheck_info()
908  * function for determining if there are multiple checks on a single page of
909  * paper. This data is build into a linked list and saved as part of the check
910  * format information. These items will be printed in the same order they are
911  * read, meaning that items listed later in the date file can be printed over
912  * top of items that appear earlier in the file.
913  */
914 static GSList *
915 format_read_item_placement(const gchar *file,
916  GKeyFile *key_file, check_format_t *format)
917 {
918  check_item_t *data = NULL;
919  GError *error = NULL;
920  GSList *list = NULL;
921  gchar *key, *value, *name;
922  int item_num;
923  gboolean bval;
924  gdouble *dd;
925  gsize dd_len;
926 
927  /* Read until failure. */
928  for (item_num = 1;; item_num++)
929  {
930 
931  /* Create the per-item data structure */
932  data = g_new0(check_item_t, 1);
933  if (NULL == data)
934  return list;
935 
936  /* Get the item type */
937  key = g_strdup_printf("%s_%d", KF_KEY_TYPE, item_num);
938  value = g_key_file_get_string(key_file, KF_GROUP_ITEMS, key, &error);
939  if (error)
940  {
941  if ((error->domain == G_KEY_FILE_ERROR)
942  && (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND))
943  {
944  /* This is the expected exit from this function. */
945  goto cleanup;
946  }
947  goto failed;
948  }
949  DEBUG("Check file %s, group %s, key %s, value: %s",
950  file, KF_GROUP_ITEMS, key, value);
951  g_free(key);
952 
953  /* Convert the type from a string to an enum, ignoring case. */
954  name = g_utf8_strup(value, -1);
955  data->type = CheckItemTypefromString(name);
956  g_free(name);
957  g_free(value);
958 
959 
960  /* Get the item location */
961  key = g_strdup_printf("%s_%d", KF_KEY_COORDS, item_num);
962  dd = g_key_file_get_double_list(key_file, KF_GROUP_ITEMS,
963  key, &dd_len, &error);
964  if (error)
965  goto failed;
966  value = doubles_to_string(dd, dd_len);
967  DEBUG("Check file %s, group %s, key %s, length %"G_GSIZE_FORMAT"; values: %s",
968  file, KF_GROUP_ITEMS, key, dd_len, value);
969  g_free(value);
970 
971  /* Must have "x;y" or "x;y;w;h". */
972  switch (dd_len)
973  {
974  case 4:
975  data->w = dd[2];
976  data->h = dd[3];
977  /* fall through */
978  case 2:
979  data->x = dd[0];
980  data->y = dd[1];
981  break;
982  default:
983  g_warning
984  ("Check file %s, group %s, key %s, error: 2 or 4 values only",
985  file, KF_GROUP_ITEMS, key);
986  goto cleanup;
987  }
988  g_free(dd);
989  g_free(key);
990 
991  /* Any text item can specify:
992  * a font FONT_n
993  * an alignment if a width was provided for the item ALIGN_n
994  * blocking chars flag BLOCKING_CHARS_n
995  * These values are optional and do not cause a failure if they are missing. */
996 
997  if (data->type != PICTURE)
998  {
999  key = g_strdup_printf("%s_%d", KF_KEY_FONT, item_num);
1000  data->font =
1001  g_key_file_get_string(key_file, KF_GROUP_ITEMS, key, &error);
1002  if (!error)
1003  {
1004  DEBUG("Check file %s, group %s, key %s, value: %s",
1005  file, KF_GROUP_ITEMS, key, data->font);
1006  }
1007  else
1008  {
1009  if (!((error->domain == G_KEY_FILE_ERROR)
1010  && (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND)))
1011  g_warning("Check file %s, group %s, key %s, error: %s",
1012  file, KF_GROUP_ITEMS, key, error->message);
1013  g_clear_error(&error);
1014  }
1015  g_free(key);
1016 
1017  key = g_strdup_printf("%s_%d", KF_KEY_ALIGN, item_num);
1018  value =
1019  g_key_file_get_string(key_file, KF_GROUP_ITEMS, key, &error);
1020  if (!error)
1021  {
1022  DEBUG("Check file %s, group %s, key %s, value: %s",
1023  file, KF_GROUP_ITEMS, key, value);
1024  name = g_utf8_strdown(value, -1);
1025  if (strcmp(name, "right") == 0)
1026  data->align = PANGO_ALIGN_RIGHT;
1027  else if (strcmp(name, "center") == 0)
1028  data->align = PANGO_ALIGN_CENTER;
1029  else
1030  data->align = PANGO_ALIGN_LEFT;
1031  g_free(name);
1032  g_free(value);
1033  }
1034  else
1035  {
1036  if (!((error->domain == G_KEY_FILE_ERROR)
1037  && (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND)))
1038  g_warning("Check file %s, group %s, key %s, error: %s",
1039  file, KF_GROUP_ITEMS, key, error->message);
1040  data->align = PANGO_ALIGN_LEFT;
1041  g_clear_error(&error);
1042  }
1043  g_free(key);
1044 
1045  key = g_strdup_printf("%s_%d", KF_KEY_BLOCKING, item_num);
1046  bval =
1047  g_key_file_get_boolean(key_file, KF_GROUP_ITEMS, key, &error);
1048  if (!error)
1049  {
1050  DEBUG("Check file %s, group %s, key %s, value: %d",
1051  file, KF_GROUP_ITEMS, key, bval);
1052  data->blocking = bval;
1053  }
1054  else
1055  {
1056  if (!((error->domain == G_KEY_FILE_ERROR)
1057  && (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND)))
1058  g_warning("Check file %s, group %s, key %s, error: %s",
1059  file, KF_GROUP_ITEMS, key, error->message);
1060  data->blocking = format->blocking;
1061  g_clear_error(&error);
1062  }
1063  g_free(key);
1064  }
1065  /* Get any extra data for specific items. */
1066  switch (data->type)
1067  {
1068  case PICTURE:
1069  key = g_strdup_printf("%s_%d", KF_KEY_FILENAME, item_num);
1070  data->filename =
1071  g_key_file_get_string(key_file, KF_GROUP_ITEMS, key,
1072  &error);
1073  if (error)
1074  goto failed;
1075  DEBUG("Check file %s, group %s, key %s, value: %s",
1076  file, KF_GROUP_ITEMS, key, data->filename);
1077  g_free(key);
1078  break;
1079  case TEXT:
1080  key = g_strdup_printf("%s_%d", KF_KEY_TEXT, item_num);
1081  data->text =
1082  g_key_file_get_string(key_file, KF_GROUP_ITEMS, key,
1083  &error);
1084  if (error)
1085  goto failed;
1086  DEBUG("Check file %s, group %s, key %s, value: %s",
1087  file, KF_GROUP_ITEMS, key, data->text);
1088  g_free(key);
1089  break;
1090  case DATE:
1091  /* no error if the date_format is not present */
1092  key = g_strdup_printf("%s_%d", KF_KEY_DATE_FORMAT, item_num);
1093  bval = g_key_file_get_boolean(key_file, KF_GROUP_ITEMS, key, &error);
1094  if (!error)
1095  {
1096  DEBUG("Check file %s, group %s, key %s, value: %d",
1097  file, KF_GROUP_ITEMS, key, bval);
1098  data->print_date_format = bval;
1099  }
1100  else
1101  {
1102  if (!((error->domain == G_KEY_FILE_ERROR)
1103  && (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND)))
1104  g_warning("Check file %s, group %s, key %s, error: %s",
1105  file, KF_GROUP_ITEMS, key, error->message);
1106  data->print_date_format = format->print_date_format;
1107  g_clear_error(&error);
1108  }
1109  g_free(key);
1110  break;
1111  default:
1112  break;
1113  }
1114 
1115  list = g_slist_append(list, data);
1116  data = NULL;
1117  }
1118 
1119  /* Should never be reached. */
1120  return list;
1121 
1122 failed:
1123  g_warning("Check file %s, group %s, key %s, error: %s",
1124  file, KF_GROUP_ITEMS, key, error->message);
1125 cleanup:
1126  if (error)
1127  g_error_free(error);
1128  if (data)
1129  g_free(data);
1130  if (key)
1131  g_free(key);
1132  return list;
1133 }
1134 
1135 
1136 /* Free the data describing the placement of a single item on a check.
1137  */
1138 static void
1139 format_free_item_placement(check_item_t *data)
1140 {
1141  if (data->font)
1142  g_free(data->font);
1143  if (data->text)
1144  g_free(data->text);
1145  if (data->filename)
1146  g_free(data->filename);
1147  g_free(data);
1148 }
1149 
1150 
1151 /* Read the information describing whether a page contains multiple checks or
1152  * a single check. If there are multiple checks on a page, this functions
1153  * builds a linked list of the position names and their offsets (from the
1154  * upper left corner of the page).
1155  */
1156 static GSList *
1157 format_read_multicheck_info(const gchar *file,
1158  GKeyFile *key_file, check_format_t *format)
1159 {
1160  GError *error = NULL;
1161  GSList *list = NULL;
1162  gchar *key, **names;
1163  gsize length;
1164  gint i;
1165 
1166  key = g_strdup_printf("%s", KF_KEY_HEIGHT);
1167  format->height = g_key_file_get_double(key_file, KF_GROUP_POS, key, &error);
1168  if (error)
1169  {
1170  if ((error->domain == G_KEY_FILE_ERROR)
1171  && ((error->code == G_KEY_FILE_ERROR_GROUP_NOT_FOUND)
1172  || (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND)))
1173  {
1174  g_clear_error(&error);
1175  format->height = 0.0;
1176  }
1177  else
1178  {
1179  g_warning("Check file %s, error reading group %s, key %s: %s",
1180  file, KF_GROUP_POS, key, error->message);
1181  g_free(key);
1182  return NULL;
1183  }
1184  }
1185 
1186  names = g_key_file_get_string_list(key_file, KF_GROUP_POS, KF_KEY_NAMES,
1187  &length, &error);
1188  if (error)
1189  {
1190  if ((error->domain == G_KEY_FILE_ERROR)
1191  && ((error->code == G_KEY_FILE_ERROR_GROUP_NOT_FOUND)
1192  || (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND)))
1193  {
1194  /* This is the expected exit from this function. */
1195  g_free(key);
1196  return NULL;
1197  }
1198  g_warning("Check file %s, error reading group %s, key %s: %s",
1199  file, KF_GROUP_POS, key, error->message);
1200  g_free(key);
1201  return list;
1202  }
1203 
1204  for (i = 0; i < length; i++)
1205  list = g_slist_append(list, g_strdup(names[i]));
1206 
1207  g_strfreev(names);
1208  return list;
1209 }
1210 
1211 
1212 /* Free the data describing the placement of multiple checks on a page.
1213  */
1214 static void
1215 free_check_position(gchar *name)
1216 {
1217  g_free(name);
1218 }
1219 
1220 
1221 /* Read the information describing the general layout of a page of checks.
1222  * All items in this section are optional except or the name of the check
1223  * style.
1224  */
1225 static gboolean
1226 format_read_general_info(const gchar *file,
1227  GKeyFile *key_file, check_format_t *format)
1228 {
1229  GError *error = NULL;
1230  gchar **parts;
1231  gchar *value;
1232  double *dd;
1233  gsize dd_len;
1234 
1235  value = g_key_file_get_string(key_file, KF_GROUP_TOP, KF_KEY_GUID, &error);
1236  if (error)
1237  {
1238  g_warning("Check file %s, group %s, key %s, error: %s",
1239  file, KF_GROUP_TOP, KF_KEY_GUID, error->message);
1240  g_error_free(error);
1241  return FALSE;
1242  }
1243  parts = g_strsplit(value, "-", -1);
1244  format->guid = g_strjoinv("", parts);
1245  g_strfreev(parts);
1246  DEBUG("Check file %s, group %s, key %s, value: %s",
1247  file, KF_GROUP_TOP, KF_KEY_GUID, format->guid);
1248 
1249  format->title =
1250  g_key_file_get_string(key_file, KF_GROUP_TOP, KF_KEY_TITLE, &error);
1251  if (!error)
1252  {
1253  DEBUG("Check file %s, group %s, key %s, value: %s",
1254  file, KF_GROUP_TOP, KF_KEY_TITLE, format->title);
1255  }
1256  else
1257  {
1258  g_warning("Check file %s, group %s, key %s, error: %s",
1259  file, KF_GROUP_TOP, KF_KEY_TITLE, error->message);
1260  return FALSE;
1261  }
1262 
1263  format->blocking =
1264  g_key_file_get_boolean(key_file, KF_GROUP_TOP, KF_KEY_BLOCKING,
1265  &error);
1266  if (!error)
1267  {
1268  DEBUG("Check file %s, group %s, key %s, value: %d",
1269  file, KF_GROUP_TOP, KF_KEY_BLOCKING, format->blocking);
1270  }
1271  else
1272  {
1273  if (!((error->domain == G_KEY_FILE_ERROR)
1274  && (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND)))
1275  g_warning("Check file %s, group %s, key %s, error: %s",
1276  file, KF_GROUP_TOP, KF_KEY_BLOCKING, error->message);
1277  if ( gnc_prefs_get_bool(GNC_PREFS_GROUP, GNC_PREF_BLOCKING_CHARS) )
1278  {
1279  format->blocking = TRUE;
1280  }
1281  else
1282  {
1283  format->blocking = FALSE;
1284  }
1285  g_clear_error(&error);
1286  }
1287 
1288  format->print_date_format =
1289  g_key_file_get_boolean(key_file, KF_GROUP_TOP, KF_KEY_DATE_FORMAT,
1290  &error);
1291  if (!error)
1292  {
1293  DEBUG("Check file %s, group %s, key %s, value: %d",
1294  file, KF_GROUP_TOP, KF_KEY_DATE_FORMAT, format->print_date_format);
1295  }
1296  else
1297  {
1298  if (!((error->domain == G_KEY_FILE_ERROR)
1299  && (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND)))
1300  g_warning("Check file %s, group %s, key %s, error: %s",
1301  file, KF_GROUP_TOP, KF_KEY_DATE_FORMAT, error->message);
1302  if ( gnc_prefs_get_bool(GNC_PREFS_GROUP, GNC_PREF_PRINT_DATE_FMT) )
1303  {
1304  format->print_date_format = TRUE;
1305  }
1306  else
1307  {
1308  format->print_date_format = FALSE;
1309  }
1310  g_clear_error(&error);
1311  }
1312 
1313  format->show_grid =
1314  g_key_file_get_boolean(key_file, KF_GROUP_TOP, KF_KEY_SHOW_GRID,
1315  &error);
1316  if (!error)
1317  {
1318  DEBUG("Check file %s, group %s, key %s, value: %d",
1319  file, KF_GROUP_TOP, KF_KEY_SHOW_GRID, format->show_grid);
1320  }
1321  else
1322  {
1323  if (!((error->domain == G_KEY_FILE_ERROR)
1324  && (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND)))
1325  g_warning("Check file %s, group %s, key %s, error: %s",
1326  file, KF_GROUP_TOP, KF_KEY_SHOW_GRID, error->message);
1327  format->show_grid = FALSE;
1328  g_clear_error(&error);
1329  }
1330 
1331  format->show_boxes =
1332  g_key_file_get_boolean(key_file, KF_GROUP_TOP, KF_KEY_SHOW_BOXES,
1333  &error);
1334  if (!error)
1335  {
1336  DEBUG("Check file %s, group %s, key %s, value: %d",
1337  file, KF_GROUP_TOP, KF_KEY_SHOW_BOXES, format->show_boxes);
1338  }
1339  else
1340  {
1341  if (!((error->domain == G_KEY_FILE_ERROR)
1342  && (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND)))
1343  g_warning("Check file %s, group %s, key %s, error: %s",
1344  file, KF_GROUP_TOP, KF_KEY_SHOW_BOXES, error->message);
1345  format->show_boxes = FALSE;
1346  g_clear_error(&error);
1347  }
1348 
1349  format->font =
1350  g_key_file_get_string(key_file, KF_GROUP_TOP, KF_KEY_FONT, &error);
1351  if (!error)
1352  {
1353  DEBUG("Check file %s, group %s, key %s, value: %s",
1354  file, KF_GROUP_TOP, KF_KEY_FONT, format->font);
1355  }
1356  else
1357  {
1358  if (!((error->domain == G_KEY_FILE_ERROR)
1359  && (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND)))
1360  g_warning("Check file %s, group %s, key %s, error: %s",
1361  file, KF_GROUP_TOP, KF_KEY_FONT, error->message);
1362  g_clear_error(&error);
1363  }
1364 
1365  format->rotation =
1366  g_key_file_get_double(key_file, KF_GROUP_TOP, KF_KEY_ROTATION, &error);
1367  if (!error)
1368  {
1369  DEBUG("Check file %s, group %s, key %s, value: %f",
1370  file, KF_GROUP_TOP, KF_KEY_ROTATION, format->rotation);
1371  }
1372  else
1373  {
1374  if (!((error->domain == G_KEY_FILE_ERROR)
1375  && (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND)))
1376  g_warning("Check file %s, group %s, key %s, error: %s",
1377  file, KF_GROUP_TOP, KF_KEY_ROTATION, error->message);
1378  format->rotation = 0.0;
1379  g_clear_error(&error);
1380  }
1381 
1382  dd = g_key_file_get_double_list(key_file, KF_GROUP_TOP, KF_KEY_TRANSLATION,
1383  &dd_len, &error);
1384  if (!error)
1385  {
1386  value = doubles_to_string(dd, dd_len);
1387  DEBUG("Check file %s, group %s, key %s, length %"G_GSIZE_FORMAT"; values: %s",
1388  file, KF_GROUP_TOP, KF_KEY_TRANSLATION, dd_len, value);
1389  g_free(value);
1390 
1391  if (dd_len == 2)
1392  {
1393  format->trans_x = dd[0];
1394  format->trans_y = dd[1];
1395  }
1396  else
1397  {
1398  g_warning("Check file %s, error top %s, key %s: 2 values only",
1399  file, KF_GROUP_TOP, KF_KEY_TRANSLATION);
1400  }
1401  g_free(dd);
1402  }
1403  else
1404  {
1405  if (!((error->domain == G_KEY_FILE_ERROR)
1406  && (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND)))
1407  g_warning("Check file %s, group top %s, key %s: %s",
1408  file, KF_GROUP_ITEMS, KF_KEY_TRANSLATION, error->message);
1409  g_clear_error(&error);
1410  }
1411 
1412  return TRUE;
1413 }
1414 
1415 
1416 /* Free all of the information describing a page of checks.
1417  */
1418 static void
1419 free_check_format(check_format_t *data)
1420 {
1421  g_return_if_fail(data);
1422  g_free(data->guid);
1423  g_free(data->filename);
1424  g_free(data->title);
1425  g_free(data->font);
1426  g_slist_foreach(data->positions, (GFunc) free_check_position, NULL);
1427  g_slist_free(data->positions);
1428  g_slist_foreach(data->items, (GFunc) format_free_item_placement, NULL);
1429  g_slist_free(data->items);
1430  g_free(data);
1431 }
1432 
1433 
1434 /* Read a single check format file and append the resulting format to the
1435  * list of all known formats. This function calls other functions to read
1436  * each section of the data file.
1437  */
1438 static check_format_t *
1439 read_one_check_format(PrintCheckDialog *pcd, const gchar *groupname,
1440  const gchar *dirname, const gchar *file)
1441 {
1442  gchar *pathname;
1443  GKeyFile *key_file;
1444  check_format_t *format;
1445 
1446  pathname = g_build_filename(dirname, file, (char *)NULL);
1447  key_file = gnc_key_file_load_from_file(pathname, FALSE, FALSE, NULL);
1448  g_free(pathname);
1449  if (!key_file)
1450  {
1451  g_warning("Check file %s, cannot load file", file);
1452  return NULL;
1453  }
1454 
1455  format = g_new0(check_format_t, 1);
1456  format->group = groupname;
1457  format->filename = g_strdup(file);
1458  if (format_read_general_info(file, key_file, format))
1459  {
1460  format->positions = format_read_multicheck_info(file, key_file, format);
1461  format->items = format_read_item_placement(file, key_file, format);
1462  }
1463 
1464  g_key_file_free(key_file);
1465  if ((NULL == format->title) || (NULL == format->items))
1466  {
1467  g_warning("Check file %s, no items read. Dropping file.", file);
1468  free_check_format(format);
1469  return NULL;
1470  }
1471 
1472  return format;
1473 }
1474 
1475 
1476 /* Iterate over a single check directory, throwing out any backup files and
1477  * then calling a helper function to read and parse the check format within
1478  * the file.
1479  */
1480 static void
1481 read_one_check_directory(PrintCheckDialog *pcd, GtkListStore *store,
1482  const gchar *groupname, const gchar *dirname)
1483 {
1484  check_format_t *format = NULL, *existing;
1485  GDir *dir;
1486  const gchar *filename;
1487  GtkTreeIter iter;
1488  GtkWidget *dialog;
1489  gboolean found = FALSE;
1490 
1491  dir = g_dir_open(dirname, 0, NULL);
1492  if (dir == NULL)
1493  return;
1494 
1495  while ((filename = g_dir_read_name(dir)) != NULL)
1496  {
1497  if (g_str_has_prefix(filename, "#"))
1498  continue;
1499  if (!g_str_has_suffix(filename, ".chk"))
1500  continue;
1501 
1502  format = read_one_check_format(pcd, groupname, dirname, filename);
1503  if (NULL == format)
1504  continue;
1505 
1506  existing = find_existing_format(store, format->guid, NULL);
1507  if (existing)
1508  {
1509  dialog = gtk_message_dialog_new
1510  (GTK_WINDOW(pcd->dialog),
1511  GTK_DIALOG_DESTROY_WITH_PARENT,
1512  GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s",
1513  _("There is a duplicate check format file."));
1514  gtk_message_dialog_format_secondary_text
1515  (GTK_MESSAGE_DIALOG(dialog),
1516  /* Translators:
1517  %1$s is the type of the first check format
1518  (user defined or application defined);
1519  %2$s is the filename of that format;
1520  %3$s the type of the other check format; and
1521  %4$s the filename of that other format. */
1522  _("The GUIDs in the %s check format file '%s' and "
1523  "the %s check format file '%s' match."),
1524  existing->group, existing->filename,
1525  format->group, format->filename);
1526  gtk_dialog_run(GTK_DIALOG(dialog));
1527  gtk_widget_destroy(dialog);
1528  free_check_format (format);
1529  }
1530  else
1531  {
1532  gtk_list_store_append(store, &iter);
1533  gtk_list_store_set(store, &iter, COL_NAME, format->title,
1534  COL_DATA, format, -1);
1535  found = TRUE;
1536  }
1537  }
1538  g_dir_close(dir);
1539 
1540  /* If any files were added to the list, add a separator between
1541  * this group and the next. */
1542  if (found)
1543  {
1544  gtk_list_store_append(store, &iter);
1545  gtk_list_store_set(store, &iter, COL_SEP, TRUE, -1);
1546  }
1547 }
1548 
1549 
1550 /* Read all check formats. This function first looks in the system directory
1551  * for check files, and then looks in the user's .gnucash directory for any
1552  * custom check files.
1553  */
1554 static void
1555 read_formats(PrintCheckDialog *pcd, GtkListStore *store)
1556 {
1557  gchar *dirname, *pkgdatadir;
1558 
1559  pkgdatadir = gnc_path_get_pkgdatadir();
1560  dirname = g_build_filename(pkgdatadir, CHECK_FMT_DIR, (char *)NULL);
1561  /* Translators: This is a directory name. It may be presented to
1562  * the user to indicate that some data file was defined by the
1563  * gnucash application. */
1564  read_one_check_directory(pcd, store, _("application"), dirname);
1565  g_free(dirname);
1566  g_free(pkgdatadir);
1567 
1568  dirname = gnc_build_userdata_path(CHECK_FMT_DIR);
1569  /* Translators: This is a directory name. It may be presented to
1570  * the user to indicate that some data file was defined by a
1571  * user herself. */
1572  read_one_check_directory(pcd, store, _("user"), dirname);
1573  g_free(dirname);
1574 }
1575 
1576 
1577 static gboolean
1578 format_is_a_separator (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1579 {
1580  gboolean separator;
1581 
1582  gtk_tree_model_get(model, iter, COL_SEP, &separator, -1);
1583  return separator;
1584 }
1585 
1586 
1587 static void
1588 initialize_format_combobox (PrintCheckDialog *pcd)
1589 {
1590  GtkListStore *store;
1591  GtkTreeIter iter;
1592 
1593  store = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_BOOLEAN);
1594  read_formats(pcd, store);
1595  gtk_list_store_append(store, &iter);
1596  gtk_list_store_set(store, &iter, COL_NAME, _("Custom"), -1);
1597  pcd->format_max = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), NULL);
1598  gtk_combo_box_set_model(GTK_COMBO_BOX(pcd->format_combobox),
1599  GTK_TREE_MODEL(store));
1600  gtk_combo_box_set_row_separator_func(GTK_COMBO_BOX(pcd->format_combobox),
1601  format_is_a_separator, NULL, NULL);
1602  g_object_unref (store);
1603 }
1604 
1605 
1606 /*****************************************************
1607  * gnc_ui_print_check_dialog_create *
1608  * make a new print check dialog and wait for it. *
1609  *****************************************************/
1610 void
1611 gnc_ui_print_check_dialog_create(GtkWidget *parent,
1612  GList *splits)
1613 {
1614  PrintCheckDialog *pcd;
1615  GtkBuilder *builder;
1616  GtkWidget *table;
1617  gchar *font;
1618  Transaction *trans = NULL;
1619 
1620  pcd = g_new0(PrintCheckDialog, 1);
1621  pcd->caller_window = GTK_WINDOW(parent);
1622  pcd->splits = g_list_copy(splits);
1623 
1624  builder = gtk_builder_new();
1625  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment1");
1626  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment2");
1627  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment3");
1628  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment4");
1629  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment5");
1630  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment6");
1631  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment7");
1632  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment8");
1633  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment9");
1634  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment10");
1635  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment11");
1636  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment12");
1637  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment13");
1638  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment14");
1639  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment15");
1640  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment16");
1641  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment17");
1642  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment18");
1643  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment19");
1644  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment20");
1645  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment21");
1646  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment22");
1647  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment23");
1648  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "adjustment24");
1649  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "liststore1");
1650  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "liststore2");
1651  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "liststore3");
1652  gnc_builder_add_from_file (builder, "dialog-print-check.glade", "print_check_dialog");
1653 
1654  gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, pcd);
1655 
1656  pcd->builder = builder;
1657  pcd->dialog = GTK_WIDGET(gtk_builder_get_object (builder, "print_check_dialog"));
1658 
1659  // Set the name for this dialog so it can be easily manipulated with css
1660  gtk_widget_set_name (GTK_WIDGET(pcd->dialog), "gnc-id-print-check");
1661 
1662  /* now pick out the relevant child widgets */
1663  pcd->format_combobox = GTK_WIDGET(gtk_builder_get_object (builder, "check_format_combobox"));
1664  pcd->position_combobox = GTK_WIDGET(gtk_builder_get_object (builder, "check_position_combobox"));
1665  pcd->first_page_count = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "first_page_count_entry"));
1666 
1667  pcd->custom_table = GTK_WIDGET(gtk_builder_get_object (builder, "custom_table"));
1668  pcd->payee_x = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "payee_x_entry"));
1669  pcd->payee_y = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "payee_y_entry"));
1670  pcd->date_x = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "date_x_entry"));
1671  pcd->date_y = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "date_y_entry"));
1672  pcd->words_x = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "amount_words_x_entry"));
1673  pcd->words_y = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "amount_words_y_entry"));
1674  pcd->number_x = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "amount_numbers_x_entry"));
1675  pcd->number_y = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "amount_numbers_y_entry"));
1676  pcd->notes_x = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "notes_x_entry"));
1677  pcd->notes_y = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "notes_y_entry"));
1678  pcd->memo_x = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "memo_x_entry"));
1679  pcd->memo_y = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "memo_y_entry"));
1680  pcd->address_x = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "address_x_entry"));
1681  pcd->address_y = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "address_y_entry"));
1682  pcd->splits_amount_x = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "splits_amount_x_entry"));
1683  pcd->splits_amount_y = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "splits_amount_y_entry"));
1684  pcd->splits_memo_x = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "splits_memo_x_entry"));
1685  pcd->splits_memo_y = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "splits_memo_y_entry"));
1686  pcd->splits_account_x = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "splits_account_x_entry"));
1687  pcd->splits_account_y = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "splits_account_y_entry"));
1688  pcd->translation_x = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "translation_x_entry"));
1689  pcd->translation_y = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "translation_y_entry"));
1690  pcd->translation_label = GTK_WIDGET(gtk_builder_get_object (builder, "translation_label"));
1691  pcd->check_rotation = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "check_rotation_entry"));
1692  pcd->units_combobox = GTK_WIDGET(gtk_builder_get_object (builder, "units_combobox"));
1693 
1694  gtk_window_set_transient_for(GTK_WINDOW(pcd->dialog), pcd->caller_window);
1695 
1696  /* Create and attach the date-format chooser */
1697  table = GTK_WIDGET(gtk_builder_get_object (builder, "options_table"));
1698  pcd->date_format = gnc_date_format_new_without_label();
1699  gtk_grid_attach (GTK_GRID(table), pcd->date_format, 1, 4, 1, 1);
1700 
1701  /* Default font (set in preferences) */
1702  font = gnc_prefs_get_string(GNC_PREFS_GROUP, GNC_PREF_DEFAULT_FONT);
1703  pcd->default_font = font && *font ? font : g_strdup(DEFAULT_FONT);
1704 
1705  /* Update the combo boxes bases on the available check formats */
1706  initialize_format_combobox(pcd);
1707 
1708  /* address */
1709  pcd->check_address_name = GTK_WIDGET(gtk_builder_get_object (builder, "check_address_name"));
1710  pcd->check_address_1 = GTK_WIDGET(gtk_builder_get_object (builder, "check_address_1"));
1711  pcd->check_address_2 = GTK_WIDGET(gtk_builder_get_object (builder, "check_address_2"));
1712  pcd->check_address_3 = GTK_WIDGET(gtk_builder_get_object (builder, "check_address_3"));
1713  pcd->check_address_4 = GTK_WIDGET(gtk_builder_get_object (builder, "check_address_4"));
1714  /* fill in any available address data */
1715  /* Can't access business objects e.g. Customer,Vendor,Employee because
1716  * it would create build problems */
1717  if (g_list_length(pcd->splits) == 1)
1718  {
1719  GncOwner txn_owner;
1720 
1721  trans = xaccSplitGetParent((Split *)(pcd->splits->data));
1722  if (gncOwnerGetOwnerFromTxn (trans, &txn_owner))
1723  {
1724  GncOwner owner;
1725  gncOwnerCopy (gncOwnerGetEndOwner (&txn_owner), &owner);
1726 
1727  /* Got a business owner, get the address */
1728  gtk_entry_set_text(GTK_ENTRY(pcd->check_address_name), gncOwnerGetName(&owner));
1729  gtk_entry_set_text(GTK_ENTRY(pcd->check_address_1), gncAddressGetAddr1 (gncOwnerGetAddr(&owner)));
1730  gtk_entry_set_text(GTK_ENTRY(pcd->check_address_2), gncAddressGetAddr2 (gncOwnerGetAddr(&owner)));
1731  gtk_entry_set_text(GTK_ENTRY(pcd->check_address_3), gncAddressGetAddr3 (gncOwnerGetAddr(&owner)));
1732  gtk_entry_set_text(GTK_ENTRY(pcd->check_address_4), gncAddressGetAddr4 (gncOwnerGetAddr(&owner)));
1733  }
1734  }
1735 
1736  /* Use transaction description as address name if no better address has been found */
1737  if ( trans && (0 == gtk_entry_get_text_length (GTK_ENTRY(pcd->check_address_name))) )
1738  gtk_entry_set_text(GTK_ENTRY(pcd->check_address_name), xaccTransGetDescription(trans));
1739 
1740  gtk_widget_destroy(GTK_WIDGET(gtk_builder_get_object (builder, "lower_left")));
1741 
1742  gnc_ui_print_restore_dialog(pcd);
1743  gnc_restore_window_size(GNC_PREFS_GROUP, GTK_WINDOW(pcd->dialog), GTK_WINDOW (parent));
1744 
1745  g_object_unref(G_OBJECT(builder));
1746  gtk_widget_show_all(pcd->dialog);
1747 }
1748 
1749 
1750 /**************************************
1751  * Print check contents to the page. *
1752  **************************************/
1753 
1754 /* Draw a grid pattern on the page to be printed. This grid is helpful when
1755  * figuring out the offsets for where to print various items on the page.
1756  */
1757 static void
1758 draw_grid(GtkPrintContext *context, gint width, gint height, const gchar *font)
1759 {
1760  const double dash_pattern[2] = { 1.0, 5.0 };
1761  PangoFontDescription *desc;
1762  PangoLayout *layout;
1763  cairo_t *cr;
1764  gchar *text;
1765  gint i;
1766 
1767  /* Initialize for printing text */
1768  layout = gtk_print_context_create_pango_layout(context);
1769  desc = pango_font_description_from_string(font);
1770  pango_layout_set_font_description(layout, desc);
1771  pango_font_description_free(desc);
1772  pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT);
1773  pango_layout_set_width(layout, -1);
1774 
1775  /* Set up the line to draw with. */
1776  cr = gtk_print_context_get_cairo_context(context);
1777  cairo_save(cr);
1778  cairo_set_line_width(cr, 1.0);
1779  cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
1780  cairo_set_dash(cr, dash_pattern, 2, 0);
1781 
1782  /* Draw horizontal lines */
1783  for (i = -200; i < (height + 200); i += 50)
1784  {
1785  text = g_strdup_printf("%d", (int)i);
1786  cairo_move_to(cr, -200, i);
1787  cairo_line_to(cr, width + 200, i);
1788  cairo_stroke(cr);
1789  pango_layout_set_text(layout, text, -1);
1790  cairo_move_to(cr, 0, i);
1791  pango_cairo_show_layout(cr, layout);
1792  g_free(text);
1793  }
1794 
1795  /* Draw vertical lines */
1796  for (i = -200; i < (width + 200); i += 50)
1797  {
1798  text = g_strdup_printf("%d", (int)i);
1799  cairo_move_to(cr, i, -200);
1800  cairo_line_to(cr, i, height + 200);
1801  cairo_stroke(cr);
1802  pango_layout_set_text(layout, text, -1);
1803  cairo_move_to(cr, i, 0);
1804  pango_cairo_show_layout(cr, layout);
1805  g_free(text);
1806  }
1807 
1808  /* Clean up after ourselves */
1809  cairo_restore(cr);
1810  g_object_unref(layout);
1811 }
1812 
1813 
1814 /* Print a single line of text to the printed page. If a width and height
1815  * are specified, the line will be wrapped at the specified width, and the
1816  * resulting text will be clipped if it does not fit in the space
1817  * available.
1818  */
1819 static gdouble
1820 draw_text(GtkPrintContext *context, const gchar *text, check_item_t *data,
1821  PangoFontDescription *default_desc)
1822 {
1823  PangoFontDescription *desc;
1824  PangoLayout *layout;
1825  cairo_t *cr;
1826  gint layout_height, layout_width;
1827  gdouble width, height;
1828  gchar *new_text;
1829 
1830  if ((NULL == text) || (strlen(text) == 0))
1831  return 0.0;
1832 
1833  /* Initialize for printing text */
1834  layout = gtk_print_context_create_pango_layout(context);
1835  if (data->font)
1836  {
1837  desc = pango_font_description_from_string(data->font);
1838  pango_layout_set_font_description(layout, desc);
1839  pango_font_description_free(desc);
1840  }
1841  else
1842  {
1843  pango_layout_set_font_description(layout, default_desc);
1844  }
1845  pango_layout_set_alignment(layout,
1846  data->w ? data->align : PANGO_ALIGN_LEFT);
1847  pango_layout_set_width(layout, data->w ? data->w * PANGO_SCALE : -1);
1848  pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
1849  if ( data->blocking )
1850  {
1851  new_text = g_strdup_printf("***%s***", text);
1852  pango_layout_set_text(layout, new_text, -1);
1853  g_free(new_text);
1854  }
1855  else
1856  {
1857  pango_layout_set_text(layout, text, -1);
1858  }
1859  pango_layout_get_size(layout, &layout_width, &layout_height);
1860  width = (gdouble) layout_width / PANGO_SCALE;
1861  height = (gdouble) layout_height / PANGO_SCALE;
1862 
1863  cr = gtk_print_context_get_cairo_context(context);
1864  cairo_save(cr);
1865 
1866  /* Clip text to the enclosing rectangle */
1867  if (data->w && data->h)
1868  {
1869  DEBUG("Text clip rectangle, coords %f,%f, size %f,%f",
1870  data->x, data->y - data->h, data->w, data->h);
1871  cairo_rectangle(cr, data->x, data->y - data->h, data->w, data->h);
1872  cairo_clip_preserve(cr);
1873  }
1874 
1875  /* Draw the text */
1876  DEBUG("Text move to %f,%f, print '%s'", data->x, data->y,
1877  text ? text : "(null)");
1878  cairo_move_to(cr, data->x, data->y - height);
1879  pango_cairo_show_layout(cr, layout);
1880 
1881  /* Clean up after ourselves */
1882  cairo_restore(cr);
1883  g_object_unref(layout);
1884  return width;
1885 
1886 }
1887 
1888 
1889 /* Find and load the specified image. If the specified filename isn't an
1890  * absolute path name, this code will also look in the gnucash system check
1891  * format directory, and then in the user's private check format
1892  * directory.
1893  *
1894  * NOTE: The gtk_image_new_from_file() function never fails. If it can't
1895  * find the specified file, it returns the "broken image" icon. This function
1896  * takes advantage of that.
1897 */
1898 static GtkWidget *
1899 read_image (const gchar *filename)
1900 {
1901  GtkWidget *image;
1902  gchar *pkgdatadir, *dirname, *tmp_name;
1903 
1904  if (g_path_is_absolute(filename))
1905  return gtk_image_new_from_file(filename);
1906 
1907  pkgdatadir = gnc_path_get_pkgdatadir();
1908  tmp_name = g_build_filename(pkgdatadir, CHECK_FMT_DIR, filename, (char *)NULL);
1909  if (!g_file_test(tmp_name, G_FILE_TEST_EXISTS))
1910  {
1911  g_free(tmp_name);
1912  dirname = gnc_build_userdata_path(CHECK_FMT_DIR);
1913  tmp_name = g_build_filename(dirname, filename, (char *)NULL);
1914  g_free(dirname);
1915  }
1916  image = gtk_image_new_from_file(tmp_name);
1917  g_free(tmp_name);
1918  return image;
1919 }
1920 
1921 
1922 /* Print a single image to the printed page. This picture will be scaled
1923  * down to fit in the specified size rectangle. Scaling is done with the
1924  * proportions locked 1:1 so as not to distort the image.
1925  */
1926 static void
1927 draw_picture(GtkPrintContext *context, check_item_t *data)
1928 {
1929  cairo_t *cr;
1930  GdkPixbuf *pixbuf, *scaled_pixbuf;
1931  GtkImage *image;
1932  gint pix_w, pix_h;
1933  gdouble scale_w, scale_h, scale;
1934 
1935  cr = gtk_print_context_get_cairo_context(context);
1936  cairo_save(cr);
1937 
1938  /* Get the picture. */
1939  image = GTK_IMAGE(read_image(data->filename));
1940  pixbuf = gtk_image_get_pixbuf(image);
1941  if (pixbuf)
1942  {
1943  g_object_ref(pixbuf);
1944  }
1945  else
1946  {
1947  GtkIconTheme *def_theme = gtk_icon_theme_get_default ();
1948 
1949  g_warning("Filename '%s' cannot be read or understood.",
1950  data->filename);
1951 
1952  pixbuf = gtk_icon_theme_load_icon (def_theme,
1953  "image-missing",
1954  16,
1955  GTK_ICON_LOOKUP_USE_BUILTIN,
1956  NULL);
1957  }
1958  pix_w = gdk_pixbuf_get_width(pixbuf);
1959  pix_h = gdk_pixbuf_get_height(pixbuf);
1960 
1961  /* Draw the enclosing rectangle */
1962  if (data->w && data->h)
1963  {
1964  cairo_rectangle(cr, data->x, data->y - data->h, data->w, data->h);
1965  DEBUG("Picture clip rectangle, user coords %f,%f, user size %f,%f",
1966  data->x, data->y - data->h, data->w, data->h);
1967  }
1968  else
1969  {
1970  cairo_rectangle(cr, data->x, data->y - pix_h, pix_w, pix_h);
1971  DEBUG("Picture clip rectangle, user coords %f,%f, pic size %d,%d",
1972  data->x, data->y - data->h, pix_w, pix_h);
1973  }
1974  cairo_clip_preserve(cr);
1975 
1976  /* Scale down to fit. Never scale up. */
1977  scale_w = scale_h = 1;
1978  if (data->w && (pix_w > data->w))
1979  scale_w = data->w / pix_w;
1980  if (data->h && (pix_h > data->h))
1981  scale_h = data->h / pix_h;
1982  scale = MIN(scale_w, scale_h);
1983 
1984  if (scale != 1)
1985  {
1986  scaled_pixbuf = gdk_pixbuf_scale_simple(pixbuf, pix_w * scale,
1987  pix_h * scale,
1988  GDK_INTERP_BILINEAR);
1989  pix_h = gdk_pixbuf_get_height(scaled_pixbuf);
1990  gdk_cairo_set_source_pixbuf(cr, scaled_pixbuf, data->x,
1991  data->y - pix_h);
1992 
1993  g_object_unref(scaled_pixbuf);
1994  }
1995  else
1996  {
1997  gdk_cairo_set_source_pixbuf(cr, pixbuf, data->x, data->y - pix_h);
1998  }
1999  g_object_unref(pixbuf);
2000  cairo_paint(cr);
2001 
2002  /* Clean up after ourselves */
2003  cairo_restore(cr);
2004  gtk_widget_destroy(GTK_WIDGET(image));
2005 }
2006 
2007 
2008 #define DATE_FMT_HEIGHT 8
2009 #define DATE_FMT_SLOP 2
2010 
2011 /* There is a new Canadian requirement that all software that prints the date
2012  * on a check must also print the format of that date underneath using a 6-8
2013  * point font. This function implements that requirement. It requires the
2014  * font description used in printing the date so that it can print in the same
2015  * font using a smaller point size. It also requires width of the printed
2016  * date as an argument, allowing it to center the format string under the
2017  * date.
2018  *
2019  * Note: This code only prints a date if the user has explicitly requested it
2020  * via a preference setting. This is because gnucash has no way of
2021  * knowing if the user's checks already have a date format printed on them.
2022  */
2023 static void
2024 draw_date_format(GtkPrintContext *context, const gchar *date_format,
2025  check_item_t *data, PangoFontDescription *default_desc,
2026  gdouble width)
2027 {
2028  PangoFontDescription *date_desc;
2029  check_item_t date_item;
2030  gchar *text = NULL, *expanded = NULL;
2031  const gchar *c;
2032  GString *cdn_fmt;
2033 
2034  setlocale(LC_ALL, NULL);
2035  if ( !data->print_date_format ) return;
2036 
2037  date_desc = pango_font_description_copy_static(default_desc);
2038  pango_font_description_set_size(date_desc, DATE_FMT_HEIGHT * PANGO_SCALE);
2039  date_item = *data;
2040  date_item.y += (DATE_FMT_HEIGHT + DATE_FMT_SLOP);
2041  date_item.w = width;
2042  date_item.h = DATE_FMT_HEIGHT + DATE_FMT_SLOP;
2043  date_item.align = PANGO_ALIGN_CENTER;
2044 
2045  /* This is a date format string. It should only contain ascii. */
2046  cdn_fmt = g_string_new_len(NULL, 50);
2047  for (c = date_format; c && *c; )
2048  {
2049  if ((c[0] != '%') || (c[1] == '\0'))
2050  {
2051  c += 1;
2052  continue;
2053  }
2054  switch (c[1])
2055  {
2056  case 'F':
2057  cdn_fmt = g_string_append(cdn_fmt, "YYYYMMDD");
2058  break;
2059  case 'Y':
2060  cdn_fmt = g_string_append(cdn_fmt, "YYYY");
2061  break;
2062  case 'y':
2063  cdn_fmt = g_string_append(cdn_fmt, "YY");
2064  break;
2065  case 'm':
2066  cdn_fmt = g_string_append(cdn_fmt, "MM");
2067  break;
2068  case 'd':
2069  case 'e':
2070  cdn_fmt = g_string_append(cdn_fmt, "DD");
2071  break;
2072  case 'x':
2073  expanded = g_strdup_printf("%s%s",
2075  c + 2);
2076  c = expanded;
2077  continue;
2078  default:
2079  break;
2080  }
2081  c += 2;
2082  }
2083 
2084  text = g_string_free(cdn_fmt, FALSE);
2085  draw_text(context, text, &date_item, date_desc);
2086  g_free(text);
2087  if (expanded)
2088  g_free(expanded);
2089  pango_font_description_free(date_desc);
2090 }
2091 
2092 
2093 /* Print each of the items that in the description of a single check. This
2094  * function uses helper functions to print text based and picture based items.
2095  */
2096 static void
2097 draw_page_items(GtkPrintContext *context,
2098  check_format_t *format, gpointer user_data)
2099 {
2100  PrintCheckDialog *pcd = (PrintCheckDialog *) user_data;
2101  PangoFontDescription *default_desc;
2102  Transaction *trans;
2103  gnc_numeric amount;
2104  GNCPrintAmountInfo info;
2105  const gchar *date_format;
2106  gchar *text = NULL, buf[100];
2107  GSList *elem;
2108  check_item_t *item;
2109  gdouble width;
2110  gchar *address = NULL;
2111 
2112  trans = xaccSplitGetParent(pcd->split);
2113  /* This was valid when the check printing dialog was instantiated. */
2114  g_return_if_fail(trans);
2115  amount = gnc_numeric_abs(xaccSplitGetAmount(pcd->split));
2116 
2117  if (format->font)
2118  default_desc = pango_font_description_from_string(format->font);
2119  else
2120  default_desc = pango_font_description_from_string(pcd->default_font);
2121 
2122  /* Now put the actual data onto the page. */
2123  for (elem = format->items; elem; elem = g_slist_next(elem))
2124  {
2125  item = elem->data;
2126 
2127  switch (item->type)
2128  {
2129  case DATE:
2130  {
2131  GDate date;
2132  g_date_clear (&date, 1);
2133  gnc_gdate_set_time64 (&date, xaccTransGetDate(trans));
2134  date_format =
2135  gnc_date_format_get_custom(GNC_DATE_FORMAT
2136  (pcd->date_format));
2137  g_date_strftime(buf, 100, date_format, &date);
2138  width = draw_text(context, buf, item, default_desc);
2139  draw_date_format(context, date_format, item, default_desc, width);
2140  break;
2141  }
2142 
2143  case PAYEE:
2144  draw_text(context, xaccTransGetDescription(trans), item, default_desc);
2145  break;
2146 
2147  case NOTES:
2148  draw_text(context, xaccTransGetNotes(trans), item, default_desc);
2149  break;
2150 
2151  case MEMO:
2152  draw_text(context, xaccSplitGetMemo(pcd->split), item, default_desc);
2153  break;
2154 
2155  case ACTION:
2156  draw_text(context, gnc_get_action_num(trans, pcd->split), item,
2157  default_desc);
2158  break;
2159 
2160  case CHECK_NUMBER:
2161  draw_text(context, gnc_get_num_action(trans, pcd->split), item,
2162  default_desc);
2163  break;
2164 
2165  case AMOUNT_NUMBER:
2166  info = gnc_default_print_info(FALSE);
2167  draw_text(context, xaccPrintAmount(amount, info),
2168  item, default_desc);
2169  break;
2170 
2171  case AMOUNT_WORDS:
2172  text = numeric_to_words(amount);
2173  draw_text(context, text, item, default_desc);
2174  g_free(text);
2175  break;
2176 
2177  case TEXT:
2178  draw_text(context, item->text, item, default_desc);
2179  break;
2180 
2181  case ADDRESS:
2182  address = get_check_address(pcd);
2183  draw_text(context, address, item, default_desc);
2184  g_free(address);
2185  break;
2186 
2187  case SPLITS_AMOUNT:
2188  text = get_check_splits_amount(pcd);
2189  draw_text(context, text, item, default_desc);
2190  g_free(text);
2191  break;
2192 
2193  case SPLITS_MEMO:
2194  text = get_check_splits_memo(pcd);
2195  draw_text(context, text, item, default_desc);
2196  g_free(text);
2197  break;
2198 
2199  case SPLITS_ACCOUNT:
2200  text = get_check_splits_account(pcd);
2201  draw_text(context, text, item, default_desc);
2202  g_free(text);
2203  break;
2204 
2205  case PICTURE:
2206  draw_picture(context, item);
2207  break;
2208 
2209  default:
2210  text = g_strdup_printf("(unknown check field, type %d)", item->type);
2211  draw_text(context, text, item, default_desc);
2212  g_free(text);
2213  break;
2214  }
2215  }
2216 
2217  pango_font_description_free(default_desc);
2218 }
2219 
2220 
2221 /* Print each of the items that in the description of a single check. This
2222  * function uses helper functions to print text based and picture based items.
2223  */
2224 static void
2225 draw_page_boxes(GtkPrintContext *context,
2226  check_format_t *format, gpointer user_data)
2227 {
2228  cairo_t *cr;
2229  GSList *elem;
2230  check_item_t *item;
2231 
2232  cr = gtk_print_context_get_cairo_context(context);
2233 
2234  /* Now put the actual data onto the page. */
2235  for (elem = format->items; elem; elem = g_slist_next(elem))
2236  {
2237  item = elem->data;
2238  if (!item->w || !item->h)
2239  continue;
2240  cairo_rectangle(cr, item->x, item->y - item->h, item->w, item->h);
2241  cairo_stroke(cr);
2242  }
2243 }
2244 
2245 
2246 /* Print an entire page based upon the layout in a check description file. This
2247  * function takes care of translating/rotating the page, calling the function to
2248  * print the grid pattern (if requested), and calls a helper function to print
2249  * all check items.
2250  */
2251 static void
2252 draw_check_format(GtkPrintContext *context, gint position,
2253  check_format_t *format, gpointer user_data)
2254 {
2255  PrintCheckDialog *pcd = (PrintCheckDialog *) user_data;
2256  gdouble x, y, r, multip;
2257  cairo_t *cr = gtk_print_context_get_cairo_context(context);
2258 
2259  /* Translate all subsequent check items if required. */
2260  if ((position > 0) && (position < pcd->position_max))
2261  {
2262  /* Standard positioning is used.
2263  * Note that the first check on the page (position 0) doesn't
2264  * need to be moved (hence the test for position > 0 above. */
2265  cairo_translate(cr, 0, format->height); /* Translation is relative to previous
2266  check translation, not to page border ! */
2267  DEBUG("Position %d translated by %f relative to previous check (pre-defined)", position, format->height);
2268  DEBUG(" by %f relative to page (pre-defined)", position * format->height);
2269  }
2270  else if (position == pcd->position_max)
2271  {
2272  /* Custom positioning is used. */
2273  multip = pcd_get_custom_multip(pcd);
2274  x = multip * gtk_spin_button_get_value(pcd->translation_x);
2275  y = multip * gtk_spin_button_get_value(pcd->translation_y);
2276  cairo_translate(cr, x, y);
2277  DEBUG("Position translated by %f,%f (custom)", x, y);
2278  r = gtk_spin_button_get_value(pcd->check_rotation);
2279  cairo_rotate(cr, r * DEGREES_TO_RADIANS);
2280  DEBUG("Position rotated by %f degrees (custom)", r);
2281  }
2282 
2283  /* Draw layout boxes if requested. Also useful when determining check
2284  * layouts. */
2285  if (format->show_boxes)
2286  draw_page_boxes(context, format, user_data);
2287 
2288  /* Draw the actual check data. */
2289  draw_page_items(context, format, user_data);
2290 }
2291 
2292 
2293 static void
2294 draw_check_custom(GtkPrintContext *context, gpointer user_data)
2295 {
2296  PrintCheckDialog *pcd = (PrintCheckDialog *) user_data;
2297  GNCPrintAmountInfo info;
2298  PangoFontDescription *desc;
2299  Transaction *trans;
2300  gnc_numeric amount;
2301  cairo_t *cr;
2302  const gchar *date_format;
2303  gchar *text = NULL, buf[100];
2304  check_item_t item = { 0 };
2305  gdouble x, y, multip, degrees;
2306  GDate date;
2307  gchar *address;
2308 
2309  trans = xaccSplitGetParent(pcd->split);
2310  /* This was valid when the check printing dialog was instantiated. */
2311  g_return_if_fail(trans);
2312 
2313  desc = pango_font_description_from_string(pcd->default_font);
2314 
2315  multip = pcd_get_custom_multip(pcd);
2316  degrees = gtk_spin_button_get_value(pcd->check_rotation);
2317  cr = gtk_print_context_get_cairo_context(context);
2318  cairo_rotate(cr, degrees * DEGREES_TO_RADIANS);
2319  DEBUG("Page rotated by %f degrees", degrees);
2320 
2321  x = multip * gtk_spin_button_get_value(pcd->translation_x);
2322  y = multip * gtk_spin_button_get_value(pcd->translation_y);
2323  cairo_translate(cr, x, y);
2324  DEBUG("Page translated by %f,%f", x, y);
2325 
2326  item.x = multip * gtk_spin_button_get_value(pcd->payee_x);
2327  item.y = multip * gtk_spin_button_get_value(pcd->payee_y);
2328  draw_text(context, xaccTransGetDescription(trans), &item, desc);
2329 
2330  item.x = multip * gtk_spin_button_get_value(pcd->date_x);
2331  item.y = multip * gtk_spin_button_get_value(pcd->date_y);
2332  g_date_clear (&date, 1);
2333  gnc_gdate_set_time64 (&date, xaccTransGetDate(trans));
2334  date_format = gnc_date_format_get_custom(GNC_DATE_FORMAT(pcd->date_format));
2335  g_date_strftime(buf, 100, date_format, &date);
2336  draw_text(context, buf, &item, desc);
2337 
2338  item.x = multip * gtk_spin_button_get_value(pcd->number_x);
2339  item.y = multip * gtk_spin_button_get_value(pcd->number_y);
2340  info = gnc_default_print_info(FALSE);
2341  amount = gnc_numeric_abs(xaccSplitGetAmount(pcd->split));
2342  draw_text(context, xaccPrintAmount(amount, info), &item, desc);
2343 
2344  item.x = multip * gtk_spin_button_get_value(pcd->words_x);
2345  item.y = multip * gtk_spin_button_get_value(pcd->words_y);
2346  text = numeric_to_words(amount);
2347  draw_text(context, text, &item, desc);
2348  g_free(text);
2349 
2350  item.x = multip * gtk_spin_button_get_value(pcd->address_x);
2351  item.y = multip * gtk_spin_button_get_value(pcd->address_y);
2352  address = get_check_address(pcd);
2353  draw_text(context, address, &item, desc);
2354  g_free(address);
2355 
2356  item.x = multip * gtk_spin_button_get_value(pcd->splits_amount_x);
2357  item.y = multip * gtk_spin_button_get_value(pcd->splits_amount_y);
2358  text = get_check_splits_amount(pcd);
2359  draw_text(context, text, &item, desc);
2360  g_free(text);
2361 
2362  item.x = multip * gtk_spin_button_get_value(pcd->splits_memo_x);
2363  item.y = multip * gtk_spin_button_get_value(pcd->splits_memo_y);
2364  text = get_check_splits_memo(pcd);
2365  draw_text(context, text, &item, desc);
2366  g_free(text);
2367 
2368  item.x = multip * gtk_spin_button_get_value(pcd->splits_account_x);
2369  item.y = multip * gtk_spin_button_get_value(pcd->splits_account_y);
2370  text = get_check_splits_account(pcd);
2371  draw_text(context, text, &item, desc);
2372  g_free(text);
2373 
2374  item.x = multip * gtk_spin_button_get_value(pcd->notes_x);
2375  item.y = multip * gtk_spin_button_get_value(pcd->notes_y);
2376  draw_text(context, xaccTransGetNotes(trans), &item, desc);
2377 
2378  item.x = multip * gtk_spin_button_get_value(pcd->memo_x);
2379  item.y = multip * gtk_spin_button_get_value(pcd->memo_y);
2380  draw_text(context, xaccSplitGetMemo(pcd->split), &item, desc);
2381 
2382  pango_font_description_free(desc);
2383 }
2384 
2385 
2386 /* Print a page of checks. This takes into account the number of checks to print,
2387  * the number of checks on a page, and the starting check position on the page.
2388  * This function is called once by the GtkPrint code once for each page to be printed.
2389  */
2390 static void
2391 draw_page(GtkPrintOperation *operation,
2392  GtkPrintContext *context, gint page_nr, gpointer user_data)
2393 {
2394  PrintCheckDialog *pcd = (PrintCheckDialog *) user_data;
2395  check_format_t *format;
2396  cairo_t *cr = gtk_print_context_get_cairo_context(context);
2397 
2398  format = pcd->selected_format;
2399  if (format)
2400  {
2401  gint first_check, last_check;
2402  gint first_page_count;
2403  guint check_count = g_list_length(pcd->splits);
2404  gint check_number;
2405  gint position = gtk_combo_box_get_active(GTK_COMBO_BOX(pcd->position_combobox));
2406  gint last_blank_check_pos;
2407  gint checks_per_page;
2408  GList *next_split;
2409 
2410  if (position == pcd->position_max)
2411  {
2412  /* Custom position, one check per page */
2413  checks_per_page = 1;
2414  first_page_count = 1;
2415  }
2416  else
2417  {
2418  checks_per_page = pcd->position_max;
2419  first_page_count = gtk_spin_button_get_value_as_int(pcd->first_page_count);
2420  }
2421 
2422  if (page_nr == 0)
2423  {
2424  first_check = 0;
2425  last_check = first_page_count - 1;
2426  next_split = pcd->splits;
2427  }
2428  else
2429  {
2430  first_check = first_page_count + (page_nr - 1) * checks_per_page;
2431  last_check = MIN(check_count - 1, first_check + checks_per_page - 1);
2432  next_split = g_list_nth(pcd->splits, first_check);
2433  /* If position is not "custom" reset it to top */
2434  if (position < pcd->position_max)
2435  position = 0;
2436  }
2437 
2438  /* Do page level translations/rotations */
2439  cairo_translate(cr, format->trans_x, format->trans_y);
2440  DEBUG("Page translated by %f,%f", format->trans_x, format->trans_y);
2441  cairo_rotate(cr, format->rotation * DEGREES_TO_RADIANS);
2442  DEBUG("Page rotated by %f degrees", format->rotation);
2443 
2444  /* The grid is useful when determining check layouts */
2445  if (format->show_grid)
2446  {
2447  draw_grid(context,
2448  gtk_print_context_get_width(context),
2449  gtk_print_context_get_height(context),
2450  pcd->default_font);
2451  }
2452 
2453  last_blank_check_pos = position - 1;
2454  /* Optionally skip blank check positions if */
2455  if ((page_nr == 0) /* on first page AND */
2456  && (last_blank_check_pos > 0) /* there's more than one blank check */
2457  && (position < pcd->position_max)) /* but don't skip for custom positioning */
2458  cairo_translate(cr, 0, format->height * last_blank_check_pos);
2459 
2460  for (check_number = first_check; check_number <= last_check;
2461  check_number++, position++)
2462  {
2463  pcd->split = (Split *) next_split->data;
2464  next_split = g_list_next(next_split);
2465  draw_check_format(context, position, format, user_data);
2466  }
2467  }
2468  else
2469  {
2470  /* Custom check format */
2471  pcd->split = (Split *) g_list_nth_data(pcd->splits, page_nr);
2472  g_return_if_fail(pcd->split);
2473  draw_check_custom(context, user_data);
2474  }
2475 }
2476 
2477 
2478 /* Compute the number of pages required to complete this print operation.
2479  * This function is called once by the GtkPrint code to determine the number
2480  * of pages required to complete the print operation.
2481  */
2482 static void
2483 begin_print(GtkPrintOperation *operation,
2484  GtkPrintContext *context, gpointer user_data)
2485 {
2486  PrintCheckDialog *pcd = (PrintCheckDialog *) user_data;
2487  guint check_count = g_list_length(pcd->splits);
2488  gint pages;
2489  gint position = gtk_combo_box_get_active(GTK_COMBO_BOX(pcd->position_combobox));
2490 
2491  if (pcd->selected_format /* User selected a format other than custom */
2492  && pcd->position_max > 1 /* The format has more than one check per page
2493  (position_max is equivalent to custom
2494  positioning, and there need to be at least two
2495  other check defined positions (0 and 1), so
2496  custom positioning should be at least
2497  at position 2, i.e. >1) */
2498  && position < pcd->position_max) /* User chose a check position other
2499  then custom (which is always at
2500  position_max in the list) */
2501  {
2502  gint first_page_count, remaining_count;
2503 
2504  first_page_count = gtk_spin_button_get_value_as_int(pcd->first_page_count);
2505  remaining_count = check_count - first_page_count;
2506  pages = 1 /* First page, will have first_page_count checks */
2507  + remaining_count / pcd->position_max;
2508  /* Subsequent pages with all positions filled */
2509  if ((remaining_count % pcd->position_max) > 0)
2510  pages++; /* Last page, not all positions are filled. Needs to be added
2511  separately because integer division rounds towards 0 and
2512  would omit the last checks if they didn't fill a full page */
2513  }
2514  else
2515  pages = check_count;
2516  gtk_print_operation_set_n_pages(operation, pages);
2517 }
2518 
2519 
2520 /************************************
2521  * gnc_ui_print_check_dialog_ok_cb *
2522  ************************************/
2523 static void
2524 gnc_ui_print_check_dialog_ok_cb(PrintCheckDialog *pcd)
2525 {
2526  GtkPrintOperation *print;
2527  GtkPrintOperationResult res;
2528 
2529  print = gtk_print_operation_new();
2530 
2531  gnc_print_operation_init(print, "GnuCash-Checks");
2532  gtk_print_operation_set_unit(print, GTK_UNIT_POINTS);
2533  gtk_print_operation_set_use_full_page(print, TRUE);
2534  g_signal_connect(print, "begin_print", G_CALLBACK(begin_print), pcd);
2535  g_signal_connect(print, "draw_page", G_CALLBACK(draw_page), pcd);
2536 
2537  res = gtk_print_operation_run(print,
2538  GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
2539  pcd->caller_window, NULL);
2540 
2541  if (res == GTK_PRINT_OPERATION_RESULT_APPLY)
2543 
2544  g_object_unref(print);
2545 }
2546 
2547 
2548 static void
2549 gnc_print_check_set_sensitive (GtkWidget *widget, gpointer data)
2550 {
2551  gboolean sensitive;
2552  if (GTK_IS_LABEL(widget) || GTK_IS_SEPARATOR(widget))
2553  return;
2554  sensitive = GPOINTER_TO_INT(data);
2555  gtk_widget_set_sensitive(widget, sensitive);
2556 }
2557 
2558 
2559 void
2560 gnc_print_check_format_changed (GtkComboBox *widget,
2561  PrintCheckDialog *pcd)
2562 {
2563  GtkListStore *p_store;
2564  GtkTreeModel *f_model;
2565  GtkTreeIter f_iter, iter;
2566  gboolean sensitive;
2567  gint pnum;
2568  check_format_t *format;
2569  gboolean separator;
2570  GSList *elem;
2571 
2572  if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(pcd->format_combobox), &f_iter))
2573  return;
2574  f_model = gtk_combo_box_get_model(GTK_COMBO_BOX(pcd->format_combobox));
2575  gtk_tree_model_get(f_model, &f_iter, COL_DATA, &format, COL_SEP, &separator, -1);
2576  if (separator)
2577  return;
2578 
2579  pnum = gtk_combo_box_get_active(GTK_COMBO_BOX(pcd->position_combobox));
2580 
2581  /* Update the positions combobox */
2582  pcd->selected_format = format;
2583  p_store = gtk_list_store_new (1, G_TYPE_STRING);
2584  gtk_combo_box_set_model(GTK_COMBO_BOX(pcd->position_combobox),
2585  GTK_TREE_MODEL(p_store));
2586  if (format)
2587  {
2588  if (format->positions)
2589  {
2590  pcd->position_max = g_slist_length(format->positions); /* -1 for 0 base, +1 for custom entry */
2591  for (elem = format->positions; elem; elem = g_slist_next(elem))
2592  {
2593  gtk_list_store_append(GTK_LIST_STORE(p_store), &iter);
2594  gtk_list_store_set (GTK_LIST_STORE(p_store), &iter, 0, elem->data, -1);
2595  }
2596  }
2597  else
2598  {
2599  /* Invent a "Top" position if format has no positions */
2600  pcd->position_max = 1;
2601  gtk_list_store_append(GTK_LIST_STORE(p_store), &iter);
2602  gtk_list_store_set (GTK_LIST_STORE(p_store), &iter, 0, _("Top"), -1);
2603  }
2604  }
2605  else
2606  {
2607  pcd->position_max = 0;
2608  }
2609  gtk_list_store_append(GTK_LIST_STORE(p_store), &iter);
2610  gtk_list_store_set (GTK_LIST_STORE(p_store), &iter, 0, _("Custom"), -1);
2611  g_object_unref (p_store);
2612 
2613  /* If there's only one thing in the position combobox, make it insensitive */
2614  sensitive = (pcd->position_max > 0);
2615  gtk_widget_set_sensitive(GTK_WIDGET(pcd->position_combobox), sensitive);
2616 
2617  /* Update the custom page, this must be done before setting the active
2618  entry in the position combo box since gnc_print_check_position_changed
2619  will adjust these settings in some cases. */
2620  sensitive = (!separator && !format);
2621  gtk_container_foreach(GTK_CONTAINER(pcd->custom_table),
2622  gnc_print_check_set_sensitive,
2623  GINT_TO_POINTER(sensitive));
2624 
2625  /* Set the active entry in the position combo box, this will trigger a
2626  call to gnc_print_check_position_changed */
2627  pnum = MAX(MIN(pnum, pcd->position_max), 0);
2628  gtk_combo_box_set_active(GTK_COMBO_BOX(pcd->position_combobox), pnum);
2629 
2630  /* Update address fields */
2631  sensitive = check_format_has_address(pcd);
2632  gtk_widget_set_sensitive(pcd->check_address_name, sensitive);
2633  gtk_widget_set_sensitive(pcd->check_address_1, sensitive);
2634  gtk_widget_set_sensitive(pcd->check_address_2, sensitive);
2635  gtk_widget_set_sensitive(pcd->check_address_3, sensitive);
2636  gtk_widget_set_sensitive(pcd->check_address_4, sensitive);
2637 }
2638 
2639 
2640 void
2641 gnc_print_check_position_changed (GtkComboBox *widget,
2642  PrintCheckDialog *pcd)
2643 {
2644  gboolean sensitive;
2645  gint pnum;
2646  guint check_count;
2647  gint first_page_max, first_page_min, first_page_value;
2648 
2649  pnum = gtk_combo_box_get_active(GTK_COMBO_BOX(pcd->position_combobox));
2650 
2651  /* Make the translation and rotation fields active if the position is "custom" */
2652  sensitive = pnum == pcd->position_max;
2653  gtk_widget_set_sensitive(GTK_WIDGET(pcd->translation_x), sensitive);
2654  gtk_widget_set_sensitive(GTK_WIDGET(pcd->translation_y), sensitive);
2655  gtk_widget_set_sensitive(GTK_WIDGET(pcd->check_rotation), sensitive);
2656  gtk_widget_set_sensitive(GTK_WIDGET(pcd->units_combobox), sensitive);
2657 
2658  /* Set up the first page check count spin box */
2659  check_count = g_list_length(pcd->splits);
2660  first_page_max = MAX(1, MIN(pcd->position_max - pnum, check_count));
2661  first_page_min = 1;
2662  pnum = gtk_spin_button_get_value_as_int(pcd->first_page_count);
2663  first_page_value = MAX(MIN(pnum, first_page_max), first_page_min);
2664  gtk_spin_button_set_range(pcd->first_page_count, (gdouble)first_page_min, (gdouble)first_page_max);
2665  gtk_spin_button_set_value(pcd->first_page_count, (gdouble)first_page_value);
2666  sensitive = first_page_max > 1;
2667  gtk_widget_set_sensitive(GTK_WIDGET(pcd->first_page_count), sensitive);
2668 }
2669 
2670 
2671 void
2672 gnc_ui_print_check_response_cb(GtkDialog *dialog,
2673  gint response,
2674  PrintCheckDialog *pcd)
2675 {
2676  switch (response)
2677  {
2678  case GTK_RESPONSE_HELP:
2679  gnc_gnome_help (GTK_WINDOW(dialog), HF_HELP, HL_PRINTCHECK);
2680  return;
2681 
2682  case GTK_RESPONSE_OK:
2683  gnc_ui_print_check_dialog_ok_cb(pcd);
2684  gnc_ui_print_save_dialog(pcd);
2685  gnc_save_window_size(GNC_PREFS_GROUP, GTK_WINDOW(dialog));
2686  break;
2687 
2688  case GTK_RESPONSE_CANCEL:
2689  gnc_save_window_size(GNC_PREFS_GROUP, GTK_WINDOW(dialog));
2690  break;
2691  }
2692 
2693  gtk_widget_destroy(pcd->dialog);
2694  g_free(pcd->default_font);
2695  g_list_free(pcd->splits);
2696  g_free(pcd);
2697 }
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:1859
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:581
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