Hi,
After reading a recent thread, I thought I might post this.
Basically I felt that the way the scheduled transactions/reminders were
presented didn't really meet my needs, either to be greeted with a
scheduled transaction druid or having to select a menu item to launch the
druid to see if I had some transactions pending.
The following patch produces a new button on the tool bar that displays the
number pending transactions. I implemented this back in 1.9.5 and it
applied up to 2.0.0 (the last I tried) (with a little fuzz.) So far is
caused me no problems, it is obviously without warranty of any kind, and
is, of course, GPL.
One one aspect of the patch is the gu_generate_instances function which is
a duplicate of the generate_instances in the gnome lib, but needed to be in
the gnome-utils lib. I took the "safe,easy,lazy" way out and duplicated the
function (and its dependencies), since I didn't know for sure the best way
to put it into a more correct lib)
I offer the following more as a demonstration solution to that feature request.
I also know that JSled is redoing the entire way the scheduled transactions
work, so obviously this will go nowhere. On the other hand, I have to say
you guys esp JSled, were very helpful this spring when I was hacking this
up...
The beauty of free software is that I had everything I needed to be able to
make gnucash do what I wanted, even if I am the only one that finds it useful.
The icons are based on the calendar icons from the Tango Project
http://tango-project.org which are licensed under Creative Commons
Attribution Share-Alike license http://creativecommons.org/licenses/by-sa/2.5/
I know of no other more specific attribution requirements of the Tango
Project for their icons or derivatives.
Andrew
--- gnucash-1.9.5-orig/src/gnome-utils/gnc-icons.h 2006-04-22 08:49:11.000000000 -0400
+++ gnucash-1.9.5/src/gnome-utils/gnc-icons.h 2006-04-22 10:05:11.000000000 -0400
@@ -22,6 +22,7 @@ G_BEGIN_DECLS
#define GNC_STOCK_INVOICE_POST "gnc-invoice-post"
#define GNC_STOCK_INVOICE_UNPOST "gnc-invoice-unpost"
#define GNC_STOCK_INVOICE_EDIT "gnc-invoice-edit"
+#define GNC_STOCK_SCHEDULE_EXEC "gnc-schedule-exec"
//FIXME: use own budget icons?
#define GNC_STOCK_BUDGET "gnc-budget"
--- gnucash-1.9.5-orig/src/gnome-utils/gnc-icons.c 2006-04-22 08:49:11.000000000 -0400
+++ gnucash-1.9.5/src/gnome-utils/gnc-icons.c 2006-04-22 10:05:09.000000000 -0400
@@ -18,6 +18,7 @@ static GtkStockItem items[] = {
{ GNC_STOCK_OPEN_ACCOUNT, N_("_Open Account"), 0, 0, NULL },
{ GNC_STOCK_TRANSFER, N_("_Transfer..."), 0, 0, NULL },
{ GNC_STOCK_SPLIT_TRANS, N_("S_plit Transaction"), 0, 0, NULL },
+ { GNC_STOCK_SCHEDULE_EXEC, N_("Since Last Run..."), 0, 0, NULL },
{ GNC_STOCK_JUMP_TO, N_("_Jump"), 0, 0, NULL },
};
@@ -41,6 +44,7 @@ static item_file item_files[] = {
{ GNC_STOCK_INVOICE_POST, "gnc-invoice-post.png", "gnc-invoice-post-16.png"},
{ GNC_STOCK_INVOICE_UNPOST, "gnc-invoice-unpost.png", "gnc-invoice-unpost-16.png"},
{ GNC_STOCK_INVOICE_EDIT, "gnc-invoice-edit.png", "gnc-invoice-edit-16.png"},
+ { GNC_STOCK_SCHEDULE_EXEC, "gnc-sx-exec.png", "gnc-sx-exec-16.png"},
{ 0 },
};
--- gnucash-1.9.5-orig/src/gnome-utils/gnc-plugin.h 2006-04-17 07:15:34.000000000 -0400
+++ gnucash-1.9.5/src/gnome-utils/gnc-plugin.h 2006-05-02 23:00:41.000000000 -0400
@@ -248,6 +248,15 @@ typedef struct {
const char *label;
} action_toolbar_labels;
+/** Same as above but make the label just a char * and not a static char *
+ */
+typedef struct {
+ /** The name of the action. */
+ const char *action_name;
+ /** The alternate toolbar label to use */
+ char *label;
+} action_toolbar_dynamic_labels;
+
/** Add "short" labels to existing actions. The "short" label is the
* string used on toolbar buttons when the action is visible. All
--- /home/andrew/devel/gnucash-1.9.5/src/gnome-utils/gnc-main-window.c 2006-05-01 23:27:55.000000000 -0400
+++ gnucash-1.9.5/src/gnome-utils/gnc-main-window.c 2006-05-05 14:20:38.000000000 -0400
@@ -68,6 +68,9 @@
#include "gnc-window.h"
#include "gnc-main.h"
#include "gnc-gconf-utils.h"
+#include "SX-book.h"
+#include "SchedXaction.h"
+#include "gnc-ui-util.h"
// +JSLED
#include "gnc-html.h"
@@ -107,7 +110,12 @@ static void gnc_main_window_destroy (Gtk
static void gnc_main_window_setup_window (GncMainWindow *window);
static void gnc_window_main_window_init (GncWindowIface *iface);
-
+static void gu_generate_instances(SchedXaction *sx,
+ GDate *end,
+ GDate *reminderEnd,
+ GList **instanceList,
+ GList **reminderList,
+ GList **deadList);
/* Callbacks */
static void gnc_main_window_add_widget (GtkUIManager *merge, GtkWidget *widget, GncMainWindow *window);
static void gnc_main_window_switch_page (GtkNotebook *notebook, GtkNotebookPage *notebook_page, gint pos, GncMainWindow *window);
@@ -2007,6 +2015,7 @@ gnc_main_window_open_page (GncMainWindow
if (tmp == NULL)
window = gnc_main_window_new ();
gtk_widget_show(GTK_WIDGET(window));
+ gnc_update_sx_pending_ui();
} else if ((window == NULL) && active_windows) {
window = active_windows->data;
}
@@ -3576,5 +3585,339 @@ gnc_main_window_all_action_set_sensitive
}
}
+/*
+** Return a handle to the window
+*/
+GncMainWindow *
+gnc_main_window_get_first_window( void )
+
+
+{
+ GncMainWindow *window;
+
+ window = g_list_nth_data(active_windows, 0);
+
+ return window;
+}
+/*
+** Function to update the pending button based on whether or not
+** there are scheduled tx to deal with
+*/
+
+void gnc_update_sx_pending_ui(void) {
+
+ GncMainWindow *window;
+ GList *walker;
+
+ action_toolbar_dynamic_labels toolbar_labels[] = {
+ { "ActionsSinceLastRunAction", NULL },
+ { NULL, NULL },
+ };
+
+ static const gchar *important_actions[] = {
+ "ActionsSinceLastRunAction",
+ NULL,
+ };
+
+ GtkActionGroup *action_group;
+ gint pending_sx_count = 0;
+
+ pending_sx_count = sxsincelast_checkpend();
+ pending_sx_count = abs(pending_sx_count);
+
+ if (pending_sx_count)
+ toolbar_labels[0].label = g_strdup_printf(N_("%d Pending"),pending_sx_count);
+ else
+ toolbar_labels[0].label = g_strdup(N_("0 Pending"));
+
+ for (walker = active_windows; walker; walker = g_list_next(walker)) {
+ window = walker->data;
+ action_group = gnc_main_window_get_action_group (window,"gnc-plugin-basic-commands-actions");
+ if (action_group != NULL) {
+ gnc_plugin_init_short_names (action_group,
+ (action_toolbar_labels *) toolbar_labels);
+ gnc_plugin_set_important_actions (action_group, important_actions);
+ }
+ }
+
+ g_free(toolbar_labels[0].label);
+
+}
+
+typedef enum {
+ TO_CREATE,
+ IGNORE,
+ POSTPONE,
+ MAX_STATE,
+ UNDEF
+} ToCreateState;
+
+typedef struct toCreateTuple_ {
+ SchedXaction *sx;
+ GList /* <toCreateInstance*> */ *instanceList;
+} toCreateTuple;
+
+typedef struct toCreateInstance_ {
+ GDate *date;
+ GHashTable *varBindings;
+ void *sxStateData;
+ GtkCTreeNode *node;
+ toCreateTuple *parentTCT;
+ /* A list of the GUIDs of transactions generated from this TCI [if
+ * any]; this will always be a subset of the
+ * sxsld->createdTxnGUIDList. */
+ GList /* <GUID*> */ *createdTxnGUIDs;
+ gboolean dirty;
+ /** How this was, originally -- for revert processing. **/
+ ToCreateState origState;
+ /** How the user would currently like to process this instance
+ * [within the druid]. */
+ ToCreateState state;
+ /** How we've previously processed this instance [within the druid]. */
+ ToCreateState prevState;
+} toCreateInstance;
+
+/**
+ * A tuple of an SX and any upcoming reminders.
+ **/
+typedef struct reminderTuple_ {
+ SchedXaction *sx;
+ GList /* <reminderInstanceTuple*> */ *instanceList;
+} reminderTuple;
+
+/**
+ * An reminder instance of the containing SX.
+ **/
+typedef struct reminderInstanceTuple_ {
+ GDate *endDate;
+ GDate *occurDate;
+ void *sxStateData;
+ gboolean isSelected;
+ reminderTuple *parentRT;
+ toCreateInstance *resultantTCI;
+} reminderInstanceTuple;
+
+typedef struct toDeleteTuple_ {
+ SchedXaction *sx;
+ GDate *endDate;
+ gboolean isSelected;
+} toDeleteTuple;
+
+typedef struct creation_helper_userdata_ {
+ /* the to-create tuple */
+ toCreateInstance *tci;
+ /* a pointer to a GList to append the GUIDs of newly-created
+ * Transactions to, or NULL */
+ GList **createdGUIDs;
+ /* a pointer to a GList<GString*> of error-messages encountered while
+ * creating the transactions. **/
+ GList **creation_errors;
+} createData;
+
+/**
+ * returns the number of pending sxsincelast transcations
+ **/
+
+gint
+sxsincelast_checkpend(void)
+{
+ int toDo = 0;
+ int toReminder = 0;
+ GList *sxList;
+ SchedXaction *sx;
+ GList *lsx = NULL;
+ GList *instanceList = NULL;
+ GList *linst;
+ GDate end, endPlusReminders;
+ gint daysInAdvance;
+
+ toCreateInstance *tci;
+
+ // GList *toCreateList = NULL;
+ GList *reminderList = NULL;
+ GList *toRemoveList = NULL;
+
+ GHashTable *sxInitStates;
+
+ sxList = gnc_book_get_schedxactions( gnc_get_current_book () );
+
+ if (sxList == NULL) {
+ DEBUG( "No scheduled transactions to populate." );
+ return toDo;
+ }
+
+ sxInitStates = g_hash_table_new( g_direct_hash, g_direct_equal );
+
+ for ( lsx = sxList ; lsx ; lsx = lsx->next ) {
+
+ sx = (SchedXaction*)lsx->data;
+
+ if ( g_hash_table_lookup( sxInitStates, sx )
+ != NULL ) {
+ PERR( "Why are we able to find a SX initial state "
+ "hash entry for something we're seeing for "
+ "the first time?" );
+ return toDo;
+ }
+ {
+ void *sx_state;
+ sx_state = gnc_sx_create_temporal_state( sx );
+ g_hash_table_insert( sxInitStates,
+ sx, sx_state );
+ sx_state = NULL;
+ }
+
+ g_date_set_time_t( &end, time(NULL) );
+ daysInAdvance = xaccSchedXactionGetAdvanceCreation( sx );
+ g_date_add_days( &end, daysInAdvance );
+
+ endPlusReminders = end;
+ daysInAdvance = xaccSchedXactionGetAdvanceReminder(sx);
+ g_date_add_days(&endPlusReminders, daysInAdvance);
+
+ {
+ GList *postponedList = NULL;
+ GList *lpostp;
+ postponedList = gnc_sx_get_defer_instances( sx );
+ for ( lpostp = postponedList; lpostp; lpostp = lpostp->next ) {
+ tci = g_new0( toCreateInstance, 1 );
+ tci->sxStateData = (void*)lpostp->data;
+ tci->date = g_date_new();
+ *tci->date = xaccSchedXactionGetNextInstance(
+ sx, tci->sxStateData );
+ tci->dirty = FALSE;
+ tci->state = POSTPONE;
+ tci->prevState = POSTPONE;
+ tci->origState = POSTPONE;
+
+ instanceList = g_list_append( instanceList, tci );
+ tci = NULL;
+ }
+
+ }
+
+ gu_generate_instances(sx,
+ &end,
+ &endPlusReminders,
+ &instanceList,
+ &reminderList,
+ &toRemoveList);
+
+ }
+
+ if (instanceList != NULL) {
+ for ( linst = instanceList ; linst ; linst = linst->next )
+ toDo++;
+ }
+
+ if (reminderList != NULL) {
+ for ( linst = reminderList ; linst ; linst = linst->next )
+ toReminder++;
+ }
+
+ /* if (toRemoveList != NULL)
+ g_list_free( toRemoveList );
+
+ if (instanceList != NULL)
+ g_list_free( instanceList );
+
+ instanceList = NULL;
+
+ if (reminderList != NULL)
+ g_list_free( reminderList );
+
+ reminderList = NULL;
+
+
+ g_list_free(sxList);
+ */
+ return toDo+toReminder;
+}
+
+static void
+gu_generate_instances(SchedXaction *sx,
+ GDate *end,
+ GDate *reminderEnd,
+ GList **instanceList,
+ GList **reminderList,
+ GList **deadList)
+{
+ GDate gd;
+ toCreateInstance *tci;
+ reminderTuple *rt;
+ reminderInstanceTuple *rit;
+ void *seqStateData;
+
+ g_assert( g_date_valid(end) );
+ g_assert( g_date_valid(reminderEnd) );
+
+ g_date_clear(&gd, 1);
+
+ /* Process valid next instances. */
+ seqStateData = gnc_sx_create_temporal_state( sx );
+ //gd = xaccSchedXactionGetNextInstance( sx, seqStateData );
+ gd = xaccSchedXactionGetInstanceAfter( sx, &gd, seqStateData );
+ while ( g_date_valid(&gd)
+ && g_date_compare( &gd, end ) <= 0 ) {
+
+ tci = g_new0( toCreateInstance, 1 );
+
+ tci->dirty = FALSE;
+ tci->date = g_date_new();
+ *tci->date = gd;
+ tci->origState = UNDEF;
+ tci->state = TO_CREATE;
+ tci->prevState = UNDEF;
+ tci->sxStateData =
+ gnc_sx_clone_temporal_state( seqStateData );
+ *instanceList = g_list_append( *instanceList, tci );
+
+ gnc_sx_incr_temporal_state( sx, seqStateData );
+ gd = xaccSchedXactionGetInstanceAfter( sx, &gd, seqStateData );
+ }
+
+ /* Process reminder instances or add to dead list [if we have one] */
+ if ( g_date_valid( &gd ) ) {
+ rt = g_new0( reminderTuple, 1 );
+ rt->sx = sx;
+ rt->instanceList = NULL;
+ while ( g_date_valid(&gd)
+ && g_date_compare( &gd, reminderEnd ) <= 0 ) {
+
+ rit = g_new0( reminderInstanceTuple, 1 );
+ rit->endDate = g_date_new();
+ *rit->endDate = *end;
+ rit->occurDate = g_date_new();
+ *rit->occurDate = gd;
+ rit->isSelected = FALSE;
+ rit->parentRT = rt;
+ rit->sxStateData =
+ gnc_sx_clone_temporal_state( seqStateData );
+ rt->instanceList = g_list_append( rt->instanceList, rit );
+
+ gnc_sx_incr_temporal_state( sx, seqStateData );
+ gd = xaccSchedXactionGetInstanceAfter( sx, &gd, seqStateData );
+ }
+ if ( rt->instanceList != NULL ) {
+ *reminderList = g_list_append( *reminderList, rt );
+ } else {
+ g_free( rt );
+ }
+ rt = NULL;
+ } else if ( deadList ) {
+ toDeleteTuple *tdt;
+
+ tdt = g_new0( toDeleteTuple, 1 );
+ tdt->sx = sx;
+ tdt->endDate = g_date_new();
+ *tdt->endDate = gd;
+ *deadList = g_list_append( *deadList, tdt );
+ } /* else { this else intentionally left blank: drop the SX on the
+ * floor at this point. } */
+
+ gnc_sx_destroy_temporal_state( seqStateData );
+ seqStateData = NULL;
+}
+
/** @} */
/** @} */
--- gnucash-1.9.5-orig/src/gnome-utils/gnc-main-window.h 2006-05-01 23:27:55.000000000 -0400
+++ gnucash-1.9.5/src/gnome-utils/gnc-main-window.h 2006-05-03 22:55:58.000000000 -0400
@@ -354,6 +354,25 @@ gboolean gnc_main_window_all_finish_pend
* this action. */
void gnc_main_window_all_action_set_sensitive (const gchar *action_name, gboolean sensitive);
+/** Return the handle to the first window
+ *
+ * @return the first window in the active_widnows Glist
+*/
+GncMainWindow *gnc_main_window_get_first_window( void );
+
+/** update the "Pending" sx button based on whether or not
+ * there are any ready to go.
+ *
+ */
+void gnc_update_sx_pending_ui(void);
+
+/** get the count of pending transactions
+ * only called from the above function
+ *
+ * @return The number of pending scheduled transactions
+*/
+gint sxsincelast_checkpend( void );
+
G_END_DECLS
#endif /* __GNC_MAIN_WINDOW_H */
--- gnucash-1.9.5-orig/src/gnome-utils/gnc-plugin-file-history.c 2006-04-17 07:15:34.000000000 -0400
+++ gnucash-1.9.5/src/gnome-utils/gnc-plugin-file-history.c 2006-05-02 20:33:00.000000000 -0400
@@ -592,6 +592,7 @@ gnc_plugin_file_history_cmd_open_file (G
gnc_window_set_progressbar_window (GNC_WINDOW(data->window));
gnc_file_open_file (filename); /* also opens new account page */
gnc_window_set_progressbar_window (NULL);
+ gnc_update_sx_pending_ui();
}
/** @} */
--- gnucash-1.9.5-orig/src/gnome/top-level.c 2006-04-21 22:40:57.000000000 -0400
+++ gnucash-1.9.5/src/gnome/top-level.c 2006-05-03 14:41:46.000000000 -0400
@@ -254,7 +254,7 @@ gnc_restore_all_state (gpointer session,
memory. Now we create their ui component. */
gnc_reports_show_all(session);
#endif
-
+ gnc_update_sx_pending_ui();
LEAVE("old");
return;
}
@@ -296,6 +296,7 @@ gnc_restore_all_state (gpointer session,
if (file_guid)
g_free(file_guid);
g_key_file_free(keyfile);
+ gnc_update_sx_pending_ui();
}
--- gnucash-1.9.5-orig/src/gnome/dialog-sxsincelast.c 2006-05-01 23:27:55.000000000 -0400
+++ gnucash-1.9.5/src/gnome/dialog-sxsincelast.c 2006-05-05 10:37:29.000000000 -0400
@@ -2408,6 +2408,7 @@ sxsincelast_destroy( GtkObject *o, gpoin
sxsld->sincelast_window );
g_free( sxsld );
+ gnc_update_sx_pending_ui(); /* Update the count on the toolbar button */
}
/**
--- gnucash-1.9.5-orig/src/gnome/gnc-plugin-basic-commands.c 2006-05-01 23:27:55.000000000 -0400
+++ gnucash-1.9.5/src/gnome/gnc-plugin-basic-commands.c 2006-05-08 23:48:52.000000000 -0400
@@ -132,8 +132,8 @@ static GtkActionEntry gnc_plugin_actions
{ "ActionsScheduledTransactionEditorAction", NULL, N_("_Scheduled Transaction Editor"), NULL,
N_("The list of Scheduled Transactions"),
G_CALLBACK (gnc_main_window_cmd_actions_scheduled_transaction_editor) },
- { "ActionsSinceLastRunAction", NULL, N_("_Since Last Run..."), NULL,
- N_("Create Scheduled Transactions since the last time run"),
+ { "ActionsSinceLastRunAction", GNC_STOCK_SCHEDULE_EXEC, N_("_Since Last Run..."), NULL,
+ N_("Create Scheduled Transactions since the last run"),
G_CALLBACK (gnc_main_window_cmd_actions_since_last_run) },
{ "ActionsMortgageLoanAction", NULL, N_("_Mortgage & Loan Repayment..."), NULL,
N_("Setup scheduled transactions for repayment of a loan"),
@@ -163,6 +163,12 @@ static GtkActionEntry gnc_plugin_actions
/** The number of actions provided by this plugin. */
static guint gnc_plugin_n_actions = G_N_ELEMENTS (gnc_plugin_actions);
+/** Short labels for use on the toolbar buttons. */
+static action_toolbar_labels toolbar_labels[] = {
+ { "FileSaveAction", N_("Save") },
+ { "ActionsSinceLastRunAction", N_("Pending") },
+ { NULL, NULL },
+};
/** These are the "important" actions provided by the basic commands
* plugin. Their labels will appear when the toolbar is set to
@@ -172,6 +178,10 @@ static const gchar *gnc_plugin_important
NULL,
};
+static const gchar *gnc_plugin_important_actions2[] = {
+ "ActionsSinceLastRunAction",
+ NULL,
+};
/** The instance private data structure for an basic commands
* plugin. */
@@ -266,6 +276,18 @@ gnc_plugin_basic_commands_class_init (Gn
static void
gnc_plugin_basic_commands_init (GncPluginBasicCommands *plugin)
{
+ GncMainWindow *window = NULL;
+ GtkActionGroup *action_group = NULL;
+ window = gnc_main_window_get_first_window();
+ if (window != NULL) {
+ action_group = gnc_main_window_get_action_group (window,
+ "gnc-plugin-basic-commands-actions");
+ if (action_group != NULL) {
+ gnc_plugin_init_short_names (action_group, toolbar_labels);
+ gnc_plugin_set_important_actions (action_group,
+ gnc_plugin_important_actions2);
+ }
+ }
}
@@ -436,6 +458,7 @@ gnc_main_window_cmd_actions_since_last_r
ret = gnc_ui_sxsincelast_dialog_create ();
if ( ret == 0 ) {
gnc_info_dialog (GTK_WIDGET(&window->gtk_window), nothing_to_do_msg);
+ // gnc_update_sx_pending_ui();
} else if ( ret < 0 ) {
gnc_info_dialog (GTK_WIDGET(&window->gtk_window), ngettext
/* Translators: %d is the number of transactions. This is a
@@ -446,6 +469,7 @@ gnc_main_window_cmd_actions_since_last_r
"(%d transactions automatically created)",
-(ret)),
-(ret));
+ // gnc_update_sx_pending_ui();
} /* else { this else [>0 means dialog was created] intentionally left
* blank. } */
}
--- gnucash-1.9.3-orig/src/gnome/gnc-plugin-basic-commands.h 2006-03-20 16:28:43.000000000 -0500
+++ gnucash-1.9.3/src/gnome/gnc-plugin-basic-commands.h 2006-03-28 15:48:31.000000000 -0500
@@ -48,6 +48,7 @@ G_BEGIN_DECLS
#define GNC_PLUGIN_BASIC_COMMANDS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNC_TYPE_PLUGIN_BASIC_COMMANDS, GncPluginBasicCommandsClass))
#define GNC_PLUGIN_BASIC_COMMANDS_NAME "gnc-plugin-basic-commands"
+#define GNC_STOCK_SCHEDULE_EXEC "gnc-schedule-exec"
/* typedefs & structures */
--- gnucash-1.9.3-orig/src/gnome/ui/gnc-plugin-basic-commands-ui.xml 2006-03-20 16:28:43.000000000 -0500
+++ gnucash-1.9.3/src/gnome/ui/gnc-plugin-basic-commands-ui.xml 2006-03-30 08:22:32.000000000 -0500
@@ -84,5 +84,7 @@
</placeholder>
<separator name="ToolbarSep1"/>
<placeholder name="DefaultToolbarPlaceholder"/>
+ <separator name="ToolbarSep67a"/>
+ <toolitem name="ToolbarSinceLastRun" action="ActionsSinceLastRunAction"/>
</toolbar>
</ui>


_______________________________________________
gnucash-devel mailing list
[email protected]
https://lists.gnucash.org/mailman/listinfo/gnucash-devel