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