1 /********************************************************************\
2  * gnc-tree-view-split-reg.c -- GtkTreeView implementation to       *
3  *                     display registers   in a GtkTreeView.        *
4  *                                                                  *
5  * Copyright (C) 2006-2007 Chris Shoemaker <c.shoemaker@cox.net>    *
6  * Copyright (C) 2012 Robert Fewell                                 *
7  *                                                                  *
8  * This program is free software; you can redistribute it and/or    *
9  * modify it under the terms of the GNU General Public License as   *
10  * published by the Free Software Foundation; either version 2 of   *
11  * the License, or (at your option) any later version.              *
12  *                                                                  *
13  * This program is distributed in the hope that it will be useful,  *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
16  * GNU General Public License for more details.                     *
17  *                                                                  *
18  * You should have received a copy of the GNU General Public License*
19  * along with this program; if not, contact:                        *
20  *                                                                  *
21  * Free Software Foundation           Voice:  +1-617-542-5942       *
22  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
23  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
24  *                                                                  *
25 \********************************************************************/
26 
27 #include <config.h>
28 
29 #include <gtk/gtk.h>
30 #include <glib/gi18n.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <gdk/gdkkeysyms.h>
34 
35 #include "gnc-tree-view.h"
36 #include "gnc-tree-view-split-reg.h"
37 #include "gnc-tree-model-split-reg.h"
38 #include "gnc-tree-control-split-reg.h"
39 #include "gnc-tree-util-split-reg.h"
40 #include "gnc-ui.h"
41 #include "gnc-warnings.h"
42 #include "dialog-utils.h"
43 #include "gnc-prefs.h"
44 #include "Transaction.h"
45 #include "engine-helpers.h"
46 #include "Scrub.h"
47 #include "gnc-exp-parser.h"
48 #include "SchedXaction.h"
49 
50 #include "gnc-amount-edit.h"
51 
52 
53 /* Signal codes */
54 enum
55 {
56     UPDATE_SIGNAL,
57     HELP_SIGNAL,
58     LAST_SIGNAL
59 };
60 
61 typedef enum {
62     RESET,  //0
63     ACCEPT, //1
64     DISCARD,//2
65     CANCEL  //3
66 }TransConfirm;
67 
68 
69 /** Static Globals *******************************************************/
70 static QofLogModule log_module = GNC_MOD_LEDGER;
71 
72 static void gnc_tree_view_split_reg_class_init (GncTreeViewSplitRegClass *klass);
73 static void gnc_tree_view_split_reg_init (GncTreeViewSplitReg *view);
74 static void gnc_tree_view_split_reg_dispose (GObject *object);
75 static void gnc_tree_view_split_reg_finalize (GObject *object);
76 
77 static guint gnc_tree_view_split_reg_signals[LAST_SIGNAL] = {0};
78 
79 static void gnc_tree_view_split_reg_pref_changed (gpointer prefs, gchar *pref, gpointer user_data);
80 
81 static void gtv_sr_cdf0 (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *s_model,
82 				GtkTreeIter *s_iter, gpointer user_data);
83 
84 static void gtv_sr_cdf1 (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *s_model,
85 				GtkTreeIter *s_iter, gpointer user_data);
86 
87 static void gtv_sr_control_cdf0 (GtkTreeViewColumn *col, GtkCellRenderer *renderer,
88                                  GtkTreeModel *s_model, GtkTreeIter *s_iter, gpointer user_data);
89 
90 static void gtv_sr_titles (GncTreeViewSplitReg *view, RowDepth depth);
91 
92 static void gtv_sr_edited_cb (GtkCellRendererText *cell, const gchar *path_string,
93 				const gchar *new_text, gpointer user_data);
94 
95 static void gtv_sr_edited_normal_cb (GtkCellRendererText *cell, const gchar *path_string,
96                                 const gchar *new_text, gpointer user_data);
97 
98 static void gtv_sr_edited_template_cb (GtkCellRendererText *cell, const gchar *path_string,
99                                 const gchar *new_text, gpointer user_data);
100 
101 //static void start_edit (GtkCellRenderer *cr, GtkCellEditable *editable,
102 //				const gchar *path, gpointer user_data); //FIXME This may not be needed
103 
104 static void gtv_sr_begin_edit (GncTreeViewSplitReg *view, Transaction *trans);
105 
106 static void gtv_sr_finish_edit (GncTreeViewSplitReg *view);
107 
108 static void gtv_sr_editable_start_editing_cb (GtkCellRenderer *cr, GtkCellEditable *editable,
109 				const gchar *path, gpointer user_data);
110 
111 static void gtv_sr_editing_canceled_cb (GtkCellRenderer *cr, gpointer user_data);
112 
113 //static void gtv_sr_match_selected_cb (GtkEntryCompletion *widget, GtkTreeModel *model,
114 //                        GtkTreeIter *iter, gpointer user_data); //FIXME This may not be needed
115 
116 //static void gtv_sr_changed_cb (GtkCellRendererCombo *widget, gchar *path_string,
117 //                        GtkTreeIter *iter, gpointer user_data); //FIXME This may not be needed
118 
119 static void gtv_sr_selection_move_delete_cb (GncTreeModelSplitReg *model, gpointer item, gpointer user_data);
120 
121 static gboolean gtv_sr_key_press_cb (GtkWidget *widget, GdkEventKey *event, gpointer user_data);
122 
123 static gboolean gtv_sr_ed_key_press_cb (GtkWidget *widget, GdkEventKey *event, gpointer user_data);
124 
125 static gboolean gtv_sr_button_cb (GtkWidget *widget, GdkEventButton *event, gpointer user_data);
126 
127 static gboolean gtv_sr_focus_out_cb (GtkWidget *widget, GdkEventFocus *event, gpointer user_data);
128 
129 static void gtv_sr_motion_cb (GtkTreeSelection *sel, gpointer user_data);
130 
131 static void gtv_sr_refresh_view_cb (GncTreeModelSplitReg *model, gpointer user_data);
132 
133 static gboolean gtv_sr_transaction_changed_confirm (GncTreeViewSplitReg *view, Transaction *new_trans);
134 
135 
136 typedef struct {
137     ViewCol viewcol;
138     gint modelcol;
139     gchar *title;
140     gchar *pref_name;
141     gchar *sizer;
142     int visibility_model_col;
143     int always_visible_col;
144     void (*edited_cb)(GtkCellRendererText *, const gchar *,
145                       const gchar *, gpointer);
146     void (*editing_started_cb)(GtkCellRenderer *, GtkCellEditable *,
147                                const gchar *, gpointer);
148     GtkTreeIterCompareFunc sort_fn;
149 } ColDef;
150 
151 
152 static ColDef all_tree_view_split_reg_columns[] = {
153     {COL_DATE, GNC_TREE_MODEL_SPLIT_REG_COL_DATE,
154      "Date", "date", "00/00/0000",
155      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS, 1,
156      gtv_sr_edited_cb, gtv_sr_editable_start_editing_cb,
157      gnc_tree_model_split_reg_sort_iter_compare_func},
158 
159     {COL_DUEDATE, -1,
160      "Due Date", "duedate", "00/00/0000",
161      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS, 1,
162      gtv_sr_edited_cb, gtv_sr_editable_start_editing_cb, NULL},
163 
164     {COL_NUMACT, GNC_TREE_MODEL_SPLIT_REG_COL_NUMACT,
165      "Num / Act / Act", "numact", "0000",
166      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS, 0,
167      gtv_sr_edited_cb, gtv_sr_editable_start_editing_cb,
168      gnc_tree_model_split_reg_sort_iter_compare_func},
169 
170     {COL_DESCNOTES, GNC_TREE_MODEL_SPLIT_REG_COL_DESCNOTES,
171      "Description / Notes / Memo", "descnotes", "xxxxxxxxxxxxxxxxxxx",
172      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS, 1,
173      gtv_sr_edited_cb, gtv_sr_editable_start_editing_cb,
174      gnc_tree_model_split_reg_sort_iter_compare_func},
175 
176     {COL_TRANSFERVOID, -1,
177      "Transfer / Void", "transfervoid", "xxxxxxxxxxxxxxxxxxx",
178      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS, 1,
179      gtv_sr_edited_cb, gtv_sr_editable_start_editing_cb, NULL},
180 
181     {COL_RECN, GNC_TREE_MODEL_SPLIT_REG_COL_RECN,
182      "R", "recn", "xx",
183      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS, 0,
184      gtv_sr_edited_cb, gtv_sr_editable_start_editing_cb,
185      gnc_tree_model_split_reg_sort_iter_compare_func},
186 
187     {COL_TYPE, -1,
188      "Type", "type", "xx",
189      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS, 0,
190      gtv_sr_edited_cb, gtv_sr_editable_start_editing_cb, NULL},
191 
192     {COL_VALUE, -1,
193      "Value", "value", "00000",
194      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS, 0,
195      gtv_sr_edited_cb, gtv_sr_editable_start_editing_cb, NULL},
196 
197     {COL_AMOUNT, -1,
198      "Amount", "amount", "00000",
199      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS, 0,
200      gtv_sr_edited_cb, gtv_sr_editable_start_editing_cb, NULL},
201 
202     {COL_AMTVAL, -1,
203      "Amount / Value", "amtval", "00000",
204      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS, 1,
205      gtv_sr_edited_cb, gtv_sr_editable_start_editing_cb, NULL},
206 
207     {COL_RATE, -1,
208      "Rate", "rate", "00000",
209      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS, 0,
210      gtv_sr_edited_cb, gtv_sr_editable_start_editing_cb, NULL},
211 
212     {COL_PRICE, -1,
213      "Price", "price", "00000",
214      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS, 1,
215      gtv_sr_edited_cb, gtv_sr_editable_start_editing_cb, NULL},
216 
217     {COL_DEBIT, GNC_TREE_MODEL_SPLIT_REG_COL_DEBIT,
218      "Debit", "debit", "00000",
219      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS, 1,
220      gtv_sr_edited_cb, gtv_sr_editable_start_editing_cb,
221      gnc_tree_model_split_reg_sort_iter_compare_func},
222 
223     {COL_CREDIT, GNC_TREE_MODEL_SPLIT_REG_COL_CREDIT,
224      "Credit", "credit", "00000",
225      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS, 1,
226      gtv_sr_edited_cb, gtv_sr_editable_start_editing_cb,
227      gnc_tree_model_split_reg_sort_iter_compare_func},
228 
229     {COL_BALANCE, -1,
230      "Balance", "balance", "00000",
231      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS, 0,
232      NULL, NULL, NULL},
233 
234     {COL_STATUS, -1,
235      " ", "status", "x",
236      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS, 1,
237      NULL, NULL, NULL},
238 
239     {COL_COMM, -1,
240      "Commodity", "commodity", "xxxxxxxx",
241      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS, 0,
242      NULL, NULL, NULL},
243 };
244 
245 
246 struct GncTreeViewSplitRegPrivate
247 {
248     gboolean             disposed;
249 
250     Account             *anchor;              // The register default Account
251     gnc_commodity       *reg_comm;            // The register commodity (which may be a non-currency)
252     gnc_commodity       *reg_currency;        // The currency for txns in this register (guaranteed to be a currency)
253 
254     Transaction         *current_trans;       // The current highlighted transaction
255     Split               *current_split;       // The current highlighted split
256     RowDepth             current_depth;       // The current depth 1=TROW1, 2=TROW2, 3=SPLIT3
257     GtkTreeRowReference *current_ref;         // The current model path reference
258 
259     Transaction         *dirty_trans;         // Set when transaction is changed
260     TransConfirm         trans_confirm;       // This is the return value for gtv_sr_transaction_changed_confirm
261 
262     GtkCellRenderer     *temp_cr;             // Pointer to Temp Cell Renderer
263     gulong               fo_handler_id;       // Focus out callback id
264 
265     gboolean             acct_short_names;    // Use account short names
266     gboolean             double_line;         // Use double line mode
267     gboolean             expanded;            // Are we expanded to splits
268     gboolean             auto_complete;       // Whether auto complete has run
269     gboolean             negative_in_red;     // Display negative numbers in red
270     gboolean             use_horizontal_lines;// Draw horizontal lines
271     gboolean             use_vertical_lines;  // Draw vertical lines
272 
273     gboolean             show_calendar_buttons;        // Show the calendar buttons
274     gboolean             show_extra_dates_on_selection;// Show the above on the selected transaction
275     gboolean             selection_to_blank_on_expand; // Move the selection to the blank split on expand
276 
277     gint                 key_length;                   // The number of characters before auto complete starts.
278     gint                 single_button_press;          // Capture single button press.
279 
280     gchar               *transfer_string;              // The transfer account string.
281     gboolean             stop_cell_move;               // Stops the cursor moving to a different cell.
282 
283 };
284 
285 /* Define some cell colors */
286 #define PINKCELL "#F8BEC6"
287 #define REDCELL "#F34943"
288 #define BLUECELL "#1D80DF"
289 #define BLACKCELL "#CBCBD2"
290 #define YELLOWCELL "#FFEF98"
291 #define ORANGECELL "#F39536"
292 
293 
294 #define GNC_PREF_SHOW_EXTRA_DATES        "show-extra-dates"
295 #define GNC_PREF_SHOW_EXTRA_DATES_ON_SEL "show-extra-dates-on-selection"
296 #define GNC_PREF_SHOW_CAL_BUTTONS        "show-calendar-buttons"
297 #define GNC_PREF_SEL_TO_BLANK_ON_EXPAND  "selection-to-blank-on-expand"
298 #define GNC_PREF_KEY_LENGTH              "key-length"
299 
300 /* This could be a preference setting, show currency / commodity symbols */
301 #define SHOW_SYMBOL FALSE
302 
303 #define GNC_TREE_VIEW_SPLIT_REG_GET_PRIVATE(o)  \
304    ((GncTreeViewSplitRegPrivate*)g_type_instance_get_private((GTypeInstance*)o, GNC_TYPE_TREE_VIEW_SPLIT_REG))
305 
306 static GObjectClass *parent_class = NULL;
307 
G_DEFINE_TYPE_WITH_PRIVATE(GncTreeViewSplitReg,gnc_tree_view_split_reg,GNC_TYPE_TREE_VIEW)308 G_DEFINE_TYPE_WITH_PRIVATE(GncTreeViewSplitReg, gnc_tree_view_split_reg, GNC_TYPE_TREE_VIEW)
309 
310 static void
311 gnc_tree_view_split_reg_class_init (GncTreeViewSplitRegClass *klass)
312 {
313     GObjectClass *o_class;
314 
315     parent_class = g_type_class_peek_parent (klass);
316 
317     o_class = G_OBJECT_CLASS (klass);
318 
319     o_class->dispose =  gnc_tree_view_split_reg_dispose;
320     o_class->finalize = gnc_tree_view_split_reg_finalize;
321 
322     gnc_tree_view_split_reg_signals[UPDATE_SIGNAL] =
323         g_signal_new("update_signal",
324                      G_TYPE_FROM_CLASS (o_class),
325                      G_SIGNAL_RUN_LAST,
326                      G_STRUCT_OFFSET (GncTreeViewSplitRegClass, update_signal),
327                      NULL, NULL,
328                      g_cclosure_marshal_VOID__VOID,
329                      G_TYPE_NONE, 0);
330 
331     gnc_tree_view_split_reg_signals[HELP_SIGNAL] =
332         g_signal_new("help_signal",
333                      G_TYPE_FROM_CLASS (o_class),
334                      G_SIGNAL_RUN_LAST,
335                      G_STRUCT_OFFSET (GncTreeViewSplitRegClass, help_signal),
336                      NULL, NULL,
337                      g_cclosure_marshal_VOID__VOID,
338                      G_TYPE_NONE, 0);
339 
340     klass->update_signal = NULL;
341     klass->help_signal = NULL;
342 }
343 
344 /*****************************************************************************/
345 
346 /* Return the tree model from the tree view */
347 GncTreeModelSplitReg *
gnc_tree_view_split_reg_get_model_from_view(GncTreeViewSplitReg * view)348 gnc_tree_view_split_reg_get_model_from_view (GncTreeViewSplitReg *view)
349 {
350     GtkTreeModelSort *s_model = GTK_TREE_MODEL_SORT (gtk_tree_view_get_model (GTK_TREE_VIEW (view)));
351     return GNC_TREE_MODEL_SPLIT_REG (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (s_model)));
352 }
353 
354 /* Get the model iter from the view path string */
355 static gboolean
gtv_sr_get_model_iter_from_view_string(GncTreeViewSplitReg * view,const gchar * path_string,GtkTreeIter * m_iter)356 gtv_sr_get_model_iter_from_view_string (GncTreeViewSplitReg *view,
357                                 const gchar *path_string, GtkTreeIter *m_iter)
358 {
359     GtkTreeModel *s_model;
360     GtkTreeIter s_iter;
361 
362     s_model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
363 
364     if (!gtk_tree_model_get_iter_from_string (s_model, &s_iter, path_string))
365     {
366         m_iter = NULL;
367         return FALSE;
368     }
369     gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (s_model), m_iter, &s_iter);
370     return TRUE;
371 }
372 
373 /* Get the model iter from the selection */
374 static gboolean
gtv_sr_get_model_iter_from_selection(GncTreeViewSplitReg * view,GtkTreeSelection * sel,GtkTreeIter * m_iter)375 gtv_sr_get_model_iter_from_selection (GncTreeViewSplitReg *view,
376                               GtkTreeSelection *sel, GtkTreeIter *m_iter)
377 {
378     GtkTreeModel *s_model;
379     GtkTreeIter s_iter;
380 
381     if (gtk_tree_selection_get_selected (sel, &s_model, &s_iter))
382     {
383         gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (s_model), m_iter, &s_iter);
384         return TRUE;
385     }
386     return FALSE;
387 }
388 
389 /* Get sort model path from the model path
390  *
391  * Return A newly allocated GtkTreePath, or NULL */
392 GtkTreePath *
gnc_tree_view_split_reg_get_sort_path_from_model_path(GncTreeViewSplitReg * view,GtkTreePath * mpath)393 gnc_tree_view_split_reg_get_sort_path_from_model_path (GncTreeViewSplitReg *view, GtkTreePath *mpath)
394 {
395     GtkTreeModel *s_model;
396     GtkTreePath *spath;
397 
398     g_return_val_if_fail (mpath, NULL);
399     s_model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
400     spath = gtk_tree_model_sort_convert_child_path_to_path (GTK_TREE_MODEL_SORT (s_model), mpath);
401     if (!spath)
402     {
403         /* No parent path available */
404         return NULL;
405     }
406     return spath;
407 }
408 
409 /* Get model path from the sort model path
410  *
411  * Return A newly allocated GtkTreePath, or NULL. */
412 GtkTreePath *
gnc_tree_view_split_reg_get_model_path_from_sort_path(GncTreeViewSplitReg * view,GtkTreePath * spath)413 gnc_tree_view_split_reg_get_model_path_from_sort_path (GncTreeViewSplitReg *view, GtkTreePath *spath)
414 {
415     GtkTreeModel *s_model;
416     GtkTreePath *mpath;
417 
418     g_return_val_if_fail (spath, NULL);
419     s_model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
420     mpath = gtk_tree_model_sort_convert_path_to_child_path (GTK_TREE_MODEL_SORT (s_model), spath);
421     if (!mpath)
422     {
423         /* No child path available */
424         return NULL;
425     }
426     return mpath;
427 }
428 
429 /*****************************************************************************/
430 
431 static void
gnc_tree_view_split_reg_init(GncTreeViewSplitReg * view)432 gnc_tree_view_split_reg_init (GncTreeViewSplitReg *view)
433 {
434     view->priv = g_new0 (GncTreeViewSplitRegPrivate, 1);
435 
436     view->priv->current_trans = NULL;
437     view->priv->current_split = NULL;
438     view->priv->current_depth = 0;
439     view->reg_closing = FALSE;
440     view->priv->fo_handler_id = 0;
441     view->priv->auto_complete = FALSE;
442     view->priv->trans_confirm = RESET;
443     view->priv->single_button_press = 0;
444 
445     view->priv->transfer_string = g_strdup ("Dummy");
446     view->priv->stop_cell_move = FALSE;
447 
448     view->priv->show_calendar_buttons = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL_REGISTER, GNC_PREF_SHOW_CAL_BUTTONS);
449     view->show_extra_dates = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL_REGISTER, GNC_PREF_SHOW_EXTRA_DATES);
450     view->priv->show_extra_dates_on_selection = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL_REGISTER, GNC_PREF_SHOW_EXTRA_DATES_ON_SEL);
451     view->priv->selection_to_blank_on_expand = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL_REGISTER, GNC_PREF_SEL_TO_BLANK_ON_EXPAND);
452     view->priv->key_length = gnc_prefs_get_float (GNC_PREFS_GROUP_GENERAL_REGISTER, GNC_PREF_KEY_LENGTH);
453 
454     view->priv->acct_short_names = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL_REGISTER, GNC_PREF_SHOW_LEAF_ACCT_NAMES);
455     view->priv->negative_in_red = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_NEGATIVE_IN_RED);
456     view->priv->use_horizontal_lines = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL_REGISTER,
457                                                            GNC_PREF_DRAW_HOR_LINES);
458 
459     view->priv->use_vertical_lines = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL_REGISTER,
460                                                          GNC_PREF_DRAW_VERT_LINES);
461 
462     gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL_REGISTER,
463                            GNC_PREF_DRAW_HOR_LINES,
464                            gnc_tree_view_split_reg_pref_changed,
465                            view);
466     gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL_REGISTER,
467                            GNC_PREF_DRAW_VERT_LINES,
468                            gnc_tree_view_split_reg_pref_changed,
469                            view);
470 }
471 
472 
473 static void
gnc_tree_view_split_reg_dispose(GObject * object)474 gnc_tree_view_split_reg_dispose (GObject *object)
475 {
476     GncTreeViewSplitReg *view;
477     GncTreeViewSplitRegPrivate *priv;
478 
479     gnc_leave_return_if_fail (object != NULL);
480     gnc_leave_return_if_fail (GNC_IS_TREE_VIEW_SPLIT_REG (object));
481 
482     view = GNC_TREE_VIEW_SPLIT_REG (object);
483     priv = GNC_TREE_VIEW_SPLIT_REG_GET_PRIVATE (view);
484 
485     if (priv->disposed)
486         return;
487 
488     ENTER("split reg view %p", object);
489 
490     priv->disposed = TRUE;
491 
492     if(view->priv->current_ref != NULL)
493     {
494         gtk_tree_row_reference_free (view->priv->current_ref);
495         view->priv->current_ref = NULL;
496     }
497 
498     if (view->help_text)
499         g_free (view->help_text);
500 
501     if (view->priv->transfer_string)
502         g_free (view->priv->transfer_string);
503 
504     gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL_REGISTER,
505                                  GNC_PREF_DRAW_HOR_LINES,
506                                  gnc_tree_view_split_reg_pref_changed,
507                                  view);
508     gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL_REGISTER,
509                                  GNC_PREF_DRAW_VERT_LINES,
510                                  gnc_tree_view_split_reg_pref_changed,
511                                  view);
512 
513     if (G_OBJECT_CLASS (parent_class)->dispose)
514         (* G_OBJECT_CLASS (parent_class)->dispose) (object);
515 
516     LEAVE(" ");
517 }
518 
519 
520 static void
gnc_tree_view_split_reg_finalize(GObject * object)521 gnc_tree_view_split_reg_finalize (GObject *object)
522 {
523     gnc_leave_return_if_fail(object != NULL);
524     gnc_leave_return_if_fail(GNC_IS_TREE_VIEW_SPLIT_REG (object));
525 
526     ENTER("split reg view %p", object);
527 
528     if (G_OBJECT_CLASS(parent_class)->finalize)
529         (* G_OBJECT_CLASS(parent_class)->finalize) (object);
530 
531     LEAVE(" ");
532 }
533 
534 
535 /* Update internal settings based on preferences */
536 void
gnc_tree_view_split_reg_refresh_from_prefs(GncTreeViewSplitReg * view)537 gnc_tree_view_split_reg_refresh_from_prefs (GncTreeViewSplitReg *view)
538 {
539     GncTreeModelSplitReg *model;
540 
541     model = gnc_tree_view_split_reg_get_model_from_view (view);
542 
543     model->use_gnc_color_theme = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL_REGISTER,
544                                                      GNC_PREF_USE_GNUCASH_COLOR_THEME);
545     model->use_accounting_labels = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL,
546                                                        GNC_PREF_ACCOUNTING_LABELS);
547 
548     model->alt_colors_by_txn = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL_REGISTER,
549                                                    GNC_PREF_ALT_COLOR_BY_TRANS);
550 
551     view->priv->negative_in_red = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL,
552                                                       GNC_PREF_NEGATIVE_IN_RED);
553 }
554 
555 
556 static void
gnc_tree_view_split_reg_pref_changed(gpointer prefs,gchar * pref,gpointer user_data)557 gnc_tree_view_split_reg_pref_changed (gpointer prefs, gchar *pref, gpointer user_data)
558 {
559     GncTreeViewSplitReg *view = user_data;
560 
561     g_return_if_fail (pref);
562 
563     if (view == NULL)
564         return;
565 
566     if (g_str_has_suffix (pref, GNC_PREF_DRAW_HOR_LINES) || g_str_has_suffix (pref, GNC_PREF_DRAW_VERT_LINES))
567     {
568         view->priv->use_horizontal_lines = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL_REGISTER,
569                                                                GNC_PREF_DRAW_HOR_LINES);
570 
571         view->priv->use_vertical_lines = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL_REGISTER,
572                                                              GNC_PREF_DRAW_VERT_LINES);
573 
574         if (view->priv->use_horizontal_lines)
575         {
576             if (view->priv->use_vertical_lines)
577                 gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (view), GTK_TREE_VIEW_GRID_LINES_BOTH);
578             else
579                 gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (view), GTK_TREE_VIEW_GRID_LINES_HORIZONTAL);
580         }
581         else if (view->priv->use_vertical_lines)
582             gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (view), GTK_TREE_VIEW_GRID_LINES_VERTICAL);
583         else
584             gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (view), GTK_TREE_VIEW_GRID_LINES_NONE);
585     }
586     else
587     {
588         g_warning("gnc_tree_view_split_reg_pref_changed: Unknown preference %s", pref);
589     }
590 }
591 
592 
593 /* Define which columns are in which views */
594 static ViewCol *
gnc_tree_view_split_reg_get_column_list(GncTreeModelSplitReg * model)595 gnc_tree_view_split_reg_get_column_list (GncTreeModelSplitReg *model)
596 {
597     DEBUG("Model-type is %d", model->type);
598 
599     switch (model->type)
600     {
601     case BANK_REGISTER2:
602     case CASH_REGISTER2:
603     case ASSET_REGISTER2:
604     case CREDIT_REGISTER2:
605     case LIABILITY_REGISTER2:
606     case INCOME_REGISTER2:
607     case EXPENSE_REGISTER2:
608     case EQUITY_REGISTER2:
609     case TRADING_REGISTER2:
610     case INCOME_LEDGER2:
611         {
612         static ViewCol col_list[] = {
613         COL_DATE, COL_NUMACT, COL_DESCNOTES, COL_TRANSFERVOID, COL_RECN,
614         COL_STATUS, COL_DEBIT, COL_CREDIT, COL_BALANCE, -1};
615         return col_list;
616         }
617         break;
618 
619     case GENERAL_JOURNAL2:
620         {
621         static ViewCol col_list[] = {
622         COL_DATE, COL_NUMACT, COL_DESCNOTES, COL_TRANSFERVOID, COL_RECN,
623         COL_STATUS, COL_COMM, COL_VALUE, COL_RATE, COL_AMOUNT, COL_DEBIT, COL_CREDIT, -1};
624         return col_list;
625         }
626         break;
627 
628     case STOCK_REGISTER2:
629     case CURRENCY_REGISTER2:
630         {
631         static ViewCol col_list[] = {
632         COL_DATE, COL_NUMACT, COL_DESCNOTES, COL_TRANSFERVOID, COL_RECN,
633         COL_STATUS, COL_AMTVAL, COL_PRICE, COL_DEBIT, COL_CREDIT, COL_BALANCE, -1};
634         return col_list;
635         }
636         break;
637 
638     case RECEIVABLE_REGISTER2:
639     case PAYABLE_REGISTER2:
640         {
641         static ViewCol col_list[] = {
642         COL_DATE, COL_TYPE, COL_DUEDATE, COL_NUMACT, COL_DESCNOTES, COL_TRANSFERVOID,
643         COL_STATUS, COL_DEBIT, COL_CREDIT, COL_BALANCE, -1};
644         return col_list;
645         }
646 
647      case PORTFOLIO_LEDGER2:
648         {
649         static ViewCol col_list[] = {
650         COL_DATE, COL_NUMACT, COL_DESCNOTES, COL_TRANSFERVOID, COL_RECN,
651         COL_STATUS, COL_AMOUNT, COL_PRICE, COL_DEBIT, COL_CREDIT, -1};
652         return col_list;
653         }
654 
655     case SEARCH_LEDGER2:
656         {
657         static ViewCol col_list[] = {
658         COL_DATE, COL_NUMACT, COL_DESCNOTES, COL_TRANSFERVOID, COL_RECN,
659         COL_STATUS, COL_DEBIT, COL_CREDIT, -1};
660         return col_list;
661         }
662         break;
663 
664     default:
665         {
666         static ViewCol col_list[] = {
667         COL_DATE, COL_NUMACT, COL_DESCNOTES, COL_TRANSFERVOID, COL_RECN,
668 	COL_STATUS,
669         COL_VALUE, COL_AMOUNT, COL_RATE, COL_PRICE, COL_DEBIT, COL_CREDIT,
670         COL_BALANCE, -1};
671         return col_list;
672         }
673     }
674 }
675 
676 
677 /* Creates a treeview with the list of fields */
678 static GncTreeViewSplitReg *
gnc_tree_view_split_reg_set_cols(GncTreeViewSplitReg * view,GncTreeModelSplitReg * model,ViewCol col_list[])679 gnc_tree_view_split_reg_set_cols (GncTreeViewSplitReg *view,
680 				  GncTreeModelSplitReg *model,
681 				  ViewCol col_list[])
682 {
683     int i = 0;
684 
685     while (col_list && col_list[i] != -1) {
686         GList *renderers;
687         GtkCellRenderer *cr0;
688         GtkCellRenderer *cr1;
689         GtkTreeViewColumn *col;
690         ColDef def;
691 
692         int j, ncol = G_N_ELEMENTS (all_tree_view_split_reg_columns);
693 
694         for (j = 0; j < ncol; j++) {
695             if (col_list[i] == all_tree_view_split_reg_columns[j].viewcol) {
696                 def = all_tree_view_split_reg_columns[j];
697                 break;
698             }
699         }
700         if (j == ncol) {
701             PERR("Failed to find column definition.");
702             i++;
703             continue;
704         }
705         if (col_list[i] == COL_TRANSFERVOID) {
706 
707             col = gnc_tree_view_add_combo_column (
708                 GNC_TREE_VIEW (view), def.title, def.pref_name, def.sizer,
709                 def.modelcol, def.visibility_model_col,
710                 GTK_TREE_MODEL (gnc_tree_model_split_reg_get_acct_list (model)), 0, def.sort_fn);
711 
712         } else if (col_list[i] == COL_DATE) {
713             col = gnc_tree_view_add_date_column (
714                 GNC_TREE_VIEW (view), def.title, def.pref_name, NULL, def.sizer,
715                 def.modelcol, def.visibility_model_col, def.sort_fn);
716 
717         } else if (col_list[i] == COL_NUMACT) {
718             col = gnc_tree_view_add_combo_column (
719                 GNC_TREE_VIEW (view), def.title, def.pref_name, def.sizer,
720                 def.modelcol, def.visibility_model_col,
721                 GTK_TREE_MODEL (gnc_tree_model_split_reg_get_action_list (model)), 0, def.sort_fn);
722 
723             // Here we are adding a second renderer, we will use the model to switch between the
724             // two by hiding one so we endup with rows of text or combo renderers.
725             cr1 = gtk_cell_renderer_text_new ();
726             gtk_tree_view_column_pack_start (col, cr1, TRUE);
727             gtk_tree_view_column_add_attribute (col, cr1, "visible", GNC_TREE_MODEL_SPLIT_REG_COL_NUM_VIS);
728 
729             // Set all the same properties as the first renderer.
730             g_object_set (cr1, "xalign", 1.0, NULL);
731             g_object_set_data (G_OBJECT(cr1), "model_column", GINT_TO_POINTER (def.modelcol));
732             g_object_set_data (G_OBJECT(cr1), "column_name", GINT_TO_POINTER (def.pref_name));
733             g_signal_connect (G_OBJECT(cr1), "editing-started", (GCallback) def.editing_started_cb, view);
734             g_signal_connect (G_OBJECT(cr1), "editing-canceled", G_CALLBACK (gtv_sr_editing_canceled_cb), view);
735             g_object_set (G_OBJECT (cr1), "editable", TRUE, NULL);
736             g_signal_connect (G_OBJECT (cr1), "edited", (GCallback) def.edited_cb, view);
737             g_object_set_data (G_OBJECT (cr1), "view_column", GINT_TO_POINTER (def.viewcol));
738             gtk_tree_view_column_set_cell_data_func (col, cr1, gtv_sr_cdf1, view, NULL);
739 
740         } else {
741             col = gnc_tree_view_add_text_column (
742                 GNC_TREE_VIEW (view), def.title, def.pref_name, NULL, def.sizer,
743                 def.modelcol, def.visibility_model_col, def.sort_fn);
744         }
745 
746         g_object_set_data (G_OBJECT (col), DEFAULT_VISIBLE, GINT_TO_POINTER (1));
747         g_object_set_data (G_OBJECT (col), ALWAYS_VISIBLE, GINT_TO_POINTER (def.always_visible_col));
748 
749         // Set the properties for the first renderer.
750         renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (col));
751         cr0 = g_list_nth_data (renderers, 0);
752         g_list_free (renderers);
753 
754         /* Setup cell background color and default alignment */
755         g_object_set (cr0, "xalign", 1.0, NULL);
756 
757         if (col_list[i] == COL_NUMACT)
758             gtk_tree_view_column_add_attribute (col, cr0, "visible", GNC_TREE_MODEL_SPLIT_REG_COL_ACT_VIS);
759 
760         /* Add the full title for status column to the object for menu creation */
761         if (col_list[i] == COL_STATUS)
762             g_object_set_data_full (G_OBJECT(col), REAL_TITLE, g_strdup (_("Status Bar")), g_free);
763 
764         /* This sets the background of the treeview control columns */
765         gnc_tree_view_set_control_column_background (GNC_TREE_VIEW (view), 0, gtv_sr_control_cdf0);
766 
767         if (def.editing_started_cb)
768         {
769             //Store the position of the column in the model
770             g_object_set_data (G_OBJECT (cr0), "model_column", GINT_TO_POINTER (def.modelcol));
771             g_object_set_data (G_OBJECT (cr0), "column_name", GINT_TO_POINTER (def.pref_name));
772             g_signal_connect (G_OBJECT (cr0), "editing-started", (GCallback) def.editing_started_cb, view);
773         }
774 
775         // Connect editing-canceled signal so that edit-cancelled can be set appropriately
776         g_signal_connect (G_OBJECT (cr0), "editing-canceled", G_CALLBACK (gtv_sr_editing_canceled_cb), view);
777 
778         gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_FIXED);
779 
780 //        gtk_tree_view_column_set_min_width (col, -1);
781 
782         // Set Columns to be resizable default.
783         g_object_set (G_OBJECT (col), "resizable", TRUE, NULL);
784 
785         // Allow the columns to be reorderable.
786         g_object_set (G_OBJECT (col), "reorderable", TRUE, NULL);
787 
788         if (def.edited_cb)
789         {
790             g_object_set (G_OBJECT (cr0), "editable", TRUE, NULL);
791             g_signal_connect (G_OBJECT (cr0), "edited", (GCallback) def.edited_cb, view);
792         }
793 
794         g_object_set_data (G_OBJECT (cr0), "view_column", GINT_TO_POINTER (def.viewcol));
795         gtk_tree_view_column_set_cell_data_func (col, cr0, gtv_sr_cdf0, view, NULL);
796 
797         i++;
798     }
799     gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (view)), GTK_SELECTION_BROWSE);
800 
801     g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (view)), "changed", G_CALLBACK (gtv_sr_motion_cb), view);
802 
803     //Add a data-edited property to keep track of transaction edits.
804     g_object_set_data (G_OBJECT (view), "data-edited", GINT_TO_POINTER (FALSE));
805 
806     // This is used to move the selected item if the selected transaction is deleted.
807     g_signal_connect (G_OBJECT (model), "selection_move_delete", G_CALLBACK (gtv_sr_selection_move_delete_cb), view);
808 
809     // This will refresh the view.
810     g_signal_connect (G_OBJECT (model), "refresh_view", G_CALLBACK (gtv_sr_refresh_view_cb), view);
811 
812     // This is for key navigation, tabbing...
813     g_signal_connect (G_OBJECT (view), "key-press-event", G_CALLBACK (gtv_sr_key_press_cb), NULL);
814 
815     // This is for mouse buttons...
816     g_signal_connect (G_OBJECT (view), "button_press_event", G_CALLBACK (gtv_sr_button_cb), NULL);
817 
818     return view;
819 }
820 
821 
822 /* Set up the view */
823 gboolean
gnc_tree_view_split_reg_set_format(GncTreeViewSplitReg * view)824 gnc_tree_view_split_reg_set_format (GncTreeViewSplitReg *view)
825 {
826     GncTreeViewSplitRegPrivate *priv;
827     GncTreeModelSplitReg *model;
828     GtkTreePath *mpath, *spath;
829     gint total_num = 0;
830 
831     ENTER(" #### Set View Format #### ");
832 
833     model = gnc_tree_view_split_reg_get_model_from_view (view);
834 
835     priv = view->priv;
836 
837     total_num = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (model), NULL);
838 
839     mpath = gtk_tree_row_reference_get_path (view->priv->current_ref);
840 
841     spath = gnc_tree_view_split_reg_get_sort_path_from_model_path (view, mpath);
842 
843     priv->expanded = FALSE;
844 
845     {
846         if (model->style == REG2_STYLE_JOURNAL)
847         {
848             gtk_tree_view_expand_all (GTK_TREE_VIEW (view));
849 
850             priv->expanded = TRUE;
851 
852             gtk_tree_path_free (mpath);
853             gtk_tree_path_free (spath);
854 
855             /* This updates the plugin page gui */
856             gnc_tree_view_split_reg_call_uiupdate_cb (view);
857 
858             LEAVE("#### Journal format ####");
859             return (FALSE);
860         }
861 
862         if (!model->use_double_line)
863         {
864             gtk_tree_view_collapse_all (GTK_TREE_VIEW (view));
865 
866             priv->expanded = FALSE;
867 
868             LEAVE("#### Single line format ####");
869         }
870 
871         if (model->use_double_line)
872         {
873             gint index = 0;
874             GtkTreePath *path;
875 
876             path = gtk_tree_path_new_first ();
877             while (index < total_num)
878             {
879                 gtk_tree_view_expand_to_path (GTK_TREE_VIEW (view), path);
880                 gtk_tree_path_down (path);
881                 gtk_tree_view_collapse_row (GTK_TREE_VIEW (view), path);
882                 gtk_tree_path_up (path);
883                 gtk_tree_path_next (path); //Next Transaction
884                 index = index + 1;
885             }
886             gtk_tree_path_free (path);
887             LEAVE("#### Double line format ####");
888         }
889 
890         /* This expands to split from top level auto.. */
891         if ((model->style == REG2_STYLE_AUTO_LEDGER) || (model->style == REG2_STYLE_JOURNAL))
892         {
893             gtk_tree_view_expand_row (GTK_TREE_VIEW (view), spath, TRUE);
894 
895             priv->expanded = TRUE;
896             LEAVE("#### Auto expand line format ####");
897         }
898     }
899 
900     gtk_tree_path_free (mpath);
901     gtk_tree_path_free (spath);
902 
903     /* This updates the plugin page gui */
904     gnc_tree_view_split_reg_call_uiupdate_cb (view);
905 
906     return (FALSE);
907 }
908 
909 
910 /* Set up the view for this transaction, used in transaction discard and cancel */
911 static gboolean
gnc_tree_view_split_reg_format_trans(GncTreeViewSplitReg * view,Transaction * trans)912 gnc_tree_view_split_reg_format_trans (GncTreeViewSplitReg *view, Transaction *trans)
913 {
914     GncTreeViewSplitRegPrivate *priv;
915     GncTreeModelSplitReg *model;
916     GtkTreePath *mpath, *spath;
917 
918     ENTER(" ");
919 
920     model = gnc_tree_view_split_reg_get_model_from_view (view);
921 
922     priv = view->priv;
923 
924     mpath = gnc_tree_model_split_reg_get_path_to_split_and_trans (model, NULL, trans);
925 
926     spath = gnc_tree_view_split_reg_get_sort_path_from_model_path (view, mpath);
927 
928     if ((!model->use_double_line) && (model->style != REG2_STYLE_JOURNAL))
929     {
930         gtk_tree_view_collapse_row (GTK_TREE_VIEW (view), spath);
931         priv->expanded = FALSE;
932         LEAVE("#### Single line transaction format ####");
933     }
934 
935     if ((model->use_double_line) && (model->style != REG2_STYLE_JOURNAL))
936     {
937         gtk_tree_view_expand_to_path (GTK_TREE_VIEW (view), spath);
938         gtk_tree_path_down (spath);
939         gtk_tree_view_collapse_row (GTK_TREE_VIEW (view), spath);
940         gtk_tree_path_up (spath);
941         priv->expanded = FALSE;
942         LEAVE("#### Double line transaction format ####");
943     }
944 
945     /* This expands to split from top level auto.. */
946     if ((model->style == REG2_STYLE_AUTO_LEDGER) || (model->style == REG2_STYLE_JOURNAL))
947     {
948         gtk_tree_view_expand_row (GTK_TREE_VIEW (view), spath, TRUE);
949         priv->expanded = TRUE;
950         LEAVE("#### Auto expand line transaction format ####");
951     }
952 
953     gtk_tree_path_free (mpath);
954     gtk_tree_path_free (spath);
955 
956     /* This updates the plugin page gui */
957     gnc_tree_view_split_reg_call_uiupdate_cb (view);
958 
959     return (FALSE);
960 }
961 
962 
963 /* Callback to update the view after transactions are added or deleted */
964 static void
gtv_sr_refresh_view_cb(GncTreeModelSplitReg * model,gpointer user_data)965 gtv_sr_refresh_view_cb (GncTreeModelSplitReg *model, gpointer user_data)
966 {
967     GncTreeViewSplitReg *view = user_data;
968 
969     gnc_tree_view_split_reg_set_format (view);
970 }
971 
972 
973 /* Create a tree view from a given model */
974 GncTreeViewSplitReg*
gnc_tree_view_split_reg_new_with_model(GncTreeModelSplitReg * model)975 gnc_tree_view_split_reg_new_with_model (GncTreeModelSplitReg *model)
976 {
977     GtkTreeModel        *s_model;
978     GncTreeViewSplitReg *view;
979     GtkTreeSelection    *selection;
980 
981     view = g_object_new (gnc_tree_view_split_reg_get_type(), NULL);
982     g_object_set (view, "name", "gnc-id-split-reg-tree", NULL);
983 
984     view->priv->anchor = gnc_tree_model_split_reg_get_anchor (model);
985     view->priv->reg_comm = xaccAccountGetCommodity (view->priv->anchor);
986     view->priv->reg_currency = gnc_account_or_default_currency (view->priv->anchor, NULL);
987     g_assert (view->priv->reg_currency);
988     g_assert (gnc_commodity_is_currency (view->priv->reg_currency));
989     view->help_text = g_strdup ("Help Text");
990 
991     /* Set the grid lines to be solid */
992     gnc_widget_style_context_add_class (GTK_WIDGET(view), "gnc-class-register2-grid-lines");
993 
994     /* TreeView Grid lines */
995     if (view->priv->use_horizontal_lines)
996     {
997         if (view->priv->use_vertical_lines)
998             gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (view), GTK_TREE_VIEW_GRID_LINES_BOTH);
999         else
1000             gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (view), GTK_TREE_VIEW_GRID_LINES_HORIZONTAL);
1001     }
1002     else if (view->priv->use_vertical_lines)
1003             gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (view), GTK_TREE_VIEW_GRID_LINES_VERTICAL);
1004     else
1005         gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (view), GTK_TREE_VIEW_GRID_LINES_NONE);
1006 
1007     // Set the view to fixed height mode...
1008 //    gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (view), TRUE);
1009 
1010     /* Expanders off */
1011     gtk_tree_view_set_show_expanders (GTK_TREE_VIEW (view), FALSE);
1012 
1013     /* Tree Selection */
1014     selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
1015 
1016     gtk_tree_selection_unselect_all (selection);
1017 
1018     // Setup the sort model
1019     s_model = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (model));
1020 
1021     PINFO("#### After Models are Setup ####");
1022 
1023     /* Set the user_data for the sort callback */
1024     gnc_tree_view_set_sort_user_data (GNC_TREE_VIEW (view), s_model);
1025 
1026     /* Set up the columns */
1027     gnc_tree_view_split_reg_set_cols (view, model, gnc_tree_view_split_reg_get_column_list (model));
1028 
1029     PINFO("#### Before View connected to Model ####");
1030 
1031     // Connect model to tree view
1032     gtk_tree_view_set_model (GTK_TREE_VIEW (view), s_model);
1033     g_object_unref (G_OBJECT (s_model));
1034 
1035     PINFO("#### After View connected to Model ####");
1036 
1037     // Default the sorting to date.
1038     gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (s_model),
1039                                           GNC_TREE_MODEL_SPLIT_REG_COL_DATE,
1040                                           GTK_SORT_ASCENDING);
1041 
1042     PINFO("#### After Set Default Sort Column ####");
1043 
1044     return view;
1045 }
1046 
1047 
1048 /* This allows the blocking / unblocking of selection */
1049 void
gnc_tree_view_split_reg_block_selection(GncTreeViewSplitReg * view,gboolean block)1050 gnc_tree_view_split_reg_block_selection (GncTreeViewSplitReg *view, gboolean block)
1051 {
1052     if (block)
1053         g_signal_handlers_block_by_func (gtk_tree_view_get_selection (GTK_TREE_VIEW (view)), gtv_sr_motion_cb, view);
1054     else
1055         g_signal_handlers_unblock_by_func (gtk_tree_view_get_selection (GTK_TREE_VIEW (view)), gtv_sr_motion_cb, view);
1056 }
1057 
1058 
1059 /* Set the default selection path */
1060 void
gnc_tree_view_split_reg_default_selection(GncTreeViewSplitReg * view)1061 gnc_tree_view_split_reg_default_selection (GncTreeViewSplitReg *view)
1062 {
1063     GncTreeModelSplitReg *model;
1064     GtkTreePath *new_mpath, *mpath, *spath;
1065     gint *indices;
1066 
1067     ENTER("#### Default Selection ####");
1068 
1069     model = gnc_tree_view_split_reg_get_model_from_view (view);
1070 
1071     /* Do we have a current transaction set on the model, use it */
1072     if (model->current_trans != NULL)
1073         view->priv->current_trans = model->current_trans;
1074 
1075     /* Set the default start position to end of list */
1076     if (view->priv->current_trans == NULL)
1077     {
1078         Transaction *btrans;
1079 
1080         btrans = gnc_tree_control_split_reg_get_blank_trans (view);
1081         mpath = gnc_tree_model_split_reg_get_path_to_split_and_trans (model, NULL, btrans);
1082         view->priv->current_trans = btrans;
1083     }
1084     else
1085         mpath = gnc_tree_model_split_reg_get_path_to_split_and_trans (model, view->priv->current_split, view->priv->current_trans);
1086 
1087     indices = gtk_tree_path_get_indices (mpath);
1088 
1089     if (view->priv->current_depth == 2)
1090         new_mpath = gtk_tree_path_new_from_indices (indices[0], indices[1], -1);
1091     else
1092         new_mpath = gtk_tree_path_new_from_indices (indices[0], -1);
1093 
1094     spath = gnc_tree_view_split_reg_get_sort_path_from_model_path (view, new_mpath);
1095 
1096     {
1097         gchar *mstring, *sstring, *tstring;
1098         mstring = gtk_tree_path_to_string (mpath);
1099         sstring = gtk_tree_path_to_string (spath);
1100         tstring = gtk_tree_path_to_string (new_mpath);
1101         DEBUG("default_selection mpath is %s, spath is %s, new path is %s", mstring, sstring, tstring);
1102         g_free (mstring);
1103         g_free (sstring);
1104         g_free (tstring);
1105     }
1106 
1107     if (view->priv->current_ref != NULL)
1108     {
1109         gtk_tree_row_reference_free (view->priv->current_ref);
1110         view->priv->current_ref = NULL;
1111     }
1112     view->priv->current_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (model), new_mpath);
1113 
1114     /* Update the titles */
1115     gtv_sr_titles (view, view->priv->current_depth);
1116 
1117     /* Make sure blank split is on current transaction */
1118     gnc_tree_model_split_reg_set_blank_split_parent (model, view->priv->current_trans, FALSE);
1119 
1120     PINFO("#### Default Selection - After Titles ####");
1121 
1122     /* Set the view format */
1123     gnc_tree_view_split_reg_set_format (view);
1124 
1125     PINFO("#### Default Selection - After View Format ####");
1126 
1127     /* scroll window to show selection */
1128     gnc_tree_view_split_reg_scroll_to_cell (view);
1129 
1130     /* Set cursor to new spath */
1131     gtk_tree_view_set_cursor (GTK_TREE_VIEW (view), spath, NULL, FALSE);
1132 
1133     gtk_tree_path_free (mpath);
1134     gtk_tree_path_free (spath);
1135     gtk_tree_path_free (new_mpath);
1136 
1137     LEAVE("#### Leave Default Selection ####");
1138 }
1139 
1140 /*###########################################################################*/
1141 
1142 /* Sets read only flag */
1143 void
gnc_tree_view_split_reg_set_read_only(GncTreeViewSplitReg * view,gboolean read_only)1144 gnc_tree_view_split_reg_set_read_only (GncTreeViewSplitReg *view, gboolean read_only)
1145 {
1146     GncTreeModelSplitReg *model;
1147 
1148     model = gnc_tree_view_split_reg_get_model_from_view (view);
1149 
1150     model->read_only = read_only;
1151 }
1152 
1153 
1154 /* Return the register commodity */
1155 gnc_commodity *
gnc_tree_view_split_reg_get_reg_commodity(GncTreeViewSplitReg * view)1156 gnc_tree_view_split_reg_get_reg_commodity (GncTreeViewSplitReg *view)
1157 {
1158     return view->priv->reg_comm;
1159 }
1160 
1161 
1162 /* Returns a Split that matches the current Account */
1163 static Split *
gtv_sr_get_this_split(GncTreeViewSplitReg * view,Transaction * trans)1164 gtv_sr_get_this_split (GncTreeViewSplitReg *view, Transaction *trans)
1165 {
1166     GncTreeModelSplitReg *model;
1167     int i;
1168     Split *split = NULL;
1169     Account *anchor;
1170 
1171     model = gnc_tree_view_split_reg_get_model_from_view (view);
1172 
1173     anchor = gnc_tree_model_split_reg_get_anchor (model);
1174 
1175     if (xaccTransCountSplits (trans) == 0) // this may be a blank or a reinit trans.
1176     {
1177         if (gnc_tree_model_split_reg_is_blank_split_parent (model, trans))
1178             return gnc_tree_model_split_get_blank_split (model);
1179     }
1180 
1181     for (i = 0; (split = xaccTransGetSplit (trans, i)); i++) {
1182         if (anchor == xaccSplitGetAccount (split))
1183             return split;
1184     }
1185     return NULL;
1186 }
1187 
1188 
1189 /* The returned Splits may be newly created and not yet belong to trans. */
1190 static gboolean
gtv_sr_get_split_pair(GncTreeViewSplitReg * view,Transaction * trans,Split ** osplit,Split ** split)1191 gtv_sr_get_split_pair (GncTreeViewSplitReg *view, Transaction *trans, Split **osplit, Split **split)
1192 {
1193     GncTreeModelSplitReg *model;
1194     QofBook       *book;
1195 
1196     gint count = xaccTransCountSplits (trans);
1197     Account *anchor = view->priv->anchor;
1198 
1199     book = gnc_get_current_book();
1200 
1201     model = gnc_tree_view_split_reg_get_model_from_view (view);
1202 
1203     if (count == 0) // blank trans
1204     {
1205         *split = gnc_tree_model_split_get_blank_split (model);
1206         xaccSplitSetAccount (*split, anchor);
1207         xaccSplitSetParent (*split, trans);
1208         *osplit = xaccMallocSplit (book);
1209         xaccSplitSetParent (*osplit, trans);
1210     }
1211     else
1212     {
1213         int i;
1214         Split *s, *first_split;
1215 
1216         first_split = xaccTransGetSplit (trans, 0);
1217 
1218         if (gnc_tree_util_split_reg_is_multi (first_split)) // multi trans
1219             return FALSE;
1220         else // two split trans
1221         {
1222             for (i = 0; (s = xaccTransGetSplit (trans, i)); i++)
1223             {
1224                 if (anchor == xaccSplitGetAccount (s))
1225                 {
1226                     *split = s;
1227                     break;
1228                 }
1229             }
1230             g_assert (*split);
1231             *osplit = xaccSplitGetOtherSplit(*split);
1232             g_assert (*osplit);
1233         }
1234     }
1235     DEBUG("gtv_sr_get_split_pair return - trans is %p, osplit is %p and split %p is set to anchor %p", trans, *osplit, *split, anchor);
1236     return TRUE;
1237 }
1238 
1239 
1240 /* Does this transaction have any Imbalance splits */
1241 static gboolean
gtv_sr_get_imbalance(Transaction * trans)1242 gtv_sr_get_imbalance (Transaction *trans)
1243 {
1244     int i;
1245     Split *split = NULL;
1246     const gchar *acc_name;
1247     const gchar *prefix = _("Imbalance");
1248 
1249     for (i = 0; (split = xaccTransGetSplit (trans, i)); i++)
1250     {
1251         if (xaccSplitGetAccount (split) != NULL)
1252         {
1253             acc_name = xaccAccountGetName (xaccSplitGetAccount (split));
1254 
1255             if (g_str_has_prefix (acc_name, prefix))
1256                 return TRUE;
1257         }
1258     }
1259     return FALSE;
1260 }
1261 
1262 
1263 /* Only allow changes to values if we have valid split accounts */
1264 static gboolean
gtv_sr_have_account(GncTreeViewSplitReg * view,RowDepth depth,gboolean expanded,gboolean is_template,Transaction * trans,Split * split)1265 gtv_sr_have_account (GncTreeViewSplitReg *view, RowDepth depth, gboolean expanded, gboolean is_template, Transaction *trans, Split *split)
1266 {
1267     gboolean gtv_sr_have_account = FALSE;
1268 
1269     DEBUG("gtv_sr_have_account trans %p, split %p, expanded %d, depth %d", trans, split, expanded, depth);
1270 
1271     if ((depth == TRANS1) && !expanded && !gnc_tree_util_split_reg_is_multi (split)) // normal trans
1272     {
1273         if (xaccSplitGetAccount (xaccSplitGetOtherSplit (split)) != NULL)
1274             gtv_sr_have_account = TRUE;
1275     }
1276 
1277     if ((depth == SPLIT3) && (xaccTransCountSplits (trans) == 0)) // blank trans, blank split
1278         gtv_sr_have_account = TRUE;
1279 
1280     if (depth == SPLIT3)
1281     {
1282         if (!is_template) // Are we using a template
1283         {
1284             Account *acc = xaccSplitGetAccount (split);
1285             if (acc != NULL)
1286             {
1287                 if (xaccAccountGetType (acc) != ACCT_TYPE_TRADING)
1288                     gtv_sr_have_account = TRUE; // normal split
1289                 else
1290                     gtv_sr_have_account = FALSE; // trading split
1291             }
1292          }
1293          else
1294          {
1295              if (gnc_tree_util_split_reg_template_get_transfer_entry (split) != NULL)
1296                  gtv_sr_have_account = TRUE;
1297          }
1298     }
1299     return gtv_sr_have_account;
1300 }
1301 
1302 /*###########################################################################*/
1303 
1304 /* This cellDataFunc is to set the cell-background property of the control columns. */
1305 static void
gtv_sr_control_cdf0(GtkTreeViewColumn * col,GtkCellRenderer * cell,GtkTreeModel * s_model,GtkTreeIter * s_iter,gpointer user_data)1306 gtv_sr_control_cdf0 (GtkTreeViewColumn *col, GtkCellRenderer *cell, GtkTreeModel *s_model,
1307     GtkTreeIter *s_iter, gpointer user_data)
1308 {
1309     GncTreeViewSplitReg *view = GNC_TREE_VIEW_SPLIT_REG (user_data);
1310     GncTreeModelSplitReg *model;
1311     GtkTreeIter m_iter;
1312     GtkTreePath *mpath;
1313     Transaction *trans;
1314     Split *split;
1315     gboolean is_split, is_blank, is_trow1, is_trow2;
1316     const gchar *row_color;
1317 
1318     gint *indices;
1319 
1320     ENTER("");
1321 
1322     model = gnc_tree_view_split_reg_get_model_from_view (view);
1323 
1324     gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (s_model), &m_iter, s_iter);
1325 
1326     g_return_if_fail (gnc_tree_model_split_reg_get_split_and_trans (
1327                          GNC_TREE_MODEL_SPLIT_REG (model), &m_iter,
1328                           &is_trow1, &is_trow2, &is_split, &is_blank,
1329                           &split, &trans));
1330 
1331     mpath = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &m_iter);
1332 
1333     indices = gtk_tree_path_get_indices (mpath);
1334 
1335     row_color = gnc_tree_model_split_reg_get_row_color (model, is_trow1, is_trow2, is_split, indices[0]);
1336 
1337     gtk_tree_path_free (mpath);
1338 
1339     /* Set the background color / this works for sorting and deleting transactions */
1340     g_object_set (cell, "cell-background", row_color, (gchar*)NULL);
1341 
1342     LEAVE("");
1343 }
1344 
1345 
1346 /* Instead of setting a different cellDataFunc for each column, we just
1347    collect everything here for the first cell renderer. */
1348 static void
gtv_sr_cdf0(GtkTreeViewColumn * col,GtkCellRenderer * cell,GtkTreeModel * s_model,GtkTreeIter * s_iter,gpointer user_data)1349 gtv_sr_cdf0 (GtkTreeViewColumn *col, GtkCellRenderer *cell, GtkTreeModel *s_model,
1350     GtkTreeIter *s_iter, gpointer user_data)
1351 {
1352     GncTreeViewSplitReg *view = GNC_TREE_VIEW_SPLIT_REG (user_data);
1353     GncTreeModelSplitReg *model;
1354     GtkTreeIter m_iter;
1355     GtkTreePath *spath;
1356     ViewCol viewcol;
1357     Transaction *trans;
1358     Split *split;
1359     gboolean is_split, is_blank, is_trow1, is_trow2;
1360     gboolean editable = FALSE, expanded = FALSE;
1361     gboolean read_only = FALSE;
1362     gboolean open_edited = FALSE;
1363     gboolean is_template = FALSE;
1364     gboolean negative_in_red = FALSE;
1365     gboolean show_extra_dates = FALSE;
1366     gnc_numeric num = gnc_numeric_zero();
1367     const gchar *s = "";
1368     const gchar *row_color;
1369     char datebuff[MAX_DATE_LENGTH + 1];
1370     RowDepth depth;
1371     gint *indices;
1372     Account *anchor = view->priv->anchor;
1373     char type;
1374 
1375     ENTER("");
1376 
1377     model = gnc_tree_view_split_reg_get_model_from_view (view);
1378 
1379     gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (s_model), &m_iter, s_iter);
1380 
1381     viewcol = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cell), "view_column"));
1382 
1383     g_return_if_fail (gnc_tree_model_split_reg_get_split_and_trans (
1384                          GNC_TREE_MODEL_SPLIT_REG (model), &m_iter,
1385                           &is_trow1, &is_trow2, &is_split, &is_blank,
1386                           &split, &trans));
1387 
1388     spath = gtk_tree_model_get_path (GTK_TREE_MODEL (s_model), s_iter);
1389 
1390     depth = gtk_tree_path_get_depth (spath);
1391 
1392     indices = gtk_tree_path_get_indices (spath);
1393 
1394     row_color = gnc_tree_model_split_reg_get_row_color (model, is_trow1, is_trow2, is_split, indices[0]);
1395 
1396     /* Lets see if the splits are expanded */
1397     if (is_trow1 || is_trow2) // transaction
1398     {
1399         if (is_trow1)
1400             gtk_tree_path_down (spath); /* Move the path down to trow2 */
1401         expanded = gtk_tree_view_row_expanded (GTK_TREE_VIEW (view), spath);
1402     }
1403     else
1404         expanded = TRUE; // splits are always expanded
1405 
1406     gtk_tree_path_free (spath);
1407 
1408     /* Set the background color / this works for sorting and deleting of transactions */
1409     g_object_set (cell, "cell-background", row_color, (gchar*)NULL);
1410 
1411     /* Get the read only model setting */
1412     gtk_tree_model_get (GTK_TREE_MODEL (model), &m_iter, GNC_TREE_MODEL_SPLIT_REG_COL_RO, &read_only, -1);
1413 
1414     /* Are we being edited in other register */
1415     if (xaccTransIsOpen (trans) && (view->priv->dirty_trans != trans))
1416     {
1417         read_only = TRUE;
1418         open_edited = TRUE;
1419     }
1420 
1421     /* Test for a transaction type of invoice, always read only */
1422     type = xaccTransGetTxnType (trans);
1423     if (model->type == RECEIVABLE_REGISTER2 || model->type == PAYABLE_REGISTER2)
1424     {
1425         if (((type == TXN_TYPE_INVOICE) || (type == TXN_TYPE_NONE)) && (view->priv->dirty_trans != trans) && !is_blank)
1426             read_only = TRUE;
1427     }
1428 
1429     /* Is this a template */
1430     is_template = gnc_tree_model_split_reg_get_template (model);
1431 
1432     /* Show negative numbers in red */
1433     negative_in_red = view->priv->negative_in_red;
1434 
1435     switch (viewcol) {
1436     case COL_DATE:
1437         /* Column is DATE */
1438         memset (datebuff, 0, sizeof(datebuff));
1439         if (is_split)
1440             g_object_set (cell, "cell-background", "white", (gchar*)NULL);
1441 
1442         // Show the extra dates for selected transaction
1443         if ((view->priv->current_trans == trans) && view->priv->show_extra_dates_on_selection)
1444             show_extra_dates = TRUE;
1445 
1446         // Show the extra dates always
1447         if (view->show_extra_dates == TRUE)
1448             show_extra_dates = TRUE;
1449 
1450         if (is_trow1) {
1451             time64 t = xaccTransRetDatePosted (trans);
1452             //If the time returned by xaccTransGetDatePostedTS is 0 then assume it
1453             //is a new transaction and set the time to current time to show current
1454             //date on new transactions
1455             if (t == 0)
1456                 t = gnc_time (NULL);
1457             qof_print_date_buff (datebuff, MAX_DATE_LENGTH, t);
1458             editable = TRUE;
1459         }
1460         else if (is_trow2 && show_extra_dates) {
1461             time64 t = xaccTransRetDateEntered (trans);
1462             g_object_set (cell, "cell-background", YELLOWCELL, (gchar*)NULL);
1463             //If the time returned by xaccTransGetDateEnteredTS is 0 then assume it
1464             //is a new transaction and set the time to current time to show current
1465             //date on new transactions
1466             if (t == 0)
1467                 t = gnc_time (NULL);
1468             qof_print_date_buff (datebuff, MAX_DATE_LENGTH, t);
1469             editable = FALSE;
1470         }
1471         else if (is_split && show_extra_dates) {
1472 
1473             if (xaccSplitGetReconcile (split) == YREC)
1474             {
1475                 time64 t = xaccSplitGetDateReconciled (split);
1476                 qof_print_date_buff (datebuff, MAX_DATE_LENGTH, t);
1477             }
1478             editable = FALSE;
1479         }
1480         else {
1481             editable = FALSE;
1482         }
1483 
1484         /* Is this a template */
1485         if (is_template && is_trow1)
1486         {
1487             /* Todo: Rethink the restriction. Greek message is "Προγραμματισμένη" */
1488             /* Translators: currently max 34 (ASCII) chars (= 17 or 8 UTF-8 chars depending on the block)
1489                See "MAX_DATE_LENGTH" in https://code.gnucash.org/docs/MAINT/group__Date.html */
1490             strncpy (datebuff,  _(" Scheduled "), MAX_DATE_LENGTH);
1491             editable = FALSE;
1492         }
1493         else if (is_template && is_trow2 && show_extra_dates)
1494         {
1495             editable = FALSE;
1496         }
1497         else if (is_template && is_split && show_extra_dates)
1498         {
1499             editable = FALSE;
1500         }
1501 
1502         editable = (read_only == TRUE) ? FALSE : editable;
1503 
1504         /* This will remove the calendar buttons if FALSE */
1505         g_object_set (cell, "use_buttons", view->priv->show_calendar_buttons, NULL );
1506         g_object_set (cell, "text", datebuff, "editable", editable, NULL);
1507         break;
1508 
1509     case COL_DUEDATE:
1510         /* Column is DUE DATE */
1511         memset (datebuff, 0, sizeof(datebuff));
1512         if (is_split)
1513             g_object_set (cell, "cell-background", "white", (gchar*)NULL);
1514 
1515         if (is_trow1) {
1516             /* Only print the due date for invoice transactions */
1517             if (type == TXN_TYPE_INVOICE)
1518             {
1519                 time64 t = xaccTransRetDateDue (trans);
1520                 qof_print_date_buff (datebuff, MAX_DATE_LENGTH, t);
1521                 editable = FALSE;
1522             }
1523             else {
1524                 editable = FALSE;
1525             }
1526         }
1527         editable = (read_only == TRUE) ? FALSE : editable;
1528 
1529         g_object_set (cell, "text", datebuff, "editable", editable, NULL);
1530         break;
1531 
1532     case COL_NUMACT:
1533         /* Column is NUM / ACT but relates to ACT */
1534         /* Override default alignment */
1535         g_object_set (cell, "xalign", 0.0, NULL );
1536 
1537         editable = TRUE;
1538 
1539         if (is_trow1)
1540             /* Get per book option */
1541             s = gnc_get_num_action (trans, gtv_sr_get_this_split (view, trans));
1542 
1543         else if (is_trow2 && expanded)
1544         {
1545             /* Get per book option */
1546             if (qof_book_use_split_action_for_num_field (gnc_get_current_book()))
1547                 s = gnc_get_action_num (trans, gtv_sr_get_this_split (view, trans));
1548             else
1549                 s = "";
1550             editable = FALSE;
1551         }
1552         else if (is_trow2 && !expanded)
1553         {
1554             /* Get per book option */
1555             if (gtv_sr_get_this_split (view, trans) != NULL) // Blank split of blank trans is not child of trans yet.
1556                s = gnc_get_action_num (trans, gtv_sr_get_this_split (view, trans));
1557             else
1558                s = "";
1559         }
1560         else if (is_split)
1561             /* Get split-action with gnc_get_num_action which is the same as
1562              * xaccSplitGetAction with these arguments */
1563             s = gnc_get_num_action (NULL, split);
1564 
1565         editable = (read_only == TRUE) ? FALSE : editable;
1566 
1567         g_object_set (cell, "text", s, "editable", editable, NULL);
1568         break;
1569 
1570     case COL_DESCNOTES:
1571         /* Column is DESCRIPTION / NOTES */
1572         /* Override default alignment */
1573         g_object_set( cell, "xalign", 0.0, NULL );
1574         if (is_trow1)
1575             s =  xaccTransGetDescription (trans);
1576         else if (is_trow2)
1577             s =  xaccTransGetNotes (trans);
1578         else if (is_split)
1579             s = xaccSplitGetMemo (split);
1580         editable = TRUE;
1581 
1582         editable = (read_only == TRUE) ? FALSE : editable;
1583 
1584         g_object_set (cell, "text", s, "editable", editable, NULL);
1585         break;
1586 
1587     case COL_TRANSFERVOID:
1588         /* Column is TRANSFER / VOID */
1589         /* Not sure if this will stay here, this sets the combo column
1590            0 for short account names, 1 for long */
1591         if (view->priv->acct_short_names)
1592             g_object_set (G_OBJECT (cell), "text-column", 0, NULL );
1593         else
1594             g_object_set (G_OBJECT (cell), "text-column", 1, NULL );
1595 
1596         {
1597             gchar *string = NULL;
1598 
1599             if (is_trow1)
1600             {
1601                 if (expanded)
1602                 {
1603                     string = g_strdup (" "); /* blank-out if splits are visible */
1604                     editable = FALSE;
1605                 }
1606                 else
1607                 {
1608                     gboolean is_multi;
1609                     string = g_strdup (gnc_tree_util_split_reg_get_transfer_entry (gtv_sr_get_this_split (view, trans), &is_multi));
1610 
1611                     editable = anchor && !expanded && !is_multi;
1612                 }
1613             }
1614             if (is_trow2)
1615             {
1616                 string = g_strdup (xaccTransGetVoidReason (trans)); // This is the Void Reason
1617                 editable = FALSE;
1618             }
1619             if (is_split)
1620             {
1621                 if (!is_template) // Are we using a template
1622                 {
1623                     Account *acct = xaccSplitGetAccount (split);
1624 
1625                     // This will be all but the General Journal which has anchor == NULL
1626                     if ((xaccTransCountSplits (trans) == 0) && (anchor != NULL)) // First split on blank transaction
1627                         acct = anchor;
1628 
1629                     if (acct != NULL)
1630                     {
1631                         if (view->priv->acct_short_names)
1632                             string = g_strdup (xaccAccountGetName (acct));
1633                         else
1634                             string = gnc_account_get_full_name (acct);
1635 
1636                     }
1637                     else
1638                         string = g_strdup (" ");
1639 
1640                     if (anchor == acct && model->type != GENERAL_JOURNAL2 && model->type != SEARCH_LEDGER2)
1641                         editable = FALSE;
1642                     else
1643                         editable = TRUE;
1644                 }
1645                 else
1646                 {
1647                     string = g_strdup (gnc_tree_util_split_reg_template_get_transfer_entry (split));
1648                     editable = TRUE;
1649                 }
1650             }
1651             editable = (read_only == TRUE) ? FALSE : editable;
1652 
1653             g_object_set (cell, "text", string, "editable", editable, NULL);
1654             g_free (string);
1655         }
1656         break;
1657 
1658     case COL_RECN:
1659         /* Column is RECN */
1660         /* Override default alignment */
1661         g_object_set( cell, "xalign", 0.5, NULL );
1662         editable = FALSE;
1663         s = "";
1664         if (is_trow1 && !expanded)
1665         {
1666             Split *this_split;
1667             char rec;
1668 
1669             this_split = gtv_sr_get_this_split (view, trans);
1670 
1671             if (this_split != NULL) // this could be a blank trans
1672             {
1673                 rec = xaccSplitGetReconcile (this_split);
1674                 if (rec == VREC || rec == FREC)
1675                     editable = FALSE;
1676                 else
1677                     editable = TRUE;
1678 
1679                 if (rec != ' ')
1680                     s = gnc_get_reconcile_str (rec);
1681                 else
1682                     s = gnc_get_reconcile_str (NREC);
1683             }
1684         }
1685 
1686         if (is_split)
1687         {
1688             char rec = xaccSplitGetReconcile (split);
1689             if (rec == VREC || rec == FREC)
1690                 editable = FALSE;
1691             else
1692                 editable = TRUE;
1693 
1694             if (rec != ' ')
1695                 s = gnc_get_reconcile_str (rec);
1696             else
1697                 s = gnc_get_reconcile_str (NREC);
1698         }
1699 
1700         editable = (read_only == TRUE) ? FALSE : editable;
1701 
1702         g_object_set (cell, "text", s, "editable", editable, NULL);
1703         break;
1704 
1705     case COL_TYPE:
1706         /* Column is TYPE */
1707         /* Override default alignment */
1708         g_object_set( cell, "xalign", 0.5, NULL );
1709         if (is_split)
1710             g_object_set (cell, "cell-background", "white", (gchar*)NULL);
1711 
1712         if (is_trow1) {
1713             static char ss[2];
1714             if (type == TXN_TYPE_NONE)
1715                 type = '?';
1716 
1717             ss[0] = type;
1718             ss[1] = '\0';
1719             editable = TRUE;
1720             g_object_set (cell, "text", ss, NULL);
1721         }
1722         else
1723         {
1724             s = "";
1725             editable = FALSE;
1726             g_object_set (cell, "text", s, NULL);
1727         }
1728 
1729         editable = (read_only == TRUE) ? FALSE : editable;
1730 
1731         g_object_set (cell, "editable", editable, NULL);
1732         break;
1733 
1734     case COL_VALUE:
1735         /* Column is VALUE */
1736         if (is_split)
1737         {
1738             num = xaccSplitGetValue (split);
1739             s = xaccPrintAmount (num, gnc_commodity_print_info (xaccTransGetCurrency (trans), SHOW_SYMBOL));
1740             editable = FALSE;
1741 
1742             if (gtv_sr_get_imbalance (trans))
1743                 g_object_set (cell, "cell-background", PINKCELL, (gchar*)NULL);
1744         }
1745         else
1746         {
1747             s = "";
1748             editable = FALSE;
1749         }
1750 
1751         editable = (read_only == TRUE) ? FALSE : editable;
1752 
1753         // Display negative numbers in red if requested in preferences
1754         if (gnc_numeric_negative_p (num) && negative_in_red)
1755             g_object_set (cell, "foreground", "red", (gchar*)NULL);
1756         else
1757             g_object_set (cell, "foreground", NULL, (gchar*)NULL);
1758 
1759         g_object_set (cell, "text", s, "editable", editable, NULL);
1760         break;
1761 
1762     case COL_RATE:
1763         /* Column is RATE */
1764         if ((is_trow1)||(is_trow2))
1765         {
1766             s = "";
1767             editable = FALSE;
1768         }
1769         else
1770         {
1771             GNCPrintAmountInfo print_info =
1772                 gnc_default_price_print_info(xaccTransGetCurrency(trans));
1773             num = gnc_tree_util_get_rate_for (view, trans, split, is_blank);
1774 
1775             if (gnc_numeric_check (num) == GNC_ERROR_OK)
1776                 s = xaccPrintAmount (num, print_info);
1777             else
1778                 s = "";
1779 
1780             editable = FALSE;
1781 
1782             if (gtv_sr_get_imbalance (trans))
1783                 g_object_set (cell, "cell-background", PINKCELL, (gchar*)NULL);
1784         }
1785 
1786         editable = (read_only == TRUE) ? FALSE : editable;
1787 
1788         g_object_set (cell, "text", s, "editable", editable, NULL);
1789         break;
1790 
1791     case COL_AMOUNT:
1792         /* Column is AMOUNT */
1793         if (is_split && (anchor == NULL))
1794         {
1795             num = xaccSplitGetAmount (split);
1796             s = xaccPrintAmount (num, gnc_account_print_info (xaccSplitGetAccount (split), SHOW_SYMBOL));
1797             editable = FALSE;
1798 
1799             if (gtv_sr_get_imbalance (trans))
1800                 g_object_set (cell, "cell-background", PINKCELL, (gchar*)NULL);
1801         }
1802         else if (is_split && (anchor))
1803         {
1804             gnc_commodity *split_comm;
1805             split_comm = xaccAccountGetCommodity (xaccSplitGetAccount (split));
1806 
1807             if (!gnc_commodity_is_currency (split_comm) || (is_blank))
1808             {
1809                 num = xaccSplitGetAmount (split);
1810                 s = xaccPrintAmount (num, gnc_account_print_info (xaccSplitGetAccount (split), SHOW_SYMBOL));
1811                 editable = TRUE;
1812             }
1813 
1814             if (gtv_sr_get_imbalance (trans))
1815                 g_object_set (cell, "cell-background", PINKCELL, (gchar*)NULL);
1816         }
1817         else
1818         {
1819             s = "";
1820             editable = FALSE;
1821         }
1822 
1823         editable = (read_only == TRUE) ? FALSE : editable;
1824 
1825         // Display negative numbers in red if requested in preferences
1826         if (gnc_numeric_negative_p (num) && negative_in_red)
1827             g_object_set (cell, "foreground", "red", (gchar*)NULL);
1828         else
1829             g_object_set (cell, "foreground", NULL, (gchar*)NULL);
1830 
1831         g_object_set (cell, "text", s, "editable", editable, NULL);
1832         break;
1833 
1834     case COL_AMTVAL:
1835         /* Column is AMOUNT / VALUE */
1836         if (is_trow2)
1837         {
1838             s = "";
1839         }
1840         else if (is_trow1) // Value
1841         {
1842             if (anchor)
1843             {
1844                 Split *this_split;
1845 
1846                 num = xaccTransGetAccountValue (trans, anchor);
1847 
1848                 if (expanded)
1849                     s = "";
1850                 else
1851                     s = xaccPrintAmount (num, gnc_commodity_print_info (xaccTransGetCurrency (trans), SHOW_SYMBOL));
1852             }
1853             else
1854             {
1855                 s = "";
1856             }
1857         }
1858 
1859         if (is_split) // Amount
1860         {
1861             if (anchor == NULL)
1862             {
1863                 num = xaccSplitGetAmount (split);
1864                 s = xaccPrintAmount (num, gnc_account_print_info (xaccSplitGetAccount (split), SHOW_SYMBOL));
1865             }
1866             else if (anchor)
1867             {
1868                 gnc_commodity *split_comm;
1869                 split_comm = xaccAccountGetCommodity (xaccSplitGetAccount (split));
1870 
1871                 if (!gnc_commodity_is_currency (split_comm) || (is_blank))
1872                 {
1873                     num = xaccSplitGetAmount (split);
1874                     s = xaccPrintAmount (num, gnc_account_print_info (xaccSplitGetAccount (split), SHOW_SYMBOL));
1875                 }
1876             }
1877             else
1878             {
1879                 s = "";
1880             }
1881 
1882             if (gtv_sr_get_imbalance (trans))
1883                 g_object_set (cell, "cell-background", PINKCELL, (gchar*)NULL);
1884         }
1885 
1886         /* Only allow changes to entries if we have a valid split accounts */
1887         editable = gtv_sr_have_account (view, depth, expanded, is_template, trans, split);
1888 
1889         editable = (read_only == TRUE) ? FALSE : editable;
1890 
1891         // Display negative numbers in red if requested in preferences
1892         if (gnc_numeric_negative_p (num) && negative_in_red)
1893             g_object_set (cell, "foreground", "red", (gchar*)NULL);
1894         else
1895             g_object_set (cell, "foreground", NULL, (gchar*)NULL);
1896 
1897         g_object_set (cell, "text", s, "editable", editable, NULL);
1898         break;
1899 
1900     case COL_PRICE:
1901         /* Column is PRICE */
1902         if (is_trow2)
1903         {
1904             s = "";
1905         }
1906         else if (is_trow1)
1907         {
1908             if (expanded)
1909             {
1910                 s = "";
1911             }
1912             else
1913             {
1914                 if (anchor)
1915                 {
1916                     Split *this_split;
1917 
1918                     this_split = gtv_sr_get_this_split (view, trans);
1919                     if (this_split != NULL) // this could be a blank split
1920                     {
1921                         if (gnc_tree_util_split_reg_is_multi (this_split))
1922                             num = gnc_numeric_zero();
1923                         else
1924                             num = xaccSplitGetSharePrice (this_split);
1925 
1926                          if (gnc_numeric_check (num) == GNC_ERROR_OK)
1927                         {
1928                             s = xaccPrintAmount (num, gnc_split_amount_print_info (split, SHOW_SYMBOL));
1929                         }
1930                         else
1931                         {
1932                             s = "";
1933                         }
1934                     }
1935                     else
1936                     {
1937                         s = "";
1938                     }
1939                 }
1940             }
1941         }
1942 
1943         if (is_split)
1944         {
1945             gnc_commodity *split_comm;
1946             split_comm = xaccAccountGetCommodity (xaccSplitGetAccount (split));
1947 
1948             if (!gnc_commodity_is_currency (split_comm) || (is_blank))
1949             {
1950                 num = xaccSplitGetSharePrice (split);
1951 
1952                 if (gnc_numeric_check (num) == GNC_ERROR_OK)
1953                 {
1954                     s = xaccPrintAmount (num, gnc_split_amount_print_info (split, SHOW_SYMBOL));
1955                 }
1956                 else
1957                 {
1958                     s = "";
1959                 }
1960             }
1961             else
1962             {
1963                 s = "";
1964             }
1965 
1966             if (gtv_sr_get_imbalance (trans))
1967                 g_object_set(cell, "cell-background", PINKCELL, (gchar*)NULL);
1968         }
1969 
1970         /* Only allow changes to entries if we have a valid split accounts */
1971         editable = gtv_sr_have_account (view, depth, expanded, is_template, trans, split);
1972 
1973         editable = (read_only == TRUE) ? FALSE : editable;
1974 
1975         g_object_set (cell, "text", s, "editable", editable, NULL);
1976         break;
1977 
1978     case COL_DEBIT:
1979     case COL_CREDIT:
1980         /* Column is CREDIT and DEBIT */
1981         {
1982             if (!is_template) // Is this a template
1983             {
1984                 GNCPrintAmountInfo print_info;
1985                 print_info = gnc_account_print_info (anchor, SHOW_SYMBOL);
1986 
1987                 if (is_split)
1988                 {
1989                     if (!gnc_tree_util_split_reg_get_debcred_entry (view, trans, split, is_blank, &num, &print_info))
1990                         num = gnc_numeric_zero();
1991 
1992                     if (gtv_sr_get_imbalance (trans))
1993                         g_object_set (cell, "cell-background", PINKCELL, (gchar*)NULL);
1994                 }
1995                 else if (is_trow1)
1996                 {
1997                     if (anchor)
1998                     {
1999                          num = xaccTransGetAccountAmount (trans, anchor);
2000                     }
2001                     else
2002                     {
2003                         num = gnc_numeric_zero();
2004                     }
2005                 }
2006                 else if (is_trow2)
2007                 {
2008                     num = gnc_numeric_zero();
2009                 }
2010 
2011                 if ((gnc_numeric_check(num) != GNC_ERROR_OK) ||
2012                      gnc_numeric_zero_p(num) ||
2013                     (gnc_numeric_negative_p(num) && viewcol == COL_DEBIT) ||
2014                     (gnc_numeric_positive_p(num) && viewcol == COL_CREDIT))
2015                 {
2016                     s = "";
2017                 }
2018                 else
2019                 {
2020                     if ((is_trow1 || is_trow2) && expanded)
2021                         s = "";
2022                     else
2023                         s = xaccPrintAmount (gnc_numeric_abs (num), print_info);
2024                 }
2025             }
2026             else
2027             {
2028 
2029                 if (is_trow1 || is_trow2)
2030                 {
2031                     s = "";
2032                 }
2033                 else if (is_split && viewcol == COL_DEBIT)
2034                     s = gnc_tree_util_split_reg_template_get_fdebt_entry (split);
2035                 else
2036                     s = gnc_tree_util_split_reg_template_get_fcred_entry (split);
2037             }
2038 
2039             /* Only allow changes to entries if we have a valid split accounts */
2040             editable = gtv_sr_have_account (view, depth, expanded, is_template, trans, split);
2041         }
2042 
2043         editable = (read_only == TRUE) ? FALSE : editable;
2044 
2045         g_object_set (cell, "text", s, "editable", editable, NULL);
2046         break;
2047 
2048     case COL_BALANCE:
2049         /* Column is BALANCE */
2050         if (is_split)
2051             g_object_set(cell, "cell-background", "white", (gchar*)NULL);
2052 
2053         if (is_trow1 && anchor) {
2054             num = xaccTransGetAccountBalance (trans, anchor);
2055             if (gnc_reverse_balance (anchor))
2056                 num = gnc_numeric_neg (num);
2057             s = xaccPrintAmount (num, gnc_account_print_info(anchor, FALSE));
2058 
2059             // Display negative numbers in red if requested in preferences
2060             if (gnc_numeric_negative_p (num) && negative_in_red)
2061                 g_object_set (cell, "foreground", "red", (gchar*)NULL);
2062             else
2063                 g_object_set (cell, "foreground", NULL, (gchar*)NULL);
2064         } else {
2065             s = "";
2066         }
2067         g_object_set (cell, "text", s, "editable", FALSE, NULL);
2068         break;
2069 
2070     case COL_STATUS:
2071         /* Column is STATUS */
2072         if (read_only && !open_edited)
2073             g_object_set(cell, "cell-background", REDCELL, (gchar*)NULL);
2074         else if (read_only && open_edited)
2075             g_object_set(cell, "cell-background", ORANGECELL, (gchar*)NULL);
2076         else if (xaccTransInFutureByPostedDate (trans))
2077             g_object_set(cell, "cell-background", BLUECELL, (gchar*)NULL);
2078         else
2079             g_object_set(cell, "cell-background", BLACKCELL, (gchar*)NULL);
2080         break;
2081 
2082     case COL_COMM:
2083         /* Column COMMODITY */
2084         {
2085             gchar *string = NULL;
2086             if (is_split)
2087             {
2088                 gnc_commodity *split_com, *txn_com;
2089 
2090                 split_com = xaccAccountGetCommodity (xaccSplitGetAccount(split));
2091                 txn_com = xaccTransGetCurrency (trans);
2092                 if (split_com == txn_com)
2093                    string = g_strconcat (gnc_commodity_get_printname (split_com), "*", NULL);
2094                 else
2095                    string = g_strdup (gnc_commodity_get_printname (split_com));
2096             }
2097             else
2098                 string = g_strdup ("");
2099 
2100             g_object_set (cell, "text", string, "editable", FALSE, NULL);
2101             g_free (string);
2102         }
2103         break;
2104 
2105     default:
2106         break;
2107     }
2108     LEAVE("");
2109 }
2110 
2111 
2112 /* Instead of setting a different cellDataFunc for each column, we just
2113    collect everything here for the second cell renderer. */
2114 static void
gtv_sr_cdf1(GtkTreeViewColumn * col,GtkCellRenderer * cell,GtkTreeModel * s_model,GtkTreeIter * s_iter,gpointer user_data)2115 gtv_sr_cdf1 (GtkTreeViewColumn *col, GtkCellRenderer *cell, GtkTreeModel *s_model,
2116     GtkTreeIter *s_iter, gpointer user_data)
2117 {
2118     GncTreeViewSplitReg *view = GNC_TREE_VIEW_SPLIT_REG (user_data);
2119     GncTreeModelSplitReg *model;
2120     GtkTreeIter m_iter;
2121     GtkTreePath *spath;
2122     ViewCol viewcol;
2123     Transaction *trans;
2124     Split *split;
2125     gboolean is_split, is_blank, is_trow1, is_trow2;
2126     gboolean editable = FALSE, expanded = FALSE;
2127     gboolean read_only = FALSE;
2128 //    gboolean open_edited = FALSE;
2129     const gchar *s = "";
2130     const gchar *row_color;
2131 //    RowDepth depth;
2132     gint *indices;
2133 //    Account *anchor = view->priv->anchor;
2134     char type;
2135 
2136     ENTER("");
2137 
2138     model = gnc_tree_view_split_reg_get_model_from_view (view);
2139 
2140     gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (s_model), &m_iter, s_iter);
2141 
2142     viewcol = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cell), "view_column"));
2143 
2144     g_return_if_fail (gnc_tree_model_split_reg_get_split_and_trans (
2145                          GNC_TREE_MODEL_SPLIT_REG (model), &m_iter,
2146                           &is_trow1, &is_trow2, &is_split, &is_blank,
2147                           &split, &trans));
2148 
2149     spath = gtk_tree_model_get_path (GTK_TREE_MODEL (s_model), s_iter);
2150 
2151 //    depth = gtk_tree_path_get_depth (spath);
2152 
2153     indices = gtk_tree_path_get_indices (spath);
2154 
2155     row_color = gnc_tree_model_split_reg_get_row_color (model, is_trow1, is_trow2, is_split, indices[0]);
2156 
2157     /* Lets see if the splits are expanded */
2158     if (is_trow1 || is_trow2) // transaction
2159     {
2160         if (is_trow1)
2161             gtk_tree_path_down (spath); /* Move the path down to trow2 */
2162         expanded = gtk_tree_view_row_expanded (GTK_TREE_VIEW (view), spath);
2163     }
2164     else
2165         expanded = TRUE; // splits are always expanded
2166 
2167     gtk_tree_path_free (spath);
2168 
2169     /* Set the background color / this works for sorting and deleting of transactions */
2170     g_object_set (cell, "cell-background", row_color, (gchar*)NULL);
2171 
2172     /* Get the read only model setting */
2173     gtk_tree_model_get (GTK_TREE_MODEL (model), &m_iter, GNC_TREE_MODEL_SPLIT_REG_COL_RO, &read_only, -1);
2174 
2175     /* Are we being edited in other register */
2176     if (xaccTransIsOpen (trans) && (view->priv->dirty_trans != trans))
2177     {
2178         read_only = TRUE;
2179 //        open_edited = TRUE;
2180     }
2181 
2182     /* Test for a transaction type of invoice, always read only */
2183     type = xaccTransGetTxnType (trans);
2184     if (model->type == RECEIVABLE_REGISTER2 || model->type == PAYABLE_REGISTER2)
2185     {
2186         if (((type == TXN_TYPE_INVOICE) || (type == TXN_TYPE_NONE)) && (view->priv->dirty_trans != trans) && !is_blank)
2187             read_only = TRUE;
2188     }
2189 
2190     switch (viewcol) {
2191     case COL_DATE:
2192         /* Column is DATE */
2193         break;
2194 
2195     case COL_DUEDATE:
2196         /* Column is DUE DATE */
2197         break;
2198 
2199     case COL_NUMACT:
2200         /* Column is NUM / ACT  but relates to NUM */
2201         /* Override default alignment */
2202         g_object_set (cell, "xalign", 0.0, NULL );
2203 
2204         editable = TRUE;
2205 
2206         if (is_trow1)
2207         {
2208             /* Get per book option */
2209             s = gnc_get_num_action (trans, gtv_sr_get_this_split (view, trans));
2210         }
2211         else if (is_trow2 && expanded)
2212         {
2213             /* Get per book option */
2214             if (qof_book_use_split_action_for_num_field (gnc_get_current_book()))
2215                 s = gnc_get_action_num (trans, gtv_sr_get_this_split (view, trans));
2216             else
2217                 s = "";
2218             editable = FALSE;
2219         }
2220         else if (is_trow2 && !expanded)
2221         {
2222             /* Get per book option */
2223             if (qof_book_use_split_action_for_num_field (gnc_get_current_book()))
2224             {
2225                if (gtv_sr_get_this_split (view, trans) != NULL) // Blank split of blank trans is not child of trans yet.
2226                    s = gnc_get_action_num (trans, gtv_sr_get_this_split (view, trans));
2227                else
2228                    s = "";
2229             }
2230             else
2231             {
2232                 s = "XY";
2233             }
2234         }
2235         else if (is_split)
2236         {
2237             s = "XZ";
2238         }
2239 
2240         editable = (read_only == TRUE) ? FALSE : editable;
2241 
2242         g_object_set (cell, "text", s, "editable", editable, NULL);
2243         break;
2244 
2245     case COL_DESCNOTES:
2246         /* Column is DESCRIPTION / NOTES */
2247         break;
2248 
2249     case COL_TRANSFERVOID:
2250         /* Column is TRANSFER / VOID */
2251         break;
2252 
2253     case COL_RECN:
2254         /* Column is RECN */
2255         break;
2256 
2257     case COL_TYPE:
2258         /* Column is TYPE */
2259         break;
2260 
2261     case COL_VALUE:
2262         /* Column is VALUE */
2263         break;
2264 
2265     case COL_RATE:
2266         /* Column is RATE */
2267         break;
2268 
2269     case COL_AMOUNT:
2270         /* Column is AMOUNT */
2271         break;
2272 
2273     case COL_AMTVAL:
2274         /* Column is AMOUNT / VALUE */
2275         break;
2276 
2277     case COL_PRICE:
2278         /* Column is PRICE */
2279         break;
2280 
2281     case COL_DEBIT:
2282     case COL_CREDIT:
2283         /* Column is CREDIT and DEBIT */
2284         break;
2285 
2286     case COL_BALANCE:
2287         /* Column is BALANCE */
2288         break;
2289 
2290     case COL_STATUS:
2291         /* Column is STATUS */
2292         break;
2293 
2294     case COL_COMM:
2295         /* Column COMMODITY */
2296         break;
2297 
2298     default:
2299         break;
2300     }
2301     LEAVE("");
2302 }
2303 
2304 
2305 /*###########################################################################*/
2306 
2307 /* Returns TRUE if dialog was canceled or discarded.
2308    Does nothing if 'new_trans' is the dirty trans. */
2309 static gboolean
gtv_sr_transaction_changed_confirm(GncTreeViewSplitReg * view,Transaction * new_trans)2310 gtv_sr_transaction_changed_confirm (GncTreeViewSplitReg *view,
2311                             Transaction *new_trans)
2312 {
2313     GtkWidget            *dialog, *window;
2314     GncTreeModelSplitReg *model;
2315     Split                *split;
2316     gint response;
2317     const char *title = _("Save the changed transaction?");
2318     const char *message = _(
2319         "The current transaction has changed. Would you like to "
2320         "record the changes, or discard the changes?");
2321 
2322     // Look for dirty_trans not being new_trans.
2323     if (!view->priv->dirty_trans || view->priv->dirty_trans == new_trans)
2324         return FALSE;
2325 
2326     model = gnc_tree_view_split_reg_get_model_from_view (view);
2327 
2328     // If using trading accounts, lets scrub them to make them work.
2329     if (xaccTransUseTradingAccounts (view->priv->dirty_trans))
2330     {
2331         Account *default_account = gnc_tree_model_split_reg_get_anchor (model);
2332         if (default_account != NULL)
2333             xaccTransScrubImbalance (view->priv->dirty_trans, gnc_account_get_root(default_account), NULL);
2334         else
2335         {
2336             Account *root = gnc_book_get_root_account (gnc_get_current_book());
2337             xaccTransScrubImbalance (view->priv->dirty_trans, root, NULL);
2338         }
2339     }
2340 
2341     // Test if the transaction is balanced.
2342     if (gnc_tree_control_split_reg_balance_trans (view, view->priv->dirty_trans))
2343     {
2344         view->priv->trans_confirm = CANCEL;
2345         return TRUE;
2346     }
2347 
2348     window = gnc_tree_view_split_reg_get_parent (view);
2349     dialog = gtk_message_dialog_new (GTK_WINDOW (window),
2350                                     GTK_DIALOG_DESTROY_WITH_PARENT,
2351                                     GTK_MESSAGE_QUESTION,
2352                                     GTK_BUTTONS_NONE,
2353                                     "%s", title);
2354     gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
2355                                              "%s", message);
2356 
2357     gtk_dialog_add_buttons (GTK_DIALOG(dialog),_("_Discard Changes"), GTK_RESPONSE_REJECT,
2358                             _("_Cancel"), GTK_RESPONSE_CANCEL,
2359                             _("_Record Changes"), GTK_RESPONSE_ACCEPT, NULL);
2360 
2361     response = gnc_dialog_run (GTK_DIALOG (dialog), GNC_PREF_WARN_REG_TRANS_MOD);
2362     gtk_widget_destroy (dialog);
2363 
2364     switch (response)
2365     {
2366     case GTK_RESPONSE_ACCEPT:
2367         g_object_set_data (G_OBJECT (view), "data-edited", GINT_TO_POINTER (FALSE));
2368         xaccTransCommitEdit (view->priv->dirty_trans);
2369         split = gnc_tree_model_split_get_blank_split (model);
2370         xaccSplitReinit (split); // Clear the blank split
2371         view->priv->dirty_trans = NULL;
2372         view->change_allowed = FALSE;
2373         view->priv->auto_complete = FALSE;
2374         view->priv->trans_confirm = ACCEPT;
2375         return FALSE;
2376         break;
2377 
2378     case GTK_RESPONSE_REJECT:
2379         if (view->priv->dirty_trans && xaccTransIsOpen (view->priv->dirty_trans))
2380         {
2381             // Move selection to trans - selection is blocked
2382             gnc_tree_control_split_reg_goto_rel_trans_row (view, 0);
2383 
2384             g_object_set_data (G_OBJECT (view), "data-edited", GINT_TO_POINTER (FALSE));
2385             xaccTransRollbackEdit (view->priv->dirty_trans);
2386             split = gnc_tree_model_split_get_blank_split (model);
2387             xaccSplitReinit (split); // Clear the blank split
2388             view->change_allowed = FALSE;
2389             view->priv->auto_complete = FALSE;
2390             view->priv->trans_confirm = DISCARD;
2391         }
2392         return TRUE;
2393         break;
2394 
2395     case GTK_RESPONSE_CANCEL:
2396         view->priv->trans_confirm = CANCEL;
2397         return TRUE;
2398         break;
2399 
2400     default:
2401         return FALSE;
2402     }
2403     return FALSE;
2404 }
2405 
2406 
2407 /*###########################################################################
2408              vvvvv    edit function call backs      vvvvvv
2409 #############################################################################*/
2410 #ifdef skip
2411 static void
start_edit(GtkCellRenderer * cr,GtkCellEditable * editable,const gchar * path_string,gpointer user_data)2412 start_edit (GtkCellRenderer *cr, GtkCellEditable *editable,
2413            const gchar *path_string, gpointer user_data)
2414 {
2415 //    GncTreeViewSplitReg *view = GNC_TREE_VIEW_SPLIT_REG (user_data);
2416 //    GncTreeModelSplitReg *model;
2417     GtkTreePath         *path;
2418 //g_print("\n\nstart_edit\n");
2419 /*FIXME Not sure if this is required, leave for now ? */
2420 
2421 //    model = gnc_tree_view_split_reg_get_model_from_view (view);
2422 
2423     gtv_sr_editable_start_editing_cb (cr, editable, path_string, user_data);
2424 /*    g_signal_connect(G_OBJECT(editable), "editing-done", (GCallback) editing_done_cb, view); */
2425 
2426 //FIXME this could be the sort path instead of model path / check !!
2427     path = gtk_tree_path_new_from_string (path_string);
2428 
2429 //FIXME stuff here...
2430 
2431     gtk_tree_path_free (path);
2432 
2433     return;
2434 }
2435 #endif
2436 
2437 /* Open Transaction for editing */
2438 static void
gtv_sr_begin_edit(GncTreeViewSplitReg * view,Transaction * trans)2439 gtv_sr_begin_edit (GncTreeViewSplitReg *view, Transaction *trans)
2440 {
2441     ENTER("gtv_sr_begin_edit trans %p", trans);
2442 
2443     if (trans != view->priv->dirty_trans)
2444     {
2445         time64 time = xaccTransRetDatePosted (trans);
2446         if (!xaccTransIsOpen (trans))
2447             xaccTransBeginEdit (trans);
2448         view->priv->dirty_trans = trans;
2449 
2450         if (!time)
2451         {
2452             //If the time returned by xaccTransGetDatePostedTS is 0 then assume it
2453             //is a new transaction and set the time to current time to show current
2454             //date on new transactions
2455             time = gnc_time (NULL);
2456             xaccTransSetDatePostedSecsNormalized (trans, time);
2457         }
2458     }
2459     LEAVE(" ");
2460 }
2461 
2462 
2463 /* Call back to remove date widget */
2464 static void
gtv_sr_remove_edit_date(GtkCellEditable * ce,gpointer user_data)2465 gtv_sr_remove_edit_date (GtkCellEditable *ce, gpointer user_data)
2466 {
2467     GncTreeViewSplitReg *view = GNC_TREE_VIEW_SPLIT_REG (user_data);
2468     GncPopupEntry *popup_entry;
2469     const gchar *new_string;
2470     const gchar *current_string;
2471     GDate date;
2472     gchar *date_string;
2473 
2474     ENTER("remove edit date and temp cell rend %p", view->priv->temp_cr);
2475 
2476     if (view->priv->temp_cr != NULL)
2477     {
2478         // These strings are used to determine if cell data was altered so that keynav works better
2479         popup_entry = GNC_POPUP_ENTRY (g_object_get_data (G_OBJECT (view->priv->temp_cr), "cell-editable"));
2480 
2481         new_string = gtk_entry_get_text (GTK_ENTRY (popup_entry->entry));
2482 
2483         current_string = g_object_get_data (G_OBJECT (view->priv->temp_cr), "current-string");
2484 
2485         DEBUG("New string is %s and Current_string is %s", new_string, current_string);
2486 
2487         // If editing wasn't canceled and strings don't match then cell data was edited
2488         if (!GPOINTER_TO_INT (g_object_get_data (G_OBJECT (view->priv->temp_cr), "edit-canceled"))
2489              && g_ascii_strcasecmp (new_string, current_string))
2490         {
2491             g_object_set_data (G_OBJECT (view), "data-edited", GINT_TO_POINTER (TRUE));
2492         }
2493 
2494         /* Lets update the help text */
2495         gnc_tree_util_split_reg_parse_date (&date, new_string);
2496         date_string = gnc_tree_util_split_reg_get_date_help (&date);
2497 
2498         if (view->help_text)
2499             g_free (view->help_text);
2500         view->help_text = g_strdup (date_string);
2501 
2502         g_signal_emit_by_name (view, "help_signal", NULL);
2503         g_free (date_string);
2504 
2505         g_object_set_data (G_OBJECT (view->priv->temp_cr), "cell-editable", NULL);
2506         view->priv->temp_cr = NULL;
2507         view->editing_now = FALSE;
2508     }
2509     LEAVE(" ");
2510 }
2511 
2512 
2513 /* Call back to remove combo widget */
2514 static void
gtv_sr_remove_edit_combo(GtkCellEditable * ce,gpointer user_data)2515 gtv_sr_remove_edit_combo (GtkCellEditable *ce, gpointer user_data)
2516 {
2517     GncTreeViewSplitReg *view = GNC_TREE_VIEW_SPLIT_REG (user_data);
2518     GtkEntry *entry;
2519     const gchar *new_string;
2520     const gchar *current_string;
2521 
2522     ENTER("remove edit combo and temp cell rend %p", view->priv->temp_cr);
2523 
2524     if (view->priv->temp_cr != NULL)
2525     {
2526         // These strings are used to determine if cell data was altered so that keynav works better
2527         entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (g_object_get_data (G_OBJECT (view->priv->temp_cr), "cell-editable"))));
2528 
2529         new_string = gtk_entry_get_text (GTK_ENTRY (entry));
2530 
2531         current_string = g_object_get_data (G_OBJECT (view->priv->temp_cr), "current-string");
2532 
2533         DEBUG("New string is %s and Current_string is %s", new_string, current_string);
2534 
2535         // If editing wasn't canceled and strings don't match then cell data was edited
2536         if (!GPOINTER_TO_INT (g_object_get_data (G_OBJECT (view->priv->temp_cr), "edit-canceled"))
2537              && g_ascii_strcasecmp (new_string, current_string))
2538         {
2539             g_object_set_data (G_OBJECT (view), "data-edited", GINT_TO_POINTER (TRUE));
2540         }
2541 
2542         g_object_set_data (G_OBJECT (view->priv->temp_cr), "cell-editable", NULL);
2543         view->priv->temp_cr = NULL;
2544         view->editing_now = FALSE;
2545     }
2546     LEAVE(" ");
2547 }
2548 
2549 
2550 /* Call back to remove entry widget */
2551 static void
gtv_sr_remove_edit_entry(GtkCellEditable * ce,gpointer user_data)2552 gtv_sr_remove_edit_entry (GtkCellEditable *ce, gpointer user_data)
2553 {
2554     GncTreeViewSplitReg *view = GNC_TREE_VIEW_SPLIT_REG (user_data);
2555     const gchar *new_string;
2556     const gchar *current_string;
2557 
2558     ENTER("remove edit entry and temp cell rend %p", view->priv->temp_cr);
2559 
2560     if (view->priv->temp_cr != NULL)
2561     {
2562         // These strings are used to determine if cell data was altered so that keynav works better
2563         new_string = gtk_entry_get_text (GTK_ENTRY (g_object_get_data (G_OBJECT (view->priv->temp_cr), "cell-editable")));
2564 
2565         current_string = g_object_get_data (G_OBJECT (view->priv->temp_cr), "current-string");
2566 
2567         DEBUG("New string is %s and Current_string is %s", new_string, current_string);
2568 
2569         // If editing wasn't canceled and strings don't match then cell data was edited
2570         if (!GPOINTER_TO_INT (g_object_get_data (G_OBJECT (view->priv->temp_cr), "edit-canceled"))
2571              && g_ascii_strcasecmp (new_string, current_string))
2572         {
2573             g_object_set_data (G_OBJECT (view), "data-edited", GINT_TO_POINTER (TRUE));
2574         }
2575         if (g_object_get_data (G_OBJECT (view->priv->temp_cr), "current-flag") != NULL) // flag
2576             g_object_set_data (G_OBJECT (view->priv->temp_cr), "current-flag", NULL);
2577 
2578         g_object_set_data (G_OBJECT (view->priv->temp_cr), "cell-editable", NULL);
2579         view->priv->temp_cr = NULL;
2580         view->editing_now = FALSE;
2581     }
2582     LEAVE(" ");
2583 }
2584 
2585 
2586 /* Explain: GtkEntry has a cursor that blinks upon
2587    g_timeout_dispatch(). It complains if it blinks after the GtkEntry
2588    loses focus. So, we can't pop up any dialogs while the blinking
2589    cursor is around. The solution is to force the editing to be
2590    finished before raising the dialog. That finalizes the
2591    gtkcelleditable. */
2592 static void
gtv_sr_finish_edit(GncTreeViewSplitReg * view)2593 gtv_sr_finish_edit (GncTreeViewSplitReg *view)
2594 {
2595     GtkCellEditable *ce;
2596 
2597     if (view->priv->temp_cr == NULL)
2598         return;
2599 
2600     DEBUG("gtv_sr_finish_edit temp_cr is %p", view->priv->temp_cr);
2601 
2602     if ((ce = GTK_CELL_EDITABLE (g_object_get_data (G_OBJECT (view->priv->temp_cr), "cell-editable"))))
2603     {
2604         DEBUG("gtv_sr_finish_edit - editing_done");
2605         gtk_cell_editable_editing_done (ce);
2606         gtk_cell_editable_remove_widget (ce);
2607     }
2608 }
2609 
2610 #ifdef skip
2611 //FIXME Not used yet
2612 /* This is used in g_idle_add to finish an edit */
2613 static gboolean
gtv_sr_idle_finish_edit(GncTreeViewSplitReg * view)2614 gtv_sr_idle_finish_edit (GncTreeViewSplitReg *view)
2615 {
2616    gtv_sr_finish_edit (view);
2617    return FALSE;
2618 }
2619 
2620 
2621 /* This is used in g_idle_add to cancel an edit */
2622 static gboolean
gtv_sr_idle_cancel_edit(GtkCellRenderer * cr)2623 gtv_sr_idle_cancel_edit (GtkCellRenderer *cr)
2624 {
2625     GtkCellEditable *ce;
2626 
2627     gtk_cell_renderer_stop_editing (cr, TRUE);
2628 
2629     ce = GTK_CELL_EDITABLE (g_object_get_data (G_OBJECT (cr), "cell-editable"));
2630     gtk_cell_editable_editing_done (ce);
2631     gtk_cell_editable_remove_widget (ce);
2632 
2633    return FALSE;
2634 }
2635 #endif
2636 
2637 /* This is used in g_idle_add to repopulate the transfer cell */
2638 static gboolean
gtv_sr_idle_transfer(GncTreeViewSplitReg * view)2639 gtv_sr_idle_transfer (GncTreeViewSplitReg *view)
2640 {
2641     GtkTreePath *spath;
2642     GList *columns;
2643     GList  *column;
2644     gint i;
2645 
2646     spath = gnc_tree_view_split_reg_get_current_path (view);
2647     columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (view));
2648 
2649     for (column = columns, i = 1; column; column = g_list_next (column), i++)
2650     {
2651         GList *renderers;
2652         GtkCellRenderer *cr0;
2653         GtkTreeViewColumn *tvc;
2654         ViewCol viewcol;
2655 
2656         tvc = column->data;
2657 
2658         // Get the first renderer, it has the view-column value.
2659         renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (tvc));
2660         cr0 = g_list_nth_data (renderers, 0);
2661         g_list_free (renderers);
2662 
2663         viewcol = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cr0), "view_column"));
2664 
2665         if (viewcol == COL_TRANSFERVOID)
2666             gtk_tree_view_set_cursor (GTK_TREE_VIEW (view), spath, tvc, TRUE);
2667     }
2668     g_list_free (columns);
2669     gtk_tree_path_free (spath);
2670     return FALSE;
2671 }
2672 
2673 /*###########################################################################*/
2674 
2675 /* Set the column titles based on register type and depth */
2676 static void
gtv_sr_titles(GncTreeViewSplitReg * view,RowDepth depth)2677 gtv_sr_titles (GncTreeViewSplitReg *view, RowDepth depth)
2678 {
2679     GncTreeModelSplitReg *model;
2680     GtkCellRenderer *cr0;
2681     GList *renderers;
2682     GList *columns;
2683     GList  *column;
2684     gint i;
2685     gboolean is_template;
2686 
2687     model = gnc_tree_view_split_reg_get_model_from_view (view);
2688     ENTER("title depth is %d and sort_depth %d, sort_col is %d", depth, model->sort_depth, model->sort_col);
2689 
2690     columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (view));
2691 
2692     is_template = gnc_tree_model_split_reg_get_template (model);
2693 
2694     for (column = columns, i = 1; column; column = g_list_next (column), i++)
2695     {
2696         GtkTreeViewColumn *tvc;
2697         ViewCol viewcol;
2698 
2699         tvc = column->data;
2700 
2701         // Get the first renderer, it has the view-column value.
2702         renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (tvc));
2703         cr0 = g_list_nth_data (renderers, 0);
2704         g_list_free (renderers);
2705 
2706         viewcol = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cr0), "view_column"));
2707 
2708         DEBUG("viewcol is %d", viewcol);
2709 
2710         switch (viewcol)
2711         {
2712         case COL_DATE:
2713             switch (model->type)
2714             {
2715             default: //FIXME These if statements may not be required
2716                 /* Display arrows if we are sorting on this row */
2717                 if (model->sort_depth == depth && model->sort_col == viewcol)
2718                     gtk_tree_view_column_set_sort_indicator (tvc, TRUE);
2719                 else
2720                     gtk_tree_view_column_set_sort_indicator (tvc, FALSE);
2721 
2722                 if (depth == TRANS1)
2723                     gtk_tree_view_column_set_title (tvc, _("Date Posted"));
2724                 else if (depth == TRANS2)
2725                     gtk_tree_view_column_set_title (tvc, _("Date Entered"));
2726                 else if (depth == SPLIT3)
2727                     gtk_tree_view_column_set_title (tvc, _("Reconciled Date"));
2728                 else
2729                     gtk_tree_view_column_set_title (tvc, _("Date Posted / Entered / Reconciled"));
2730                 break;
2731             }
2732             break;
2733 
2734         case COL_DUEDATE:
2735             switch (model->type)
2736             {
2737             default: //FIXME These if statements may not be required
2738                 if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
2739                     gtk_tree_view_column_set_title (tvc, _("Due Date"));
2740                 break;
2741             }
2742             break;
2743 
2744         case COL_NUMACT:
2745             switch (model->type)
2746             {
2747             case RECEIVABLE_REGISTER2:
2748             case PAYABLE_REGISTER2:
2749                 if (depth == TRANS1)
2750                     gtk_tree_view_column_set_title (tvc, _("Reference"));
2751                 else if (depth == TRANS2)
2752                     gtk_tree_view_column_set_title (tvc, _("Action"));
2753                 else if (depth == SPLIT3)
2754                     gtk_tree_view_column_set_title (tvc, _("Action"));
2755                 else
2756                     gtk_tree_view_column_set_title (tvc, _("Reference / Action"));
2757                 break;
2758 
2759 
2760             default:
2761                 /* Display arrows if we are sorting on this row */
2762                 if (model->sort_depth == depth && model->sort_col == viewcol)
2763                     gtk_tree_view_column_set_sort_indicator (tvc, TRUE);
2764                 else
2765                     gtk_tree_view_column_set_sort_indicator (tvc, FALSE);
2766 
2767                 if (depth == TRANS1)
2768                     gtk_tree_view_column_set_title (tvc, _("Number"));
2769                 else if (depth == TRANS2 && (qof_book_use_split_action_for_num_field (gnc_get_current_book())))
2770                     gtk_tree_view_column_set_title (tvc, _("T-Number"));
2771                 else if (depth == TRANS2 && (!qof_book_use_split_action_for_num_field (gnc_get_current_book())))
2772                     gtk_tree_view_column_set_title (tvc, _("Action"));
2773                 else if (depth == SPLIT3)
2774                     gtk_tree_view_column_set_title (tvc, _("Action"));
2775                 else
2776                     gtk_tree_view_column_set_title (tvc, _("Number / Action"));
2777                 break;
2778             }
2779             break;
2780 
2781         case COL_DESCNOTES:
2782             switch (model->type)
2783             {
2784             case RECEIVABLE_REGISTER2:
2785                 if (depth == TRANS1)
2786                     gtk_tree_view_column_set_title (tvc, _("Customer"));
2787                 else if (depth == TRANS2)
2788                     gtk_tree_view_column_set_title (tvc, _("Memo"));
2789                 else if (depth == SPLIT3)
2790                     gtk_tree_view_column_set_title (tvc, _("Memo"));
2791                 else
2792                     gtk_tree_view_column_set_title (tvc, _("Customer / Memo"));
2793                 break;
2794 
2795             case PAYABLE_REGISTER2:
2796                 if (depth == TRANS1)
2797                     gtk_tree_view_column_set_title (tvc, _("Vendor"));
2798                 else if (depth == TRANS2)
2799                     gtk_tree_view_column_set_title (tvc, _("Memo"));
2800                 else if (depth == SPLIT3)
2801                     gtk_tree_view_column_set_title (tvc, _("Memo"));
2802                 else
2803                     gtk_tree_view_column_set_title (tvc, _("Vendor / Memo"));
2804                 break;
2805 
2806 
2807             default:
2808                 /* Display arrows if we are sorting on this row */
2809                 if (model->sort_depth == depth && model->sort_col == viewcol)
2810                     gtk_tree_view_column_set_sort_indicator (tvc, TRUE);
2811                 else
2812                     gtk_tree_view_column_set_sort_indicator (tvc, FALSE);
2813 
2814                 if (depth == TRANS1)
2815                     gtk_tree_view_column_set_title (tvc, _("Description"));
2816                 else if (depth == TRANS2)
2817                     gtk_tree_view_column_set_title (tvc, _("Notes"));
2818                 else if (depth == SPLIT3)
2819                     gtk_tree_view_column_set_title (tvc, _("Memo"));
2820                 else
2821                     gtk_tree_view_column_set_title (tvc, _("Description / Notes / Memo"));
2822                 break;
2823             }
2824             break;
2825 
2826         case COL_TRANSFERVOID:
2827             switch (model->type)
2828             {
2829             case RECEIVABLE_REGISTER2:
2830             case PAYABLE_REGISTER2:
2831                 if (depth == TRANS1)
2832                     gtk_tree_view_column_set_title (tvc, _("Accounts"));
2833                 else if (depth == TRANS2)
2834                     gtk_tree_view_column_set_title (tvc, _("Accounts"));
2835                 else if (depth == SPLIT3)
2836                     gtk_tree_view_column_set_title (tvc, _("Accounts"));
2837                 else
2838                     gtk_tree_view_column_set_title (tvc, _("Accounts"));
2839                 break;
2840 
2841             default:
2842                 /* Display arrows if we are sorting on this row */
2843                 if (model->sort_depth == depth && model->sort_col == viewcol)
2844                     gtk_tree_view_column_set_sort_indicator (tvc, TRUE);
2845                 else
2846                     gtk_tree_view_column_set_sort_indicator (tvc, FALSE);
2847 
2848                 if (depth == TRANS1)
2849                     gtk_tree_view_column_set_title (tvc, _("Accounts"));
2850                 else if (depth == TRANS2)
2851                     gtk_tree_view_column_set_title (tvc, _("Void Reason"));
2852                 else if (depth == SPLIT3)
2853                     gtk_tree_view_column_set_title (tvc, _("Accounts"));
2854                 else
2855                     gtk_tree_view_column_set_title (tvc, _("Accounts / Void Reason"));
2856                 break;
2857             }
2858             break;
2859 
2860         case COL_RECN:
2861             switch (model->type)
2862             {
2863             default: //FIXME These if statements may not be required
2864                 if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
2865                     gtk_tree_view_column_set_title (tvc, _("R"));
2866                 break;
2867             }
2868             break;
2869 
2870         case COL_TYPE:
2871             switch (model->type)
2872             {
2873             default: //FIXME These if statements may not be required
2874                 if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
2875                     gtk_tree_view_column_set_title (tvc, _("Type"));
2876                 break;
2877             }
2878             break;
2879 
2880         case COL_VALUE:
2881             switch (model->type)
2882             {
2883             default: //FIXME These if statements may not be required
2884                 if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
2885                     gtk_tree_view_column_set_title (tvc, _("Value"));
2886                 break;
2887             }
2888             break;
2889 
2890         case COL_AMOUNT:
2891             switch (model->type)
2892             {
2893             default: //FIXME These if statements may not be required
2894                 if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
2895                     gtk_tree_view_column_set_title (tvc, _("Amount"));
2896                 break;
2897             }
2898             break;
2899 
2900         case COL_AMTVAL:
2901             switch (model->type)
2902             {
2903             default:
2904                 if (depth == TRANS1 || depth == TRANS2)
2905                     gtk_tree_view_column_set_title (tvc, _("Value"));
2906                 else if (depth == SPLIT3)
2907                     gtk_tree_view_column_set_title (tvc, _("Amount"));
2908                 else
2909                     gtk_tree_view_column_set_title (tvc, _("Amount / Value"));
2910                 break;
2911             }
2912             break;
2913 
2914         case COL_COMM:
2915             switch (model->type)
2916             {
2917             default: //FIXME These if statements may not be required
2918                 if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
2919                     gtk_tree_view_column_set_title (tvc, _("Commodity"));
2920                 break;
2921             }
2922             break;
2923 
2924         case COL_RATE:
2925             switch (model->type)
2926             {
2927             default: //FIXME These if statements may not be required
2928                 if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
2929                     gtk_tree_view_column_set_title (tvc, _("Rate"));
2930                 break;
2931             }
2932             break;
2933 
2934         case COL_PRICE:
2935             switch (model->type)
2936             {
2937             default: //FIXME These if statements may not be required
2938                 if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
2939                     gtk_tree_view_column_set_title (tvc, _("Price"));
2940                 break;
2941             }
2942             break;
2943 
2944         case COL_CREDIT:
2945             if(!(model->use_accounting_labels))
2946             {
2947                 switch (model->type)
2948                 {
2949                 case BANK_REGISTER2: //FIXME These if statements may not be required
2950                 if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
2951                         gtk_tree_view_column_set_title (tvc, _("Withdrawal"));
2952                     break;
2953 
2954                 case CASH_REGISTER2:
2955                 if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
2956                         gtk_tree_view_column_set_title (tvc, _("Spend"));
2957                     break;
2958 
2959                 case ASSET_REGISTER2:
2960                 if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
2961                         gtk_tree_view_column_set_title (tvc, _("Decrease"));
2962                     break;
2963 
2964                 case LIABILITY_REGISTER2:
2965                 case EQUITY_REGISTER2:
2966                 case TRADING_REGISTER2:
2967                 if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
2968                         gtk_tree_view_column_set_title (tvc, _("Increase"));
2969                     break;
2970 
2971                 case CREDIT_REGISTER2:
2972                 if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
2973                         gtk_tree_view_column_set_title (tvc, _("Charge"));
2974                     break;
2975 
2976                 case INCOME_REGISTER2:
2977                 case INCOME_LEDGER2:
2978                 if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
2979                         gtk_tree_view_column_set_title (tvc, _("Income"));
2980                     break;
2981 
2982                 case EXPENSE_REGISTER2:
2983                 if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
2984                         gtk_tree_view_column_set_title (tvc, _("Rebate"));
2985                     break;
2986 
2987                 case STOCK_REGISTER2:
2988                 case CURRENCY_REGISTER2:
2989                 case PORTFOLIO_LEDGER2:
2990                 if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
2991                         gtk_tree_view_column_set_title (tvc, _("Sell"));
2992                     break;
2993 
2994                 case RECEIVABLE_REGISTER2:
2995                 if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
2996                         gtk_tree_view_column_set_title (tvc, _("Payment"));
2997                     break;
2998 
2999                 case PAYABLE_REGISTER2:
3000                 if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
3001                         gtk_tree_view_column_set_title (tvc, _("Bill"));
3002                     break;
3003 
3004                 case GENERAL_JOURNAL2:
3005                 if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
3006                         gtk_tree_view_column_set_title (tvc, _("Funds Out"));
3007                     break;
3008 
3009                 case SEARCH_LEDGER2:
3010                 if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
3011                 {
3012                     if (!is_template)
3013                         gtk_tree_view_column_set_title (tvc, _("Funds Out"));
3014                     else
3015                         gtk_tree_view_column_set_title (tvc, _("Credit Formula"));
3016                 }
3017                     break;
3018 
3019                 default:
3020                     if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
3021                         gtk_tree_view_column_set_title (tvc, _("Credit"));
3022                     break;
3023                 }
3024             }
3025             else
3026                 gtk_tree_view_column_set_title (tvc, _("Credit"));
3027             break;
3028 
3029         case COL_DEBIT:
3030             if(!(model->use_accounting_labels))
3031             {
3032                 switch (model->type)
3033                 {
3034                 case BANK_REGISTER2: //FIXME These if statements may not be required
3035                 if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
3036                         gtk_tree_view_column_set_title (tvc, _("Deposit"));
3037                     break;
3038 
3039                 case CASH_REGISTER2:
3040                 if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
3041                         gtk_tree_view_column_set_title (tvc, _("Receive"));
3042                     break;
3043 
3044                 case ASSET_REGISTER2:
3045                 if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
3046                         gtk_tree_view_column_set_title (tvc, _("Increase"));
3047                     break;
3048 
3049                 case LIABILITY_REGISTER2:
3050                 case EQUITY_REGISTER2:
3051                 case TRADING_REGISTER2:
3052                 if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
3053                         gtk_tree_view_column_set_title (tvc, _("Decrease"));
3054                     break;
3055 
3056                 case INCOME_REGISTER2:
3057                 case INCOME_LEDGER2:
3058                 if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
3059                         gtk_tree_view_column_set_title (tvc, _("Charge"));
3060                     break;
3061 
3062                 case EXPENSE_REGISTER2:
3063                 if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
3064                         gtk_tree_view_column_set_title (tvc, _("Expense"));
3065                     break;
3066 
3067                 case STOCK_REGISTER2:
3068                 case CURRENCY_REGISTER2:
3069                 case PORTFOLIO_LEDGER2:
3070                 if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
3071                         gtk_tree_view_column_set_title (tvc, _("Buy"));
3072                     break;
3073 
3074                 case RECEIVABLE_REGISTER2:
3075                 if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
3076                         gtk_tree_view_column_set_title (tvc, _("Invoice"));
3077                     break;
3078 
3079                 case CREDIT_REGISTER2:
3080                 case PAYABLE_REGISTER2:
3081                 if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
3082                         gtk_tree_view_column_set_title (tvc, _("Payment"));
3083                     break;
3084 
3085                 case GENERAL_JOURNAL2:
3086                 if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
3087                         gtk_tree_view_column_set_title (tvc, _("Funds In"));
3088                     break;
3089 
3090                 case SEARCH_LEDGER2:
3091                 if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
3092                 {
3093                     if (!is_template)
3094                         gtk_tree_view_column_set_title (tvc, _("Funds In"));
3095                     else
3096                         gtk_tree_view_column_set_title (tvc, _("Debit Formula"));
3097                 }
3098                     break;
3099 
3100                 default:
3101                 if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
3102                         gtk_tree_view_column_set_title (tvc, _("Debit"));
3103                     break;
3104                 }
3105             }
3106             else
3107                 gtk_tree_view_column_set_title (tvc, _("Debit"));
3108             break;
3109 
3110         case COL_BALANCE:
3111             switch (model->type)
3112             {
3113             default: //FIXME These if statements may not be required
3114                 if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
3115                     gtk_tree_view_column_set_title (tvc, _("Balance"));
3116                 break;
3117             }
3118             break;
3119 
3120         default:
3121             break;
3122         }
3123     }
3124     LEAVE(" ");
3125     g_list_free (columns);
3126 }
3127 
3128 
3129 /* Update the help text */
3130 static void
gtv_sr_help(GncTreeViewSplitReg * view,GtkCellRenderer * cr,ViewCol viewcol,RowDepth depth)3131 gtv_sr_help (GncTreeViewSplitReg *view, GtkCellRenderer *cr, ViewCol viewcol, RowDepth depth)
3132 {
3133     GncTreeModelSplitReg *model;
3134     gchar *help = NULL;
3135     const gchar *current_string;
3136 
3137     ENTER("Help Viewcol is %d and depth is %d", viewcol, depth);
3138 
3139     model = gnc_tree_view_split_reg_get_model_from_view (view);
3140 
3141     switch (viewcol)
3142     {
3143     case COL_DATE:
3144         switch (model->type)
3145         {
3146         default: //FIXME These if statements may not be required
3147             if (depth == TRANS1)
3148             {
3149                 GDate date;
3150 
3151                 current_string = g_object_get_data (G_OBJECT (cr), "current-string");
3152                 g_date_set_parse (&date, current_string);
3153                 help = gnc_tree_util_split_reg_get_date_help (&date);
3154             }
3155             else
3156                 help = g_strdup (" ");
3157             break;
3158         }
3159         break;
3160 
3161     case COL_DUEDATE:
3162         switch (model->type)
3163         {
3164         default: //FIXME These if statements may not be required
3165             if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
3166                 help = g_strdup (_("Enter Due Date"));
3167             break;
3168         }
3169         break;
3170 
3171     case COL_NUMACT:
3172         switch (model->type)
3173         {
3174         case RECEIVABLE_REGISTER2:
3175         case PAYABLE_REGISTER2:
3176             if (depth == TRANS1)
3177                 help = g_strdup (_("Enter the transaction reference, such as the invoice or check number"));
3178             else if (depth == TRANS2 || depth == SPLIT3)
3179                 help = g_strdup (_("Enter the type of transaction, or choose one from the list"));
3180             break;
3181 
3182         default:
3183             if (depth == TRANS1)
3184                 help = g_strdup (_("Enter the transaction number, such as the check number"));
3185             else if (depth == TRANS2 || depth == SPLIT3)
3186                 help = g_strdup (_("Enter the type of transaction, or choose one from the list"));
3187             break;
3188         }
3189         break;
3190 
3191     case COL_DESCNOTES:
3192         switch (model->type)
3193         {
3194         case RECEIVABLE_REGISTER2:
3195             if (depth == TRANS1)
3196                 help = g_strdup (_("Enter the name of the Customer"));
3197             else if (depth == TRANS2)
3198                 help = g_strdup (_("Enter notes for the transaction"));
3199             else if (depth == SPLIT3)
3200                 help = g_strdup (_("Enter a description of the split"));
3201             break;
3202 
3203         case PAYABLE_REGISTER2:
3204             if (depth == TRANS1)
3205                 help = g_strdup (_("Enter the name of the Vendor"));
3206             else if (depth == TRANS2)
3207                 help = g_strdup (_("Enter notes for the transaction"));
3208             else if (depth == SPLIT3)
3209                 help = g_strdup (_("Enter a description of the split"));
3210             break;
3211 
3212         default:
3213             if (depth == TRANS1)
3214                 help = g_strdup (_("Enter a description of the transaction"));
3215             else if (depth == TRANS2)
3216                 help = g_strdup (_("Enter notes for the transaction"));
3217             else if (depth == SPLIT3)
3218                 help = g_strdup (_("Enter a description of the split"));
3219             break;
3220         }
3221         break;
3222 
3223     case COL_TRANSFERVOID:
3224         switch (model->type)
3225         {
3226         default:
3227             if (depth == TRANS1)
3228                 help = g_strdup (_("Enter the account to transfer from, or choose one from the list"));
3229             else if (depth == TRANS2)
3230                 help = g_strdup (_("Reason the transaction was voided"));
3231             else if (depth == SPLIT3)
3232                 help = g_strdup (_("Enter the account to transfer from, or choose one from the list"));
3233             break;
3234         }
3235         break;
3236 
3237     case COL_RECN:
3238         switch (model->type)
3239         {
3240         default: //FIXME These if statements may not be required
3241             if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
3242                 help = g_strdup (_("Enter the reconcile type"));
3243             break;
3244         }
3245         break;
3246 
3247     case COL_TYPE:
3248         switch (model->type)
3249         {
3250         default: //FIXME These if statements may not be required
3251             if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
3252                 help = g_strdup (_("Enter the type of transaction"));
3253             break;
3254         }
3255         break;
3256 
3257     case COL_VALUE:
3258         switch (model->type)
3259         {
3260         default: //FIXME These if statements may not be required
3261             if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
3262                 help = g_strdup (_("Enter the value of shares bought or sold"));
3263             break;
3264         }
3265         break;
3266 
3267     case COL_AMOUNT:
3268         switch (model->type)
3269         {
3270         default: //FIXME These if statements may not be required
3271             if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
3272                 help = g_strdup (_("Enter the number of shares bought or sold"));
3273             break;
3274         }
3275         break;
3276 
3277     case COL_AMTVAL:
3278         switch (model->type)
3279         {
3280         default:
3281             if ((depth == TRANS1) || (depth == TRANS2))
3282                 help = g_strdup (_("Enter the value of shares bought or sold"));
3283             else if (depth == SPLIT3)
3284                 help = g_strdup (_("Enter the number of shares bought or sold"));
3285             break;
3286         }
3287         break;
3288 
3289     case COL_COMM:
3290         switch (model->type)
3291         {
3292         default: //FIXME These if statements may not be required
3293             if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
3294                 help = g_strdup (_("* Indicates the transaction Commodity."));
3295             break;
3296         }
3297         break;
3298 
3299     case COL_RATE:
3300         switch (model->type)
3301         {
3302         default: //FIXME These if statements may not be required
3303             if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
3304                 help = g_strdup (_("Enter the rate"));
3305             break;
3306         }
3307         break;
3308 
3309     case COL_PRICE:
3310         switch (model->type)
3311         {
3312         default: //FIXME These if statements may not be required
3313             if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
3314                 help = g_strdup (_("Enter the effective share price"));
3315             break;
3316         }
3317         break;
3318 
3319     case COL_CREDIT:
3320         switch (model->type)
3321         {
3322         default: //FIXME These if statements may not be required
3323             if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
3324                 help = g_strdup (_("Enter credit formula for real transaction"));
3325             break;
3326         }
3327         break;
3328 
3329     case COL_DEBIT:
3330         switch (model->type)
3331         {
3332         default: //FIXME These if statements may not be required
3333             if (depth == TRANS1 || depth == TRANS2 || depth == SPLIT3)
3334                 help = g_strdup (_("Enter debit formula for real transaction"));
3335             break;
3336         }
3337         break;
3338 
3339     default:
3340             help = g_strdup (" ");
3341         break;
3342     }
3343 
3344     LEAVE("Help text is - %s", help);
3345     if (view->help_text)
3346         g_free (view->help_text);
3347     view->help_text = g_strdup (help);
3348     g_free (help);
3349     g_signal_emit_by_name (view, "help_signal", NULL);
3350 }
3351 
3352 /*###########################################################################*/
3353 
3354 /* Move the selection to the blank split when expanded */
3355 static gboolean
gtv_sr_selection_to_blank(GncTreeViewSplitReg * view)3356 gtv_sr_selection_to_blank (GncTreeViewSplitReg *view)
3357 {
3358     GncTreeModelSplitReg *model;
3359     GtkTreePath *bpath, *spath;
3360     Split *bsplit;
3361 
3362     /* give gtk+ a chance to handle pending events */
3363     while (gtk_events_pending ())
3364         gtk_main_iteration ();
3365 
3366     /* Make sure we have expanded splits */
3367     if (view->priv->expanded == FALSE)
3368         return FALSE;
3369 
3370     model = gnc_tree_view_split_reg_get_model_from_view (view);
3371 
3372     bsplit = gnc_tree_model_split_get_blank_split (model);
3373     bpath =  gnc_tree_model_split_reg_get_path_to_split_and_trans (model, bsplit, NULL);
3374 
3375     spath = gnc_tree_view_split_reg_get_sort_path_from_model_path (view, bpath);
3376 
3377     /* Set cursor to new spath */
3378     gtk_tree_view_set_cursor (GTK_TREE_VIEW (view), spath, NULL, FALSE);
3379 
3380     gtk_tree_path_free (bpath);
3381     gtk_tree_path_free (spath);
3382 
3383     return FALSE;
3384 }
3385 
3386 
3387 /* Call back for when a change to a Transaction requires the selection to get out of the way */
3388 static void
gtv_sr_selection_move_delete_cb(GncTreeModelSplitReg * model,gpointer item,gpointer user_data)3389 gtv_sr_selection_move_delete_cb (GncTreeModelSplitReg *model, gpointer item, gpointer user_data)
3390 {
3391     GncTreeViewSplitReg *view = user_data;
3392     Transaction *trans = item;
3393 
3394     DEBUG("gtv_sr_selection_move_delete_cb view %p model %p trans %p", view, model, trans);
3395 
3396     DEBUG("gtv_sr_selection_move_delete_cb current_trans %p trans %p", view->priv->current_trans, trans);
3397 
3398     /* if same, lets get out of the way, so move */
3399     if (trans == view->priv->current_trans)
3400         gnc_tree_control_split_reg_goto_rel_trans_row (view, 1);
3401 
3402 }
3403 
3404 
3405 /* Call back for focus out event so we can finish edit */
3406 static gboolean
gtv_sr_focus_out_cb(GtkWidget * widget,GdkEventFocus * event,gpointer user_data)3407 gtv_sr_focus_out_cb (GtkWidget *widget, GdkEventFocus *event, gpointer user_data)
3408 {
3409     GncTreeViewSplitReg *view = GNC_TREE_VIEW_SPLIT_REG (user_data);
3410 
3411     gnc_tree_view_split_reg_finish_edit (view);
3412 
3413     return FALSE;
3414 }
3415 
3416 
3417 /* Reconcile column tests */
3418 static gboolean
gtv_sr_recn_tests(GncTreeViewSplitReg * view,GtkTreeViewColumn * column,GtkTreePath * spath)3419 gtv_sr_recn_tests (GncTreeViewSplitReg *view, GtkTreeViewColumn *column, GtkTreePath *spath)
3420 {
3421     GtkCellRenderer *cr0;
3422     GList *renderers;
3423     ViewCol viewcol;
3424 
3425     ENTER(" ");
3426 
3427     // Get the first renderer, it has the view-column value.
3428     renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
3429     cr0 = g_list_nth_data (renderers, 0);
3430     g_list_free (renderers);
3431 
3432     viewcol = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cr0), "view_column"));
3433 
3434     /* Test for change of RECN COLUMN setting from reconciled */
3435     if (viewcol == COL_RECN)
3436     {
3437         /* Are we trying to change the reconcile setting */
3438         if (!gnc_tree_control_split_reg_recn_change (view, spath))
3439         {
3440             LEAVE("Not allowed to change reconciled transaction");
3441             return TRUE;
3442         }
3443     }
3444 
3445     /* Ask, are we allowed to change reconciled values other than 'description / notes / memo'
3446        which we can change always */
3447     if (viewcol != COL_DESCNOTES && viewcol != COL_RECN)
3448     {
3449         if (!gnc_tree_control_split_reg_recn_test (view, spath))
3450         {
3451             LEAVE("Not allowed to edit reconciled transaction");
3452             return TRUE;
3453         }
3454     }
3455     LEAVE(" ");
3456     return FALSE;
3457 }
3458 
3459 
3460 /* Test to see if we need to do a move */
3461 static void
gtv_split_reg_test_for_move(GncTreeModelSplitReg * model,GtkTreePath * spath)3462 gtv_split_reg_test_for_move (GncTreeModelSplitReg *model, GtkTreePath *spath)
3463 {
3464     gint num_of_trans, trans_pos;
3465     gint *indices;
3466 
3467     indices = gtk_tree_path_get_indices (spath);
3468     num_of_trans = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (model), NULL);
3469 
3470     trans_pos = indices[0];
3471 
3472     if (trans_pos < num_of_trans*1/3)
3473         gnc_tree_model_split_reg_move (model, VIEW_UP);
3474 
3475     if (trans_pos > num_of_trans*2/3)
3476         gnc_tree_model_split_reg_move (model, VIEW_DOWN);
3477 }
3478 
3479 /*###########################################################################*/
3480 
3481 /* This is the callback for the mouse click */
3482 static gboolean
gtv_sr_button_cb(GtkWidget * widget,GdkEventButton * event,gpointer user_data)3483 gtv_sr_button_cb (GtkWidget *widget, GdkEventButton *event, gpointer user_data)
3484 {
3485     GncTreeViewSplitReg  *view = GNC_TREE_VIEW_SPLIT_REG (widget);
3486     GncTreeModelSplitReg *model;
3487     GtkTreePath *mpath, *spath;
3488     GtkTreeViewColumn    *col;
3489     GtkTreeIter m_iter;
3490     Split *split = NULL;
3491     Split *rotate_split = NULL;
3492     Transaction *trans = NULL;
3493     gboolean is_trow1, is_trow2, is_split, is_blank;
3494 
3495     model = gnc_tree_view_split_reg_get_model_from_view (view);
3496 
3497     /* This is for a single click */
3498     if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
3499     {
3500         GdkWindow *window = gtk_tree_view_get_bin_window (GTK_TREE_VIEW (view));
3501 
3502         if (event->window != window)
3503             return FALSE;
3504 
3505         // Make sure we have stopped editing.
3506         gnc_tree_view_split_reg_finish_edit (view);
3507 
3508         // This prevents the cell changing.
3509         if (view->priv->stop_cell_move == TRUE)
3510             return TRUE;
3511 
3512         /* Get tree path for row that was clicked, true if row exists */
3513         if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (view), (gint) event->x, (gint) event->y,
3514                                              &spath, &col, NULL, NULL))
3515         {
3516             DEBUG("event->x is %d and event->y is %d", (gint)event->x, (gint)event->y);
3517 
3518             mpath = gnc_tree_view_split_reg_get_model_path_from_sort_path (view, spath);
3519 
3520             /* This is to block the single click on a double click */
3521             if (view->priv->single_button_press > 0)
3522             {
3523                 view->priv->single_button_press = view->priv->single_button_press -1;
3524                 return TRUE;
3525             }
3526 
3527             if (gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &m_iter, mpath))
3528             {
3529                 gchar *mstring, *sstring;
3530                 mstring = gtk_tree_path_to_string (mpath);
3531                 sstring = gtk_tree_path_to_string (spath);
3532                 DEBUG("Mouse Button Press - mpath is %s, spath is %s", mstring, sstring);
3533                 g_free (mstring);
3534                 g_free (sstring);
3535 
3536                 // Reset the transaction confirm flag.
3537                 view->priv->trans_confirm = RESET;
3538 
3539                 gnc_tree_model_split_reg_get_split_and_trans (
3540                        GNC_TREE_MODEL_SPLIT_REG (model), &m_iter, &is_trow1, &is_trow2, &is_split, &is_blank, &split, &trans);
3541 
3542                 // Ask for confirmation if data has been edited, gtv_sr_transaction_changed_confirm return TRUE if canceled
3543                 if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (view), "data-edited")) && gtv_sr_transaction_changed_confirm (view, trans))
3544                 {
3545                     DEBUG("MB - Restore position - Cancel / Discard");
3546 
3547                     /* Restore position - Cancel / Discard */
3548                     if (view->priv->trans_confirm == CANCEL)
3549                     {
3550                         DEBUG("MB - Cancel");
3551 
3552                         // Expand trans on split-trans (We only expand on cancel and more than two splits)
3553                         if ((xaccTransCountSplits (view->priv->dirty_trans) > 2) && view->priv->dirty_trans != NULL)
3554                         {
3555                             // Jump to the first split of dirty_trans.
3556                             gnc_tree_control_split_reg_jump_to (view, NULL, xaccTransGetSplit (view->priv->dirty_trans, 0), FALSE);
3557                         }
3558                         else
3559                             // Jump to the dirty_trans.
3560                             gnc_tree_control_split_reg_jump_to (view, view->priv->dirty_trans, NULL, FALSE);
3561 
3562                         gtk_tree_path_free (spath);
3563                         gtk_tree_path_free (mpath);
3564                         return TRUE;
3565                     }
3566 
3567                     if (view->priv->trans_confirm == DISCARD)
3568                     {
3569                         DEBUG("MB - Discard");
3570                         view->priv->dirty_trans = NULL;
3571                     }
3572                 }
3573                 /* Skip */
3574 
3575                 /* Test for change of transaction */
3576                 if (view->priv->current_trans != trans)
3577                     /* Reset allow changes for reconciled transactions */
3578                     view->change_allowed = FALSE;
3579 
3580                 // Reconcile tests
3581                 if (gtv_sr_recn_tests (view, col, spath))
3582                 {
3583                     gtk_tree_path_free (spath);
3584                     gtk_tree_path_free (mpath);
3585                     return TRUE;
3586                 }
3587 
3588                 // Get the right split for rotate test
3589                 if (is_split)
3590                     rotate_split = split;
3591                 else
3592                     rotate_split = gtv_sr_get_this_split (view, trans);
3593 
3594                 /* Set cursor to column */
3595                 if (!gnc_tree_util_split_reg_rotate (view, col, trans, rotate_split))
3596                     gtk_tree_view_set_cursor (GTK_TREE_VIEW (view), spath, col, TRUE);
3597                 else
3598                     gtk_tree_view_set_cursor (GTK_TREE_VIEW (view), spath, col, FALSE);
3599 
3600                 /* Test to see if we need to do a move */
3601                 gtv_split_reg_test_for_move (model, spath);
3602 
3603                 gtk_tree_path_free (spath);
3604                 gtk_tree_path_free (mpath);
3605                 return TRUE;
3606             }
3607             gtk_tree_path_free (spath);
3608             gtk_tree_path_free (mpath);
3609         }
3610     }
3611 
3612     /* This is for a double click */
3613     if (event->button == 1 && event->type == GDK_2BUTTON_PRESS)
3614     {
3615         GdkWindow *window = gtk_tree_view_get_bin_window (GTK_TREE_VIEW (view));
3616 
3617         if (event->window != window)
3618             return FALSE;
3619 
3620         /* this works on non editable cells like void, balance */
3621         if (model->style != REG2_STYLE_JOURNAL)
3622         {
3623             /* This is to block the single click on a double click */
3624             view->priv->single_button_press = 1;
3625 
3626             if (view->priv->expanded)
3627                 gnc_tree_view_split_reg_collapse_trans (view, NULL);
3628             else
3629                 gnc_tree_view_split_reg_expand_trans (view, NULL);
3630 
3631             /* This updates the plugin page gui */
3632             gnc_tree_view_split_reg_call_uiupdate_cb(view);
3633         }
3634         return TRUE;
3635     }
3636     return FALSE;
3637 }
3638 
3639 
3640 static gboolean
gtv_sr_transaction_changed(GncTreeViewSplitReg * view)3641 gtv_sr_transaction_changed (GncTreeViewSplitReg *view)
3642 {
3643     GncTreeModelSplitReg *model;
3644     GtkTreeViewColumn *col;
3645     GtkTreePath *spath;
3646 
3647     model = gnc_tree_view_split_reg_get_model_from_view (view);
3648 
3649     // spath is where we are...
3650     gtk_tree_view_get_cursor (GTK_TREE_VIEW (view), &spath, &col);
3651 
3652     if (!spath)
3653         return FALSE;
3654 
3655     if (gtv_sr_recn_tests (view, col, spath))
3656     {
3657         gtk_tree_path_free (spath);
3658         return FALSE;
3659     }
3660     gtk_tree_path_free (spath);
3661 
3662     // Reset the transaction confirm flag.
3663     view->priv->trans_confirm = RESET;
3664 
3665     //Ask for confirmation if data has been edited, gtv_sr_transaction_changed_confirm return TRUE if canceled
3666     if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (view), "data-edited")) && gtv_sr_transaction_changed_confirm (view, NULL))
3667     {
3668         /* Restore position - Cancel / Discard */
3669         DEBUG("KB - Restore position - Cancel / Discard");
3670 
3671         if (view->priv->trans_confirm == CANCEL)
3672         {
3673             DEBUG("KB - Cancel");
3674 
3675             // Expand trans on split-trans (We only expand on cancel)
3676             if ((xaccTransCountSplits (view->priv->dirty_trans) > 2) && view->priv->dirty_trans != NULL)
3677             {
3678                 // Jump to the first split of dirty_trans.
3679                 gnc_tree_control_split_reg_jump_to (view, NULL, xaccTransGetSplit (view->priv->dirty_trans, 0), FALSE);
3680             }
3681             else
3682                 // Jump to the dirty_trans.
3683                 gnc_tree_control_split_reg_jump_to (view, view->priv->dirty_trans, NULL, FALSE);
3684 
3685             return TRUE;
3686         }
3687 
3688         if (view->priv->trans_confirm == DISCARD)
3689         {
3690             DEBUG("KB - Discard");
3691 
3692             gnc_tree_view_split_reg_block_selection (view, TRUE);
3693 
3694             // Check to see if dirty_trans expanded, collapse it.
3695             if (gnc_tree_view_split_reg_trans_expanded (view, view->priv->dirty_trans))
3696                 gnc_tree_view_split_reg_collapse_trans (view, view->priv->dirty_trans);
3697 
3698             gnc_tree_view_split_reg_block_selection (view, FALSE);
3699 
3700             /* Remove the blank split and re-add - done so we keep it last in list */
3701             gnc_tree_model_split_reg_set_blank_split_parent (model, view->priv->dirty_trans, TRUE);
3702             gnc_tree_model_split_reg_set_blank_split_parent (model, view->priv->dirty_trans, FALSE);
3703 
3704             // Set the transaction to show correct view
3705             gnc_tree_view_split_reg_format_trans (view, view->priv->dirty_trans);
3706             view->priv->dirty_trans = NULL;
3707         }
3708     }
3709     return FALSE;
3710 }
3711 
3712 
3713 /* Return whether the cell is in editing mode */
3714 static gboolean
gtv_sr_get_editing(GtkTreeViewColumn * col)3715 gtv_sr_get_editing (GtkTreeViewColumn *col)
3716 {
3717     GtkCellRenderer *cr0 = NULL, *cr1 = NULL;
3718     GList *renderers;
3719     gboolean cell_editing0 = FALSE;
3720     gboolean cell_editing1 = FALSE;
3721     gboolean editing = FALSE;
3722 
3723     renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (col));
3724     cr0 = g_list_nth_data (renderers, 0); // We always have one renderer
3725     if (g_list_length (renderers) == 2)
3726         cr1 = g_list_nth_data (renderers, 1); // There is only one column with two renderers
3727     g_list_free (renderers);
3728 
3729     if (gtk_cell_renderer_get_visible (cr0))
3730         g_object_get (G_OBJECT (cr0), "editing", &cell_editing0, NULL);
3731 
3732     if (cr1 && gtk_cell_renderer_get_visible (cr1))
3733         g_object_get (G_OBJECT (cr1), "editing", &cell_editing1, NULL);
3734 
3735     if (cell_editing0 || cell_editing1)
3736         editing = TRUE;
3737 
3738     DEBUG("editing is %d for column title %s", editing, gtk_tree_view_column_get_title (col));
3739 
3740     return editing;
3741 }
3742 
3743 
3744 /* For handling keynav */
3745 static gboolean
gtv_sr_key_press_cb(GtkWidget * widget,GdkEventKey * event,gpointer user_data)3746 gtv_sr_key_press_cb (GtkWidget *widget, GdkEventKey *event, gpointer user_data)
3747 {
3748     GncTreeViewSplitReg *view = GNC_TREE_VIEW_SPLIT_REG (widget);
3749     GncTreeModelSplitReg *model;
3750     GtkTreeViewColumn *col;
3751     GtkTreePath *spath;
3752     GtkTreePath *start_path, *end_path;
3753     gboolean editing = FALSE;
3754     gboolean step_off = FALSE;
3755     gboolean trans_changed = FALSE;
3756     Transaction *btrans, *ctrans, *hetrans;
3757     gboolean goto_blank = FALSE;
3758     gboolean next_trans = TRUE;
3759 
3760     // spath is where we are, before key press...
3761     gtk_tree_view_get_cursor (GTK_TREE_VIEW (view), &spath, &col);
3762 
3763     if (event->type != GDK_KEY_PRESS)
3764     {
3765         if (spath)
3766             gtk_tree_path_free (spath);
3767         return FALSE;
3768     }
3769 
3770     switch (event->keyval)
3771     {
3772     case GDK_KEY_plus:
3773     case GDK_KEY_minus:
3774     case GDK_KEY_KP_Add:
3775     case GDK_KEY_KP_Subtract:
3776 
3777         if (!spath)
3778             return TRUE;
3779 
3780         gtk_tree_path_free (spath);
3781         return TRUE; //FIXME I may use these to expand / collapse to splits later...
3782         break;
3783 
3784     case GDK_KEY_Up:
3785     case GDK_KEY_Down:
3786 
3787         model = gnc_tree_view_split_reg_get_model_from_view (view);
3788 
3789         if (event->keyval == GDK_KEY_Up)
3790         {
3791             gnc_tree_model_split_reg_move (model, VIEW_UP);
3792         }
3793         else
3794             gnc_tree_model_split_reg_move (model, VIEW_DOWN);
3795 
3796         return FALSE;
3797         break;
3798 
3799     case GDK_KEY_Page_Up:
3800     case GDK_KEY_Page_Down:
3801 
3802         model = gnc_tree_view_split_reg_get_model_from_view (view);
3803 
3804         if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (view), &start_path, &end_path))
3805         {
3806             if (event->keyval == GDK_KEY_Page_Up)
3807             {
3808                 GtkTreePath *new_start_path;
3809                 gint *start_indices, *end_indices;
3810                 gint new_start;
3811                 gint num_of_trans;
3812 
3813                 start_indices = gtk_tree_path_get_indices (start_path);
3814                 end_indices = gtk_tree_path_get_indices (end_path);
3815                 num_of_trans = end_indices[0] - start_indices[0];
3816 
3817                 new_start = start_indices[0] - num_of_trans + 2;
3818 
3819                 if (new_start < 0)
3820                     new_start = 0;
3821 
3822                 new_start_path = gtk_tree_path_new_from_indices (new_start, -1);
3823 
3824                 /* Scroll to cell, top of view */
3825                 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (view), new_start_path, NULL, TRUE, 0.0, 0.0);
3826 
3827                 /* Set cursor to new top row */
3828                 gtk_tree_view_set_cursor (GTK_TREE_VIEW (view), new_start_path, col, FALSE);
3829 
3830                 gtk_tree_path_free (new_start_path);
3831 
3832                 gnc_tree_model_split_reg_move (model, VIEW_UP);
3833             }
3834             else
3835             {
3836                 GtkTreePath *new_end_path;
3837                 gint *start_indices, *end_indices;
3838                 gint new_end;
3839                 gint num_of_trans, total_num;
3840 
3841                 start_indices = gtk_tree_path_get_indices (start_path);
3842                 end_indices = gtk_tree_path_get_indices (end_path);
3843                 num_of_trans = end_indices[0] - start_indices[0];
3844 
3845                 total_num = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (model), NULL);
3846 
3847                 new_end = end_indices[0] + num_of_trans - 1;
3848 
3849                 if (new_end > (total_num - 1))
3850                     new_end = total_num -1;
3851 
3852                 new_end_path = gtk_tree_path_new_from_indices (new_end, -1);
3853 
3854                 /* Scroll to cell, bottom of view */
3855                 if (model->use_double_line == TRUE)
3856                 {
3857                     gtk_tree_path_down (new_end_path);
3858                     gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (view), new_end_path, NULL, TRUE, 1.0, 0.0);
3859                     gtk_tree_path_up (new_end_path);
3860                 }
3861                 else
3862                     gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (view), new_end_path, NULL, TRUE, 1.0, 0.0);
3863 
3864                 /* Set cursor to new bottom row */
3865                 gtk_tree_view_set_cursor (GTK_TREE_VIEW (view), new_end_path, col, FALSE);
3866 
3867                 gtk_tree_path_free (new_end_path);
3868 
3869                 gnc_tree_model_split_reg_move (model, VIEW_DOWN);
3870             }
3871             gtk_tree_path_free (start_path);
3872             gtk_tree_path_free (end_path);
3873         }
3874         return TRUE;
3875         break;
3876 
3877     case GDK_KEY_Home:
3878     case GDK_KEY_End:
3879 
3880         model = gnc_tree_view_split_reg_get_model_from_view (view);
3881 
3882         if (event->keyval == GDK_KEY_Home)
3883             hetrans = gnc_tree_model_split_reg_get_first_trans (model);
3884         else
3885             hetrans = gnc_tree_model_split_get_blank_trans (model);
3886 
3887         model->current_trans = hetrans;
3888 
3889         if (!gnc_tree_model_split_reg_trans_is_in_view (model, hetrans))
3890             g_signal_emit_by_name (model, "refresh_trans");
3891         else
3892             gnc_tree_control_split_reg_jump_to (view, hetrans, NULL, FALSE);
3893 
3894         return TRUE;
3895         break;
3896 
3897     case GDK_KEY_Return:
3898     case GDK_KEY_space:
3899 
3900         if (!spath)
3901             return TRUE;
3902 
3903         // Do the reconcile tests.
3904         if (!gtv_sr_recn_tests (view, col, spath))
3905         {
3906             /* Set cursor to new column, open for editing */
3907             gtk_tree_view_set_cursor (GTK_TREE_VIEW (view), spath, col, TRUE);
3908         }
3909 
3910         gtk_tree_path_free (spath);
3911         return TRUE;
3912         break;
3913 
3914     case GDK_KEY_KP_Enter:
3915 
3916         if (!spath)
3917             return TRUE;
3918 
3919         goto_blank = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL_REGISTER,
3920                                          GNC_PREF_ENTER_MOVES_TO_END);
3921 
3922         model = gnc_tree_view_split_reg_get_model_from_view (view);
3923         btrans = gnc_tree_model_split_get_blank_trans (model);
3924         ctrans = gnc_tree_view_split_reg_get_current_trans (view);
3925 
3926         /* Are we on the blank transaction */
3927         if (btrans == ctrans)
3928             next_trans = FALSE;
3929 
3930         /* First record the transaction */
3931         if (gnc_tree_view_split_reg_enter (view))
3932         {
3933             /* Now move. */
3934             if (goto_blank)
3935                 g_idle_add ((GSourceFunc)gnc_tree_control_split_reg_jump_to_blank, view);
3936             else if (next_trans)
3937                 gnc_tree_control_split_reg_goto_rel_trans_row (view, 1);
3938         }
3939         return TRUE;
3940         break;
3941 
3942     case GDK_KEY_Tab:
3943     case GDK_KEY_ISO_Left_Tab:
3944     case GDK_KEY_KP_Tab:
3945 
3946         if (!spath)
3947             return TRUE;
3948 
3949         // Bypass Auto-complete
3950         if (event->state & GDK_CONTROL_MASK)
3951             view->priv->auto_complete = TRUE;
3952 
3953         // Make sure we have stopped editing.
3954         gnc_tree_view_split_reg_finish_edit (view);
3955 
3956         // This prevents the cell changing.
3957         if (view->priv->stop_cell_move == TRUE)
3958         {
3959             gtk_tree_path_free (spath);
3960             return TRUE;
3961         }
3962 
3963         while (!editing && !step_off) // lets step over non editable columns
3964         {
3965             // Create a copy of the path we started with.
3966             GtkTreePath *start_spath = gtk_tree_path_copy (spath);
3967             gint *start_indices = gtk_tree_path_get_indices (start_spath);
3968             gint *next_indices;
3969 
3970             {
3971                 gchar *string = gtk_tree_path_to_string (start_spath);
3972                 DEBUG("Column title is %s and start path is %s", gtk_tree_view_column_get_title (col), string);
3973                 g_free (string);
3974             }
3975 
3976             /* Step to the next column, we may wrap */
3977             gnc_tree_view_keynav (GNC_TREE_VIEW (view), &col, spath, event); // returns path and column
3978 
3979             {
3980                 gchar *string = gtk_tree_path_to_string (spath);
3981                 DEBUG("Column title is %s and spath is %s", gtk_tree_view_column_get_title (col), string);
3982                 g_free (string);
3983             }
3984 
3985             // Have we changed transactions
3986             next_indices = gtk_tree_path_get_indices (spath);
3987             if (start_indices[0] != next_indices[0])
3988             {
3989                  if (view->priv->dirty_trans != NULL) // from a dirty trans
3990                     trans_changed = TRUE;
3991 
3992                  /* Reset allow changes for reconciled transctions */
3993                  view->change_allowed = FALSE;
3994             }
3995 
3996             // Do the reconcile tests.
3997             if (gnc_tree_view_path_is_valid (GNC_TREE_VIEW (view), spath))
3998             {
3999                 if (gtv_sr_recn_tests (view, col, spath))
4000                 {
4001                     gtk_tree_path_free (start_spath);
4002                     gtk_tree_path_free (spath);
4003                     return TRUE;
4004                 }
4005             }
4006 
4007             // Have we stepped off the end
4008             if (!spath || !gnc_tree_view_path_is_valid (GNC_TREE_VIEW (view), spath) || trans_changed) // We have stepped off the end / or changed trans
4009             {
4010                 // Test for transaction changed.
4011                 if (gtv_sr_transaction_changed (view))
4012                 {
4013                     gtk_tree_path_free (start_spath);
4014                     gtk_tree_path_free (spath);
4015                     return TRUE;
4016                 }
4017                 step_off = TRUE;
4018             }
4019             // This stops the cell activation on discard
4020             if (view->priv->trans_confirm != DISCARD)
4021             {
4022                 // Set cursor to new column, open for editing
4023                 gtk_tree_view_set_cursor (GTK_TREE_VIEW (view), spath, col, TRUE);
4024             }
4025             // Is this an editable cell ?
4026             editing = gtv_sr_get_editing (col);
4027             gtk_tree_path_free (start_spath);
4028         }
4029         gtk_tree_path_free (spath);
4030         return TRUE;
4031         break;
4032 
4033     default:
4034         gtk_tree_path_free (spath);
4035 	return FALSE;
4036     }
4037 }
4038 
4039 
4040 /*###########################################################################*/
4041 
4042 /* Callback for selection move */
4043 static void
gtv_sr_motion_cb(GtkTreeSelection * sel,gpointer user_data)4044 gtv_sr_motion_cb (GtkTreeSelection *sel, gpointer user_data)
4045 {
4046     GncTreeViewSplitReg *view = GNC_TREE_VIEW_SPLIT_REG (user_data);
4047     GncTreeModelSplitReg *model;
4048     GtkTreePath *mpath, *spath;
4049     Split *split = NULL;
4050     Transaction *trans = NULL;
4051     Transaction *old_trans;
4052     gboolean is_trow1, is_trow2, is_split, is_blank;
4053     RowDepth depth = 0;
4054     GtkTreeIter m_iter;
4055     gint *indices;
4056 
4057     model = gnc_tree_view_split_reg_get_model_from_view (view);
4058 
4059     ENTER("View is %p and Model is %p", view, model);
4060 
4061     DEBUG("Current trans %p, Split %p, Depth %d and Dirty Trans %p", view->priv->current_trans, view->priv->current_split,
4062                                                                      view->priv->current_depth, view->priv->dirty_trans);
4063 
4064     /* Reset help text */
4065     if (view->help_text)
4066         g_free (view->help_text);
4067     view->help_text = g_strdup (" ");
4068     g_signal_emit_by_name (view, "help_signal", NULL);
4069 
4070     if (gtv_sr_get_model_iter_from_selection (view, sel, &m_iter))
4071     {
4072         gchar *mstring, *sstring;
4073 
4074         mpath = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &m_iter);
4075         spath = gnc_tree_view_split_reg_get_sort_path_from_model_path (view, mpath);
4076 
4077         mstring = gtk_tree_path_to_string (mpath);
4078         sstring = gtk_tree_path_to_string (spath);
4079         DEBUG("Valid Selection - mpath is %s, spath is %s", mstring, sstring);
4080         g_free (mstring);
4081         g_free (sstring);
4082 
4083         /* save the current path */
4084         gnc_tree_view_split_reg_set_current_path (view, mpath);
4085 
4086         /* Use depth to determine if it is a split or transaction */
4087         depth = gtk_tree_path_get_depth (mpath);
4088 
4089         gtk_tree_path_free (mpath);
4090 
4091         gnc_tree_model_split_reg_get_split_and_trans (
4092                 GNC_TREE_MODEL_SPLIT_REG (model), &m_iter, &is_trow1, &is_trow2, &is_split, &is_blank, &split, &trans);
4093 
4094         DEBUG("Get model trans %p, split %p, is_split %d, is_blank %d\n", trans, split, is_split, is_blank);
4095 
4096         /* Update the titles if depth changes, we change rows */
4097         if (depth != view->priv->current_depth)
4098             gtv_sr_titles (view, depth);
4099 
4100         /* Move the blank split */
4101         gnc_tree_model_split_reg_set_blank_split_parent (model, trans, FALSE);
4102 
4103         /* Save trans / split / depth to the current values */
4104         old_trans = view->priv->current_trans;
4105         view->priv->current_trans = trans;
4106         view->priv->current_split = split;
4107         view->priv->current_depth = depth;
4108 
4109         DEBUG("Current trans %p, split %p, depth %d and old_trans %p", view->priv->current_trans, view->priv->current_split,
4110                                                                      view->priv->current_depth, old_trans);
4111 
4112         /* Save trans and current row to model */
4113         model->current_trans = trans;
4114         indices = gtk_tree_path_get_indices (spath);
4115         model->current_row = indices[0];
4116         gnc_tree_model_split_reg_sync_scrollbar (model);
4117 
4118         /* Test for change of transaction and old transaction equals a dirty transaction */
4119         if ((trans != old_trans) && (old_trans == view->priv->dirty_trans))
4120         {
4121             if (gtv_sr_transaction_changed (view))
4122             {
4123                 gtk_tree_path_free (spath);
4124                 LEAVE("Leave Transaction Changed");
4125                 return;
4126             }
4127         }
4128         if (view->priv->trans_confirm == CANCEL)
4129         {
4130             gtk_tree_path_free (spath);
4131             LEAVE("Leave Transaction Changed - Cancel");
4132             return;
4133         }
4134 
4135         /* Auto expand transaction and collapse previous transaction */
4136         if (old_trans != trans)
4137         {
4138             if (model->style != REG2_STYLE_JOURNAL)
4139             {
4140                 gnc_tree_view_split_reg_block_selection (view, TRUE);
4141 
4142                 if (gnc_tree_view_split_reg_trans_expanded (view, old_trans))
4143                     gnc_tree_view_split_reg_collapse_trans (view, old_trans);
4144 
4145                 gnc_tree_view_split_reg_block_selection (view, FALSE);
4146             }
4147             else
4148                 gnc_tree_view_split_reg_expand_trans (view, NULL);
4149 
4150             if (model->style == REG2_STYLE_AUTO_LEDGER)
4151             {
4152                 gtk_tree_view_expand_row (GTK_TREE_VIEW (view), spath, TRUE);
4153 
4154                 view->priv->expanded = TRUE;
4155 
4156                 if (view->priv->selection_to_blank_on_expand)
4157                     gtv_sr_selection_to_blank (view);
4158             }
4159         }
4160         gtk_tree_path_free (spath);
4161 
4162         // Check to see if current trans is expanded and remember
4163         if (gnc_tree_view_split_reg_trans_expanded (view, trans))
4164             view->priv->expanded = TRUE;
4165         else
4166             view->priv->expanded = FALSE;
4167     }
4168     else
4169     {
4170         DEBUG("Not Valid Selection");
4171         /* We do not have a valid iter */
4172         gtv_sr_titles (view, 0);
4173 
4174         /* Move the blank split to the last transaction */
4175         gnc_tree_model_split_reg_set_blank_split_parent (model, NULL, FALSE);
4176 
4177         /* Set the default selection start position */
4178         gnc_tree_view_split_reg_default_selection (view);
4179     }
4180 
4181     /* This updates the plugin page gui */
4182     gnc_tree_view_split_reg_call_uiupdate_cb (view);
4183 
4184     LEAVE(" ");
4185 }
4186 
4187 /*###########################################################################*/
4188 
4189 /* Connected to "edited" from cellrenderer. For reference, see
4190    split-register-model-save.c */
4191 static void
gtv_sr_edited_cb(GtkCellRendererText * cell,const gchar * path_string,const gchar * new_text,gpointer user_data)4192 gtv_sr_edited_cb (GtkCellRendererText *cell, const gchar *path_string,
4193                const gchar *new_text, gpointer user_data)
4194 {
4195     GncTreeViewSplitReg  *view = GNC_TREE_VIEW_SPLIT_REG (user_data);
4196     GncTreeModelSplitReg *model;
4197     GtkCellEditable      *editable;
4198 
4199     editable = g_object_get_data (G_OBJECT (cell), "cell-editable");
4200 
4201     DEBUG("cell is %p editable pointer is %p and id %lu", cell, editable, view->priv->fo_handler_id);
4202 
4203     /* Remove the focus out cb if one exists */
4204     if (view->priv->fo_handler_id != 0)
4205     {
4206         if (g_signal_handler_is_connected (G_OBJECT (editable), view->priv->fo_handler_id))
4207             g_signal_handler_disconnect (G_OBJECT (editable), view->priv->fo_handler_id);
4208     }
4209     view->priv->fo_handler_id = 0;
4210 
4211     /* Make sure we set focus to the tree view after cell editing */
4212     gtk_widget_grab_focus (GTK_WIDGET (view));
4213 
4214     if (g_strcmp0 (g_object_get_data (G_OBJECT (cell), "current-string"), new_text) == 0) // No change, return
4215     {
4216         if (view->priv->stop_cell_move == FALSE)
4217             return;
4218     }
4219 
4220     model = gnc_tree_view_split_reg_get_model_from_view (view);
4221     g_return_if_fail (model);
4222 
4223     /* Are we using a template or not */
4224     if (!gnc_tree_model_split_reg_get_template (model))
4225         gtv_sr_edited_normal_cb (cell, path_string, new_text, view);
4226     else
4227         gtv_sr_edited_template_cb (cell, path_string, new_text, view);
4228 }
4229 
4230 
4231 /* This is used for the normal registers */
4232 static void
gtv_sr_edited_normal_cb(GtkCellRendererText * cell,const gchar * path_string,const gchar * new_text,gpointer user_data)4233 gtv_sr_edited_normal_cb (GtkCellRendererText *cell, const gchar *path_string,
4234                const gchar *new_text, gpointer user_data)
4235 {
4236     GncTreeViewSplitReg  *view = GNC_TREE_VIEW_SPLIT_REG (user_data);
4237     GncTreeModelSplitReg *model;
4238     GtkCellEditable      *editable;
4239     GtkTreeIter           m_iter;
4240     Split                *split;
4241     Transaction          *trans;
4242     gboolean              is_trow1, is_trow2, is_split, is_blank;
4243     ViewCol               viewcol;
4244     char                 *error_loc = NULL;
4245     Account              *anchor = view->priv->anchor;
4246 
4247     editable = g_object_get_data (G_OBJECT (cell), "cell-editable");
4248 
4249     DEBUG("cell is %p editable pointer is %p", cell, editable);
4250 
4251     g_return_if_fail (gtv_sr_get_model_iter_from_view_string (view, path_string, &m_iter));
4252 
4253     viewcol = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cell), "view_column"));
4254 
4255     model = gnc_tree_view_split_reg_get_model_from_view (view);
4256     g_return_if_fail (model);
4257 
4258     gnc_tree_model_split_reg_get_split_and_trans (
4259         model, &m_iter, &is_trow1, &is_trow2, &is_split, &is_blank, &split, &trans);
4260 
4261     switch (viewcol) {
4262     case COL_DATE:
4263         /* Column is DATE */
4264         if (is_trow1)
4265         {
4266             GDate parsed_date;
4267             gnc_tree_util_split_reg_parse_date (&parsed_date, new_text);
4268             if (g_date_valid (&parsed_date))
4269             {
4270                 gtv_sr_begin_edit (view, trans);
4271                 xaccTransSetDate (trans, g_date_get_day (&parsed_date), g_date_get_month (&parsed_date), g_date_get_year (&parsed_date));
4272             }
4273             else
4274             {
4275                 // We should never get here
4276                 PERR("invalid date '%s'", new_text);
4277             }
4278         }
4279         break;
4280 
4281     case COL_NUMACT:
4282         /* Column is NUM / ACT */
4283         gtv_sr_begin_edit (view, trans);
4284         if (is_trow1)
4285         {
4286             /* set per book option */
4287             gnc_set_num_action (trans, gtv_sr_get_this_split (view, trans),
4288                                                                 new_text, NULL);
4289 
4290             if (!qof_book_use_split_action_for_num_field (gnc_get_current_book()))
4291             {
4292                 // Set the last number value for this account.
4293                 if (gnc_strisnum (new_text) && anchor != NULL)
4294                     xaccAccountSetLastNum (anchor, new_text);
4295             }
4296         }
4297         if (is_trow2)
4298         {
4299             /* set per book option */
4300             gnc_set_num_action (trans, gtv_sr_get_this_split (view, trans),
4301                                                                 NULL, new_text);
4302 
4303             if (qof_book_use_split_action_for_num_field (gnc_get_current_book()))
4304             {
4305                 // Set the last number value for this account.
4306                 if (gnc_strisnum (new_text) && anchor != NULL)
4307                     xaccAccountSetLastNum (anchor, new_text);
4308             }
4309         }
4310         if (is_split)
4311         {
4312             /* Set split-action with gnc_set_num_action which is the same as
4313              * xaccSplitSetAction with these arguments */
4314             gnc_set_num_action (NULL, split, NULL, new_text);
4315         }
4316         break;
4317 
4318     case COL_DESCNOTES:
4319         /* Column is DESCRIPTION / NOTES / MEMO */
4320         gtv_sr_begin_edit (view, trans);
4321         if (is_trow1)
4322         {
4323             xaccTransSetDescription (trans, new_text);
4324             // This will potentially fill in the rest of the transaction.
4325             if (view->priv->auto_complete == FALSE)
4326             {
4327                 gnc_tree_control_auto_complete (view, trans, new_text);
4328                 view->priv->auto_complete = TRUE;
4329             }
4330         }
4331         if (is_trow2)
4332             xaccTransSetNotes (trans, new_text);
4333 
4334         if (is_split)
4335             xaccSplitSetMemo (split, new_text);
4336 
4337         break;
4338 
4339     case COL_RECN:
4340         /* Column is RECONCILE */
4341         gtv_sr_begin_edit (view, trans);
4342         {
4343             char rec = 'n';
4344 
4345             if (new_text != NULL)
4346             {
4347                 const gchar *cflag = gnc_get_reconcile_str (CREC);
4348                 const gchar *nflag = gnc_get_reconcile_str (NREC);
4349                 const char recn_flags[] = {NREC, CREC, 0}; // List of reconciled flags
4350                 const gchar *flags;
4351                 gchar *this_flag;
4352                 gint index = 0;
4353 
4354                 flags = g_strconcat (nflag, cflag, NULL); // List of translated strings.
4355 
4356                 /* Find the current flag in the list of flags */
4357                 this_flag = strstr (flags, new_text);
4358 
4359                 if (this_flag != NULL)
4360                 {
4361                     index = this_flag - flags;
4362                     rec = recn_flags[index];
4363                 }
4364             }
4365             if (is_trow1)
4366                 xaccSplitSetReconcile (gtv_sr_get_this_split (view, trans), rec);
4367             if (is_split)
4368                 xaccSplitSetReconcile (split, rec);
4369         }
4370         break;
4371 
4372     case COL_TYPE:
4373         /* Column is TYPE */
4374         gtv_sr_begin_edit (view, trans);
4375         {
4376             char type = TXN_TYPE_NONE;
4377             if (new_text != NULL)
4378                 type = new_text[0];
4379 
4380             if (is_trow1)
4381                 xaccTransSetTxnType (trans, type);
4382         }
4383         break;
4384 
4385     case COL_TRANSFERVOID:
4386     case COL_AMTVAL:
4387     case COL_AMOUNT:
4388     case COL_PRICE:
4389     case COL_DEBIT:
4390     case COL_CREDIT:
4391         {
4392             Account       *acct, *old_acct;
4393             gnc_numeric    input;
4394             Split         *osplit = NULL;
4395             gboolean       valid_input = FALSE;
4396             gboolean       force = FALSE;
4397             gboolean       input_used = FALSE;
4398 
4399             gtv_sr_begin_edit (view, trans);
4400 
4401             /* Get the split pair if anchored to a register */
4402             if (!is_split && anchor)
4403             {
4404                 if (!gtv_sr_get_split_pair (view, trans, &osplit, &split))
4405                 {
4406                     DEBUG("couldn't get split pair");
4407                     break;
4408                 }
4409             }
4410 
4411             /* Setup the account field */
4412             if (viewcol == COL_TRANSFERVOID)
4413             {
4414                 view->priv->stop_cell_move = FALSE;
4415                 acct = gnc_tree_control_split_reg_get_account_by_name (view, new_text);
4416                 if (acct == NULL)
4417                 {
4418                     DEBUG("Account is NULL");
4419                     xaccSplitReinit(split);
4420                     if (osplit)
4421                         xaccSplitDestroy (osplit);
4422 
4423                     g_free (view->priv->transfer_string);
4424                     view->priv->transfer_string = g_strdup (new_text);
4425                     view->priv->stop_cell_move = TRUE;
4426 
4427                     /* this will populate cell with original value */
4428                     g_idle_add ((GSourceFunc) gtv_sr_idle_transfer, view);
4429                     break;
4430                 }
4431 
4432                 if (acct != NULL && is_split)
4433                 {
4434                     old_acct = xaccSplitGetAccount (split);
4435                     xaccSplitSetAccount (split, acct);
4436                     if (!gnc_commodity_equiv (xaccAccountGetCommodity (old_acct), xaccAccountGetCommodity (acct)))
4437                         force = TRUE;
4438                 }
4439                 else
4440                 {
4441                     old_acct = xaccSplitGetAccount (osplit);
4442                     xaccSplitSetAccount (osplit, acct);
4443                     if (!gnc_commodity_equiv (xaccAccountGetCommodity (old_acct), xaccAccountGetCommodity (acct)))
4444                         force = TRUE;
4445                 }
4446             }
4447             else
4448             {
4449                 if (!gnc_exp_parser_parse (new_text, &input, &error_loc))
4450                     break;
4451                 else
4452                     valid_input = TRUE;
4453             }
4454 
4455             /* Get the account for this split */
4456             acct = xaccSplitGetAccount (split);
4457             if (!acct)
4458             {
4459                 if (anchor)
4460                 {
4461                     xaccSplitSetAccount (split, anchor);
4462                     acct = xaccSplitGetAccount (split);
4463                 }
4464                 else
4465                 {
4466                     break; //Well, what else is there to do?
4467                 }
4468             }
4469 
4470             /* Set the transaction currency if not set */
4471             if (!xaccTransGetCurrency (trans))
4472             {
4473                 // set transaction currency to that of register (which is guaranteed to be a currency)
4474                 xaccTransSetCurrency (trans, view->priv->reg_currency);
4475 
4476                 // We are on General ledger
4477                 if (!anchor)
4478                 {
4479                     xaccTransSetCurrency (trans, gnc_account_or_default_currency (xaccSplitGetAccount (split), NULL));
4480                 }
4481             }
4482 
4483             // No need to check for a non-currency register because that's what
4484             // was already checked when reg_currency was stored.
4485 
4486             /* This computes the value if we just commit the split after entering account */
4487             if (!valid_input)
4488                 input = gnc_tree_util_split_reg_get_value_for (view, trans, split, is_blank);
4489 
4490             // Negate the input if COL_CREDIT
4491             if (viewcol == COL_CREDIT)
4492                 input = gnc_numeric_neg (input);
4493 
4494             // Set the split parent trans
4495             xaccSplitSetParent (split, trans);
4496 
4497             // If we are at transaction level, column is value, split level is amount
4498             if (viewcol == COL_AMTVAL)
4499             {
4500                 gnc_tree_util_set_number_for_input (view, trans, split, input, COL_AMTVAL);
4501                 input_used = TRUE;
4502             }
4503 
4504             // The price of stock / shares, editable only when expanded and sub_account
4505             if (viewcol == COL_AMOUNT)
4506             {
4507                 gnc_tree_util_set_number_for_input (view, trans, split, input, COL_AMTVAL);
4508                 input_used = TRUE;
4509             }
4510 
4511             // The price of stock / shares
4512             if (viewcol == COL_PRICE)
4513             {
4514                 gnc_tree_util_set_number_for_input (view, trans, split, input, COL_PRICE);
4515                 input_used = TRUE;
4516             }
4517 
4518             // Check if this is a stock / share amount
4519             if (viewcol == COL_CREDIT || viewcol == COL_DEBIT)
4520             {
4521                 if (!gnc_commodity_is_currency (xaccAccountGetCommodity (acct)))
4522                 {
4523                     gnc_tree_util_set_number_for_input (view, trans, split, input, viewcol);
4524                     input_used = TRUE;
4525                 }
4526             }
4527 
4528             // This is used in transaction mode, two splits
4529             if (input_used == FALSE)
4530             {
4531                 if (gnc_commodity_is_currency (xaccAccountGetCommodity (acct)))
4532                     gnc_tree_util_split_reg_set_value_for (view, trans, split, input, force);
4533                 else
4534                     gnc_tree_util_set_value_for_amount (view, trans, split, input);
4535             }
4536 
4537             // If this is the blank split, promote it.
4538             if (is_blank)
4539             {
4540                 /*FIXME May be this should be a signal - Promote the blank split to a real split */
4541                 g_idle_add ((GSourceFunc) gnc_tree_model_split_reg_commit_blank_split, gnc_tree_view_split_reg_get_model_from_view (view));
4542 
4543                 /* scroll when view idle */
4544                 g_idle_add ((GSourceFunc) gnc_tree_view_split_reg_scroll_to_cell, view);
4545             }
4546 
4547             // In transaction mode, two splits only, set up the other split.
4548             if (osplit)
4549             {
4550                 xaccSplitSetParent (osplit, trans);
4551 
4552                 if (gnc_commodity_is_currency (xaccAccountGetCommodity (acct)))
4553                     gnc_tree_util_split_reg_set_value_for (view, trans, osplit, gnc_numeric_neg (input), force);
4554                 else
4555                     gnc_tree_util_set_value_for_amount (view, trans, osplit, gnc_numeric_neg (xaccSplitGetValue (split)));
4556             }
4557         }
4558         break;
4559 
4560     default:
4561         //g_assert_not_reached();
4562         break;
4563     }
4564 }
4565 
4566 
4567 /* This is used for the template registers */
4568 static void
gtv_sr_edited_template_cb(GtkCellRendererText * cell,const gchar * path_string,const gchar * new_text,gpointer user_data)4569 gtv_sr_edited_template_cb (GtkCellRendererText *cell, const gchar *path_string,
4570                const gchar *new_text, gpointer user_data)
4571 {
4572     GncTreeViewSplitReg  *view = GNC_TREE_VIEW_SPLIT_REG (user_data);
4573     GncTreeModelSplitReg *model;
4574     GtkCellEditable      *editable;
4575     GtkTreeIter           m_iter;
4576     Split                *split;
4577     Transaction          *trans;
4578     gboolean              is_trow1, is_trow2, is_split, is_blank;
4579     ViewCol               viewcol;
4580     Account              *anchor = view->priv->anchor;
4581 
4582     editable = g_object_get_data (G_OBJECT (cell), "cell-editable");
4583 
4584     DEBUG("cell is %p editable pointer is %p", cell, editable);
4585 
4586     g_return_if_fail (gtv_sr_get_model_iter_from_view_string (view, path_string, &m_iter));
4587 
4588     viewcol = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cell), "view_column"));
4589 
4590     model = gnc_tree_view_split_reg_get_model_from_view (view);
4591     g_return_if_fail (model);
4592 
4593     gnc_tree_model_split_reg_get_split_and_trans (
4594         model, &m_iter, &is_trow1, &is_trow2, &is_split, &is_blank, &split, &trans);
4595 
4596     switch (viewcol) {
4597     case COL_NUMACT:
4598         /* Column is NUM / ACT */
4599         gtv_sr_begin_edit (view, trans);
4600         if (is_trow1)
4601         {
4602             /* set per book option */
4603             gnc_set_num_action (trans, gtv_sr_get_this_split (view, trans),
4604                                                                 new_text, NULL);
4605 
4606             if (!qof_book_use_split_action_for_num_field (gnc_get_current_book()))
4607             {
4608                 // Set the last number value for this account.
4609                 if (gnc_strisnum (new_text) && anchor != NULL)
4610                     xaccAccountSetLastNum (anchor, new_text);
4611             }
4612         }
4613         if (is_trow2)
4614         {
4615             /* set per book option */
4616             gnc_set_num_action (trans, gtv_sr_get_this_split (view, trans),
4617                                                                 NULL, new_text);
4618 
4619             if (qof_book_use_split_action_for_num_field (gnc_get_current_book()))
4620             {
4621                 // Set the last number value for this account.
4622                 if (gnc_strisnum (new_text) && anchor != NULL)
4623                     xaccAccountSetLastNum (anchor, new_text);
4624             }
4625         }
4626         if (is_split)
4627         {
4628             /* Set split-action with gnc_set_num_action which is the same as
4629              * xaccSplitSetAction with these arguments */
4630             gnc_set_num_action (NULL, split, NULL, new_text);
4631         }
4632         break;
4633 
4634     case COL_DESCNOTES:
4635         /* Column is DESCRIPTION / NOTES / MEMO */
4636         gtv_sr_begin_edit (view, trans);
4637         if (is_trow1)
4638         {
4639             xaccTransSetDescription (trans, new_text);
4640             // This will potentially fill in the rest of the transaction.
4641             if (view->priv->auto_complete == FALSE)
4642             {
4643                 gnc_tree_control_auto_complete (view, trans, new_text);
4644                 view->priv->auto_complete = TRUE;
4645             }
4646         }
4647         if (is_trow2)
4648             xaccTransSetNotes (trans, new_text);
4649 
4650         if (is_split)
4651             xaccSplitSetMemo (split, new_text);
4652 
4653         break;
4654 
4655     case COL_RECN:
4656         /* Column is RECONCILE */
4657         gtv_sr_begin_edit (view, trans);
4658         {
4659             char rec = 'n';
4660 
4661             if (new_text != NULL)
4662             {
4663                 const gchar *cflag = gnc_get_reconcile_str (CREC);
4664                 const gchar *nflag = gnc_get_reconcile_str (NREC);
4665                 const char recn_flags[] = {NREC, CREC, 0}; // List of reconciled flags
4666                 const gchar *flags;
4667                 gchar *this_flag;
4668                 gint index = 0;
4669 
4670                 flags = g_strconcat (nflag, cflag, NULL); // List of translated strings.
4671 
4672                 /* Find the current flag in the list of flags */
4673                 this_flag = strstr (flags, new_text);
4674 
4675                 if (this_flag != NULL)
4676                 {
4677                     index = this_flag - flags;
4678                     rec = recn_flags[index];
4679                 }
4680             }
4681             if (is_trow1)
4682                 xaccSplitSetReconcile (gtv_sr_get_this_split (view, trans), rec);
4683             if (is_split)
4684                 xaccSplitSetReconcile (split, rec);
4685         }
4686         break;
4687 
4688     case COL_TRANSFERVOID:
4689     case COL_DEBIT:
4690     case COL_CREDIT:
4691         {
4692             gtv_sr_begin_edit (view, trans);
4693 
4694             /* Setup the account field */
4695             if (viewcol == COL_TRANSFERVOID)
4696             {
4697                 Account *template_acc;
4698 		Account *acct;
4699                 const GncGUID *acctGUID;
4700 
4701                 /* save the account GncGUID into the kvp_data. */
4702                 view->priv->stop_cell_move = FALSE;
4703                 acct = gnc_tree_control_split_reg_get_account_by_name (view, new_text);
4704                 if (acct == NULL)
4705                 {
4706                     DEBUG("Template Account is NULL");
4707 
4708                     g_free (view->priv->transfer_string);
4709                     view->priv->transfer_string = g_strdup (new_text);
4710                     view->priv->stop_cell_move = TRUE;
4711 
4712                     /* this will populate cell with original value */
4713                     g_idle_add ((GSourceFunc) gtv_sr_idle_transfer, view);
4714                     break;
4715                 }
4716 
4717                 acctGUID = xaccAccountGetGUID (acct);
4718 		qof_instance_set (QOF_INSTANCE (split),
4719 				  "sx-account", acctGUID,
4720 				  NULL);
4721 
4722                 template_acc = gnc_tree_model_split_reg_get_template_account (model);
4723 
4724                 /* set the actual account to the fake account for these templates */
4725                 xaccAccountInsertSplit (template_acc, split);
4726             }
4727 
4728             /* Set the transaction currency if not set */
4729             if (!xaccTransGetCurrency (trans))
4730             {
4731                 xaccTransSetCurrency (trans, gnc_account_or_default_currency (xaccSplitGetAccount (split), NULL));
4732             }
4733 
4734             // No need to check for a non-currency register because that's what
4735             // was already checked when reg_currency was stored.
4736 
4737             /* Setup the debit and credit fields */
4738             if (viewcol == COL_DEBIT)
4739             {
4740                 char *error_loc;
4741                 gnc_numeric new_value;
4742                 gboolean parse_result;
4743 
4744                 /* Setup the debit formula */
4745 
4746                 /* If the value can be parsed into a numeric result, store that
4747                  * numeric value additionally. See above comment.*/
4748                 parse_result = gnc_exp_parser_parse_separate_vars (new_text, &new_value, &error_loc, NULL);
4749                 if (!parse_result)
4750                 {
4751                     new_value = gnc_numeric_zero();
4752                 }
4753 		qof_instance_set (QOF_INSTANCE (split),
4754 				  "sx-debit-formula", new_text,
4755 				  "sx-debit-numeric", &new_value,
4756 				  "sx-credit-formula", NULL,
4757 				  "sx-credit-numeric", NULL,
4758 				  NULL);
4759             }
4760 
4761             /* Setup the debit and credit fields */
4762             if (viewcol == COL_CREDIT)
4763             {
4764                 char *error_loc;
4765                 gnc_numeric new_value;
4766                 gboolean parse_result;
4767 
4768                /* If the value can be parsed into a numeric result (without any
4769                  * further variable definitions), store that numeric value
4770                  * additionally in the kvp. Otherwise store a zero numeric
4771                  * there.*/
4772                 parse_result = gnc_exp_parser_parse_separate_vars (new_text, &new_value, &error_loc, NULL);
4773                 if (!parse_result)
4774                 {
4775                     new_value = gnc_numeric_zero();
4776                 }
4777 		qof_instance_set (QOF_INSTANCE (split),
4778 				  "sx-credit-formula", new_text,
4779 				  "sx-credit-numeric", &new_value,
4780 				  "sx-debit-formula", NULL,
4781 				  "sx-debit-numeric", NULL,
4782 				  NULL);
4783             }
4784             /* set the amount to an innocuous value */
4785             xaccSplitSetValue (split, gnc_numeric_create (0, 1));
4786 
4787             // Set the split parent trans
4788             xaccSplitSetParent (split, trans);
4789 
4790             // If this is the blank split, promote it.
4791             if (is_blank)
4792             {
4793                 /*FIXME May be this should be a signal - Promote the blank split to a real split */
4794                 g_idle_add ((GSourceFunc) gnc_tree_model_split_reg_commit_blank_split, gnc_tree_view_split_reg_get_model_from_view (view));
4795 
4796                 /* scroll when view idle */
4797                 g_idle_add ((GSourceFunc) gnc_tree_view_split_reg_scroll_to_cell, view);
4798             }
4799         }
4800         break;
4801 
4802     default:
4803         //g_assert_not_reached();
4804         break;
4805     }
4806 }
4807 
4808 /*###########################################################################*/
4809 
4810 /* Parses the string value and returns true if it is a
4811  * number. In that case, *num is set to the value parsed. */
4812 static gboolean
gtv_sr_parse_num(const char * string,long int * num)4813 gtv_sr_parse_num (const char *string, long int *num)
4814 {
4815     long int number;
4816 
4817     if (string == NULL)
4818         return FALSE;
4819 
4820     if (!gnc_strisnum (string))
4821         return FALSE;
4822 
4823     number = strtol (string, NULL, 10);
4824 
4825     if ((number == LONG_MIN) || (number == LONG_MAX))
4826         return FALSE;
4827 
4828     if (num != NULL)
4829         *num = number;
4830 
4831     return TRUE;
4832 }
4833 
4834 /* Callback for Number Accelerator key */
4835 static void
gtv_sr_num_cb(GtkEntry * entry,const gchar * text,gint length,gint * position,gpointer user_data)4836 gtv_sr_num_cb (GtkEntry    *entry,
4837                           const gchar *text,
4838                           gint         length,
4839                           gint        *position,
4840                           gpointer     user_data)
4841 {
4842     GtkEditable *editable = GTK_EDITABLE (entry);
4843     GncTreeViewSplitReg *view = GNC_TREE_VIEW_SPLIT_REG (user_data);
4844     GncTreeModelSplitReg *model;
4845     RowDepth depth;
4846     Account *account;
4847     gchar *entered_string;
4848     gchar *leave_string = NULL;
4849 
4850     gboolean accel = FALSE;
4851     gboolean is_num;
4852     long int number = 0;
4853     gunichar uc;
4854 
4855     model = gnc_tree_view_split_reg_get_model_from_view (view);
4856 
4857     account = gnc_tree_model_split_reg_get_anchor (model);
4858 
4859     depth = gnc_tree_view_reg_get_selected_row_depth (view);
4860 
4861     // This only works on the number field.
4862     if ((depth == TRANS2 || depth == SPLIT3))
4863         return;
4864 
4865     // Get entered string
4866     entered_string = gtk_editable_get_chars (editable, 0, -1);
4867 
4868     // Test for number and return it.
4869     is_num = gtv_sr_parse_num (entered_string, &number);
4870 
4871     if (is_num && (number < 0))
4872         is_num = FALSE;
4873 
4874     // Test for accelerator keys.
4875     uc = g_utf8_get_char (text);
4876     switch (uc)
4877     {
4878     case '+':
4879     case '=':
4880         number++;
4881         accel = TRUE;
4882         break;
4883 
4884     case '_':
4885     case '-':
4886         number--;
4887         accel = TRUE;
4888         break;
4889 
4890     case '}':
4891     case ']':
4892         number += 10;
4893         accel = TRUE;
4894         break;
4895 
4896     case '{':
4897     case '[':
4898         number -= 10;
4899         accel = TRUE;
4900         break;
4901     }
4902 
4903     if (number < 0)
4904         number = 0;
4905 
4906     /* If there is already a non-number there, don't accelerate. */
4907     if (accel && !is_num && (g_strcmp0 (entered_string, "") != 0))
4908         accel = FALSE;
4909 
4910     // See if entered string is empty, try and get the last number.
4911     if (accel && (g_strcmp0 (entered_string, "") == 0))
4912     {
4913         if (account != NULL)
4914         {
4915             if (gtv_sr_parse_num (xaccAccountGetLastNum (account), &number))
4916                 number = number + 1;
4917             else
4918                 number = 1;
4919         }
4920         else
4921             number = 1;
4922 
4923         is_num = TRUE;
4924     }
4925 
4926     if (!accel)
4927     {
4928         leave_string = g_strconcat (entered_string, text, NULL);
4929     }
4930 
4931     if (accel && is_num)
4932     {
4933         char buff[128];
4934 
4935         strcpy (buff, "");
4936         snprintf (buff, sizeof(buff), "%ld", number);
4937 
4938         if (g_strcmp0 (buff, "") == 0)
4939             leave_string = g_strdup ("");
4940         else
4941             leave_string = g_strdup (buff);
4942     }
4943 
4944     g_signal_handlers_block_by_func (editable, (gpointer) gtv_sr_num_cb, user_data);
4945 
4946     gtk_editable_delete_text (editable, 0, -1);
4947     gtk_editable_set_position (editable, 0);
4948 
4949     if (leave_string != NULL)
4950         gtk_editable_insert_text (editable, leave_string, -1, position);
4951 
4952     g_signal_handlers_unblock_by_func (editable, (gpointer) gtv_sr_num_cb, user_data);
4953 
4954     g_signal_stop_emission_by_name (editable, "insert_text");
4955 
4956     if (leave_string)
4957         g_free (leave_string);
4958 
4959     g_free (entered_string);
4960 }
4961 
4962 
4963 /* Callback for Account separator key */
4964 static void
gtv_sr_acct_cb(GtkEntry * entry,const gchar * text,gint length,gint * position,gpointer user_data)4965 gtv_sr_acct_cb (GtkEntry    *entry,
4966                           const gchar *text,
4967                           gint         length,
4968                           gint        *position,
4969                           gpointer     user_data)
4970 {
4971     GtkEditable *editable = GTK_EDITABLE (entry);
4972     GncTreeViewSplitReg *view = GNC_TREE_VIEW_SPLIT_REG (user_data);
4973     GtkEntryCompletion *completion;
4974     GtkTreeModel *model;
4975     GtkTreeIter  iter;
4976 
4977     const gchar *sep_char;
4978     gchar       *entered_string;
4979     gchar       *acct_string = NULL;
4980 
4981     gint         num_of_items = 0;
4982     gboolean     valid;
4983     gboolean     all_the_same = TRUE;
4984 
4985     sep_char = gnc_get_account_separator_string ();
4986 
4987     if (g_strcmp0 (text, sep_char) == 0)
4988         entered_string = g_strconcat (gtk_editable_get_chars (editable, 0, -1), NULL);
4989     else
4990         entered_string = g_strconcat (gtk_editable_get_chars (editable, 0, -1), text, NULL);
4991 
4992     // Get the completion and model
4993     completion = gtk_entry_get_completion (entry);
4994     model = gtk_entry_completion_get_model (completion);
4995 
4996     // Get the first item in the list
4997     valid = gtk_tree_model_get_iter_first (model, &iter);
4998     while (valid)
4999     {
5000         gchar *item, *item_string, *l_item, *l_entered_string, *l_acct_string;
5001 
5002         // Walk through the list, reading each row
5003         if (view->priv->acct_short_names)
5004             gtk_tree_model_get (model, &iter, 0, &item, -1);
5005         else
5006             gtk_tree_model_get (model, &iter, 1, &item, -1);
5007 
5008         item_string = g_strconcat (item, sep_char, NULL);
5009 
5010         l_item = g_utf8_strdown (item_string, -1);
5011         l_entered_string = g_utf8_strdown (entered_string, -1);
5012 
5013         if (g_str_has_prefix (l_item, l_entered_string))
5014         {
5015             if (num_of_items == 0)
5016                 acct_string = g_strdup (item);
5017             else
5018             {
5019                 l_acct_string = g_utf8_strdown (acct_string, -1);
5020                 if (!g_str_has_prefix (g_utf8_strdown (l_item, -1), l_acct_string))
5021                     all_the_same = FALSE;
5022                 g_free (l_acct_string);
5023             }
5024             num_of_items = num_of_items + 1;
5025         }
5026         g_free (item);
5027         g_free (item_string);
5028         g_free (l_item);
5029         g_free (l_entered_string);
5030         valid = gtk_tree_model_iter_next (model, &iter);
5031     }
5032 
5033     g_signal_handlers_block_by_func (editable, (gpointer) gtv_sr_acct_cb, user_data);
5034 
5035     gtk_editable_delete_text (editable, 0, -1);
5036     gtk_editable_set_position (editable, 0);
5037 
5038     if (num_of_items == 0)
5039         gtk_editable_insert_text (editable, entered_string, -1, position);
5040     else
5041     {
5042         if (num_of_items == 1)
5043             gtk_editable_insert_text (editable, acct_string, -1, position);
5044         else
5045         {
5046             if (all_the_same)
5047             {
5048                 if (g_strcmp0 (text, sep_char) == 0)
5049                     gtk_editable_insert_text (editable, g_strconcat (acct_string, sep_char, NULL), -1, position);
5050                 else
5051                     gtk_editable_insert_text (editable, entered_string, -1, position);
5052             }
5053             else
5054                gtk_editable_insert_text (editable, entered_string, -1, position);
5055         }
5056     }
5057     g_signal_handlers_unblock_by_func (editable, (gpointer) gtv_sr_acct_cb, user_data);
5058 
5059     g_signal_stop_emission_by_name (editable, "insert_text");
5060     g_free (acct_string);
5061     g_free (entered_string);
5062 }
5063 
5064 
5065 /* Callback for changing reconcile setting with space bar */
5066 static void
gtv_sr_recn_cb(GtkEntry * entry,const gchar * text,gint length,gint * position,gpointer user_data)5067 gtv_sr_recn_cb (GtkEntry    *entry,
5068                           const gchar *text,
5069                           gint         length,
5070                           gint        *position,
5071                           gpointer     user_data)
5072 {
5073     GtkEditable *editable = GTK_EDITABLE (entry);
5074     GncTreeViewSplitReg *view = GNC_TREE_VIEW_SPLIT_REG (user_data);
5075 
5076     const gchar *cflag = gnc_get_reconcile_str (CREC);
5077     const gchar *nflag = gnc_get_reconcile_str (NREC);
5078 
5079     const gchar *flags;
5080     gchar *this_flag;
5081     gchar *result;
5082     static char ss[2];
5083     gint index = 0;
5084 
5085     result = g_ascii_strdown (text, length);
5086 
5087     if (g_object_get_data (G_OBJECT (view->priv->temp_cr), "current-flag") != NULL)
5088         index = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (view->priv->temp_cr), "current-flag"));
5089     else
5090     {
5091         if (g_strcmp0 (g_object_get_data (G_OBJECT (view->priv->temp_cr), "current-string"), nflag) == 0)
5092             index = 0;
5093     }
5094 
5095     flags = g_strconcat (nflag, cflag, NULL);
5096 
5097     /* So we can test for space */
5098     ss[0] = ' ';
5099     ss[1] = '\0';
5100 
5101     /* Find the entered text in the list of flags */
5102     this_flag = strstr (flags, text);
5103 
5104     if (this_flag == NULL || *this_flag == '\0')
5105     {
5106         if (g_strcmp0 (text, ss) == 0)  // test for space
5107         {
5108             /* In the list, choose the next item in the list
5109                (wrapping around as necessary). */
5110 
5111             if (flags[index + 1] != '\0')
5112                 index = index + 1;
5113             else
5114                 index = 0;
5115 
5116             g_free (result);
5117             result = g_strdup_printf("%c", flags[index]);
5118         }
5119         else
5120         {
5121             /* If it's not there (or the list is empty) use default_flag */
5122             g_free (result);
5123             result = g_strdup (gnc_get_reconcile_str (NREC));
5124         }
5125     }
5126     else
5127     {
5128         g_free (result);
5129         result = g_strdup (text);
5130     }
5131 
5132     /* save the index in the cellrenderer */
5133     g_object_set_data (G_OBJECT (view->priv->temp_cr), "current-flag", GINT_TO_POINTER (index));
5134 
5135     g_signal_handlers_block_by_func (editable, (gpointer) gtv_sr_recn_cb, user_data);
5136 
5137     gtk_editable_delete_text (editable, 0, -1);
5138     gtk_editable_insert_text (editable, result, length, position);
5139 
5140     g_signal_handlers_unblock_by_func (editable, (gpointer) gtv_sr_recn_cb, user_data);
5141 
5142     g_signal_stop_emission_by_name (editable, "insert_text");
5143 
5144     g_free (result);
5145 }
5146 
5147 
5148 /* Callback for changing type setting with space bar */
5149 static void
gtv_sr_type_cb(GtkEntry * entry,const gchar * text,gint length,gint * position,gpointer user_data)5150 gtv_sr_type_cb (GtkEntry    *entry,
5151                           const gchar *text,
5152                           gint         length,
5153                           gint        *position,
5154                           gpointer     user_data)
5155 {
5156     GtkEditable *editable = GTK_EDITABLE (entry);
5157     GncTreeViewSplitReg *view = GNC_TREE_VIEW_SPLIT_REG (user_data);
5158     const gchar *flags;
5159     const char type_flags[] = {TXN_TYPE_INVOICE, TXN_TYPE_PAYMENT, 0};
5160     gchar *this_flag;
5161     gchar *result;
5162     static char ss[2];
5163     gint index = 0;
5164 
5165     flags = type_flags;
5166 
5167     result = g_ascii_strup (text, length);
5168 
5169     if (g_object_get_data (G_OBJECT (view->priv->temp_cr), "current-flag") != NULL)
5170         index = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (view->priv->temp_cr), "current-flag"));
5171     else
5172     {
5173         if (g_strcmp0 (g_object_get_data (G_OBJECT (view->priv->temp_cr), "current-string"), "I") == 0)
5174             index = 0;
5175     }
5176 
5177     /* So we can test for space */
5178     ss[0] = ' ';
5179     ss[1] = '\0';
5180 
5181     /* Find the entered text in the list of flags */
5182     this_flag = strstr (flags, text);
5183 
5184     if (this_flag == NULL || *this_flag == '\0')
5185     {
5186         if (g_strcmp0 (text, ss) == 0)  // test for space
5187         {
5188             /* In the list, choose the next item in the list
5189                (wrapping around as necessary). */
5190 
5191             if (flags[index + 1] != '\0')
5192                 index = index + 1;
5193             else
5194                 index = 0;
5195 
5196             g_free (result);
5197             result = g_strdup_printf("%c", flags[index]);
5198         }
5199         else
5200         {
5201             /* If it's not there (or the list is empty) use default_flag */
5202             g_free (result);
5203             result  = NULL;
5204         }
5205     }
5206     else
5207     {
5208         g_free (result);
5209         result = g_strdup (text);
5210     }
5211 
5212     /* save the index in the cellrenderer */
5213     g_object_set_data (G_OBJECT (view->priv->temp_cr), "current-flag", GINT_TO_POINTER (index));
5214 
5215     g_signal_handlers_block_by_func (editable, (gpointer) gtv_sr_type_cb, user_data);
5216 
5217     gtk_editable_delete_text (editable, 0, -1);
5218     gtk_editable_insert_text (editable, result, length, position);
5219 
5220     g_signal_handlers_unblock_by_func (editable, (gpointer) gtv_sr_type_cb, user_data);
5221 
5222     g_signal_stop_emission_by_name (editable, "insert_text");
5223 
5224     g_free (result);
5225 }
5226 
5227 
5228 /* For handling keynav */
5229 static gboolean
gtv_sr_ed_key_press_cb(GtkWidget * widget,GdkEventKey * event,gpointer user_data)5230 gtv_sr_ed_key_press_cb (GtkWidget *widget, GdkEventKey *event, gpointer user_data)
5231 {
5232     GncTreeViewSplitReg *view = GNC_TREE_VIEW_SPLIT_REG (user_data);
5233     GncTreeModelSplitReg *model;
5234     GtkTreeViewColumn *col;
5235     GtkTreePath *spath;
5236     gboolean goto_blank = FALSE;
5237     gboolean next_trans = TRUE;
5238     Transaction *btrans, *ctrans;
5239     gint depth;
5240     gboolean auto_popped = FALSE;
5241 
5242     // spath is where we are, before key press...
5243     gtk_tree_view_get_cursor (GTK_TREE_VIEW (view), &spath, &col);
5244 
5245     if (event->type != GDK_KEY_PRESS)
5246     {
5247         if (spath)
5248             gtk_tree_path_free (spath);
5249         return FALSE;
5250     }
5251 
5252     switch (event->keyval)
5253     {
5254 
5255     case GDK_KEY_Up:
5256     case GDK_KEY_Down:
5257 
5258         if (!spath)
5259             return TRUE;
5260 
5261         // This is to test for the auto completion popup window
5262         {
5263             GtkWidget *toplevel;
5264             GtkWindowGroup *window_group;
5265             GList *win_list;
5266 
5267             toplevel = gtk_widget_get_toplevel (widget);
5268             if (GTK_IS_WINDOW (toplevel))
5269             {
5270                 window_group = gtk_window_get_group (GTK_WINDOW (toplevel));
5271                 win_list = gtk_window_group_list_windows (window_group);
5272                 if (g_list_length (win_list) == 1 && gtk_widget_get_visible (GTK_WIDGET (win_list->data)))
5273                     auto_popped = TRUE;
5274 
5275             g_list_free (win_list);
5276             }
5277         }
5278 
5279         // Auto complete window popped
5280         if (auto_popped == TRUE)
5281         {
5282             gtk_tree_path_free (spath);
5283             return FALSE;
5284         }
5285 
5286         model = gnc_tree_view_split_reg_get_model_from_view (view);
5287 
5288         // Make sure we have stopped editing.
5289         gnc_tree_view_split_reg_finish_edit (view);
5290 
5291         // This stops the cell changing.
5292         if (view->priv->stop_cell_move == TRUE)
5293         {
5294             gtk_tree_path_free (spath);
5295             return TRUE;
5296         }
5297 
5298         depth = gtk_tree_path_get_depth (spath);
5299         if (event->keyval == GDK_KEY_Up)
5300         {
5301             if (depth == 1)
5302             {
5303                 if (gtk_tree_path_prev (spath))
5304                 {
5305                     if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (view), spath))
5306                     {
5307                         gtk_tree_path_down (spath);
5308 
5309                         if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (view), spath) && model->type == GENERAL_JOURNAL2)
5310                         {
5311                             gtk_tree_path_down (spath);
5312 
5313                             while (gnc_tree_view_path_is_valid (GNC_TREE_VIEW (view), spath))
5314                             {
5315                                 gtk_tree_path_next (spath);
5316                             }
5317                             gtk_tree_path_prev (spath);
5318                         }
5319                     }
5320                 }
5321             }
5322             else if (!gtk_tree_path_prev (spath) && depth > 1)
5323             {
5324                 gtk_tree_path_up (spath);
5325             }
5326         }
5327         else if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (view), spath))
5328         {
5329             gtk_tree_path_down (spath);
5330         }
5331         else
5332         {
5333             gtk_tree_path_next (spath);
5334             if (!gnc_tree_view_path_is_valid (GNC_TREE_VIEW (view), spath) && depth > 2)
5335             {
5336                 gtk_tree_path_prev (spath);
5337                 gtk_tree_path_up (spath);
5338                 gtk_tree_path_next (spath);
5339             }
5340             if (!gnc_tree_view_path_is_valid (GNC_TREE_VIEW (view), spath) && depth > 1)
5341             {
5342                 gtk_tree_path_prev (spath);
5343                 gtk_tree_path_up (spath);
5344                 gtk_tree_path_next (spath);
5345             }
5346         }
5347 
5348         /* Set cursor to new column, open for editing */
5349         gtk_tree_view_set_cursor (GTK_TREE_VIEW (view), spath, col, TRUE);
5350 
5351         if (event->keyval == GDK_KEY_Up)
5352         {
5353             gnc_tree_model_split_reg_move (model, VIEW_UP);
5354         }
5355         else
5356             gnc_tree_model_split_reg_move (model, VIEW_DOWN);
5357 
5358         return TRUE;
5359         break;
5360 
5361     case GDK_KEY_Return:
5362 
5363         if (!spath)
5364             return TRUE;
5365 
5366         // This stops the cell changing.
5367         if (view->priv->stop_cell_move == TRUE)
5368         {
5369             gtk_tree_path_free (spath);
5370             return TRUE;
5371         }
5372 
5373         // Do sums if we have ctrl key
5374         if (event->state & GDK_CONTROL_MASK)
5375         {
5376             // Make sure we have stopped editing.
5377             gnc_tree_view_split_reg_finish_edit (view);
5378 
5379             /* Set cursor to the column, open for editing */
5380             gtk_tree_view_set_cursor (GTK_TREE_VIEW (view), spath, col, TRUE);
5381             gtk_tree_path_free (spath);
5382             return TRUE;
5383         }
5384         return FALSE;
5385         break;
5386 
5387     case GDK_KEY_KP_Enter:
5388 
5389         if (!spath)
5390             return TRUE;
5391 
5392         // This stops the cell changing.
5393         if (view->priv->stop_cell_move == TRUE)
5394         {
5395             gtk_tree_path_free (spath);
5396             return TRUE;
5397         }
5398 
5399         goto_blank = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL_REGISTER,
5400                                          GNC_PREF_ENTER_MOVES_TO_END);
5401 
5402         model = gnc_tree_view_split_reg_get_model_from_view (view);
5403         btrans = gnc_tree_model_split_get_blank_trans (model);
5404         ctrans = gnc_tree_view_split_reg_get_current_trans (view);
5405 
5406         /* Are we on the blank transaction */
5407         if (btrans == ctrans)
5408             next_trans = FALSE;
5409 
5410         /* First record the transaction */
5411         if (gnc_tree_view_split_reg_enter (view))
5412         {
5413             /* Now move. */
5414             if (goto_blank)
5415                 g_idle_add ((GSourceFunc)gnc_tree_control_split_reg_jump_to_blank, view);
5416             else if (next_trans)
5417                 gnc_tree_control_split_reg_goto_rel_trans_row (view, 1);
5418         }
5419         return TRUE;
5420         break;
5421 
5422     default:
5423         gtk_tree_path_free (spath);
5424 	return FALSE;
5425     }
5426 }
5427 
5428 /*###########################################################################*/
5429 
5430 /* The main Start Editing Call back for the TEXT columns */
5431 static void
gtv_sr_editable_start_editing_cb(GtkCellRenderer * cr,GtkCellEditable * editable,const gchar * path_string,gpointer user_data)5432 gtv_sr_editable_start_editing_cb (GtkCellRenderer *cr, GtkCellEditable *editable,
5433                               const gchar *path_string, gpointer user_data)
5434 {
5435     GncTreeViewSplitReg  *view = GNC_TREE_VIEW_SPLIT_REG (user_data);
5436     GncTreeModelSplitReg *model;
5437     GtkTreePath          *spath;
5438     GtkEntry             *entry = NULL;
5439     ViewCol               viewcol;
5440     RowDepth              depth;
5441     gint                 *indices;
5442 
5443     GtkListStore *description_list;
5444     GtkListStore *memo_list;
5445     GtkListStore *notes_list;
5446     GtkListStore *account_list;
5447 
5448     GtkEntryCompletion *completion = gtk_entry_completion_new();
5449 
5450     ENTER("gtv_sr_editable_start_editing_cb Path string is '%s'", path_string);
5451 
5452     model = gnc_tree_view_split_reg_get_model_from_view (view);
5453 
5454     /* Description / Notes / Memo / Accounts Completion Lists */
5455     description_list = gnc_tree_model_split_reg_get_description_list (model);
5456     notes_list = gnc_tree_model_split_reg_get_notes_list (model);
5457     memo_list = gnc_tree_model_split_reg_get_memo_list (model);
5458     account_list = gnc_tree_model_split_reg_get_acct_list (model);
5459 
5460     // Use depth to determine if it is a split or transaction
5461     spath = gtk_tree_path_new_from_string (path_string);
5462     depth = gtk_tree_path_get_depth (spath);
5463     indices = gtk_tree_path_get_indices (spath);
5464 
5465     viewcol = GPOINTER_TO_INT (g_object_get_data (G_OBJECT(cr), "view_column"));
5466 
5467     DEBUG("editable Depth is %u and ViewCol is %d", depth, viewcol);
5468 
5469     g_object_set_data (G_OBJECT (cr), "cell-editable", editable);
5470 
5471     // This is for key navigation...
5472     g_signal_connect (G_OBJECT (editable), "key-press-event", G_CALLBACK (gtv_sr_ed_key_press_cb), view);
5473 
5474     /* DATE COLUMN */
5475     if (viewcol == COL_DATE)
5476     {
5477         entry = GTK_ENTRY (GNC_POPUP_ENTRY (editable)->entry);
5478 
5479         //Copy the string in the GtkEntry for later comparison
5480         g_object_set_data_full (G_OBJECT (cr), "current-string", g_strdup (gtk_entry_get_text (entry)), g_free);
5481 
5482         g_signal_connect (G_OBJECT (editable), "remove-widget", (GCallback) gtv_sr_remove_edit_date, view);
5483 
5484         DEBUG("Current String date is '%s'", gtk_entry_get_text (entry));
5485     }
5486 
5487     /* TRANSFER / VOID COLUMN */
5488     else if (viewcol == COL_TRANSFERVOID)
5489     {
5490         entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (editable)));
5491 
5492         // This is for key navigation...
5493         g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK (gtv_sr_ed_key_press_cb), view);
5494 
5495         {
5496             GtkEditable *editable = GTK_EDITABLE (entry);
5497 
5498             if (view->priv->stop_cell_move == TRUE)
5499             {
5500                 gint textPosition = 0;
5501                 gtk_editable_insert_text (GTK_EDITABLE (editable), view->priv->transfer_string, -1, &textPosition);
5502                 gtk_editable_set_position (GTK_EDITABLE (editable), -1);
5503             }
5504         }
5505 
5506         // Update the Account list combo.
5507         gnc_tree_model_split_reg_update_account_list (model);
5508 
5509         gtk_entry_set_completion (entry, completion);
5510         gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (account_list));
5511 
5512         /* This sets which text column to use, 0 for short names, 1 for long */
5513         if (view->priv->acct_short_names)
5514             gtk_entry_completion_set_text_column (completion, 0);
5515         else
5516             gtk_entry_completion_set_text_column (completion, 1);
5517 
5518         gtk_entry_completion_set_popup_completion (completion, TRUE);
5519         gtk_entry_completion_set_inline_selection (completion, TRUE);
5520         gtk_entry_completion_set_popup_set_width (completion, FALSE);
5521         gtk_entry_completion_set_minimum_key_length (completion, 1);
5522 //??        g_signal_connect(G_OBJECT(completion), "match-selected", (GCallback) gtv_sr_match_selected_cb, view);
5523         g_object_unref (completion);
5524 
5525         //Copy the string in the GtkEntry for later comparison
5526         g_object_set_data_full (G_OBJECT (cr), "current-string", g_strdup (gtk_entry_get_text (entry)), g_free);
5527 
5528         g_signal_connect (G_OBJECT (entry), "insert_text", (GCallback) gtv_sr_acct_cb, view);
5529 
5530 //??        g_signal_connect (G_OBJECT (cr), "changed", (GCallback) gtv_sr_changed_cb, view);
5531         g_signal_connect (G_OBJECT (editable), "remove-widget", (GCallback) gtv_sr_remove_edit_combo, view);
5532 
5533         DEBUG("Current String tv is '%s'", gtk_entry_get_text (entry));
5534     }
5535 
5536     /* NUMBER / ACTION COLUMN */
5537     else if (viewcol == COL_NUMACT)
5538     {
5539         if ((depth == TRANS1) || ((depth == TRANS2) && (qof_book_use_split_action_for_num_field (gnc_get_current_book()))))
5540         {
5541             entry = GTK_ENTRY (editable);
5542 
5543             //Copy the string in the GtkEntry for later comparison
5544             g_object_set_data_full (G_OBJECT (cr), "current-string", g_strdup (gtk_entry_get_text (entry)), g_free);
5545 
5546             g_signal_connect (G_OBJECT (GTK_ENTRY (entry)), "insert_text", (GCallback) gtv_sr_num_cb, view);
5547 
5548             view->priv->fo_handler_id = g_signal_connect (G_OBJECT (editable), "focus-out-event", (GCallback) gtv_sr_focus_out_cb, view);
5549 
5550             g_signal_connect (G_OBJECT (editable), "remove-widget", (GCallback) gtv_sr_remove_edit_entry, view);
5551 
5552 //??        g_signal_connect (G_OBJECT (cr), "changed", (GCallback)gtv_sr_changed_cb, view);
5553             DEBUG("Current String num is '%s'", gtk_entry_get_text (entry));
5554         }
5555 
5556         if ((depth == SPLIT3) || ((depth == TRANS2) && (!qof_book_use_split_action_for_num_field (gnc_get_current_book()))))
5557         {
5558             gnc_tree_model_split_reg_update_action_list (model);
5559 
5560             entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (editable)));
5561 
5562             //Copy the string in the GtkEntry for later comparison
5563             g_object_set_data_full (G_OBJECT (cr), "current-string", g_strdup (gtk_entry_get_text (entry)), g_free);
5564 
5565 //??          g_signal_connect (G_OBJECT (cr), "changed", (GCallback) gtv_sr_changed_cb, view);
5566             g_signal_connect (G_OBJECT (editable), "remove-widget", (GCallback) gtv_sr_remove_edit_combo, view);
5567 
5568             DEBUG("Current String action is '%s'", gtk_entry_get_text (entry));
5569         }
5570     }
5571 
5572     /* DESCRIPTION / NOTES / MEMO COLUMN */
5573     else if (viewcol == COL_DESCNOTES)
5574     {
5575         entry = GTK_ENTRY (editable);
5576 
5577         // Update the auto completion lists.
5578         gnc_tree_model_split_reg_update_completion (model);
5579 
5580         //Data used for completion is set based on if editing split or not
5581         if (depth == TRANS1)
5582         {
5583             gtk_entry_set_completion (entry, completion);
5584             gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (description_list));
5585             gtk_entry_completion_set_text_column (completion, 0);
5586         }
5587         else if (depth == TRANS2)
5588         {
5589             gtk_entry_set_completion (entry, completion);
5590             gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (notes_list));
5591             gtk_entry_completion_set_text_column (completion, 0);
5592         }
5593         else if (depth == SPLIT3)
5594         {
5595             gtk_entry_set_completion (entry, completion);
5596             gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (memo_list));
5597             gtk_entry_completion_set_text_column (completion, 0);
5598         }
5599 
5600         //To emit "match-selected" signal we need to have a list of matches to
5601         //select from instead of using inline autocompletion
5602         gtk_entry_completion_set_popup_completion (completion, TRUE);
5603         gtk_entry_completion_set_inline_selection (completion, TRUE);
5604         gtk_entry_completion_set_minimum_key_length (completion, view->priv->key_length);
5605 //??        g_signal_connect (G_OBJECT (completion), "match-selected", (GCallback) gtv_sr_match_selected_cb, view);
5606 
5607         g_object_unref (completion);
5608 
5609         //Copy the string in the GtkEntry for later comparison
5610         g_object_set_data_full (G_OBJECT (cr), "current-string", g_strdup (gtk_entry_get_text (entry)), g_free);
5611 
5612         view->priv->fo_handler_id = g_signal_connect (G_OBJECT (editable), "focus-out-event", (GCallback) gtv_sr_focus_out_cb, view);
5613 
5614         g_signal_connect (G_OBJECT (editable), "remove-widget", (GCallback) gtv_sr_remove_edit_entry, view);
5615 
5616         DEBUG("Current String dnm is '%s'", gtk_entry_get_text (entry));
5617     }
5618 
5619     /* RECN COLUMN */
5620     else if (viewcol == COL_RECN)
5621     {
5622         entry = GTK_ENTRY (editable);
5623 
5624         //Copy the string in the GtkEntry for later comparison
5625         g_object_set_data_full (G_OBJECT (cr), "current-string", g_strdup (gtk_entry_get_text (entry)), g_free);
5626 
5627         g_signal_connect (G_OBJECT (GTK_ENTRY (editable)), "insert_text", (GCallback)gtv_sr_recn_cb, view);
5628 
5629         view->priv->fo_handler_id = g_signal_connect (G_OBJECT (editable), "focus-out-event", (GCallback) gtv_sr_focus_out_cb, view);
5630 
5631         g_signal_connect (G_OBJECT (editable), "remove-widget", (GCallback) gtv_sr_remove_edit_entry, view);
5632 
5633 //??        g_signal_connect (G_OBJECT (cr), "changed", (GCallback) gtv_sr_changed_cb, view);
5634         DEBUG("Current String recn is '%s'", gtk_entry_get_text (entry));
5635     }
5636 
5637     /* TYPE COLUMN */
5638     else if (viewcol == COL_TYPE)
5639     {
5640         entry = GTK_ENTRY (editable);
5641 
5642         //Copy the string in the GtkEntry for later comparison
5643         g_object_set_data_full (G_OBJECT (cr), "current-string", g_strdup (gtk_entry_get_text (entry)), g_free);
5644 
5645         g_signal_connect (G_OBJECT (GTK_ENTRY (editable)), "insert_text", (GCallback)gtv_sr_type_cb, view);
5646 
5647         view->priv->fo_handler_id = g_signal_connect (G_OBJECT (editable), "focus-out-event", (GCallback) gtv_sr_focus_out_cb, view);
5648 
5649         g_signal_connect (G_OBJECT (editable), "remove-widget", (GCallback) gtv_sr_remove_edit_entry, view);
5650 
5651 //??        g_signal_connect (G_OBJECT (cr), "changed", (GCallback) gtv_sr_changed_cb, view);
5652         DEBUG("Current String type is '%s'", gtk_entry_get_text (entry));
5653     }
5654 
5655     /* THE REST OF THE COLUMNS */
5656     else
5657     {
5658         entry = GTK_ENTRY (editable);
5659 
5660         //Copy the string in the GtkEntry for later comparison
5661         g_object_set_data_full (G_OBJECT (cr), "current-string", g_strdup (gtk_entry_get_text (entry)), g_free);
5662 
5663         view->priv->fo_handler_id = g_signal_connect (G_OBJECT (editable), "focus-out-event", (GCallback) gtv_sr_focus_out_cb, view);
5664 
5665         g_signal_connect (G_OBJECT (editable), "remove-widget", (GCallback) gtv_sr_remove_edit_entry, view);
5666 
5667 //??        g_signal_connect (G_OBJECT (cr), "changed", (GCallback)gtv_sr_changed_cb, view);
5668         DEBUG("Current String rest is '%s'", gtk_entry_get_text (entry));
5669     }
5670 
5671     /* Lets change the background of the entry widgets */
5672     {
5673         GdkRGBA      color;
5674         const gchar *row_color;
5675         gboolean     is_trow1 = FALSE;
5676         gboolean     is_trow2 = FALSE;
5677         gboolean     is_split = FALSE;
5678 
5679         if (depth == TRANS1)
5680             is_trow1 = TRUE;
5681         if (depth == TRANS2)
5682             is_trow2 = TRUE;
5683         if (depth == SPLIT3)
5684             is_split = TRUE;
5685 
5686         row_color = gnc_tree_model_split_reg_get_row_color (model, is_trow1, is_trow2, is_split, indices[0]);
5687 
5688         if (gdk_rgba_parse (&color, row_color))
5689         {
5690             if (entry != NULL)
5691             {
5692                 GtkStyleContext *stylectxt = gtk_widget_get_style_context (GTK_WIDGET (entry));
5693                 GtkCssProvider *provider = gtk_css_provider_new();
5694                 gchar *col_str = gdk_rgba_to_string (&color);
5695                 gchar *widget_css = g_strconcat ("*{\n  background-color:", col_str, ";\n}\n", NULL);
5696 
5697                 gtk_css_provider_load_from_data (provider, widget_css, -1, NULL);
5698                 gtk_style_context_add_provider (stylectxt, GTK_STYLE_PROVIDER (provider),
5699                                                 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
5700                 g_object_unref (provider);
5701                 g_free (col_str);
5702                 g_free (widget_css);
5703             }
5704         }
5705     }
5706 
5707     gtv_sr_help (view, cr, viewcol, depth);
5708     gtk_tree_path_free (spath);
5709 
5710     view->priv->temp_cr = cr;
5711     view->editing_now = TRUE;
5712 
5713     DEBUG("Temp Cell Rend %p", view->priv->temp_cr);
5714 
5715     //Add edit-canceled property to cr so we can distinguish between
5716     //cancelled and actual changes
5717     g_object_set_data (G_OBJECT (cr), "edit-canceled", GINT_TO_POINTER (FALSE));
5718     LEAVE(" ");
5719 }
5720 
5721 #ifdef skip
5722 // Handle the "match-selected" signal
5723 static void
gtv_sr_match_selected_cb(GtkEntryCompletion * widget,GtkTreeModel * model,GtkTreeIter * iter,gpointer user_data)5724 gtv_sr_match_selected_cb (GtkEntryCompletion *widget, GtkTreeModel *model,
5725                         GtkTreeIter *iter, gpointer user_data)
5726 {
5727 //    GncTreeViewSplitReg *view = GNC_TREE_VIEW_SPLIT_REG (user_data);
5728 
5729 //FIXME g_print("gtv_sr_match_selected_cb\n\n");
5730 
5731 /* Not sure what I am going to put in here yet if anything */
5732 }
5733 #endif
5734 
5735 #ifdef skip
5736 // Handle the "changed" signal
5737 static void
gtv_sr_changed_cb(GtkCellRendererCombo * widget,gchar * path_string,GtkTreeIter * iter,gpointer user_data)5738 gtv_sr_changed_cb (GtkCellRendererCombo *widget, gchar *path_string,
5739                         GtkTreeIter *iter, gpointer user_data)
5740 {
5741 //    GncTreeViewSplitReg *view = GNC_TREE_VIEW_SPLIT_REG (user_data);
5742 
5743 //FIXME g_print("gtv_sr_changed_cb path string is '%s'\n\n", path_string);
5744 
5745 /* Not sure what I am going to put in here yet if anything */
5746 
5747 }
5748 #endif
5749 
5750 // Handle the "editing-canceled" signal
5751 static void
gtv_sr_editing_canceled_cb(GtkCellRenderer * cr,gpointer user_data)5752 gtv_sr_editing_canceled_cb (GtkCellRenderer *cr, gpointer user_data)
5753 {
5754     GncTreeViewSplitReg *view = GNC_TREE_VIEW_SPLIT_REG (user_data);
5755 
5756     if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (view), "data-edited")) == FALSE) // Not edited, reset edit path
5757     {
5758         view->priv->dirty_trans = NULL;
5759     }
5760 
5761     /* Reset stop_cell_move */
5762     if (view->priv->stop_cell_move == TRUE)
5763     {
5764         view->priv->stop_cell_move = FALSE;
5765 
5766         /* this will populate cell with original value */
5767         g_idle_add ((GSourceFunc) gtv_sr_idle_transfer, view);
5768     }
5769 
5770     /* Reset Help text */
5771     if (view->help_text)
5772         g_free (view->help_text);
5773     view->help_text = g_strdup (" ");
5774     g_signal_emit_by_name (view, "help_signal", NULL);
5775 
5776     //Set edit-canceled property
5777     g_object_set_data (G_OBJECT (cr), "edit-canceled", GINT_TO_POINTER (TRUE));
5778 }
5779 
5780 /*####################################################################
5781           ^^^^   gtv function call backs    ^^^^
5782 #####################################################################*/
5783 
5784 /* Scroll the view to show selected row based on sort direction */
5785 gboolean
gnc_tree_view_split_reg_scroll_to_cell(GncTreeViewSplitReg * view)5786 gnc_tree_view_split_reg_scroll_to_cell (GncTreeViewSplitReg *view)
5787 {
5788     GncTreeModelSplitReg *model;
5789     GtkTreePath *mpath, *spath;
5790 
5791     PINFO("#### Start Scroll to Cell ####");
5792 
5793     model = gnc_tree_view_split_reg_get_model_from_view (view);
5794 
5795     mpath = gnc_tree_view_split_reg_get_current_path (view);
5796     spath = gnc_tree_view_split_reg_get_sort_path_from_model_path (view, mpath);
5797 
5798     if (model->sort_direction == GTK_SORT_DESCENDING)
5799     {
5800         gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (view), spath, NULL, TRUE, 0.5, 0.0); //0.0
5801     }
5802     else
5803     {
5804         if (model->use_double_line)
5805         {
5806             gtk_tree_path_down (spath); // move to the second row of transaction
5807             gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (view), spath, NULL, TRUE, 1.0, 0.0); //1.0
5808         }
5809         else
5810         {
5811             gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (view), spath, NULL, TRUE, 1.0, 0.0); //1.0
5812         }
5813     }
5814 
5815     gtk_tree_path_free (mpath);
5816     gtk_tree_path_free (spath);
5817 
5818     PINFO("#### End Scroll to Cell ####");
5819 
5820     return (FALSE);
5821 }
5822 
5823 
5824 /* Scroll the view to show the blank split with least movement */
5825 gboolean
gnc_tree_view_split_reg_scroll_to_bsplit(GncTreeViewSplitReg * view)5826 gnc_tree_view_split_reg_scroll_to_bsplit (GncTreeViewSplitReg *view)
5827 {
5828     GncTreeModelSplitReg *model;
5829     GtkTreePath *bsplit_mpath, *bsplit_spath;
5830     Split *bsplit;
5831 
5832     model = gnc_tree_view_split_reg_get_model_from_view (view);
5833 
5834     /* Get the blank split spath */
5835     bsplit = gnc_tree_model_split_get_blank_split (model);
5836     bsplit_mpath = gnc_tree_model_split_reg_get_path_to_split_and_trans (model, bsplit, NULL);
5837     bsplit_spath = gnc_tree_view_split_reg_get_sort_path_from_model_path (view, bsplit_mpath);
5838 
5839     gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (view), bsplit_spath, NULL, FALSE, 1.0, 0.0);
5840 
5841     gtk_tree_path_free (bsplit_mpath);
5842     gtk_tree_path_free (bsplit_spath);
5843     return (FALSE);
5844 }
5845 
5846 
5847 /* Returns the Transaction at the current selected position */
5848 Transaction *
gnc_tree_view_split_reg_get_current_trans(GncTreeViewSplitReg * view)5849 gnc_tree_view_split_reg_get_current_trans (GncTreeViewSplitReg *view)
5850 {
5851     return view->priv->current_trans;
5852 }
5853 
5854 
5855 /* Returns the Split at the current selected position or NULL */
5856 Split *
gnc_tree_view_split_reg_get_current_split(GncTreeViewSplitReg * view)5857 gnc_tree_view_split_reg_get_current_split (GncTreeViewSplitReg *view)
5858 {
5859     return view->priv->current_split;
5860 }
5861 
5862 
5863 /* Returns the depth of the selected row */
5864 RowDepth
gnc_tree_view_reg_get_selected_row_depth(GncTreeViewSplitReg * view)5865 gnc_tree_view_reg_get_selected_row_depth (GncTreeViewSplitReg *view)
5866 {
5867     return view->priv->current_depth;
5868 }
5869 
5870 
5871 /* Returns the dirty_trans or NULL */
5872 Transaction *
gnc_tree_view_split_reg_get_dirty_trans(GncTreeViewSplitReg * view)5873 gnc_tree_view_split_reg_get_dirty_trans (GncTreeViewSplitReg *view)
5874 {
5875     return view->priv->dirty_trans;
5876 }
5877 
5878 
5879 /* Sets dirty_trans to trans or NULL to clear */
5880 void
gnc_tree_view_split_reg_set_dirty_trans(GncTreeViewSplitReg * view,Transaction * trans)5881 gnc_tree_view_split_reg_set_dirty_trans (GncTreeViewSplitReg *view, Transaction *trans)
5882 {
5883     if (trans == NULL)
5884     {
5885         g_object_set_data (G_OBJECT (view), "data-edited", GINT_TO_POINTER (FALSE));
5886         view->priv->dirty_trans = NULL;
5887     }
5888     else
5889     {
5890         g_object_set_data (G_OBJECT (view), "data-edited", GINT_TO_POINTER (TRUE));
5891         view->priv->dirty_trans = trans;
5892     }
5893 }
5894 
5895 
5896 /* Returns the current path, or NULL if the current path is the blank split. */
5897 GtkTreePath *
gnc_tree_view_split_reg_get_current_path(GncTreeViewSplitReg * view)5898 gnc_tree_view_split_reg_get_current_path (GncTreeViewSplitReg *view)
5899 {
5900     if (!view->priv->current_ref)
5901         return NULL;
5902     return gtk_tree_row_reference_get_path (view->priv->current_ref);
5903 }
5904 
5905 
5906 /* Sets the current path reference to path */
5907 void
gnc_tree_view_split_reg_set_current_path(GncTreeViewSplitReg * view,GtkTreePath * mpath)5908 gnc_tree_view_split_reg_set_current_path (GncTreeViewSplitReg *view, GtkTreePath *mpath)
5909 {
5910     GncTreeModelSplitReg *model;
5911 
5912     model = gnc_tree_view_split_reg_get_model_from_view (view);
5913 
5914     if (view->priv->current_ref != NULL)
5915     {
5916         gtk_tree_row_reference_free (view->priv->current_ref);
5917         view->priv->current_ref = NULL;
5918     }
5919     view->priv->current_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (model), mpath);
5920 }
5921 
5922 
5923 /* Reinit transaction / delete the splits */
5924 void
gnc_tree_view_split_reg_reinit_trans(GncTreeViewSplitReg * view)5925 gnc_tree_view_split_reg_reinit_trans (GncTreeViewSplitReg *view)
5926 {
5927     Transaction           *trans;
5928     RowDepth               depth;
5929 
5930     /* Make sure we have stopped editing */
5931     gnc_tree_view_split_reg_finish_edit (view);
5932 
5933     trans = view->priv->current_trans;
5934 
5935     // Lets get out of the way, move selection to trans - selection is blocked
5936     gnc_tree_control_split_reg_goto_rel_trans_row (view, 0);
5937 
5938     depth = view->priv->current_depth;
5939 
5940     if (trans && (depth != SPLIT3))
5941     {
5942         Split *s;
5943         int i = 0;
5944 
5945         if (!xaccTransIsOpen (trans))
5946             xaccTransBeginEdit (trans);
5947 
5948         gnc_tree_view_split_reg_set_dirty_trans (view, trans);
5949 
5950         while ((s = xaccTransGetSplit (trans, i)) != NULL)
5951         {
5952             if (xaccTransGetRateForCommodity (trans, view->priv->reg_comm, s, NULL))
5953                 xaccSplitDestroy (s);
5954             else i++;
5955         }
5956     }
5957 }
5958 
5959 
5960 /* Delete the current split */
5961 void
gnc_tree_view_split_reg_delete_current_split(GncTreeViewSplitReg * view)5962 gnc_tree_view_split_reg_delete_current_split (GncTreeViewSplitReg *view)
5963 {
5964     Transaction           *trans;
5965     Split                 *split;
5966 
5967     /* Make sure we have stopped editing */
5968     gnc_tree_view_split_reg_finish_edit (view);
5969 
5970     trans = view->priv->current_trans;
5971     split = view->priv->current_split;
5972 
5973     if (!xaccTransIsOpen (trans))
5974         xaccTransBeginEdit (trans);
5975 
5976     gnc_tree_view_split_reg_set_dirty_trans (view, trans);
5977 
5978     // Lets get out of the way, move selection to trans - selection is blocked
5979     gnc_tree_control_split_reg_goto_rel_trans_row (view, 0);
5980 
5981     xaccSplitDestroy (split);
5982 }
5983 
5984 
5985 /* Delete the current transaction */
5986 void
gnc_tree_view_split_reg_delete_current_trans(GncTreeViewSplitReg * view)5987 gnc_tree_view_split_reg_delete_current_trans (GncTreeViewSplitReg *view)
5988 {
5989     Transaction           *trans;
5990 
5991     /* We do not use the normal confirmation with this one as we have
5992        all ready asked the user to confirm delete */
5993 
5994     /* Make sure we have stopped editing */
5995     gnc_tree_view_split_reg_finish_edit (view);
5996 
5997     trans = view->priv->current_trans;
5998 
5999     /* We need to go back one to select the next transaction */
6000     gnc_tree_control_split_reg_goto_rel_trans_row (view, 1);
6001 
6002     if (!xaccTransIsOpen (trans))
6003         xaccTransBeginEdit (trans);
6004     gnc_tree_view_split_reg_set_dirty_trans (view, trans);
6005 
6006     xaccTransDestroy (trans);
6007     xaccTransCommitEdit (trans);
6008 
6009     gnc_tree_view_split_reg_set_dirty_trans (view, NULL);
6010 }
6011 
6012 
6013 /* Record changes */
6014 gboolean
gnc_tree_view_split_reg_enter(GncTreeViewSplitReg * view)6015 gnc_tree_view_split_reg_enter (GncTreeViewSplitReg *view)
6016 {
6017     /* Make sure we have stopped editing */
6018     gnc_tree_view_split_reg_finish_edit (view);
6019 
6020     // Test for transaction changed
6021     if (gtv_sr_transaction_changed (view))
6022         return FALSE;
6023 
6024     // Return FALSE on discard
6025     if (view->priv->trans_confirm == DISCARD)
6026         return FALSE;
6027 
6028     return TRUE;
6029 }
6030 
6031 
6032 /* Cancel the edit and rollback changes */
6033 void
gnc_tree_view_split_reg_cancel_edit(GncTreeViewSplitReg * view,gboolean reg_closing)6034 gnc_tree_view_split_reg_cancel_edit (GncTreeViewSplitReg *view, gboolean reg_closing)
6035 {
6036     GncTreeModelSplitReg *model;
6037     Transaction          *trans = view->priv->dirty_trans;
6038     Split                *split;
6039 
6040     ENTER("gnc_tree_view_split_reg_cancel_edit view is %p and reg_closing is %d", view, reg_closing);
6041 
6042     model = gnc_tree_view_split_reg_get_model_from_view (view);
6043 
6044     if (trans && xaccTransIsOpen (trans))
6045     {
6046         // Move selection to trans - selection is blocked
6047         gnc_tree_control_split_reg_goto_rel_trans_row (view, 0);
6048 
6049         // Remove the split before rollback.
6050         gnc_tree_model_split_reg_set_blank_split_parent (model, trans, TRUE);
6051 
6052         g_object_set_data (G_OBJECT (view), "data-edited", GINT_TO_POINTER (FALSE));
6053         xaccTransRollbackEdit (view->priv->dirty_trans);
6054 
6055         // Add the split after rollback so it is last in list.
6056         gnc_tree_model_split_reg_set_blank_split_parent (model, trans, FALSE);
6057 
6058         // Set the transaction to show correct view
6059         gnc_tree_view_split_reg_format_trans (view, view->priv->dirty_trans);
6060 
6061         gnc_tree_view_split_reg_set_dirty_trans (view, NULL);
6062 
6063         split = gnc_tree_model_split_get_blank_split (model);
6064         xaccSplitReinit (split); // Clear the blank split
6065     }
6066     /* Reset allow changes for reconciled transctions */
6067     view->change_allowed = FALSE;
6068 
6069     view->priv->auto_complete = FALSE; // reset auto_complete has run flag
6070 
6071     /* This updates the plugin page gui */
6072     gnc_tree_view_split_reg_call_uiupdate_cb(view);
6073 
6074     LEAVE(" ");
6075 }
6076 
6077 
6078 /* Make sure we have stopped editing */
6079 void
gnc_tree_view_split_reg_finish_edit(GncTreeViewSplitReg * view)6080 gnc_tree_view_split_reg_finish_edit (GncTreeViewSplitReg *view)
6081 {
6082     gtv_sr_finish_edit (view);
6083 
6084     /* give gtk+ a chance to handle pending events */
6085     while (gtk_events_pending ())
6086        gtk_main_iteration ();
6087 }
6088 
6089 
6090 /* Returns whether the splits are revealed for the transaction or current position
6091    if transaction is NULL */
6092 gboolean
gnc_tree_view_split_reg_trans_expanded(GncTreeViewSplitReg * view,Transaction * trans)6093 gnc_tree_view_split_reg_trans_expanded (GncTreeViewSplitReg *view, Transaction *trans)
6094 {
6095     GncTreeModelSplitReg *model;
6096     GtkTreePath *mpath, *spath;
6097     gboolean expanded;
6098 
6099     /* if trans is NULL use priv->expanded */
6100     if (trans == NULL)
6101         expanded = view->priv->expanded;
6102     else
6103     {
6104         model = gnc_tree_view_split_reg_get_model_from_view (view);
6105 
6106         mpath = gnc_tree_model_split_reg_get_path_to_split_and_trans (model, NULL, trans);
6107 
6108         spath = gnc_tree_view_split_reg_get_sort_path_from_model_path (view, mpath);
6109 
6110         gtk_tree_path_down (spath); /* Move the path down to trow2 */
6111 
6112         expanded = gtk_tree_view_row_expanded (GTK_TREE_VIEW (view), spath);
6113 
6114         gtk_tree_path_free (mpath);
6115         gtk_tree_path_free (spath);
6116     }
6117     return expanded;
6118 }
6119 
6120 
6121 /* Collapse the transaction, if trans is NULL, use current_ref */
6122 void
gnc_tree_view_split_reg_collapse_trans(GncTreeViewSplitReg * view,Transaction * trans)6123 gnc_tree_view_split_reg_collapse_trans (GncTreeViewSplitReg *view, Transaction *trans)
6124 {
6125     GncTreeModelSplitReg *model;
6126     GtkTreePath *temp_spath, *mpath, *spath;
6127     GtkTreeIter m_iter;
6128     gint *indices;
6129     RowDepth depth;
6130 
6131     ENTER("gnc_tree_view_split_reg_collapse_trans and trans is %p", trans);
6132 
6133     model = gnc_tree_view_split_reg_get_model_from_view (view);
6134 
6135     /* Make sure we have stopped editing */
6136     gnc_tree_view_split_reg_finish_edit (view);
6137 
6138     /* if trans is NULL use current_ref */
6139     if (trans == NULL)
6140         mpath = gtk_tree_row_reference_get_path (view->priv->current_ref);
6141     else
6142         mpath = gnc_tree_model_split_reg_get_path_to_split_and_trans (model, NULL, trans);
6143 
6144     spath = gnc_tree_view_split_reg_get_sort_path_from_model_path (view, mpath);
6145 
6146     /* Collapse the view back to the transaction */
6147     indices = gtk_tree_path_get_indices (spath);
6148     depth = gtk_tree_path_get_depth (spath);
6149 
6150     if (model->use_double_line)
6151         temp_spath = gtk_tree_path_new_from_indices (indices[0], 0, -1);
6152     else
6153         temp_spath = gtk_tree_path_new_from_indices (indices[0], -1);
6154 
6155     /* if trans is NULL, collapse and update current_ref */
6156     if (trans == NULL)
6157     {
6158         GtkTreePath *temp_mpath;
6159 
6160         gnc_tree_view_split_reg_block_selection (view, TRUE);
6161 
6162         /* Change the selection to last available row of transaction - double */
6163         if ((model->use_double_line) && (depth == SPLIT3))
6164             gtk_tree_selection_select_path (gtk_tree_view_get_selection (GTK_TREE_VIEW (view)), temp_spath);
6165 
6166         /* Change the selection to last available row of transaction - single */
6167         if ((!model->use_double_line) && (depth != TRANS1))
6168             gtk_tree_selection_select_path (gtk_tree_view_get_selection (GTK_TREE_VIEW (view)), temp_spath);
6169 
6170         gtk_tree_view_collapse_row (GTK_TREE_VIEW (view), temp_spath);
6171 
6172         /* Get the selection */
6173         if (gtv_sr_get_model_iter_from_selection (view, gtk_tree_view_get_selection (GTK_TREE_VIEW (view)), &m_iter))
6174         {
6175             temp_mpath = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &m_iter);
6176 
6177             /* Update the tree view titles */
6178             gtv_sr_titles (view, gtk_tree_path_get_depth (temp_mpath));
6179 
6180             /* Save the new model path to path ref */
6181             gnc_tree_view_split_reg_set_current_path (view, temp_mpath);
6182 
6183             gtk_tree_path_free (temp_mpath);
6184         }
6185         gnc_tree_view_split_reg_block_selection (view, FALSE);
6186     }
6187     else
6188         gtk_tree_view_collapse_row (GTK_TREE_VIEW (view), temp_spath);
6189 
6190     gtk_tree_path_free (temp_spath);
6191     gtk_tree_path_free (mpath);
6192     gtk_tree_path_free (spath);
6193 
6194     view->priv->expanded = FALSE;
6195 
6196     /* This updates the plugin page gui */
6197     gnc_tree_view_split_reg_call_uiupdate_cb(view);
6198 
6199     LEAVE(" ");
6200 }
6201 
6202 
6203 /* Expands the transaction or the current transaction if NULL */
6204 void
gnc_tree_view_split_reg_expand_trans(GncTreeViewSplitReg * view,Transaction * trans)6205 gnc_tree_view_split_reg_expand_trans (GncTreeViewSplitReg *view, Transaction *trans)
6206 {
6207     GncTreeModelSplitReg *model;
6208     GtkTreePath *mpath, *spath;
6209     GtkTreePath *start_path, *end_path;
6210     gint *indices_spath;
6211     gint num_splits;
6212 
6213     ENTER("gnc_tree_view_split_reg_expand_trans and trans is %p", trans);
6214 
6215     model = gnc_tree_view_split_reg_get_model_from_view (view);
6216 
6217     /* Make sure we have stopped editing */
6218     gnc_tree_view_split_reg_finish_edit (view);
6219 
6220     if (trans == NULL)
6221         mpath = gtk_tree_row_reference_get_path (view->priv->current_ref);
6222     else
6223         mpath = gnc_tree_model_split_reg_get_path_to_split_and_trans (model, NULL, trans);
6224 
6225     spath = gnc_tree_view_split_reg_get_sort_path_from_model_path (view, mpath);
6226 
6227     gtk_tree_view_expand_row (GTK_TREE_VIEW (view), spath, TRUE);
6228 
6229     view->priv->expanded = TRUE;
6230 
6231     if (view->priv->selection_to_blank_on_expand && (model->style != REG2_STYLE_JOURNAL))
6232         gtv_sr_selection_to_blank (view);
6233 
6234     /* Get spath indices and the number of splits */
6235     indices_spath = gtk_tree_path_get_indices (spath);
6236     num_splits = xaccTransCountSplits (view->priv->current_trans);
6237 
6238     if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (view), &start_path, &end_path))
6239     {
6240 //        gint *indices_start;
6241         gint *indices_end;
6242         gint lines = 0;
6243 
6244         /* The first and last visible path */
6245 //        indices_start = gtk_tree_path_get_indices (start_path);
6246         indices_end = gtk_tree_path_get_indices (end_path);
6247 
6248         if (model->use_double_line)
6249             lines = (indices_end[0] - indices_spath[0])*2;
6250         else
6251             lines = indices_end[0] - indices_spath[0];
6252 
6253         if ((num_splits + 1) > lines)
6254         {
6255             /* scroll window to show selection when view is idle */
6256             g_idle_add ((GSourceFunc) gnc_tree_view_split_reg_scroll_to_bsplit, view );
6257         }
6258         gtk_tree_path_free (start_path);
6259         gtk_tree_path_free (end_path);
6260     }
6261     gtk_tree_path_free (mpath);
6262     gtk_tree_path_free (spath);
6263 
6264     /* This updates the plugin page gui */
6265     gnc_tree_view_split_reg_call_uiupdate_cb(view);
6266 
6267     LEAVE(" ");
6268 }
6269 
6270 
6271 /* Return the credit and debit titles of those columns */
6272 const char *
gnc_tree_view_split_reg_get_credit_debit_string(GncTreeViewSplitReg * view,gboolean credit)6273 gnc_tree_view_split_reg_get_credit_debit_string (GncTreeViewSplitReg *view, gboolean credit)
6274 {
6275     GtkCellRenderer *cr0;
6276     GList *renderers;
6277     GList *columns;
6278     GList  *column;
6279     gint i;
6280     const char *title = NULL;
6281 
6282     columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (view));
6283 
6284     for ( column = columns, i = 1; column; column = g_list_next (column), i++)
6285     {
6286         GtkTreeViewColumn *tvc;
6287         ViewCol viewcol;
6288 
6289         tvc = column->data;
6290 
6291         // Get the first renderer, it has the view-column value.
6292         renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (tvc));
6293         cr0 = g_list_nth_data (renderers, 0);
6294         g_list_free (renderers);
6295 
6296         viewcol = GPOINTER_TO_INT (g_object_get_data (G_OBJECT(cr0), "view_column"));
6297 
6298         DEBUG("viewcol is %d", viewcol);
6299 
6300         if (viewcol == COL_CREDIT && credit)
6301             title = gtk_tree_view_column_get_title (tvc);
6302 
6303         if (viewcol == COL_DEBIT && !credit)
6304             title = gtk_tree_view_column_get_title (tvc);
6305     }
6306     g_list_free (columns);
6307     return title;
6308 }
6309 
6310 
6311 /* Returns the parent Window */
6312 GtkWidget *
gnc_tree_view_split_reg_get_parent(GncTreeViewSplitReg * view)6313 gnc_tree_view_split_reg_get_parent (GncTreeViewSplitReg *view)
6314 {
6315     GncTreeModelSplitReg *model;
6316     model = gnc_tree_view_split_reg_get_model_from_view (view);
6317     return gnc_tree_model_split_reg_get_parent (model);
6318 }
6319 
6320 
6321 /* This sets up the page gui update from the tree view motion callback */
6322 void
gnc_tree_view_split_reg_set_uiupdate_cb(GncTreeViewSplitReg * view,GFunc cb,gpointer cb_data)6323 gnc_tree_view_split_reg_set_uiupdate_cb (GncTreeViewSplitReg *view, GFunc cb, gpointer cb_data)
6324 {
6325     view->uiupdate_cb = cb;
6326     view->uiupdate_cb_data = cb_data;
6327 }
6328 
6329 /** Call the moved_cb callback that is used to update the page ui, if it is
6330 set. If it is not set, this function does nothing.
6331 
6332 \return FALSE so that this function can be used in g_idle_add() */
gnc_tree_view_split_reg_call_uiupdate_cb(GncTreeViewSplitReg * view)6333 gboolean gnc_tree_view_split_reg_call_uiupdate_cb(GncTreeViewSplitReg *view)
6334 {
6335     g_assert(view);
6336     if (view->uiupdate_cb)
6337         (view->uiupdate_cb)(view, view->uiupdate_cb_data);
6338     return FALSE;
6339 }
6340 
6341