GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
dialog-lot-viewer.c
1 /*******************************************************************\
2  * dialog-lot-viewer.c -- a basic lot viewer for GnuCash *
3  * Copyright (C) 2003 Linas Vepstas <[email protected]> *
4  * Copyright (C) 2011 Geert Janssens <[email protected]> *
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 [email protected] *
22  * *
23 \********************************************************************/
24 
25 /* XXX todo: The button "view lot in register" is not implemented.
26  * it needs to open register window showing only the splits in the
27  * given lot ...
28  */
29 
30 #include "config.h"
31 
32 #include <gtk/gtk.h>
33 #include <glib/gi18n.h>
34 
35 #include "Account.h"
36 #include "cap-gains.h"
37 #include "gnc-commodity.h"
38 #include "qof.h"
39 #include "gnc-lot.h"
40 #include "Scrub3.h"
41 #include "ScrubBusiness.h"
42 #include "Transaction.h"
43 #include "engine-helpers.h"
44 #include "gncInvoice.h"
45 
46 #include "dialog-utils.h"
47 #include "dialog-lot-viewer.h"
48 #include "gnc-component-manager.h"
49 #include "gnc-prefs.h"
50 #include "gnc-ui-util.h"
51 #include "misc-gnome-utils.h"
52 #include "tree-view-utils.h"
53 
54 #define LOT_VIEWER_CM_CLASS "dialog-lot-viewer"
55 
56 enum lot_cols
57 {
58  LOT_COL_TYPE = 0,
59  LOT_COL_OPEN,
60  LOT_COL_CLOSE,
61  LOT_COL_TITLE,
62  LOT_COL_BALN,
63  LOT_COL_GAINS,
64  LOT_COL_PNTR,
65  NUM_LOT_COLS
66 };
67 
68 enum split_cols
69 {
70  SPLIT_COL_DATE = 0,
71  SPLIT_COL_NUM,
72  SPLIT_COL_DESCRIPTION,
73  SPLIT_COL_AMOUNT,
74  SPLIT_COL_VALUE,
75  SPLIT_COL_GAIN_LOSS,
76  SPLIT_COL_BALANCE,
77  SPLIT_COL_PNTR,
78  NUM_SPLIT_COLS
79 };
80 
81 #define RESPONSE_VIEW 1
82 #define RESPONSE_DELETE 2
83 #define RESPONSE_SCRUB_LOT 3
84 #define RESPONSE_SCRUB_ACCOUNT 4
85 #define RESPONSE_NEW_LOT 5
86 
87 #define GNC_PREFS_GROUP "dialogs.lot-viewer"
88 #define GNC_PREF_HPOS "hpane-position"
89 #define GNC_PREF_VPOS "vpane-position"
90 
92 {
93  GtkWidget * window;
94 #ifdef LOTS_READY_FOR_SHOWTIME
95  GtkButton * regview_button;
96 #endif
97  GtkButton * delete_button;
98  GtkButton * scrub_lot_button;
99  GtkButton * new_lot_button;
100  GtkTreeView * lot_view;
101  GtkListStore * lot_store;
102  GtkTextView * lot_notes;
103  GtkEntry * title_entry;
104  GtkTreeView * split_in_lot_view;
105  GtkListStore * split_in_lot_store;
106  GtkTreeView * split_free_view;
107  GtkListStore * split_free_store;
108  GtkButton * add_split_to_lot_button;
109  GtkButton * remove_split_from_lot_button;
110  GtkToggleButton * only_show_open_lots_checkbutton;
111 
112  Account * account;
113  GNCLot * selected_lot;
114 };
115 
116 static void gnc_lot_viewer_fill (GNCLotViewer *lv);
117 static void gnc_split_viewer_fill (GNCLotViewer *lv, GtkListStore *store, SplitList *split_list);
118 
119 /* ======================================================================== */
120 /* Callback prototypes */
121 
122 void lv_title_entry_changed_cb (GtkEntry *ent, gpointer user_data);
123 void lv_response_cb (GtkDialog *dialog, gint response, gpointer data);
124 void lv_window_destroy_cb (GtkObject *object, gpointer user_data);
125 void lv_paned_notify_cb (GObject *gobject,
126  GParamSpec *pspec,
127  gpointer user_data);
128 
129 /* ======================================================================== */
130 /* Get the realized gains for this lot. This routine or a variant of it
131  * should probably be moved to gnc-lot.c.
132  * The conceptual difficulty here is that this works only if all of the
133  * realized gains in the lot are of the
134  */
135 
136 static gnc_commodity *
137 find_first_currency (GNCLot *lot)
138 {
139  SplitList *split_list, *node;
140 
141  split_list = gnc_lot_get_split_list(lot);
142  for (node = split_list; node; node = node->next)
143  {
144  Split *s = node->data;
145  Transaction *trans;
146  if (FALSE == gnc_numeric_zero_p(xaccSplitGetAmount(s))) continue;
147  trans = xaccSplitGetParent (s);
148  return xaccTransGetCurrency (trans);
149  }
150  return NULL;
151 }
152 
153 static gnc_numeric
154 get_realized_gains (GNCLot *lot, gnc_commodity *currency)
155 {
156  gnc_numeric zero = gnc_numeric_zero();
157  gnc_numeric gains = zero;
158  SplitList *split_list, *node;
159 
160  if (!currency) return zero;
161 
162  split_list = gnc_lot_get_split_list(lot);
163  for (node = split_list; node; node = node->next)
164  {
165  Split *s = node->data;
166  Transaction *trans;
167 
168  if (FALSE == gnc_numeric_zero_p(xaccSplitGetAmount(s))) continue;
169  trans = xaccSplitGetParent (s);
170  if (FALSE == gnc_commodity_equal (xaccTransGetCurrency(trans), currency)) continue;
171 
173  }
174  return gains;
175 }
176 
177 
178 /* ======================================================================== */
179 /* Populate the lot split list view based on the currently selected lot */
180 
181 static void
182 lv_show_splits_in_lot (GNCLotViewer *lv)
183 {
184  GNCLot *lot = lv->selected_lot;
185  SplitList *split_list;
186 
187  if (NULL == lot) return;
188 
189  split_list = gnc_lot_get_split_list (lot);
190  gnc_split_viewer_fill(lv, lv->split_in_lot_store, split_list);
191 }
192 
193 /* ======================================================================== */
194 /* Remove all splits from the split list view */
195 
196 static void
197 lv_clear_splits_in_lot (GNCLotViewer *lv)
198 {
199  gtk_list_store_clear (lv->split_in_lot_store);
200 }
201 
202 /* ======================================================================== */
203 /* Populate the free split list view */
204 
205 static void
206 lv_show_splits_free (GNCLotViewer *lv)
207 {
208  SplitList *split_list, *node;
209  SplitList *filtered_list = NULL;
210 
211  /* cleanup */
212  gtk_list_store_clear (lv->split_free_store);
213 
214  /* get splits */
215  split_list = xaccAccountGetSplitList(lv->account);
216 
217  /* filter splits */
218  for (node = split_list; node; node = node->next)
219  {
220  Split *split = node->data;
221  if (NULL == xaccSplitGetLot(split))
222  {
223  filtered_list = g_list_append(filtered_list, split);
224  }
225  }
226 
227  /* display list */
228  gnc_split_viewer_fill(lv, lv->split_free_store, filtered_list);
229 }
230 
231 /* ======================================================================== */
232 /* Save potential changes to the currently selected lot */
233 
234 static void
235 lv_save_current_lot (GNCLotViewer *lv)
236 {
237  GNCLot *lot = lv->selected_lot;
238  const char * str;
239  char * notes;
240 
241  if (lot)
242  {
243  gnc_lot_begin_edit(lot);
244 
245  /* Get the title, save_the_title */
246  str = gtk_entry_get_text (lv->title_entry);
247  gnc_lot_set_title (lot, str);
248 
249  /* Get the notes, save the notes */
250  notes = xxxgtk_textview_get_text (lv->lot_notes);
251  gnc_lot_set_notes (lot, notes);
252  g_free(notes);
253 
254  gnc_lot_commit_edit(lot);
255  }
256 }
257 
258 /* ======================================================================== */
259 /* Clear all information related to the currently selected lot */
260 
261 static void
262 lv_unset_lot (GNCLotViewer *lv)
263 {
264  /* Set immediately, to avoid recursion in gtkentry "changed" cb. */
265  lv->selected_lot = NULL;
266 
267  /* Blank the title widget */
268  gtk_entry_set_text (lv->title_entry, "");
269  gtk_editable_set_editable (GTK_EDITABLE(lv->title_entry), FALSE);
270 
271  /* Blank the notes area */
272  xxxgtk_textview_set_text (lv->lot_notes, "");
273  gtk_text_view_set_editable (lv->lot_notes, FALSE);
274 
275  /* Erase the mini-view area */
276  lv_clear_splits_in_lot (lv);
277 
278 #ifdef LOTS_READY_FOR_SHOWTIME
279  gtk_widget_set_sensitive(GTK_WIDGET(lv->regview_button), FALSE);
280 #endif
281  gtk_widget_set_sensitive(GTK_WIDGET(lv->delete_button), FALSE);
282  gtk_widget_set_sensitive(GTK_WIDGET(lv->scrub_lot_button), FALSE);
283 }
284 
285 /* ======================================================================== */
286 /* Select a row in the lot list */
287 
288 static void
289 lv_select_row (GNCLotViewer *lv,
290  GNCLot *lot)
291 {
292  const char * str;
293 
294  lv_save_current_lot (lv);
295 
296  str = gnc_lot_get_title (lot);
297  if (!str) str = "";
298  gtk_entry_set_text (lv->title_entry, str);
299  gtk_editable_set_editable (GTK_EDITABLE(lv->title_entry), TRUE);
300 
301  /* Set the notes field */
302  str = gnc_lot_get_notes (lot);
303  if (!str) str = "";
304  xxxgtk_textview_set_text (lv->lot_notes, str);
305  gtk_text_view_set_editable (lv->lot_notes, TRUE);
306 
307  /* Don't set until end, to avoid recursion in gtkentry "changed" cb. */
308  lv->selected_lot = lot;
309  lv_show_splits_in_lot (lv);
310 
311 #ifdef LOTS_READY_FOR_SHOWTIME
312  gtk_widget_set_sensitive(GTK_WIDGET(lv->regview_button), TRUE);
313 #endif
314  gtk_widget_set_sensitive(GTK_WIDGET(lv->delete_button), TRUE);
315  gtk_widget_set_sensitive(GTK_WIDGET(lv->scrub_lot_button), TRUE);
316 }
317 
318 /* ======================================================================== */
319 /* Un-select a row the the lot list */
320 
321 static void
322 lv_unselect_row (GNCLotViewer *lv)
323 {
324  lv_save_current_lot (lv);
325  lv_unset_lot (lv);
326 }
327 
328 /* ======================================================================== */
329 /* Populate the lot list view */
330 
331 static void
332 gnc_lot_viewer_fill (GNCLotViewer *lv)
333 {
334  LotList *lot_list, *node;
335  GNCLot *this_lot, *selected_lot = NULL;
336  GtkListStore *store;
337  GtkTreeModel *model;
338  GtkTreeIter iter;
339  GtkTreeSelection *selection;
340  gboolean found = FALSE;
341 
342  lot_list = xaccAccountGetLotList (lv->account);
343 
344  selection = gtk_tree_view_get_selection(lv->lot_view);
345  if (gtk_tree_selection_get_selected (selection, &model, &iter))
346  gtk_tree_model_get(model, &iter, LOT_COL_PNTR, &selected_lot, -1);
347 
348  /* Crazy. Should update in place if possible. */
349  gtk_list_store_clear (lv->lot_store);
350 
351  for (node = lot_list; node; node = node->next)
352  {
353  char type_buff[200];
354  char baln_buff[200];
355  char gain_buff[200];
356  GNCLot *lot = node->data;
357  Split *esplit = gnc_lot_get_earliest_split (lot);
358  Transaction *etrans = xaccSplitGetParent (esplit);
359  time64 open_date = xaccTransGetDate (etrans);
360  gnc_numeric amt_baln = gnc_lot_get_balance (lot);
361  gnc_commodity *currency = find_first_currency (lot);
362  gnc_numeric gains_baln = get_realized_gains (lot, currency);
363 
364  /* Skip closed lots when only open should be shown */
365  if (TRUE == gtk_toggle_button_get_active(lv->only_show_open_lots_checkbutton) && gnc_lot_is_closed (lot))
366  {
367  continue;
368  }
369 
370  store = lv->lot_store;
371  gtk_list_store_append(store, &iter);
372 
373  /* Part of invoice */
374  type_buff[0] = '\0';
375  if ( NULL != gncInvoiceGetInvoiceFromLot(lot) )
376  {
377  snprintf(type_buff, 200, "I");
378  }
379  gtk_list_store_set(store, &iter, LOT_COL_TYPE, type_buff, -1);
380 
381  /* Opening date */
382  gtk_list_store_set(store, &iter, LOT_COL_OPEN, open_date, -1);
383 
384  /* Closing date */
385  if (gnc_lot_is_closed (lot))
386  {
387  Split *fsplit = gnc_lot_get_latest_split (lot);
388  Transaction *ftrans = xaccSplitGetParent (fsplit);
389  time64 close_date = xaccTransGetDate (ftrans);
390 
391  gtk_list_store_set(store, &iter, LOT_COL_CLOSE, close_date, -1);
392  }
393  else
394  {
395  gtk_list_store_set(store, &iter, LOT_COL_CLOSE, 0, -1);
396  }
397 
398  /* Title */
399  gtk_list_store_set(store, &iter, LOT_COL_TITLE, gnc_lot_get_title(lot), -1);
400 
401  /* Amount */
402  xaccSPrintAmount (baln_buff, amt_baln,
403  gnc_account_print_info (lv->account, TRUE));
404  gtk_list_store_set(store, &iter, LOT_COL_BALN, baln_buff, -1);
405 
406  /* Capital Gains/Losses Appreciation/Depreciation */
407  xaccSPrintAmount (gain_buff, gains_baln,
408  gnc_commodity_print_info (currency, TRUE));
409  gtk_list_store_set(store, &iter, LOT_COL_GAINS, gain_buff, -1);
410 
411  /* Self-reference */
412  gtk_list_store_set(store, &iter, LOT_COL_PNTR, lot, -1);
413  }
414  g_list_free(lot_list);
415 
416  /* re-select the row that the user had previously selected,
417  * if possible. */
418  if (selected_lot)
419  {
420  model = GTK_TREE_MODEL(lv->lot_store);
421  if (gtk_tree_model_get_iter_first(model, &iter))
422  {
423  do
424  {
425  gtk_tree_model_get(model, &iter, LOT_COL_PNTR, &this_lot, -1);
426  if (this_lot == selected_lot)
427  {
428  gtk_tree_selection_select_iter(selection, &iter);
429  found = TRUE;
430  break;
431  }
432  }
433  while (gtk_tree_model_iter_next(model, &iter));
434  }
435  }
436 
437  if (!found)
438  gtk_tree_selection_unselect_all(selection);
439 }
440 
441 /* ======================================================================== */
442 /* Get selected split in a split list view */
443 
444 static Split *
445 lv_get_selected_split (GNCLotViewer *lv, GtkTreeView *view)
446 {
447  Split *split = NULL;
448  GtkTreeModel *model;
449  GtkTreeSelection *selection;
450  GtkTreeIter iter;
451 
452  selection = gtk_tree_view_get_selection(view);
453  if (gtk_tree_selection_get_selected (selection, &model, &iter))
454  {
455  gtk_tree_model_get(model, &iter, SPLIT_COL_PNTR, &split, -1);
456  }
457 
458  return split;
459 }
460 
461 /* ======================================================================== */
462 /* Check if split is main invoice split in lot */
463 
464 static gboolean
465 lv_can_remove_split_from_lot(Split * split, GNCLot * lot)
466 {
467  GncInvoice *lot_invoice, *txn_invoice;
468  Transaction *txn;
469 
470  lot_invoice = gncInvoiceGetInvoiceFromLot(lot);
471  txn = xaccSplitGetParent(split);
472  txn_invoice = gncInvoiceGetInvoiceFromTxn(txn);
473  if ( lot_invoice != NULL && lot_invoice == txn_invoice )
474  return FALSE;
475 
476  return TRUE;
477 }
478 
479 /* ======================================================================== */
480 /* Populate a split list view */
481 
482 static void
483 gnc_split_viewer_fill (GNCLotViewer *lv, GtkListStore *store, SplitList *split_list)
484 {
485  SplitList *node;
486  GtkTreeIter iter;
487 
488  gnc_numeric baln = gnc_numeric_zero();
489  gtk_list_store_clear (lv->split_in_lot_store);
490  for (node = split_list; node; node = node->next)
491  {
492  Split *split = node->data;
493  char amtbuff[200];
494  char valbuff[200];
495  char gainbuff[200];
496  char balnbuff[200];
497  gnc_commodity *currency;
498  Transaction *trans = xaccSplitGetParent (split);
499  time64 date = xaccTransGetDate (trans);
500  gnc_numeric amnt, valu, gains;
501 
502  /* Do not show gains splits */
503  if (gnc_numeric_zero_p (xaccSplitGetAmount(split))) continue;
504 
505  gtk_list_store_append(store, &iter);
506 
507  /* Date */
508  gtk_list_store_set (store, &iter, SPLIT_COL_DATE, date, -1);
509 
510  /* Num - retrieve number based on book option */
511  gtk_list_store_set (store, &iter, SPLIT_COL_NUM,
512  gnc_get_num_action (trans, split), -1);
513 
514  /* Description */
515  gtk_list_store_set (store, &iter, SPLIT_COL_DESCRIPTION, xaccTransGetDescription (trans), -1);
516 
517  /* Amount */
518  amnt = xaccSplitGetAmount (split);
519  xaccSPrintAmount (amtbuff, amnt,
520  gnc_account_print_info (lv->account, TRUE));
521  gtk_list_store_set (store, &iter, SPLIT_COL_AMOUNT, amtbuff, -1);
522 
523  /* Value. Invert the sign on the first, opening entry. */
524  currency = xaccTransGetCurrency (trans);
525  valu = xaccSplitGetValue (split);
526  if (node != split_list)
527  {
528  valu = gnc_numeric_neg (valu);
529  }
530  xaccSPrintAmount (valbuff, valu,
531  gnc_commodity_print_info (currency, TRUE));
532  gtk_list_store_set (store, &iter, SPLIT_COL_VALUE, valbuff, -1);
533 
534  /* Gains. Blank if none. */
535  gains = xaccSplitGetCapGains (split);
536  if (gnc_numeric_zero_p(gains))
537  {
538  gainbuff[0] = 0;
539  }
540  else
541  {
542  xaccSPrintAmount (gainbuff, gains,
543  gnc_commodity_print_info (currency, TRUE));
544  }
545  gtk_list_store_set (store, &iter, SPLIT_COL_GAIN_LOSS, gainbuff, -1);
546 
547  /* Balance of Gains */
548  baln = gnc_numeric_add_fixed (baln, amnt);
549  if (gnc_numeric_zero_p(baln))
550  {
551  balnbuff[0] = 0;
552  }
553  else
554  {
555  xaccSPrintAmount (balnbuff, baln,
556  gnc_account_print_info (lv->account, TRUE));
557  }
558  gtk_list_store_set (store, &iter, SPLIT_COL_BALANCE, balnbuff, -1);
559 
560  /* Self-reference */
561  gtk_list_store_set(store, &iter, SPLIT_COL_PNTR, split, -1);
562  }
563 }
564 
565 /* ======================================================================== */
566 
567 static void
568 lv_update_split_buttons(GNCLotViewer *lv)
569 {
570  Split * split;
571  gtk_widget_set_sensitive(GTK_WIDGET(lv->add_split_to_lot_button), FALSE);
572  gtk_widget_set_sensitive(GTK_WIDGET(lv->remove_split_from_lot_button), FALSE);
573  if (NULL != lv->selected_lot)
574  {
575  if (NULL != lv_get_selected_split(lv, lv->split_free_view) )
576  {
577  gtk_widget_set_sensitive(GTK_WIDGET(lv->add_split_to_lot_button), TRUE);
578  }
579  split = lv_get_selected_split(lv, lv->split_in_lot_view);
580  if (NULL != split && TRUE == lv_can_remove_split_from_lot(split, lv->selected_lot))
581  {
582  gtk_widget_set_sensitive(GTK_WIDGET(lv->remove_split_from_lot_button), TRUE);
583  }
584  }
585 }
586 
587 static void lv_refresh(GNCLotViewer * lv)
588 {
589  gnc_lot_viewer_fill (lv);
590  lv_show_splits_free (lv);
591  lv_show_splits_in_lot (lv);
592 }
593 
594 /* ======================================================================== */
595 
596 static void
597 lv_refresh_handler (GHashTable *changes, gpointer user_data)
598 {
599  GNCLotViewer *lv = user_data;
600  lv_refresh (lv);
601 }
602 
603 static void
604 lv_close_handler (gpointer user_data)
605 {
606  GNCLotViewer *lv = user_data;
607 
608  lv_save_current_lot (lv);
609 
610  gnc_save_window_size(GNC_PREFS_GROUP, GTK_WINDOW(lv->window));
611  gtk_widget_destroy (lv->window);
612 }
613 
614 /* =========================== Callbacks ============================ */
615 /* ======================================================================== */
616 /* The lot title in the entry widget changed */
617 
618 void
619 lv_title_entry_changed_cb (GtkEntry *ent, gpointer user_data)
620 {
621  GNCLotViewer *lv = user_data;
622  GtkTreeModel *model;
623  GtkTreeIter iter;
624  GtkTreeSelection *selection;
625  const char * title;
626  title = gtk_entry_get_text (lv->title_entry);
627 
628  selection = gtk_tree_view_get_selection(lv->lot_view);
629  if (gtk_tree_selection_get_selected (selection, &model, &iter))
630  {
631  gtk_list_store_set(GTK_LIST_STORE(model), &iter, LOT_COL_TITLE, title, -1);
632  }
633 }
634 
635 /* ======================================================================== */
636 /* Selection in the lot list view changed */
637 
638 static void
639 lv_selection_changed_cb (GtkTreeSelection *selection,
640  GNCLotViewer *lv)
641 {
642  GNCLot *lot;
643  GtkTreeModel *model;
644  GtkTreeIter iter;
645 
646  if (gtk_tree_selection_get_selected (selection, &model, &iter))
647  {
648  gtk_tree_model_get(model, &iter, LOT_COL_PNTR, &lot, -1);
649  lv_select_row(lv, lot);
650  }
651  else
652  {
653  lv_unselect_row(lv);
654  }
655  lv_update_split_buttons(lv);
656 }
657 
658 /* ======================================================================== */
659 /* Lot viewer window closed */
660 
661 void
662 lv_window_destroy_cb (GtkObject *object, gpointer user_data)
663 {
664  GNCLotViewer *lv = user_data;
665  gnc_close_gui_component_by_data (LOT_VIEWER_CM_CLASS, lv);
666  gnc_unregister_gui_component_by_data (LOT_VIEWER_CM_CLASS, lv);
667  g_free (lv);
668 }
669 
670 static void
671 lv_split_selection_changed_cb (GtkTreeSelection *selection,
672  GNCLotViewer *lv)
673 {
674  lv_update_split_buttons(lv);
675 }
676 
677 static void
678 lv_add_split_to_lot_cb (GtkWidget *widget, GNCLotViewer * lv)
679 {
680  Split *split;
681 
682  if ( NULL == lv->selected_lot ) return;
683  split = lv_get_selected_split(lv, lv->split_free_view);
684  if ( NULL == split ) return;
685 
686  xaccAccountBeginEdit(lv->account);
687  gnc_lot_add_split(lv->selected_lot, split);
688  xaccAccountCommitEdit(lv->account);
689 
690  lv_refresh(lv);
691 }
692 
693 static void
694 lv_remove_split_from_lot_cb (GtkWidget *widget, GNCLotViewer * lv)
695 {
696  Split *split;
697 
698  if ( NULL == lv->selected_lot ) return;
699  split = lv_get_selected_split(lv, lv->split_in_lot_view);
700  if ( NULL == split ) return;
701 
702  if ( FALSE == lv_can_remove_split_from_lot(split, lv->selected_lot) )
703  return;
704 
705  xaccAccountBeginEdit(lv->account);
706  gnc_lot_remove_split(lv->selected_lot, split);
707  xaccAccountCommitEdit(lv->account);
708 
709  lv_refresh(lv);
710 }
711 
712 static void
713 lv_only_show_open_lots_changed_cb (GtkWidget *widget, GNCLotViewer * lv)
714 {
715  lv_refresh(lv);
716 }
717 
718 /* ======================================================================== */
719 /* Any button was pressed */
720 
721 void
722 lv_response_cb (GtkDialog *dialog, gint response, gpointer data)
723 {
724  GNCLotViewer *lv = data;
725  GNCLot *lot = lv->selected_lot;
726 
727  switch (response)
728  {
729  case GTK_RESPONSE_CLOSE:
730  lv_close_handler(lv);
731  return;
732 
733  case RESPONSE_VIEW:
734  if (NULL == lot)
735  return;
736  printf ("UNIMPLEMENTED: need to display register showing only this one lot \n");
737  break;
738 
739  case RESPONSE_DELETE:
740  if (NULL == lot)
741  return;
742  /* Prevent broken invoices */
743  if (NULL != gncInvoiceGetInvoiceFromLot(lot))
744  return;
745  xaccAccountRemoveLot (gnc_lot_get_account(lot), lot);
746  gnc_lot_destroy (lot);
747  lv_unset_lot (lv);
748  gnc_lot_viewer_fill (lv);
749  break;
750 
751  case RESPONSE_SCRUB_LOT:
752  if (NULL == lot)
753  return;
754  if (xaccAccountIsAPARType (xaccAccountGetType(lv->account)))
755  gncScrubBusinessLot (lot);
756  else
757  xaccScrubLot (lot);
758  gnc_lot_viewer_fill (lv);
759  lv_show_splits_in_lot (lv);
760  break;
761 
762  case RESPONSE_SCRUB_ACCOUNT:
763  gnc_suspend_gui_refresh ();
764  if (xaccAccountIsAPARType (xaccAccountGetType(lv->account)))
765  gncScrubBusinessAccountLots (lv->account);
766  else
767  xaccAccountScrubLots (lv->account);
768  gnc_resume_gui_refresh ();
769  gnc_lot_viewer_fill (lv);
770  lv_show_splits_free (lv);
771  lv_show_splits_in_lot (lv);
772  break;
773 
774  case RESPONSE_NEW_LOT:
775  lv_save_current_lot (lv);
776  lot = gnc_lot_make_default (lv->account);
777  xaccAccountInsertLot (lv->account, lot);
778  break;
779  }
780 }
781 
782 /* ======================================================================== */
783 
784 static void print_date (GtkTreeViewColumn *tree_column,
785  GtkCellRenderer *cell,
786  GtkTreeModel *tree_model,
787  GtkTreeIter *iter,
788  gpointer data)
789 {
790  GValue value = { 0 };
791  time64 doc_date_time;
792  gchar *doc_date_str = g_strdup (_("Open"));
793  gint col = GPOINTER_TO_INT(data);
794 
795  g_return_if_fail (cell && iter && tree_model);
796 
797  gtk_tree_model_get_value (tree_model, iter, col, &value);
798  doc_date_time = (time64) g_value_get_int64 (&value);
799  g_value_unset (&value);
800 
801  if (doc_date_time) /* assumes 0 represents an invalid date/time */
802  {
803  g_free (doc_date_str);
804  doc_date_str = qof_print_date (doc_date_time);
805  }
806  g_object_set (G_OBJECT (cell), "text", doc_date_str, NULL);
807  g_free (doc_date_str);
808 }
809 
810 static void
811 lv_init_lot_view (GNCLotViewer *lv)
812 {
813  GtkTreeView *view;
814  GtkListStore *store;
815  GtkTreeViewColumn *column;
816  GtkTreeSelection *selection;
817  GtkCellRenderer *renderer;
818 
819  g_return_if_fail(GTK_IS_TREE_VIEW(lv->lot_view));
820 
821  view = lv->lot_view;
822  store = gtk_list_store_new(NUM_LOT_COLS, G_TYPE_STRING, G_TYPE_INT64,
823  G_TYPE_INT64, G_TYPE_STRING, G_TYPE_STRING,
824  G_TYPE_STRING, G_TYPE_POINTER);
825  gtk_tree_view_set_model(view, GTK_TREE_MODEL(store));
826  g_object_unref(store);
827  lv->lot_store = store;
828 
829  /* Set up the columns */
830  renderer = gtk_cell_renderer_text_new();
831  column = gtk_tree_view_column_new_with_attributes(_("Type"), renderer,
832  "text", LOT_COL_TYPE, NULL);
833  gtk_tree_view_column_set_sort_column_id(column, LOT_COL_TYPE);
834  gtk_tree_view_append_column(view, column);
835 
836  renderer = gtk_cell_renderer_text_new();
837  column = gtk_tree_view_column_new_with_attributes(_("Opened"), renderer,
838  "text", LOT_COL_OPEN, NULL);
839  gtk_tree_view_column_set_sort_column_id(column, LOT_COL_OPEN);
840  tree_view_column_set_default_width (view, column, "31-12-2013");
841  gtk_tree_view_column_set_cell_data_func (column, renderer,
842  (GtkTreeCellDataFunc) print_date,
843  GINT_TO_POINTER (LOT_COL_OPEN), NULL);
844  gtk_tree_view_append_column(view, column);
845 
846  renderer = gtk_cell_renderer_text_new();
847  column = gtk_tree_view_column_new_with_attributes(_("Closed"), renderer,
848  "text", LOT_COL_CLOSE, NULL);
849  gtk_tree_view_column_set_sort_column_id(column, LOT_COL_CLOSE);
850  tree_view_column_set_default_width (view, column, "31-12-2013");
851  gtk_tree_view_column_set_cell_data_func (column, renderer,
852  (GtkTreeCellDataFunc) print_date,
853  GINT_TO_POINTER (LOT_COL_CLOSE), NULL);
854  gtk_tree_view_append_column(view, column);
855 
856  renderer = gtk_cell_renderer_text_new();
857  column = gtk_tree_view_column_new_with_attributes(_("Title"), renderer,
858  "text", LOT_COL_TITLE, NULL);
859  gtk_tree_view_column_set_sort_column_id(column, LOT_COL_TITLE);
860  gtk_tree_view_append_column(view, column);
861 
862  renderer = gtk_cell_renderer_text_new();
863  column = gtk_tree_view_column_new_with_attributes(_("Balance"), renderer,
864  "text", LOT_COL_BALN, NULL);
865  gtk_tree_view_column_set_sort_column_id(column, LOT_COL_BALN);
866  gtk_tree_view_append_column(view, column);
867 
868  renderer = gtk_cell_renderer_text_new();
869  column = gtk_tree_view_column_new_with_attributes(_("Gains"), renderer,
870  "text", LOT_COL_GAINS, NULL);
871  gtk_tree_view_column_set_sort_column_id(column, LOT_COL_GAINS);
872  gtk_tree_view_append_column(view, column);
873 
874  /* Set up signals */
875  selection = gtk_tree_view_get_selection(view);
876  g_signal_connect(selection, "changed",
877  G_CALLBACK(lv_selection_changed_cb), lv);
878  g_signal_connect(lv->only_show_open_lots_checkbutton, "toggled",
879  G_CALLBACK(lv_only_show_open_lots_changed_cb), lv);
880 
881 }
882 
883 /* ======================================================================== */
884 
885 static GtkListStore *
886 lv_init_split_view (GNCLotViewer *lv, GtkTreeView *view)
887 {
888  GtkListStore *store;
889  GtkTreeViewColumn *column;
890  GtkTreeSelection *selection;
891  GtkCellRenderer *renderer;
892 
893  g_return_val_if_fail(GTK_IS_TREE_VIEW(view), NULL);
894 
895  store = gtk_list_store_new(NUM_SPLIT_COLS, G_TYPE_INT64, G_TYPE_STRING,
896  G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
897  G_TYPE_STRING, G_TYPE_STRING,
898  G_TYPE_POINTER);
899  gtk_tree_view_set_model(view, GTK_TREE_MODEL(store));
900  g_object_unref(store);
901 
902  /* Set up the columns */
903  renderer = gtk_cell_renderer_text_new();
904  column = gtk_tree_view_column_new_with_attributes(_("Date"), renderer,
905  "text", SPLIT_COL_DATE, NULL);
906  gtk_tree_view_column_set_sort_column_id(column, SPLIT_COL_DATE);
907  tree_view_column_set_default_width (view, column, "31-12-2013");
908  gtk_tree_view_column_set_cell_data_func (column, renderer,
909  (GtkTreeCellDataFunc) print_date,
910  GINT_TO_POINTER (SPLIT_COL_DATE), NULL);
911  gtk_tree_view_append_column(view, column);
912 
913  renderer = gtk_cell_renderer_text_new();
914  column = gtk_tree_view_column_new_with_attributes(_("Num"), renderer,
915  "text", SPLIT_COL_NUM, NULL);
916  gtk_tree_view_column_set_sort_column_id(column, SPLIT_COL_NUM);
917  gtk_tree_view_append_column(view, column);
918 
919  renderer = gtk_cell_renderer_text_new();
920  column = gtk_tree_view_column_new_with_attributes(_("Description"), renderer,
921  "text", SPLIT_COL_DESCRIPTION, NULL);
922  gtk_tree_view_column_set_sort_column_id(column, SPLIT_COL_DESCRIPTION);
923  gtk_tree_view_append_column(view, column);
924 
925  renderer = gtk_cell_renderer_text_new();
926  column = gtk_tree_view_column_new_with_attributes(_("Amount"), renderer,
927  "text", SPLIT_COL_AMOUNT, NULL);
928  gtk_tree_view_column_set_sort_column_id(column, SPLIT_COL_AMOUNT);
929  gtk_tree_view_append_column(view, column);
930 
931  renderer = gtk_cell_renderer_text_new();
932  column = gtk_tree_view_column_new_with_attributes(_("Value"), renderer,
933  "text", SPLIT_COL_VALUE, NULL);
934  gtk_tree_view_column_set_sort_column_id(column, SPLIT_COL_VALUE);
935  gtk_tree_view_append_column(view, column);
936 
937  renderer = gtk_cell_renderer_text_new();
938  column = gtk_tree_view_column_new_with_attributes(_("Gain/Loss"), renderer,
939  "text", SPLIT_COL_GAIN_LOSS, NULL);
940  gtk_tree_view_column_set_sort_column_id(column, SPLIT_COL_GAIN_LOSS);
941  gtk_tree_view_append_column(view, column);
942 
943  renderer = gtk_cell_renderer_text_new();
944  column = gtk_tree_view_column_new_with_attributes(_("Balance"), renderer,
945  "text", SPLIT_COL_BALANCE, NULL);
946  gtk_tree_view_column_set_sort_column_id(column, SPLIT_COL_BALANCE);
947  gtk_tree_view_append_column(view, column);
948 
949  /* Set up the selection callbacks */
950  selection = gtk_tree_view_get_selection(view);
951  g_signal_connect(selection, "changed",
952  G_CALLBACK(lv_split_selection_changed_cb), lv);
953 
954  return store;
955 }
956 
957 static void
958 lv_init_split_views (GNCLotViewer *lv)
959 {
960  lv->split_free_store = lv_init_split_view (lv, lv->split_free_view);
961  lv->split_in_lot_store = lv_init_split_view (lv, lv->split_in_lot_view);
962 }
963 
964 static void
965 lv_init_split_buttons (GNCLotViewer *lv)
966 {
967  /* Set up the add/remove callbacks */
968  g_signal_connect(G_OBJECT(lv->add_split_to_lot_button), "clicked",
969  G_CALLBACK(lv_add_split_to_lot_cb), lv);
970  g_signal_connect(G_OBJECT(lv->remove_split_from_lot_button), "clicked",
971  G_CALLBACK(lv_remove_split_from_lot_cb), lv);
972 }
973 
974 /* ======================================================================== */
975 
976 static void
977 lv_create (GNCLotViewer *lv)
978 {
979  gchar *win_title;
980  gint position;
981  GtkBuilder *builder;
982  GtkWidget *widget;
983 
984  builder = gtk_builder_new();
985  gnc_builder_add_from_file (builder, "dialog-lot-viewer.glade", "Lot Viewer Window");
986 
987  lv->window = GTK_WIDGET(gtk_builder_get_object (builder, "Lot Viewer Window"));
988 
989  win_title = g_strdup_printf (_("Lots in Account %s"),
990  xaccAccountGetName(lv->account));
991  gtk_window_set_title (GTK_WINDOW (lv->window), win_title);
992  g_free (win_title);
993 
994 #ifdef LOTS_READY_FOR_SHOWTIME
995  lv->regview_button = GTK_BUTTON(glade_xml_get_widget (builder, "regview button"));
996 #endif
997  lv->delete_button = GTK_BUTTON(gtk_builder_get_object (builder, "delete button"));
998  lv->scrub_lot_button = GTK_BUTTON(gtk_builder_get_object (builder, "scrub lot button"));
999  lv->new_lot_button = GTK_BUTTON(gtk_builder_get_object (builder, "new lot button"));
1000 
1001  lv->lot_view = GTK_TREE_VIEW(gtk_builder_get_object (builder, "lot view"));
1002  lv->only_show_open_lots_checkbutton = GTK_TOGGLE_BUTTON(gtk_builder_get_object (builder, "only show open lots checkbutton"));
1003  lv_init_lot_view(lv);
1004  lv->lot_notes = GTK_TEXT_VIEW(gtk_builder_get_object (builder, "lot notes text"));
1005  lv->title_entry = GTK_ENTRY (gtk_builder_get_object (builder, "lot title entry"));
1006 
1007  lv->split_in_lot_view = GTK_TREE_VIEW(gtk_builder_get_object (builder, "split in lot view"));
1008  lv->split_free_view = GTK_TREE_VIEW(gtk_builder_get_object (builder, "split free view"));
1009  lv_init_split_views(lv);
1010 
1011  lv->add_split_to_lot_button = GTK_BUTTON(gtk_builder_get_object (builder, "add split to lot button"));
1012  lv->remove_split_from_lot_button = GTK_BUTTON(gtk_builder_get_object (builder, "remove split from lot button"));
1013  lv_init_split_buttons(lv);
1014 
1015 
1016  if (gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_SAVE_GEOMETRY))
1017  {
1018  GObject *object;
1019  object = gtk_builder_get_object (builder, "lot vpaned");
1020  gnc_prefs_bind (GNC_PREFS_GROUP, GNC_PREF_VPOS, object, "position");
1021 
1022  object = gtk_builder_get_object (builder, "lot hpaned");
1023  gnc_prefs_bind (GNC_PREFS_GROUP, GNC_PREF_HPOS, object, "position");
1024  }
1025 
1026  lv->selected_lot = NULL;
1027 
1028  /* Setup signals */
1029  gtk_builder_connect_signals(builder, lv);
1030  g_object_unref(G_OBJECT(builder));
1031 
1032  lv_update_split_buttons(lv);
1033 
1034  gnc_restore_window_size(GNC_PREFS_GROUP, GTK_WINDOW(lv->window));
1035 }
1036 
1037 /* ======================================================================== */
1038 
1039 GNCLotViewer *
1040 gnc_lot_viewer_dialog (Account *account)
1041 {
1042  GNCLotViewer *lv;
1043  gint component_id;
1044 
1045  if (!account) return NULL;
1046 
1047  lv = g_new0 (GNCLotViewer, 1);
1048  lv->account = account;
1049  lv_create (lv);
1050  gnc_lot_viewer_fill (lv);
1051  lv_show_splits_free (lv);
1052 
1053  component_id = gnc_register_gui_component (LOT_VIEWER_CM_CLASS,
1054  lv_refresh_handler,
1055  lv_close_handler,
1056  lv);
1057 
1058  gnc_gui_component_watch_entity_type (component_id,
1059  GNC_ID_LOT,
1060  QOF_EVENT_CREATE | QOF_EVENT_ADD | QOF_EVENT_REMOVE | QOF_EVENT_MODIFY | QOF_EVENT_DESTROY);
1061 
1062  gtk_widget_show_all (lv->window);
1063  gnc_window_adjust_for_screen (GTK_WINDOW(lv->window));
1064 
1065  return lv;
1066 }
1067 
1068 /* ============================ END OF FILE =============================== */
High-Level API for imposing Lot constraints.
void tree_view_column_set_default_width(GtkTreeView *view, GtkTreeViewColumn *column, const gchar *sizing_text)
GList LotList
Definition: gnc-engine.h:201
time64 xaccTransGetDate(const Transaction *trans)
Definition: Transaction.c:2215
SplitList * xaccAccountGetSplitList(const Account *acc)
Definition: Account.c:3717
utility functions for the GnuCash UI
GNCAccountType xaccAccountGetType(const Account *acc)
Definition: Account.c:3009
void gncScrubBusinessAccountLots(Account *acc)
gnc_numeric gnc_numeric_neg(gnc_numeric a)
gboolean gnc_commodity_equal(const gnc_commodity *a, const gnc_commodity *b)
void gnc_lot_add_split(GNCLot *lot, Split *split)
Definition: gnc-lot.c:569
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
gboolean gnc_numeric_zero_p(gnc_numeric a)
void xaccAccountInsertLot(Account *acc, GNCLot *lot)
Definition: Account.c:1920
Transaction * xaccSplitGetParent(const Split *split)
Definition: Split.c:1903
gnc_numeric xaccSplitGetCapGains(Split *split)
Definition: cap-gains.c:911
void xaccAccountScrubLots(Account *acc)
Definition: Scrub3.c:159
Cleanup functions for business objects.
Split * gnc_lot_get_earliest_split(GNCLot *lot)
Definition: gnc-lot.c:648
GncInvoice * gncInvoiceGetInvoiceFromTxn(const Transaction *txn)
Definition: gncInvoice.c:1203
const char * gnc_lot_get_title(const GNCLot *lot)
Definition: gnc-lot.c:437
Split * gnc_lot_get_latest_split(GNCLot *lot)
Definition: gnc-lot.c:660
char * qof_print_date(time64 secs)
GList SplitList
Definition: gnc-engine.h:203
Account handling public routines.
void gnc_prefs_bind(const gchar *group, const gchar *pref_name, gpointer object, const gchar *property)
Definition: gnc-prefs.c:186
SplitList * gnc_lot_get_split_list(const GNCLot *lot)
Definition: gnc-lot.c:417
LotList * xaccAccountGetLotList(const Account *acc)
Definition: Account.c:3744
const char * xaccTransGetDescription(const Transaction *trans)
Definition: Transaction.c:2184
gboolean xaccAccountIsAPARType(GNCAccountType t)
Definition: Account.c:4225
int xaccSPrintAmount(char *bufp, gnc_numeric val, GNCPrintAmountInfo info)
Definition: gnc-ui-util.c:1437
Generic api to store and retrieve preferences.
GncInvoice * gncInvoiceGetInvoiceFromLot(GNCLot *lot)
Definition: gncInvoice.c:1174
Business Invoice Interface.
GNCLot * gnc_lot_make_default(Account *acc)
Definition: gnc-lot.c:755
gboolean gnc_lot_is_closed(GNCLot *lot)
Definition: gnc-lot.c:376
Definition: SplitP.h:71
gnc_numeric xaccSplitGetValue(const Split *split)
Definition: Split.c:1993
gboolean gncScrubBusinessLot(GNCLot *lot)
void xaccAccountBeginEdit(Account *acc)
Definition: Account.c:1280
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
Definition: Transaction.c:1348
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Definition: gnc-prefs.c:196
gboolean xaccScrubLot(GNCLot *lot)
Definition: Scrub3.c:85
gint64 time64
Definition: gnc-date.h:83
Account * gnc_lot_get_account(const GNCLot *lot)
Definition: gnc-lot.c:386
const char * xaccAccountGetName(const Account *acc)
Definition: Account.c:3031
#define GNC_DENOM_AUTO
Definition: gnc-numeric.h:246
API for Transactions and Splits (journal entries)
void xaccAccountCommitEdit(Account *acc)
Definition: Account.c:1321
Utilities to Automatically Compute Capital Gains/Losses.
Commodity handling public routines.
gnc_numeric gnc_lot_get_balance(GNCLot *lot)
Definition: gnc-lot.c:477
GNCLot * xaccSplitGetLot(const Split *split)
Definition: Split.c:1953
gnc_numeric xaccSplitGetAmount(const Split *split)
Definition: Split.c:1987