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  setlocale(LC_ALL, NULL);
2078  if ( !data->print_date_format ) return;
2079 
2080  date_desc = pango_font_description_copy_static(default_desc);
2081  pango_font_description_set_size(date_desc, DATE_FMT_HEIGHT * PANGO_SCALE);
2082  date_item = *data;
2083  date_item.y += (DATE_FMT_HEIGHT + DATE_FMT_SLOP);
2084  date_item.w = width;
2085  date_item.h = DATE_FMT_HEIGHT + DATE_FMT_SLOP;
2086  date_item.align = PANGO_ALIGN_CENTER;
2087 
2088  /* This is a date format string. It should only contain ascii. */
2089  cdn_fmt = g_string_new_len(NULL, 50);
2090  for (c = date_format; c && *c; )
2091  {
2092  if ((c[0] != '%') || (c[1] == '\0'))
2093  {
2094  c += 1;
2095  continue;
2096  }
2097  switch (c[1])
2098  {
2099  case 'F':
2100  cdn_fmt = g_string_append(cdn_fmt, "YYYYMMDD");
2101  break;
2102  case 'Y':
2103  cdn_fmt = g_string_append(cdn_fmt, "YYYY");
2104  break;
2105  case 'y':
2106  cdn_fmt = g_string_append(cdn_fmt, "YY");
2107  break;
2108  case 'm':
2109  cdn_fmt = g_string_append(cdn_fmt, "MM");
2110  break;
2111  case 'd':
2112  case 'e':
2113  cdn_fmt = g_string_append(cdn_fmt, "DD");
2114  break;
2115  case 'x':
2116  expanded = g_strdup_printf("%s%s",
2118  c + 2);
2119  c = expanded;
2120  continue;
2121  default:
2122  break;
2123  }
2124  c += 2;
2125  }
2126 
2127  text = g_string_free(cdn_fmt, FALSE);
2128  draw_text(context, text, &date_item, date_desc);
2129  g_free(text);
2130  if (expanded)
2131  g_free(expanded);
2132  pango_font_description_free(date_desc);
2133 }
2134 
2135 
2136 /* Print each of the items that in the description of a single check. This
2137  * function uses helper functions to print text based and picture based items.
2138  */
2139 static void
2140 draw_page_items(GtkPrintContext *context,
2141  check_format_t *format, gpointer user_data)
2142 {
2143  PrintCheckDialog *pcd = (PrintCheckDialog *) user_data;
2144  PangoFontDescription *default_desc;
2145  Transaction *trans;
2146  gnc_numeric amount;
2147  GNCPrintAmountInfo info;
2148  const gchar *date_format;
2149  gchar *text = NULL, buf[100];
2150  GSList *elem;
2151  check_item_t *item;
2152  gdouble width;
2153  gchar *address = NULL;
2154 
2155  trans = xaccSplitGetParent(pcd->split);
2156  /* This was valid when the check printing dialog was instantiated. */
2157  g_return_if_fail(trans);
2158  amount = get_check_amount(pcd);
2159 
2160  if (format->font)
2161  default_desc = pango_font_description_from_string(format->font);
2162  else
2163  default_desc = pango_font_description_from_string(pcd->default_font);
2164 
2165  /* Now put the actual data onto the page. */
2166  for (elem = format->items; elem; elem = g_slist_next(elem))
2167  {
2168  item = elem->data;
2169 
2170  switch (item->type)
2171  {
2172  case DATE:
2173  {
2174  GDate date;
2175  g_date_clear (&date, 1);
2176  gnc_gdate_set_time64 (&date, xaccTransGetDate(trans));
2177  date_format =
2178  gnc_date_format_get_custom(GNC_DATE_FORMAT
2179  (pcd->date_format));
2180  g_date_strftime(buf, 100, date_format, &date);
2181  width = draw_text(context, buf, item, default_desc);
2182  draw_date_format(context, date_format, item, default_desc, width);
2183  break;
2184  }
2185 
2186  case PAYEE:
2187  draw_text(context, xaccTransGetDescription(trans), item, default_desc);
2188  break;
2189 
2190  case NOTES:
2191  draw_text(context, xaccTransGetNotes(trans), item, default_desc);
2192  break;
2193 
2194  case MEMO:
2195  draw_text(context, xaccSplitGetMemo(pcd->split), item, default_desc);
2196  break;
2197 
2198  case ACTION:
2199  draw_text(context, gnc_get_action_num(trans, pcd->split), item,
2200  default_desc);
2201  break;
2202 
2203  case CHECK_NUMBER:
2204  draw_text(context, gnc_get_num_action(trans, pcd->split), item,
2205  default_desc);
2206  break;
2207 
2208  case AMOUNT_NUMBER:
2209  info = gnc_default_print_info(FALSE);
2210  draw_text(context, xaccPrintAmount(amount, info),
2211  item, default_desc);
2212  break;
2213 
2214  case AMOUNT_WORDS:
2215  text = numeric_to_words(amount);
2216  draw_text(context, text, item, default_desc);
2217  g_free(text);
2218  break;
2219 
2220  case TEXT:
2221  draw_text(context, item->text, item, default_desc);
2222  break;
2223 
2224  case ADDRESS:
2225  address = get_check_address(pcd);
2226  draw_text(context, address, item, default_desc);
2227  g_free(address);
2228  break;
2229 
2230  case SPLITS_AMOUNT:
2231  text = get_check_splits_amount(pcd);
2232  draw_text(context, text, item, default_desc);
2233  g_free(text);
2234  break;
2235 
2236  case SPLITS_MEMO:
2237  text = get_check_splits_memo(pcd);
2238  draw_text(context, text, item, default_desc);
2239  g_free(text);
2240  break;
2241 
2242  case SPLITS_ACCOUNT:
2243  text = get_check_splits_account(pcd);
2244  draw_text(context, text, item, default_desc);
2245  g_free(text);
2246  break;
2247 
2248  case PICTURE:
2249  draw_picture(context, item);
2250  break;
2251 
2252  default:
2253  text = g_strdup_printf("(unknown check field, type %d)", item->type);
2254  draw_text(context, text, item, default_desc);
2255  g_free(text);
2256  break;
2257  }
2258  }
2259 
2260  pango_font_description_free(default_desc);
2261 }
2262 
2263 
2264 /* Print each of the items that in the description of a single check. This
2265  * function uses helper functions to print text based and picture based items.
2266  */
2267 static void
2268 draw_page_boxes(GtkPrintContext *context,
2269  check_format_t *format, gpointer user_data)
2270 {
2271  cairo_t *cr;
2272  GSList *elem;
2273  check_item_t *item;
2274 
2275  cr = gtk_print_context_get_cairo_context(context);
2276 
2277  /* Now put the actual data onto the page. */
2278  for (elem = format->items; elem; elem = g_slist_next(elem))
2279  {
2280  item = elem->data;
2281  if (!item->w || !item->h)
2282  continue;
2283  cairo_rectangle(cr, item->x, item->y - item->h, item->w, item->h);
2284  cairo_stroke(cr);
2285  }
2286 }
2287 
2288 
2289 /* Print an entire page based upon the layout in a check description file. This
2290  * function takes care of translating/rotating the page, calling the function to
2291  * print the grid pattern (if requested), and calls a helper function to print
2292  * all check items.
2293  */
2294 static void
2295 draw_check_format(GtkPrintContext *context, gint position,
2296  check_format_t *format, gpointer user_data)
2297 {
2298  PrintCheckDialog *pcd = (PrintCheckDialog *) user_data;
2299  gdouble x, y, r, multip;
2300  cairo_t *cr = gtk_print_context_get_cairo_context(context);
2301 
2302  /* Translate all subsequent check items if required. */
2303  if ((position > 0) && (position < pcd->position_max))
2304  {
2305  /* Standard positioning is used.
2306  * Note that the first check on the page (position 0) doesn't
2307  * need to be moved (hence the test for position > 0 above. */
2308  cairo_translate(cr, 0, format->height); /* Translation is relative to previous
2309  check translation, not to page border ! */
2310  DEBUG("Position %d translated by %f relative to previous check (pre-defined)", position, format->height);
2311  DEBUG(" by %f relative to page (pre-defined)", position * format->height);
2312  }
2313  else if (position == pcd->position_max)
2314  {
2315  /* Custom positioning is used. */
2316  multip = pcd_get_custom_multip(pcd);
2317  x = multip * gtk_spin_button_get_value(pcd->translation_x);
2318  y = multip * gtk_spin_button_get_value(pcd->translation_y);
2319  cairo_translate(cr, x, y);
2320  DEBUG("Position translated by %f,%f (custom)", x, y);
2321  r = gtk_spin_button_get_value(pcd->check_rotation);
2322  cairo_rotate(cr, r * DEGREES_TO_RADIANS);
2323  DEBUG("Position rotated by %f degrees (custom)", r);
2324  }
2325 
2326  /* Draw layout boxes if requested. Also useful when determining check
2327  * layouts. */
2328  if (format->show_boxes)
2329  draw_page_boxes(context, format, user_data);
2330 
2331  /* Draw the actual check data. */
2332  draw_page_items(context, format, user_data);
2333 }
2334 
2335 
2336 static void
2337 draw_check_custom(GtkPrintContext *context, gpointer user_data)
2338 {
2339  PrintCheckDialog *pcd = (PrintCheckDialog *) user_data;
2340  GNCPrintAmountInfo info;
2341  PangoFontDescription *desc;
2342  Transaction *trans;
2343  gnc_numeric amount;
2344  cairo_t *cr;
2345  const gchar *date_format;
2346  gchar *text = NULL, buf[100];
2347  check_item_t item = { 0 };
2348  gdouble x, y, multip, degrees;
2349  GDate date;
2350  gchar *address;
2351 
2352  trans = xaccSplitGetParent(pcd->split);
2353  /* This was valid when the check printing dialog was instantiated. */
2354  g_return_if_fail(trans);
2355 
2356  desc = pango_font_description_from_string(pcd->default_font);
2357 
2358  multip = pcd_get_custom_multip(pcd);
2359  degrees = gtk_spin_button_get_value(pcd->check_rotation);
2360  cr = gtk_print_context_get_cairo_context(context);
2361  cairo_rotate(cr, degrees * DEGREES_TO_RADIANS);
2362  DEBUG("Page rotated by %f degrees", degrees);
2363 
2364  x = multip * gtk_spin_button_get_value(pcd->translation_x);
2365  y = multip * gtk_spin_button_get_value(pcd->translation_y);
2366  cairo_translate(cr, x, y);
2367  DEBUG("Page translated by %f,%f", x, y);
2368 
2369  item.x = multip * gtk_spin_button_get_value(pcd->payee_x);
2370  item.y = multip * gtk_spin_button_get_value(pcd->payee_y);
2371  draw_text(context, xaccTransGetDescription(trans), &item, desc);
2372 
2373  item.x = multip * gtk_spin_button_get_value(pcd->date_x);
2374  item.y = multip * gtk_spin_button_get_value(pcd->date_y);
2375  g_date_clear (&date, 1);
2376  gnc_gdate_set_time64 (&date, xaccTransGetDate(trans));
2377  date_format = gnc_date_format_get_custom(GNC_DATE_FORMAT(pcd->date_format));
2378  g_date_strftime(buf, 100, date_format, &date);
2379  draw_text(context, buf, &item, desc);
2380 
2381  item.x = multip * gtk_spin_button_get_value(pcd->number_x);
2382  item.y = multip * gtk_spin_button_get_value(pcd->number_y);
2383  info = gnc_default_print_info(FALSE);
2384  amount = gnc_numeric_abs(xaccSplitGetAmount(pcd->split));
2385  draw_text(context, xaccPrintAmount(amount, info), &item, desc);
2386 
2387  item.x = multip * gtk_spin_button_get_value(pcd->words_x);
2388  item.y = multip * gtk_spin_button_get_value(pcd->words_y);
2389  text = numeric_to_words(amount);
2390  draw_text(context, text, &item, desc);
2391  g_free(text);
2392 
2393  item.x = multip * gtk_spin_button_get_value(pcd->address_x);
2394  item.y = multip * gtk_spin_button_get_value(pcd->address_y);
2395  address = get_check_address(pcd);
2396  draw_text(context, address, &item, desc);
2397  g_free(address);
2398 
2399  item.x = multip * gtk_spin_button_get_value(pcd->splits_amount_x);
2400  item.y = multip * gtk_spin_button_get_value(pcd->splits_amount_y);
2401  text = get_check_splits_amount(pcd);
2402  draw_text(context, text, &item, desc);
2403  g_free(text);
2404 
2405  item.x = multip * gtk_spin_button_get_value(pcd->splits_memo_x);
2406  item.y = multip * gtk_spin_button_get_value(pcd->splits_memo_y);
2407  text = get_check_splits_memo(pcd);
2408  draw_text(context, text, &item, desc);
2409  g_free(text);
2410 
2411  item.x = multip * gtk_spin_button_get_value(pcd->splits_account_x);
2412  item.y = multip * gtk_spin_button_get_value(pcd->splits_account_y);
2413  text = get_check_splits_account(pcd);
2414  draw_text(context, text, &item, desc);
2415  g_free(text);
2416 
2417  item.x = multip * gtk_spin_button_get_value(pcd->notes_x);
2418  item.y = multip * gtk_spin_button_get_value(pcd->notes_y);
2419  draw_text(context, xaccTransGetNotes(trans), &item, desc);
2420 
2421  item.x = multip * gtk_spin_button_get_value(pcd->memo_x);
2422  item.y = multip * gtk_spin_button_get_value(pcd->memo_y);
2423  draw_text(context, xaccSplitGetMemo(pcd->split), &item, desc);
2424 
2425  pango_font_description_free(desc);
2426 }
2427 
2428 
2429 /* Print a page of checks. This takes into account the number of checks to print,
2430  * the number of checks on a page, and the starting check position on the page.
2431  * This function is called once by the GtkPrint code once for each page to be printed.
2432  */
2433 static void
2434 draw_page(GtkPrintOperation *operation,
2435  GtkPrintContext *context, gint page_nr, gpointer user_data)
2436 {
2437  PrintCheckDialog *pcd = (PrintCheckDialog *) user_data;
2438  check_format_t *format;
2439  cairo_t *cr = gtk_print_context_get_cairo_context(context);
2440 
2441  format = pcd->selected_format;
2442  if (format)
2443  {
2444  gint first_check, last_check;
2445  gint first_page_count;
2446  guint check_count = g_list_length(pcd->splits);
2447  gint check_number;
2448  gint position = gtk_combo_box_get_active(GTK_COMBO_BOX(pcd->position_combobox));
2449  gint last_blank_check_pos;
2450  gint checks_per_page;
2451  GList *next_split;
2452 
2453  if (position == pcd->position_max)
2454  {
2455  /* Custom position, one check per page */
2456  checks_per_page = 1;
2457  first_page_count = 1;
2458  }
2459  else
2460  {
2461  checks_per_page = pcd->position_max;
2462  first_page_count = gtk_spin_button_get_value_as_int(pcd->first_page_count);
2463  }
2464 
2465  if (page_nr == 0)
2466  {
2467  first_check = 0;
2468  last_check = first_page_count - 1;
2469  next_split = pcd->splits;
2470  }
2471  else
2472  {
2473  first_check = first_page_count + (page_nr - 1) * checks_per_page;
2474  last_check = MIN(check_count - 1, first_check + checks_per_page - 1);
2475  next_split = g_list_nth(pcd->splits, first_check);
2476  /* If position is not "custom" reset it to top */
2477  if (position < pcd->position_max)
2478  position = 0;
2479  }
2480 
2481  /* Do page level translations/rotations */
2482  cairo_translate(cr, format->trans_x, format->trans_y);
2483  DEBUG("Page translated by %f,%f", format->trans_x, format->trans_y);
2484  cairo_rotate(cr, format->rotation * DEGREES_TO_RADIANS);
2485  DEBUG("Page rotated by %f degrees", format->rotation);
2486 
2487  /* The grid is useful when determining check layouts */
2488  if (format->show_grid)
2489  {
2490  draw_grid(context,
2491  gtk_print_context_get_width(context),
2492  gtk_print_context_get_height(context),
2493  pcd->default_font);
2494  }
2495 
2496  last_blank_check_pos = position - 1;
2497  /* Optionally skip blank check positions if */
2498  if ((page_nr == 0) /* on first page AND */
2499  && (last_blank_check_pos > 0) /* there's more than one blank check */
2500  && (position < pcd->position_max)) /* but don't skip for custom positioning */
2501  cairo_translate(cr, 0, format->height * last_blank_check_pos);
2502 
2503  for (check_number = first_check; check_number <= last_check;
2504  check_number++, position++)
2505  {
2506  pcd->split = (Split *) next_split->data;
2507  next_split = g_list_next(next_split);
2508  draw_check_format(context, position, format, user_data);
2509  }
2510  }
2511  else
2512  {
2513  /* Custom check format */
2514  pcd->split = (Split *) g_list_nth_data(pcd->splits, page_nr);
2515  g_return_if_fail(pcd->split);
2516  draw_check_custom(context, user_data);
2517  }
2518 }
2519 
2520 
2521 /* Compute the number of pages required to complete this print operation.
2522  * This function is called once by the GtkPrint code to determine the number
2523  * of pages required to complete the print operation.
2524  */
2525 static void
2526 begin_print(GtkPrintOperation *operation,
2527  GtkPrintContext *context, gpointer user_data)
2528 {
2529  PrintCheckDialog *pcd = (PrintCheckDialog *) user_data;
2530  guint check_count = g_list_length(pcd->splits);
2531  gint pages;
2532  gint position = gtk_combo_box_get_active(GTK_COMBO_BOX(pcd->position_combobox));
2533 
2534  if (pcd->selected_format /* User selected a format other than custom */
2535  && pcd->position_max > 1 /* The format has more than one check per page
2536  (position_max is equivalent to custom
2537  positioning, and there need to be at least two
2538  other check defined positions (0 and 1), so
2539  custom positioning should be at least
2540  at position 2, i.e. >1) */
2541  && position < pcd->position_max) /* User chose a check position other
2542  then custom (which is always at
2543  position_max in the list) */
2544  {
2545  gint first_page_count, remaining_count;
2546 
2547  first_page_count = gtk_spin_button_get_value_as_int(pcd->first_page_count);
2548  remaining_count = check_count - first_page_count;
2549  pages = 1 /* First page, will have first_page_count checks */
2550  + remaining_count / pcd->position_max;
2551  /* Subsequent pages with all positions filled */
2552  if ((remaining_count % pcd->position_max) > 0)
2553  pages++; /* Last page, not all positions are filled. Needs to be added
2554  separately because integer division rounds towards 0 and
2555  would omit the last checks if they didn't fill a full page */
2556  }
2557  else
2558  pages = check_count;
2559  gtk_print_operation_set_n_pages(operation, pages);
2560 }
2561 
2562 
2563 /************************************
2564  * gnc_ui_print_check_dialog_ok_cb *
2565  ************************************/
2566 static void
2567 gnc_ui_print_check_dialog_ok_cb(PrintCheckDialog *pcd)
2568 {
2569  GtkPrintOperation *print;
2570  GtkPrintOperationResult res;
2571 
2572  print = gtk_print_operation_new();
2573 
2574  gnc_print_operation_init(print, "GnuCash-Checks");
2575  gtk_print_operation_set_unit(print, GTK_UNIT_POINTS);
2576  gtk_print_operation_set_use_full_page(print, TRUE);
2577  g_signal_connect(print, "begin_print", G_CALLBACK(begin_print), pcd);
2578  g_signal_connect(print, "draw_page", G_CALLBACK(draw_page), pcd);
2579 
2580  res = gtk_print_operation_run(print,
2581  GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
2582  pcd->caller_window, NULL);
2583 
2584  if (res == GTK_PRINT_OPERATION_RESULT_APPLY)
2586 
2587  g_object_unref(print);
2588 }
2589 
2590 
2591 static void
2592 gnc_print_check_set_sensitive (GtkWidget *widget, gpointer data)
2593 {
2594  gboolean sensitive;
2595  if (GTK_IS_LABEL(widget) || GTK_IS_SEPARATOR(widget))
2596  return;
2597  sensitive = GPOINTER_TO_INT(data);
2598  gtk_widget_set_sensitive(widget, sensitive);
2599 }
2600 
2601 
2602 void
2603 gnc_print_check_format_changed (GtkComboBox *widget,
2604  PrintCheckDialog *pcd)
2605 {
2606  GtkListStore *p_store;
2607  GtkTreeModel *f_model;
2608  GtkTreeIter f_iter, iter;
2609  gboolean sensitive;
2610  gint pnum;
2611  check_format_t *format;
2612  gboolean separator;
2613  GSList *elem;
2614 
2615  if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(pcd->format_combobox), &f_iter))
2616  return;
2617  f_model = gtk_combo_box_get_model(GTK_COMBO_BOX(pcd->format_combobox));
2618  gtk_tree_model_get(f_model, &f_iter, COL_DATA, &format, COL_SEP, &separator, -1);
2619  if (separator)
2620  return;
2621 
2622  pnum = gtk_combo_box_get_active(GTK_COMBO_BOX(pcd->position_combobox));
2623 
2624  /* Update the positions combobox */
2625  pcd->selected_format = format;
2626  p_store = gtk_list_store_new (1, G_TYPE_STRING);
2627  gtk_combo_box_set_model(GTK_COMBO_BOX(pcd->position_combobox),
2628  GTK_TREE_MODEL(p_store));
2629  if (format)
2630  {
2631  if (format->positions)
2632  {
2633  pcd->position_max = g_slist_length(format->positions); /* -1 for 0 base, +1 for custom entry */
2634  for (elem = format->positions; elem; elem = g_slist_next(elem))
2635  {
2636  gtk_list_store_append(GTK_LIST_STORE(p_store), &iter);
2637  gtk_list_store_set (GTK_LIST_STORE(p_store), &iter, 0, elem->data, -1);
2638  }
2639  }
2640  else
2641  {
2642  /* Invent a "Top" position if format has no positions */
2643  pcd->position_max = 1;
2644  gtk_list_store_append(GTK_LIST_STORE(p_store), &iter);
2645  gtk_list_store_set (GTK_LIST_STORE(p_store), &iter, 0, _("Top"), -1);
2646  }
2647  }
2648  else
2649  {
2650  pcd->position_max = 0;
2651  }
2652  gtk_list_store_append(GTK_LIST_STORE(p_store), &iter);
2653  gtk_list_store_set (GTK_LIST_STORE(p_store), &iter, 0, _("Custom"), -1);
2654  g_object_unref (p_store);
2655 
2656  /* If there's only one thing in the position combobox, make it insensitive */
2657  sensitive = (pcd->position_max > 0);
2658  gtk_widget_set_sensitive(GTK_WIDGET(pcd->position_combobox), sensitive);
2659 
2660  /* Update the custom page, this must be done before setting the active
2661  entry in the position combo box since gnc_print_check_position_changed
2662  will adjust these settings in some cases. */
2663  sensitive = (!separator && !format);
2664  gtk_container_foreach(GTK_CONTAINER(pcd->custom_table),
2665  gnc_print_check_set_sensitive,
2666  GINT_TO_POINTER(sensitive));
2667 
2668  /* Set the active entry in the position combo box, this will trigger a
2669  call to gnc_print_check_position_changed */
2670  pnum = MAX(MIN(pnum, pcd->position_max), 0);
2671  gtk_combo_box_set_active(GTK_COMBO_BOX(pcd->position_combobox), pnum);
2672 
2673  /* Update address fields */
2674  sensitive = check_format_has_address(pcd);
2675  gtk_widget_set_sensitive(pcd->check_address_name, sensitive);
2676  gtk_widget_set_sensitive(pcd->check_address_1, sensitive);
2677  gtk_widget_set_sensitive(pcd->check_address_2, sensitive);
2678  gtk_widget_set_sensitive(pcd->check_address_3, sensitive);
2679  gtk_widget_set_sensitive(pcd->check_address_4, sensitive);
2680 }
2681 
2682 
2683 void
2684 gnc_print_check_position_changed (GtkComboBox *widget,
2685  PrintCheckDialog *pcd)
2686 {
2687  gboolean sensitive;
2688  gint pnum;
2689  guint check_count;
2690  gint first_page_max, first_page_min, first_page_value;
2691 
2692  pnum = gtk_combo_box_get_active(GTK_COMBO_BOX(pcd->position_combobox));
2693 
2694  /* Make the translation and rotation fields active if the position is "custom" */
2695  sensitive = pnum == pcd->position_max;
2696  gtk_widget_set_sensitive(GTK_WIDGET(pcd->translation_x), sensitive);
2697  gtk_widget_set_sensitive(GTK_WIDGET(pcd->translation_y), sensitive);
2698  gtk_widget_set_sensitive(GTK_WIDGET(pcd->check_rotation), sensitive);
2699  gtk_widget_set_sensitive(GTK_WIDGET(pcd->units_combobox), sensitive);
2700 
2701  /* Set up the first page check count spin box */
2702  check_count = g_list_length(pcd->splits);
2703  first_page_max = MAX(1, MIN(pcd->position_max - pnum, check_count));
2704  first_page_min = 1;
2705  pnum = gtk_spin_button_get_value_as_int(pcd->first_page_count);
2706  first_page_value = MAX(MIN(pnum, first_page_max), first_page_min);
2707  gtk_spin_button_set_range(pcd->first_page_count, (gdouble)first_page_min, (gdouble)first_page_max);
2708  gtk_spin_button_set_value(pcd->first_page_count, (gdouble)first_page_value);
2709  sensitive = first_page_max > 1;
2710  gtk_widget_set_sensitive(GTK_WIDGET(pcd->first_page_count), sensitive);
2711 }
2712 
2713 
2714 void
2715 gnc_ui_print_check_response_cb(GtkDialog *dialog,
2716  gint response,
2717  PrintCheckDialog *pcd)
2718 {
2719  switch (response)
2720  {
2721  case GTK_RESPONSE_HELP:
2722  gnc_gnome_help (GTK_WINDOW(dialog), DF_MANUAL, DL_PRINTCHECK);
2723  return;
2724 
2725  case GTK_RESPONSE_OK:
2726  gnc_ui_print_check_dialog_ok_cb(pcd);
2727  gnc_ui_print_save_dialog(pcd);
2728  gnc_save_window_size(GNC_PREFS_GROUP, GTK_WINDOW(dialog));
2729  break;
2730 
2731  case GTK_RESPONSE_CANCEL:
2732  gnc_save_window_size(GNC_PREFS_GROUP, GTK_WINDOW(dialog));
2733  break;
2734  }
2735 
2736  gtk_widget_destroy(pcd->dialog);
2737  g_free(pcd->default_font);
2738  g_list_free(pcd->splits);
2739  g_free(pcd);
2740 }
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.
void gnc_account_foreach_descendant(const Account *acc, AccountCb thunk, gpointer user_data)
This method will traverse all children of this accounts and their descendants, calling &#39;func&#39; on each...
Definition: Account.cpp:3197
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:288
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:361
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
API for Transactions and Splits (journal entries)
const gchar * group
The group where this format was found.
gchar * guid
Unique identifier for this format.
gchar * guid_to_string_buff(const GncGUID *guid, gchar *str)
The guid_to_string_buff() routine puts a null-terminated string encoding of the id into the memory po...
Definition: guid.cpp: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:319
const gchar * gnc_userdata_dir(void)
Ensure that the user&#39;s configuration directory exists and is minimally populated. ...
gint gnc_prefs_get_int(const gchar *group, const gchar *pref_name)
Get an integer value from the preferences backend.
gboolean gnc_key_file_save_to_file(const gchar *filename, GKeyFile *key_file, GError **error)
Write a key/value file from memory to disk.
const char * xaccTransGetNotes(const Transaction *trans)
Gets the transaction Notes.
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:308
gboolean gnc_prefs_set_coords(const gchar *group, const gchar *pref_name, gdouble x, gdouble y)
Store coordinates into the preferences backend.
Definition: gnc-prefs.c:341
void gnc_prefs_get_coords(const gchar *group, const gchar *pref_name, gdouble *x, gdouble *y)
Get a pair of coordinates from the preferences backend.
gchar * font
Default font for this page of checks.
const char * xaccTransGetDescription(const Transaction *trans)
Gets the transaction Description.
gnc_numeric gnc_numeric_abs(gnc_numeric a)
Returns a newly created gnc_numeric that is the absolute value of the given gnc_numeric value...
All type declarations for the whole Gnucash engine.
gdouble height
Height of one check on a page.
Generic api to store and retrieve preferences.
const GncOwner * gncOwnerGetEndOwner(const GncOwner *owner)
Get the "parent" Owner or GncGUID thereof.
Definition: gncOwner.c:572
void gnc_gdate_set_time64(GDate *gd, time64 time)
Set a GDate to a time64.
Definition: gnc-date.cpp:1243
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