GnuCash c935c2f+
Loading...
Searching...
No Matches
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 */
64G_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 */
122typedef enum format_combo_col_t
123{
124 COL_NAME = 0,
128 COL_DATA,
132 COL_SEP,
135
136void gnc_ui_print_check_response_cb (GtkDialog *dialog, gint response, PrintCheckDialog *pcd);
137void gnc_print_check_format_changed (GtkComboBox *widget, PrintCheckDialog *pcd);
138void gnc_print_check_position_changed (GtkComboBox *widget, PrintCheckDialog *pcd);
139void gnc_print_check_save_button_clicked (GtkButton *button, PrintCheckDialog *pcd);
140void gnc_check_format_title_changed (GtkEditable *editable, GtkWidget *ok_button);
141
142static void initialize_format_combobox (PrintCheckDialog *pcd);
143gchar* get_check_address (PrintCheckDialog *pcd);
144static gboolean check_format_has_address (PrintCheckDialog *pcd);
145gchar* get_check_splits_amount (PrintCheckDialog *pcd);
146gchar* get_check_splits_memo (PrintCheckDialog *pcd);
147gchar* 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
174DEFINE_ENUM(CheckItemType, ENUM_CHECK_ITEM_TYPE)
175FROM_STRING_DEC(CheckItemType, ENUM_CHECK_ITEM_TYPE)
176FROM_STRING_FUNC(CheckItemType, ENUM_CHECK_ITEM_TYPE)
177AS_STRING_DEC(CheckItemType, ENUM_CHECK_ITEM_TYPE)
178AS_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 */
183typedef 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;
217 PangoAlignment align;
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 */
227typedef struct _check_format
228{
229
230 gchar *guid;
232 const gchar *group;
234 gchar *filename;
237 gchar *title;
240 gboolean blocking;
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 */
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 */
324static check_format_t *
325find_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 */
360gchar *
361get_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}
372
374{
375 const Transaction* trans;
376 gnc_numeric amount;
377};
378
379static void
380subtotal_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 */
390static gnc_numeric
391get_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 */
417gchar *
418get_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 */
455gchar *
456get_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 */
493gchar *
494get_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 */
534static gboolean
535check_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
562static void
563gnc_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
637static void
638gnc_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
732static gdouble
733pcd_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 */
756static void
757pcd_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 */
777static void
778pcd_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 */
796static void
797pcd_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 */
877void
878gnc_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 */
893void
894gnc_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 */
931static gchar *
932doubles_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 */
953static GSList *
954format_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
1161failed:
1162 g_warning("Check file %s, group %s, key %s, error: %s",
1163 file, KF_GROUP_ITEMS, key, error->message);
1164cleanup:
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 */
1177static void
1178format_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 */
1195static GSList *
1196format_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 */
1253static void
1254free_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 */
1264static gboolean
1265format_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 */
1457static void
1458free_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 */
1477static check_format_t *
1478read_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 */
1519static void
1520read_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 */
1593static void
1594read_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
1616static gboolean
1617format_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
1626static void
1627initialize_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 *****************************************************/
1651void
1652gnc_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 */
1800static void
1801draw_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 */
1862static gdouble
1863draw_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*/
1941static GtkWidget *
1942read_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 */
1969static void
1970draw_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 */
2066static void
2067draw_date_format(GtkPrintContext *context, const gchar *date_format,
2068 check_item_t *data, PangoFontDescription *default_desc,
2069 gdouble width)
2070{
2071 PangoFontDescription *date_desc;
2072 check_item_t date_item;
2073 gchar *text = NULL, *expanded = NULL;
2074 const gchar *c;
2075 GString *cdn_fmt;
2076
2077 if ( !data->print_date_format ) return;
2078
2079 date_desc = pango_font_description_copy_static(default_desc);
2080 pango_font_description_set_size(date_desc, DATE_FMT_HEIGHT * PANGO_SCALE);
2081 date_item = *data;
2082 date_item.y += (DATE_FMT_HEIGHT + DATE_FMT_SLOP);
2083 date_item.w = width;
2084 date_item.h = DATE_FMT_HEIGHT + DATE_FMT_SLOP;
2085 date_item.align = PANGO_ALIGN_CENTER;
2086
2087 /* This is a date format string. It should only contain ascii. */
2088 cdn_fmt = g_string_new_len(NULL, 50);
2089 for (c = date_format; c && *c; )
2090 {
2091 if ((c[0] != '%') || (c[1] == '\0'))
2092 {
2093 c += 1;
2094 continue;
2095 }
2096 switch (c[1])
2097 {
2098 case 'F':
2099 cdn_fmt = g_string_append(cdn_fmt, "YYYYMMDD");
2100 break;
2101 case 'Y':
2102 cdn_fmt = g_string_append(cdn_fmt, "YYYY");
2103 break;
2104 case 'y':
2105 cdn_fmt = g_string_append(cdn_fmt, "YY");
2106 break;
2107 case 'm':
2108 cdn_fmt = g_string_append(cdn_fmt, "MM");
2109 break;
2110 case 'd':
2111 case 'e':
2112 cdn_fmt = g_string_append(cdn_fmt, "DD");
2113 break;
2114 case 'x':
2115 expanded = g_strdup_printf("%s%s",
2117 c + 2);
2118 c = expanded;
2119 continue;
2120 default:
2121 break;
2122 }
2123 c += 2;
2124 }
2125
2126 text = g_string_free(cdn_fmt, FALSE);
2127 draw_text(context, text, &date_item, date_desc);
2128 g_free(text);
2129 if (expanded)
2130 g_free(expanded);
2131 pango_font_description_free(date_desc);
2132}
2133
2134
2135/* Print each of the items that in the description of a single check. This
2136 * function uses helper functions to print text based and picture based items.
2137 */
2138static void
2139draw_page_items(GtkPrintContext *context,
2140 check_format_t *format, gpointer user_data)
2141{
2142 PrintCheckDialog *pcd = (PrintCheckDialog *) user_data;
2143 PangoFontDescription *default_desc;
2144 Transaction *trans;
2145 gnc_numeric amount;
2146 GNCPrintAmountInfo info;
2147 const gchar *date_format;
2148 gchar *text = NULL, buf[100];
2149 GSList *elem;
2150 check_item_t *item;
2151 gdouble width;
2152 gchar *address = NULL;
2153
2154 trans = xaccSplitGetParent(pcd->split);
2155 /* This was valid when the check printing dialog was instantiated. */
2156 g_return_if_fail(trans);
2157 amount = get_check_amount(pcd);
2158
2159 if (format->font)
2160 default_desc = pango_font_description_from_string(format->font);
2161 else
2162 default_desc = pango_font_description_from_string(pcd->default_font);
2163
2164 /* Now put the actual data onto the page. */
2165 for (elem = format->items; elem; elem = g_slist_next(elem))
2166 {
2167 item = elem->data;
2168
2169 switch (item->type)
2170 {
2171 case DATE:
2172 {
2173 GDate date;
2174 g_date_clear (&date, 1);
2175 gnc_gdate_set_time64 (&date, xaccTransGetDate(trans));
2176 date_format =
2177 gnc_date_format_get_custom(GNC_DATE_FORMAT
2178 (pcd->date_format));
2179 g_date_strftime(buf, 100, date_format, &date);
2180 width = draw_text(context, buf, item, default_desc);
2181 draw_date_format(context, date_format, item, default_desc, width);
2182 break;
2183 }
2184
2185 case PAYEE:
2186 draw_text(context, xaccTransGetDescription(trans), item, default_desc);
2187 break;
2188
2189 case NOTES:
2190 draw_text(context, xaccTransGetNotes(trans), item, default_desc);
2191 break;
2192
2193 case MEMO:
2194 draw_text(context, xaccSplitGetMemo(pcd->split), item, default_desc);
2195 break;
2196
2197 case ACTION:
2198 draw_text(context, gnc_get_action_num(trans, pcd->split), item,
2199 default_desc);
2200 break;
2201
2202 case CHECK_NUMBER:
2203 draw_text(context, gnc_get_num_action(trans, pcd->split), item,
2204 default_desc);
2205 break;
2206
2207 case AMOUNT_NUMBER:
2208 info = gnc_default_print_info(FALSE);
2209 draw_text(context, xaccPrintAmount(amount, info),
2210 item, default_desc);
2211 break;
2212
2213 case AMOUNT_WORDS:
2214 text = numeric_to_words(amount);
2215 draw_text(context, text, item, default_desc);
2216 g_free(text);
2217 break;
2218
2219 case TEXT:
2220 draw_text(context, item->text, item, default_desc);
2221 break;
2222
2223 case ADDRESS:
2224 address = get_check_address(pcd);
2225 draw_text(context, address, item, default_desc);
2226 g_free(address);
2227 break;
2228
2229 case SPLITS_AMOUNT:
2230 text = get_check_splits_amount(pcd);
2231 draw_text(context, text, item, default_desc);
2232 g_free(text);
2233 break;
2234
2235 case SPLITS_MEMO:
2236 text = get_check_splits_memo(pcd);
2237 draw_text(context, text, item, default_desc);
2238 g_free(text);
2239 break;
2240
2241 case SPLITS_ACCOUNT:
2242 text = get_check_splits_account(pcd);
2243 draw_text(context, text, item, default_desc);
2244 g_free(text);
2245 break;
2246
2247 case PICTURE:
2248 draw_picture(context, item);
2249 break;
2250
2251 default:
2252 text = g_strdup_printf("(unknown check field, type %d)", item->type);
2253 draw_text(context, text, item, default_desc);
2254 g_free(text);
2255 break;
2256 }
2257 }
2258
2259 pango_font_description_free(default_desc);
2260}
2261
2262
2263/* Print each of the items that in the description of a single check. This
2264 * function uses helper functions to print text based and picture based items.
2265 */
2266static void
2267draw_page_boxes(GtkPrintContext *context,
2268 check_format_t *format, gpointer user_data)
2269{
2270 cairo_t *cr;
2271 GSList *elem;
2272 check_item_t *item;
2273
2274 cr = gtk_print_context_get_cairo_context(context);
2275
2276 /* Now put the actual data onto the page. */
2277 for (elem = format->items; elem; elem = g_slist_next(elem))
2278 {
2279 item = elem->data;
2280 if (!item->w || !item->h)
2281 continue;
2282 cairo_rectangle(cr, item->x, item->y - item->h, item->w, item->h);
2283 cairo_stroke(cr);
2284 }
2285}
2286
2287
2288/* Print an entire page based upon the layout in a check description file. This
2289 * function takes care of translating/rotating the page, calling the function to
2290 * print the grid pattern (if requested), and calls a helper function to print
2291 * all check items.
2292 */
2293static void
2294draw_check_format(GtkPrintContext *context, gint position,
2295 check_format_t *format, gpointer user_data)
2296{
2297 PrintCheckDialog *pcd = (PrintCheckDialog *) user_data;
2298 gdouble x, y, r, multip;
2299 cairo_t *cr = gtk_print_context_get_cairo_context(context);
2300
2301 /* Translate all subsequent check items if required. */
2302 if ((position > 0) && (position < pcd->position_max))
2303 {
2304 /* Standard positioning is used.
2305 * Note that the first check on the page (position 0) doesn't
2306 * need to be moved (hence the test for position > 0 above. */
2307 cairo_translate(cr, 0, format->height); /* Translation is relative to previous
2308 check translation, not to page border ! */
2309 DEBUG("Position %d translated by %f relative to previous check (pre-defined)", position, format->height);
2310 DEBUG(" by %f relative to page (pre-defined)", position * format->height);
2311 }
2312 else if (position == pcd->position_max)
2313 {
2314 /* Custom positioning is used. */
2315 multip = pcd_get_custom_multip(pcd);
2316 x = multip * gtk_spin_button_get_value(pcd->translation_x);
2317 y = multip * gtk_spin_button_get_value(pcd->translation_y);
2318 cairo_translate(cr, x, y);
2319 DEBUG("Position translated by %f,%f (custom)", x, y);
2320 r = gtk_spin_button_get_value(pcd->check_rotation);
2321 cairo_rotate(cr, r * DEGREES_TO_RADIANS);
2322 DEBUG("Position rotated by %f degrees (custom)", r);
2323 }
2324
2325 /* Draw layout boxes if requested. Also useful when determining check
2326 * layouts. */
2327 if (format->show_boxes)
2328 draw_page_boxes(context, format, user_data);
2329
2330 /* Draw the actual check data. */
2331 draw_page_items(context, format, user_data);
2332}
2333
2334
2335static void
2336draw_check_custom(GtkPrintContext *context, gpointer user_data)
2337{
2338 PrintCheckDialog *pcd = (PrintCheckDialog *) user_data;
2339 GNCPrintAmountInfo info;
2340 PangoFontDescription *desc;
2341 Transaction *trans;
2342 gnc_numeric amount;
2343 cairo_t *cr;
2344 const gchar *date_format;
2345 gchar *text = NULL, buf[100];
2346 check_item_t item = { 0 };
2347 gdouble x, y, multip, degrees;
2348 GDate date;
2349 gchar *address;
2350
2351 trans = xaccSplitGetParent(pcd->split);
2352 /* This was valid when the check printing dialog was instantiated. */
2353 g_return_if_fail(trans);
2354
2355 desc = pango_font_description_from_string(pcd->default_font);
2356
2357 multip = pcd_get_custom_multip(pcd);
2358 degrees = gtk_spin_button_get_value(pcd->check_rotation);
2359 cr = gtk_print_context_get_cairo_context(context);
2360 cairo_rotate(cr, degrees * DEGREES_TO_RADIANS);
2361 DEBUG("Page rotated by %f degrees", degrees);
2362
2363 x = multip * gtk_spin_button_get_value(pcd->translation_x);
2364 y = multip * gtk_spin_button_get_value(pcd->translation_y);
2365 cairo_translate(cr, x, y);
2366 DEBUG("Page translated by %f,%f", x, y);
2367
2368 item.x = multip * gtk_spin_button_get_value(pcd->payee_x);
2369 item.y = multip * gtk_spin_button_get_value(pcd->payee_y);
2370 draw_text(context, xaccTransGetDescription(trans), &item, desc);
2371
2372 item.x = multip * gtk_spin_button_get_value(pcd->date_x);
2373 item.y = multip * gtk_spin_button_get_value(pcd->date_y);
2374 g_date_clear (&date, 1);
2375 gnc_gdate_set_time64 (&date, xaccTransGetDate(trans));
2376 date_format = gnc_date_format_get_custom(GNC_DATE_FORMAT(pcd->date_format));
2377 g_date_strftime(buf, 100, date_format, &date);
2378 draw_text(context, buf, &item, desc);
2379
2380 item.x = multip * gtk_spin_button_get_value(pcd->number_x);
2381 item.y = multip * gtk_spin_button_get_value(pcd->number_y);
2382 info = gnc_default_print_info(FALSE);
2383 amount = gnc_numeric_abs(xaccSplitGetAmount(pcd->split));
2384 draw_text(context, xaccPrintAmount(amount, info), &item, desc);
2385
2386 item.x = multip * gtk_spin_button_get_value(pcd->words_x);
2387 item.y = multip * gtk_spin_button_get_value(pcd->words_y);
2388 text = numeric_to_words(amount);
2389 draw_text(context, text, &item, desc);
2390 g_free(text);
2391
2392 item.x = multip * gtk_spin_button_get_value(pcd->address_x);
2393 item.y = multip * gtk_spin_button_get_value(pcd->address_y);
2394 address = get_check_address(pcd);
2395 draw_text(context, address, &item, desc);
2396 g_free(address);
2397
2398 item.x = multip * gtk_spin_button_get_value(pcd->splits_amount_x);
2399 item.y = multip * gtk_spin_button_get_value(pcd->splits_amount_y);
2400 text = get_check_splits_amount(pcd);
2401 draw_text(context, text, &item, desc);
2402 g_free(text);
2403
2404 item.x = multip * gtk_spin_button_get_value(pcd->splits_memo_x);
2405 item.y = multip * gtk_spin_button_get_value(pcd->splits_memo_y);
2406 text = get_check_splits_memo(pcd);
2407 draw_text(context, text, &item, desc);
2408 g_free(text);
2409
2410 item.x = multip * gtk_spin_button_get_value(pcd->splits_account_x);
2411 item.y = multip * gtk_spin_button_get_value(pcd->splits_account_y);
2412 text = get_check_splits_account(pcd);
2413 draw_text(context, text, &item, desc);
2414 g_free(text);
2415
2416 item.x = multip * gtk_spin_button_get_value(pcd->notes_x);
2417 item.y = multip * gtk_spin_button_get_value(pcd->notes_y);
2418 draw_text(context, xaccTransGetNotes(trans), &item, desc);
2419
2420 item.x = multip * gtk_spin_button_get_value(pcd->memo_x);
2421 item.y = multip * gtk_spin_button_get_value(pcd->memo_y);
2422 draw_text(context, xaccSplitGetMemo(pcd->split), &item, desc);
2423
2424 pango_font_description_free(desc);
2425}
2426
2427
2428/* Print a page of checks. This takes into account the number of checks to print,
2429 * the number of checks on a page, and the starting check position on the page.
2430 * This function is called once by the GtkPrint code once for each page to be printed.
2431 */
2432static void
2433draw_page(GtkPrintOperation *operation,
2434 GtkPrintContext *context, gint page_nr, gpointer user_data)
2435{
2436 PrintCheckDialog *pcd = (PrintCheckDialog *) user_data;
2437 check_format_t *format;
2438 cairo_t *cr = gtk_print_context_get_cairo_context(context);
2439
2440 format = pcd->selected_format;
2441 if (format)
2442 {
2443 gint first_check, last_check;
2444 gint first_page_count;
2445 guint check_count = g_list_length(pcd->splits);
2446 gint check_number;
2447 gint position = gtk_combo_box_get_active(GTK_COMBO_BOX(pcd->position_combobox));
2448 gint last_blank_check_pos;
2449 gint checks_per_page;
2450 GList *next_split;
2451
2452 if (position == pcd->position_max)
2453 {
2454 /* Custom position, one check per page */
2455 checks_per_page = 1;
2456 first_page_count = 1;
2457 }
2458 else
2459 {
2460 checks_per_page = pcd->position_max;
2461 first_page_count = gtk_spin_button_get_value_as_int(pcd->first_page_count);
2462 }
2463
2464 if (page_nr == 0)
2465 {
2466 first_check = 0;
2467 last_check = first_page_count - 1;
2468 next_split = pcd->splits;
2469 }
2470 else
2471 {
2472 first_check = first_page_count + (page_nr - 1) * checks_per_page;
2473 last_check = MIN(check_count - 1, first_check + checks_per_page - 1);
2474 next_split = g_list_nth(pcd->splits, first_check);
2475 /* If position is not "custom" reset it to top */
2476 if (position < pcd->position_max)
2477 position = 0;
2478 }
2479
2480 /* Do page level translations/rotations */
2481 cairo_translate(cr, format->trans_x, format->trans_y);
2482 DEBUG("Page translated by %f,%f", format->trans_x, format->trans_y);
2483 cairo_rotate(cr, format->rotation * DEGREES_TO_RADIANS);
2484 DEBUG("Page rotated by %f degrees", format->rotation);
2485
2486 /* The grid is useful when determining check layouts */
2487 if (format->show_grid)
2488 {
2489 draw_grid(context,
2490 gtk_print_context_get_width(context),
2491 gtk_print_context_get_height(context),
2492 pcd->default_font);
2493 }
2494
2495 last_blank_check_pos = position - 1;
2496 /* Optionally skip blank check positions if */
2497 if ((page_nr == 0) /* on first page AND */
2498 && (last_blank_check_pos > 0) /* there's more than one blank check */
2499 && (position < pcd->position_max)) /* but don't skip for custom positioning */
2500 cairo_translate(cr, 0, format->height * last_blank_check_pos);
2501
2502 for (check_number = first_check; check_number <= last_check;
2503 check_number++, position++)
2504 {
2505 pcd->split = (Split *) next_split->data;
2506 next_split = g_list_next(next_split);
2507 draw_check_format(context, position, format, user_data);
2508 }
2509 }
2510 else
2511 {
2512 /* Custom check format */
2513 pcd->split = (Split *) g_list_nth_data(pcd->splits, page_nr);
2514 g_return_if_fail(pcd->split);
2515 draw_check_custom(context, user_data);
2516 }
2517}
2518
2519
2520/* Compute the number of pages required to complete this print operation.
2521 * This function is called once by the GtkPrint code to determine the number
2522 * of pages required to complete the print operation.
2523 */
2524static void
2525begin_print(GtkPrintOperation *operation,
2526 GtkPrintContext *context, gpointer user_data)
2527{
2528 PrintCheckDialog *pcd = (PrintCheckDialog *) user_data;
2529 guint check_count = g_list_length(pcd->splits);
2530 gint pages;
2531 gint position = gtk_combo_box_get_active(GTK_COMBO_BOX(pcd->position_combobox));
2532
2533 if (pcd->selected_format /* User selected a format other than custom */
2534 && pcd->position_max > 1 /* The format has more than one check per page
2535 (position_max is equivalent to custom
2536 positioning, and there need to be at least two
2537 other check defined positions (0 and 1), so
2538 custom positioning should be at least
2539 at position 2, i.e. >1) */
2540 && position < pcd->position_max) /* User chose a check position other
2541 then custom (which is always at
2542 position_max in the list) */
2543 {
2544 gint first_page_count, remaining_count;
2545
2546 first_page_count = gtk_spin_button_get_value_as_int(pcd->first_page_count);
2547 remaining_count = check_count - first_page_count;
2548 pages = 1 /* First page, will have first_page_count checks */
2549 + remaining_count / pcd->position_max;
2550 /* Subsequent pages with all positions filled */
2551 if ((remaining_count % pcd->position_max) > 0)
2552 pages++; /* Last page, not all positions are filled. Needs to be added
2553 separately because integer division rounds towards 0 and
2554 would omit the last checks if they didn't fill a full page */
2555 }
2556 else
2557 pages = check_count;
2558 gtk_print_operation_set_n_pages(operation, pages);
2559}
2560
2561
2562/************************************
2563 * gnc_ui_print_check_dialog_ok_cb *
2564 ************************************/
2565static void
2566gnc_ui_print_check_dialog_ok_cb(PrintCheckDialog *pcd)
2567{
2568 GtkPrintOperation *print;
2569 GtkPrintOperationResult res;
2570
2571 print = gtk_print_operation_new();
2572
2573 gnc_print_operation_init(print, "GnuCash-Checks");
2574 gtk_print_operation_set_unit(print, GTK_UNIT_POINTS);
2575 gtk_print_operation_set_use_full_page(print, TRUE);
2576 g_signal_connect(print, "begin_print", G_CALLBACK(begin_print), pcd);
2577 g_signal_connect(print, "draw_page", G_CALLBACK(draw_page), pcd);
2578
2579 res = gtk_print_operation_run(print,
2580 GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
2581 pcd->caller_window, NULL);
2582
2583 if (res == GTK_PRINT_OPERATION_RESULT_APPLY)
2585
2586 g_object_unref(print);
2587}
2588
2589
2590static void
2591gnc_print_check_set_sensitive (GtkWidget *widget, gpointer data)
2592{
2593 gboolean sensitive;
2594 if (GTK_IS_LABEL(widget) || GTK_IS_SEPARATOR(widget))
2595 return;
2596 sensitive = GPOINTER_TO_INT(data);
2597 gtk_widget_set_sensitive(widget, sensitive);
2598}
2599
2600
2601void
2602gnc_print_check_format_changed (GtkComboBox *widget,
2603 PrintCheckDialog *pcd)
2604{
2605 GtkListStore *p_store;
2606 GtkTreeModel *f_model;
2607 GtkTreeIter f_iter, iter;
2608 gboolean sensitive;
2609 gint pnum;
2610 check_format_t *format;
2611 gboolean separator;
2612 GSList *elem;
2613
2614 if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(pcd->format_combobox), &f_iter))
2615 return;
2616 f_model = gtk_combo_box_get_model(GTK_COMBO_BOX(pcd->format_combobox));
2617 gtk_tree_model_get(f_model, &f_iter, COL_DATA, &format, COL_SEP, &separator, -1);
2618 if (separator)
2619 return;
2620
2621 pnum = gtk_combo_box_get_active(GTK_COMBO_BOX(pcd->position_combobox));
2622
2623 /* Update the positions combobox */
2624 pcd->selected_format = format;
2625 p_store = gtk_list_store_new (1, G_TYPE_STRING);
2626 gtk_combo_box_set_model(GTK_COMBO_BOX(pcd->position_combobox),
2627 GTK_TREE_MODEL(p_store));
2628 if (format)
2629 {
2630 if (format->positions)
2631 {
2632 pcd->position_max = g_slist_length(format->positions); /* -1 for 0 base, +1 for custom entry */
2633 for (elem = format->positions; elem; elem = g_slist_next(elem))
2634 {
2635 gtk_list_store_append(GTK_LIST_STORE(p_store), &iter);
2636 gtk_list_store_set (GTK_LIST_STORE(p_store), &iter, 0, elem->data, -1);
2637 }
2638 }
2639 else
2640 {
2641 /* Invent a "Top" position if format has no positions */
2642 pcd->position_max = 1;
2643 gtk_list_store_append(GTK_LIST_STORE(p_store), &iter);
2644 gtk_list_store_set (GTK_LIST_STORE(p_store), &iter, 0, _("Top"), -1);
2645 }
2646 }
2647 else
2648 {
2649 pcd->position_max = 0;
2650 }
2651 gtk_list_store_append(GTK_LIST_STORE(p_store), &iter);
2652 gtk_list_store_set (GTK_LIST_STORE(p_store), &iter, 0, _("Custom"), -1);
2653 g_object_unref (p_store);
2654
2655 /* If there's only one thing in the position combobox, make it insensitive */
2656 sensitive = (pcd->position_max > 0);
2657 gtk_widget_set_sensitive(GTK_WIDGET(pcd->position_combobox), sensitive);
2658
2659 /* Update the custom page, this must be done before setting the active
2660 entry in the position combo box since gnc_print_check_position_changed
2661 will adjust these settings in some cases. */
2662 sensitive = (!separator && !format);
2663 gtk_container_foreach(GTK_CONTAINER(pcd->custom_table),
2664 gnc_print_check_set_sensitive,
2665 GINT_TO_POINTER(sensitive));
2666
2667 /* Set the active entry in the position combo box, this will trigger a
2668 call to gnc_print_check_position_changed */
2669 pnum = MAX(MIN(pnum, pcd->position_max), 0);
2670 gtk_combo_box_set_active(GTK_COMBO_BOX(pcd->position_combobox), pnum);
2671
2672 /* Update address fields */
2673 sensitive = check_format_has_address(pcd);
2674 gtk_widget_set_sensitive(pcd->check_address_name, sensitive);
2675 gtk_widget_set_sensitive(pcd->check_address_1, sensitive);
2676 gtk_widget_set_sensitive(pcd->check_address_2, sensitive);
2677 gtk_widget_set_sensitive(pcd->check_address_3, sensitive);
2678 gtk_widget_set_sensitive(pcd->check_address_4, sensitive);
2679}
2680
2681
2682void
2683gnc_print_check_position_changed (GtkComboBox *widget,
2684 PrintCheckDialog *pcd)
2685{
2686 gboolean sensitive;
2687 gint pnum;
2688 guint check_count;
2689 gint first_page_max, first_page_min, first_page_value;
2690
2691 pnum = gtk_combo_box_get_active(GTK_COMBO_BOX(pcd->position_combobox));
2692
2693 /* Make the translation and rotation fields active if the position is "custom" */
2694 sensitive = pnum == pcd->position_max;
2695 gtk_widget_set_sensitive(GTK_WIDGET(pcd->translation_x), sensitive);
2696 gtk_widget_set_sensitive(GTK_WIDGET(pcd->translation_y), sensitive);
2697 gtk_widget_set_sensitive(GTK_WIDGET(pcd->check_rotation), sensitive);
2698 gtk_widget_set_sensitive(GTK_WIDGET(pcd->units_combobox), sensitive);
2699
2700 /* Set up the first page check count spin box */
2701 check_count = g_list_length(pcd->splits);
2702 first_page_max = MAX(1, MIN(pcd->position_max - pnum, check_count));
2703 first_page_min = 1;
2704 pnum = gtk_spin_button_get_value_as_int(pcd->first_page_count);
2705 first_page_value = MAX(MIN(pnum, first_page_max), first_page_min);
2706 gtk_spin_button_set_range(pcd->first_page_count, (gdouble)first_page_min, (gdouble)first_page_max);
2707 gtk_spin_button_set_value(pcd->first_page_count, (gdouble)first_page_value);
2708 sensitive = first_page_max > 1;
2709 gtk_widget_set_sensitive(GTK_WIDGET(pcd->first_page_count), sensitive);
2710}
2711
2712
2713void
2714gnc_ui_print_check_response_cb(GtkDialog *dialog,
2715 gint response,
2716 PrintCheckDialog *pcd)
2717{
2718 switch (response)
2719 {
2720 case GTK_RESPONSE_HELP:
2721 gnc_gnome_help (GTK_WINDOW(dialog), DF_MANUAL, DL_PRINTCHECK);
2722 return;
2723
2724 case GTK_RESPONSE_OK:
2725 gnc_ui_print_check_dialog_ok_cb(pcd);
2726 gnc_ui_print_save_dialog(pcd);
2727 gnc_save_window_size(GNC_PREFS_GROUP, GTK_WINDOW(dialog));
2728 break;
2729
2730 case GTK_RESPONSE_CANCEL:
2731 gnc_save_window_size(GNC_PREFS_GROUP, GTK_WINDOW(dialog));
2732 break;
2733 }
2734
2735 gtk_widget_destroy(pcd->dialog);
2736 g_free(pcd->default_font);
2737 g_list_free(pcd->splits);
2738 g_free(pcd);
2739}
API for Transactions and Splits (journal entries)
API for Transactions and Splits (journal entries)
format_combo_col
@ COL_NAME
This column holds a copy of the check format name and is what is displayed to the user in the combobo...
@ COL_DATA
This column holds a pointer to the check format data read in from a file.
@ COL_SEP
This column contains the value TRUE if this entry specifies a separator line.
Date and Time handling routines.
All type declarations for the whole Gnucash engine.
File path resolution utility functions.
GKeyFile helper routines.
An exact-rational-number library for gnucash.
Functions providing a register page for the GnuCash UI.
Generic api to store and retrieve preferences.
utility functions 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 ...
void gnc_print_operation_save_print_settings(GtkPrintOperation *op)
Retrieve the print settings from the GtkPrintOperation op and save them in a static variable.
void gnc_gdate_set_time64(GDate *gd, time64 time)
Set a GDate to a time64.
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:502
@ QOF_DATE_FORMAT_LOCALE
Take from locale information.
Definition gnc-date.h:128
@ QOF_DATE_FORMAT_CUSTOM
Used by the check printing code.
Definition gnc-date.h:130
const char * xaccTransGetDescription(const Transaction *trans)
Gets the transaction Description.
const char * xaccTransGetNotes(const Transaction *trans)
Gets the transaction Notes.
gnc_numeric xaccTransGetAccountAmount(const Transaction *trans, const Account *acc)
Same as xaccTransGetAccountValue, but uses the Account's commodity.
SplitList * xaccTransGetSplitList(const Transaction *trans)
The xaccTransGetSplitList() method returns a GList of the splits in a transaction.
GList SplitList
GList of Split.
Definition gnc-engine.h:207
time64 xaccTransGetDate(const Transaction *trans)
Retrieve the posted date of the transaction.
gboolean gnc_key_file_save_to_file(const gchar *filename, GKeyFile *key_file, GError **error)
Write a key/value file from memory to disk.
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.
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
Definition guid.h:84
void guid_replace(GncGUID *guid)
Generate a new guid.
Definition guid.cpp:178
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:208
void gnc_gnome_help(GtkWindow *parent, const char *file_name, const char *anchor)
Launch the systems default help browser, gnome's yelp for linux, and open to a given link within a gi...
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 ...
const char * xaccPrintAmount(gnc_numeric val, GNCPrintAmountInfo info)
Make a string representation of a gnc_numeric.
#define DEBUG(format, args...)
Print a debugging message.
Definition qoflog.h:264
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.
const GncOwner * gncOwnerGetEndOwner(const GncOwner *owner)
Get the "parent" Owner or GncGUID thereof.
Definition gncOwner.c:572
gboolean gncOwnerGetOwnerFromTxn(Transaction *txn, GncOwner *owner)
Convenience function to get the owner from a transaction.
Definition gncOwner.c:674
gboolean gnc_prefs_set_float(const gchar *group, const gchar *pref_name, gdouble value)
Store a float value into the preferences backend.
gdouble gnc_prefs_get_float(const gchar *group, const gchar *pref_name)
Get an float value from the preferences backend.
gint gnc_prefs_get_int(const gchar *group, const gchar *pref_name)
Get an integer value from the preferences backend.
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.
void gnc_prefs_reset(const gchar *group, const gchar *pref_name)
Reset a preference to its default value in the preferences backend.
gboolean gnc_prefs_set_coords(const gchar *group, const gchar *pref_name, gdouble x, gdouble y)
Store coordinates into the preferences backend.
gchar * gnc_prefs_get_string(const gchar *group, const gchar *pref_name)
Get a string value from the preferences backend.
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
gboolean gnc_prefs_set_string(const gchar *group, const gchar *pref_name, const gchar *value)
Store a string into the preferences backend.
gboolean gnc_prefs_set_int(const gchar *group, const gchar *pref_name, gint value)
Store an integer value into the preferences backend.
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
const char * xaccSplitGetMemo(const Split *split)
Returns the memo string.
gnc_numeric xaccSplitGetAmount(const Split *split)
Returns the amount of the split in the account's commodity.
Functions for printing.
STRUCTS.
The type used to store guids in C.
Definition guid.h:75
gdouble trans_x
Move entire page left by this amount.
gboolean show_grid
Print a grid pattern on the page.
gboolean print_date_format
Default for printing date format characters for this page of checks.
const gchar * group
The group where this format was found.
gboolean show_boxes
Print boxes when width and height are known.
gchar * font
Default font for this page of checks.
gchar * filename
The name of the file from which this data was read.
gchar * title
Title of this check format.
gdouble rotation
Rotate the entire page by this amount.
gdouble height
Height of one check on a page.
gboolean blocking
Default for printing blocking characters for this page of checks.
GSList * items
List of items printed on each check.
gdouble trans_y
Move entire page down by this amount.
gchar * guid
Unique identifier for this format.
GSList * positions
Names of the checks on the page.
gdouble h
Optional.
gdouble y
The x/y coordinates where this item should be printed.
PangoAlignment align
The alignment of a text based item.
gboolean print_date_format
Optional.
gchar * filename
The filename for picture items.
gchar * font
The font to use for text based items.
gchar * text
The text to be displayed (for text based items.) Otherwise unused.
CheckItemType type
What type of item is this?
gboolean blocking
Optional.