Fold branches/sx-cleanup/ [14463:15384] back into trunk/.

git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@15399 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
Joshua Sled 2007-01-19 23:45:45 +00:00
parent e3a674154b
commit eb73260220
52 changed files with 7861 additions and 7058 deletions

View File

@ -77,6 +77,17 @@
relocation of previously compiled-in paths. With the exception of
the env variables in src/bin/gnucash, we're fully relocatable now.
2006-09-16 Joshua Sled <jsled@asynchronous.org>
* src/gnome/gnc-plugin-page-sx-list.c (sxsl_get_sx_vars): Add
variable extraction to instance-model creation. The
GncSxInstances now has a hashtable of variables parsed from the
formula, and the GncSxInstance has a copy of that variables hash.
Not finished, but mostly in place.
* src/gnome/dialog-sx-since-last-run.c: New, simplified version of
the since-last-run dialog. GncSxSlrTreeModelAdapter.
2006-09-13 Christian Stimming <stimming@tuhh.de>
* src/import-export/hbci/gnc-plugin-hbci.c: Move the MT940
@ -270,6 +281,30 @@
preferences_dialog->Windows. Move "Show close button on notebook
tabs" from General to Windows. Fixes #340299.
2006-07-27 Joshua Sled <jsled@asynchronous.org>
* src/gnome-utils/gnc-dense-cal.c
(gnc_dense_cal_transient_model_new): Actually implement
GncDenseCalTransientModel.
* src/gnome/dialog-sx-editor.c (gnc_sxed_update_cal):
* src/gnome/dialog-sx-from-trans.c (sxftd_update_example_cal):
Use GncDenseCalTransientModel from previous ad-hoc updaters.
2006-07-25 Joshua Sled <jsled@asynchronous.org>
* src/gnome-utils/gnc-dense-cal.c:
Add GncDenseCalModel interface, support.
Add unfinished GncDenseCalTransient model impl. for
one-off being-edited-SX calendar usage.
* src/gnome/gnc-plugin-page-sx-list.c:
Add GncSxInstanceDenseCalAdapter between GncSxInstanceModel and
GncDenseCalModel. Start to hook up 'added' and 'removing' signals
on the GncSxInstanceModel. The SX-List dense-cal works again, and
reflects both removed and new SXes.
2006-07-24 Derek Atkins <derek@ihtfp.com>
* [lots of Makefile.am files]:
@ -302,6 +337,27 @@
extra de-quoting of path names that is done on the GNC_MODULE_PATH
env variable.
2006-07-16 Joshua Sled <jsled@asynchronous.org>
* src/engine/SX-book-p.h:
* src/engine/SX-book.h:
* src/engine/SX-book.c: Promote SX list from a GList to a
`SchedXactions` QOF Entity. Create add/remove API that emits
GNC_EVENT_{INSERT,REMOVE} signals. Correctly associate the SX
List with the collection of SchedXaction qof-type rather than the
SX template transactions qof-type. Remove some (now-)dead
code. Fix long-standing bug in registration of SX qof types.
* src/gnome/dialog-sx-editor.[ch]:
* src/gnome/dialog-schedxaction.[ch]:
Move the SX editor dialog subset of dialog-schedxaction to
dialog-sx-editor.[ch].
* src/gnome/gnc-plugin-page-sx-list.c:
Hookup SX editor for both 'new' and 'edit' actions. Hookup
row-activation from tree-view. Extend GncSxInstanceModel to
support SchedXactions (sx list) modification events.
2006-07-16 Derek Atkins <derek@ihtfp.com>
* configure.in:
@ -331,6 +387,18 @@
* src/gnome-utils/gnc-main-window.c: Do not move windows on
restoration that would be offscreen.
2006-07-15 Joshua Sled <jsled@asynchronous.org>
* src/engine/SchedXaction.h (GNC_IS_SX,GNC_SX): added for convenience.
* src/gnome/ui/gnc-plugin-page-sx-list-ui.xml:
* src/gnome/gnc-plugin-page-sx-list.[ch]: SX List as a plugin page.
* src/gnome/gnc-plugin-basic-commands.c: Call SX List plugin page,
not dialog.
* src/doc/sx.rst: Added.
2006-07-15 Derek Atkins <derek@ihtfp.com>
* src/business/business-core/gncAddress.[ch]:

View File

@ -34,6 +34,7 @@ libgncmod_app_utils_la_SOURCES = \
gnc-exp-parser.c \
gnc-gettext-util.c \
gnc-helpers.c \
gnc-sx-instance-model.c \
gncmod-app-utils.c \
gnc-ui-util.c \
guile-util.c \
@ -59,6 +60,7 @@ gncinclude_HEADERS = \
gnc-exp-parser.h \
gnc-gettext-util.h \
gnc-helpers.h \
gnc-sx-instance-model.h \
gnc-ui-common.h \
gnc-ui-util.h \
guile-util.h \

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,210 @@
/*
* gnc-sx-instance-model.h
*
* Copyright (C) 2006 Josh Sled <jsled@asynchronous.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact:
*
* Free Software Foundation Voice: +1-617-542-5942
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
* Boston, MA 02110-1301, USA gnu@gnu.org
*/
#ifndef _GNC_SX_INSTANCE_MODEL_H
#define _GNC_SX_INSTANCE_MODEL_H
#include "config.h"
#include <glib.h>
#include <glib-object.h>
#include "gnc-numeric.h"
#include "SchedXaction.h"
G_BEGIN_DECLS
#define GNC_TYPE_SX_INSTANCE_MODEL (gnc_sx_instance_model_get_type ())
#define GNC_SX_INSTANCE_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNC_TYPE_SX_INSTANCE_MODEL, GncSxInstanceModel))
#define GNC_SX_INSTANCE_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNC_TYPE_SX_INSTANCE_MODEL, GncSxInstanceModelClass))
#define GNC_IS_SX_INSTANCE_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNC_TYPE_SX_INSTANCE_MODEL))
#define GNC_IS_SX_INSTANCE_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNC_TYPE_SX_INSTANCE_MODEL))
#define GNC_SX_INSTANCE_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNC_TYPE_SX_INSTANCE_MODEL, GncSxInstanceModelClass))
typedef struct _GncSxInstanceModel
{
GObject parent;
gboolean disposed;
/* private */
gint qof_event_handler_id;
/* signals */
/* void (*added)(SchedXaction *sx); // gpointer user_data */
/* void (*updated)(SchedXaction *sx); // gpointer user_data */
/* void (*removing)(SchedXaction *sx); // gpointer user_data */
/* public */
GDate range_end;
GList *sx_instance_list; /* <GncSxInstances*> */
} GncSxInstanceModel;
typedef struct _GncSxInstanceModelClass
{
GObjectClass parent;
guint removing_signal_id;
guint updated_signal_id;
guint added_signal_id;
} GncSxInstanceModelClass;
typedef struct _GncSxInstances
{
SchedXaction *sx;
GHashTable /** <name:char*,GncSxVariable*> **/ *variable_names;
gboolean variable_names_parsed;
GDate next_instance_date;
/** GList<GncSxInstance*> **/
GList *list; /* @fixme: s/list/?/ */
} GncSxInstances;
typedef enum
{
SX_INSTANCE_STATE_IGNORED,
SX_INSTANCE_STATE_POSTPONED,
SX_INSTANCE_STATE_TO_CREATE,
SX_INSTANCE_STATE_REMINDER,
SX_INSTANCE_STATE_CREATED,
SX_INSTANCE_STATE_MAX_STATE
} GncSxInstanceState;
typedef struct _GncSxVariable
{
gchar *name;
gnc_numeric value; /**< only numeric values are supported. **/
gboolean editable;
} GncSxVariable;
typedef struct _GncSxInstance
{
GncSxInstances *parent; /**< the parent instances collection. **/
void *temporal_state; /**< the sx creation temporal state. **/
GncSxInstanceState orig_state; /**< the original state at generation time. **/
GncSxInstanceState state; /**< the current state of the instance (during editing) **/
GDate date; /**< the instance date. **/
GHashTable *variable_bindings; /**< variable bindings. **/
} GncSxInstance;
typedef struct _GncSxVariableNeeded
{
GncSxInstance *instance;
GncSxVariable *variable;
} GncSxVariableNeeded;
GType gnc_sx_instance_model_get_type(void);
GncSxInstanceModel* gnc_sx_get_current_instances(void);
GncSxInstanceModel* gnc_sx_get_instances(GDate *range_end);
/**
* Regenerates and updates the GncSxInstances* for the given SX. Model
* consumers are probably going to call this in response to seeing the
* "update" signal, unless they need to be doing something else like
* finishing an iteration over an existing GncSxInstances*.
**/
void gnc_sx_instance_model_update_sx_instances(GncSxInstanceModel *model, SchedXaction *sx);
void gnc_sx_instance_model_remove_sx_instances(GncSxInstanceModel *model, SchedXaction *sx);
/** @return GList<GncSxVariable*> **/
GList *gnc_sx_instance_get_variables(GncSxInstance *inst);
Account* gnc_sx_get_template_transaction_account(SchedXaction *sx);
/**
* @return caller-owned data struct.
**/
GHashTable* gnc_sx_instance_get_variables_for_parser(GHashTable *instance_var_hash);
GncSxVariable* gnc_sx_variable_new_full(gchar *name, gnc_numeric value, gboolean editable);
void gnc_sx_variable_free(GncSxVariable *var);
/**
* There is a constraint around a sequence of upcoming instance states. In
* short: the last-created state and a list of postponed instances are modeled,
* but upcoming reminders are not. As such, a reminder can never be before any
* other (modeled) instance type. For instance, the following sequences are
* disallowed:
*
* [...]
* remind <- will be lost/skipped over; must be converted to `postponed`.
* to-create <- this will be the last-recorded state.
* [...]
*
* [...]
* remind <- same as previous; will be lost/skipped; must be `postponed`.
* postponed
* [...]
*
* remind <- same...
* ignore
* [...]
*
*
* As such, the SinceLastRun model will enforce that there are no previous
* `remind` instances at every state change. They will be silently converted to
* `postponed`-state transactions.
**/
void gnc_sx_instance_model_change_instance_state(GncSxInstanceModel *model,
GncSxInstance *instance,
GncSxInstanceState new_state);
void gnc_sx_instance_model_set_variable(GncSxInstanceModel *model,
GncSxInstance *instance,
GncSxVariable *variable,
gnc_numeric *new_value);
/**
* @return List<GncSxVariableNeeded> of unbound {instance,variable} pairs;
* the caller owns the list and the items.
**/
GList* gnc_sx_instance_model_check_variables(GncSxInstanceModel *model);
void gnc_sx_instance_model_effect_change(GncSxInstanceModel *model,
gboolean auto_create_only,
GList **created_transaction_guids,
GList **creation_errors);
typedef struct _GncSxSummary
{
gboolean need_dialog; /**< If the dialog needs to be displayed. **/
gint num_instances; /**< The number of total instances (in any state). **/
gint num_to_create_instances; /**< The number of (not-auto-create) to-create instances. **/
gint num_auto_create_instances; /**< The total number of auto-create instances. **/
gint num_auto_create_no_notify_instances; /**< The number of automatically-created instances that do no request notification. **/
} GncSxSummary;
/**
* @param summary Caller-provided, populated with a summarization of the
* state of the model. Specifically, used to determine if there are SLR SXes
* that need either auto-creation or user-interaction.
**/
void gnc_sx_instance_model_summarize(GncSxInstanceModel *model, GncSxSummary *summary);
void gnc_sx_summary_print(GncSxSummary *summary);
void gnc_sx_get_variables(SchedXaction *sx, GHashTable *var_hash);
int gnc_sx_parse_vars_from_formula(const char *formula, GHashTable *var_hash, gnc_numeric *result);
void gnc_sx_randomize_variables(GHashTable *vars);
G_END_DECLS
#endif // _GNC_SX_INSTANCE_MODEL_H

View File

@ -3,7 +3,8 @@ TESTS = \
test-load-module \
test-exp-parser \
test-scm-query-string \
test-print-parse-amount
test-print-parse-amount \
test-sx
test_exp_parser_SOURCES = \
${top_builddir}/src/core-utils/gnc-gconf-utils.c \
@ -48,7 +49,8 @@ check_PROGRAMS = \
test-exp-parser \
test-print-parse-amount \
test-scm-query-string \
test-print-queries
test-print-queries \
test-sx
EXTRA_DIST = \
test-load-module

View File

@ -208,11 +208,23 @@ test_parser (void)
success ("shutdown expression parser");
}
static void
test_variable_expressions()
{
gnc_numeric num;
gchar *errLoc = NULL;
GHashTable *vars = g_hash_table_new(g_str_hash, g_str_equal);
do_test(gnc_exp_parser_parse_separate_vars("123 + a", &num, &errLoc, vars), "parsing");
do_test(g_hash_table_size(vars) == 1, "'a' is the variable; good job, gnc-exp-parser!");
success("variable found");
}
static void
real_main (void *closure, int argc, char **argv)
{
/* set_should_print_success (TRUE); */
test_parser();
test_variable_expressions();
print_test_results();
exit(get_rv());
}

View File

@ -0,0 +1,197 @@
#include "config.h"
#include <stdlib.h>
#include <glib.h>
#include "SX-book.h"
#include "gnc-sx-instance-model.h"
#include "gnc-ui-util.h"
#include "test-stuff.h"
#include "test-engine-stuff.h"
static void
test_basic()
{
GncSxInstanceModel *model;
GDate *yesterday, *range_end_tomorrow;
SchedXaction *foo;
yesterday = g_date_new();
g_date_clear(yesterday, 1);
g_date_set_time_t(yesterday, time(NULL));
g_date_subtract_days(yesterday, 1);
foo = add_daily_sx("foo", yesterday, NULL, NULL);
range_end_tomorrow = g_date_new();
g_date_clear(range_end_tomorrow, 1);
g_date_set_time_t(range_end_tomorrow, time(NULL));
g_date_add_days(range_end_tomorrow, 1);
model = gnc_sx_get_instances(range_end_tomorrow);
{
GncSxInstances *insts;
GList *iter;
do_test(g_list_length(model->sx_instance_list) == 1, "1 GncSxInstances");
insts = (GncSxInstances*)model->sx_instance_list->data;
do_test(g_list_length(insts->list) == 3, "yesterday, today and tomorrow");
for (iter = insts->list; iter != NULL; iter = iter->next)
{
GncSxInstance *inst = (GncSxInstance*)iter->data;
do_test(inst->state == SX_INSTANCE_STATE_TO_CREATE, "to-create");
}
}
g_object_unref(G_OBJECT(model));
remove_sx(foo);
}
static void
test_empty()
{
// no sxes should exist at this point.
int way_in_the_future_year = 2038;
GDate *end;
GncSxInstanceModel *model;
end = g_date_new_dmy(31, 12, way_in_the_future_year);
model = gnc_sx_get_instances(end);
do_test(g_list_length(model->sx_instance_list) == 0, "no instances");
g_object_unref(G_OBJECT(model));
success("empty");
}
static void
test_once()
{
SchedXaction *lonely;
GDate *when, *end;
int random_offset_within_one_year = 0;
GncSxInstanceModel *model;
GncSxInstances *instances;
GncSxInstance *instance;
when = g_date_new();
g_date_clear(when, 1);
g_date_set_time_t(when, time(NULL));
while (random_offset_within_one_year == 0)
random_offset_within_one_year = get_random_int_in_range(-365, 365);
g_date_add_days(when, random_offset_within_one_year);
end = g_date_new();
g_date_clear(end, 1);
g_date_set_time_t(end, time(NULL));
g_date_add_years(end, 1);
lonely = add_once_sx("once", when);
model = gnc_sx_get_instances(end);
do_test(g_list_length(model->sx_instance_list) == 1, "1 instances");
instances = (GncSxInstances*)model->sx_instance_list->data;
do_test(g_list_length(instances->list) == 1, "1 instance");
instance = (GncSxInstance*)instances->list->data;
do_test(g_date_compare(when, &instances->next_instance_date) == 0, "next instance is expected");
do_test(g_date_compare(when, &instance->date) == 0, "instance date is expected");
g_object_unref(model);
success("model unref");
remove_sx(lonely);
}
static GncSxInstance*
_nth_instance(GncSxInstances *instances, int i)
{
return (GncSxInstance*)g_list_nth_data(instances->list, i);
}
static void
test_state_changes()
{
SchedXaction *foo;
GDate *start, *end;
GncSxInstanceModel *model;
GncSxInstances *insts;
GncSxInstance *inst;
start = g_date_new();
g_date_set_time_t(start, time(NULL));
end = g_date_new();
g_date_set_time_t(end, time(NULL));
g_date_add_days(end, 3);
foo = add_daily_sx("foo", start, NULL, NULL);
model = gnc_sx_get_instances(end);
do_test(g_list_length(model->sx_instance_list) == 1, "one sx");
insts = (GncSxInstances*)g_list_nth_data(model->sx_instance_list, 0);
do_test(g_list_length(insts->list) == 4, "4 instances");
inst = _nth_instance(insts, 2);
gnc_sx_instance_model_change_instance_state(model, inst, SX_INSTANCE_STATE_TO_CREATE);
{
int i;
for (i = 0; i < 4; i++)
{
inst = _nth_instance(insts, i);
do_test(inst->state == SX_INSTANCE_STATE_TO_CREATE, "0 didn't change");
}
success("nothing else changed");
}
inst = _nth_instance(insts, 1);
gnc_sx_instance_model_change_instance_state(model, inst, SX_INSTANCE_STATE_POSTPONED);
{
int i;
inst = _nth_instance(insts, 1);
do_test(inst->state == SX_INSTANCE_STATE_POSTPONED, "as we said");
for (i = 0; i < 4; i++)
{
if (i == 1)
continue; // skip
inst = _nth_instance(insts, i);
do_test(inst->state == SX_INSTANCE_STATE_TO_CREATE, "still to create");
}
}
success("postponed changed what it needed to");
inst = _nth_instance(insts, 1);
gnc_sx_instance_model_change_instance_state(model, inst, SX_INSTANCE_STATE_REMINDER);
success("changed to reminder");
{
int i;
inst = _nth_instance(insts, 0);
do_test(inst->state == SX_INSTANCE_STATE_TO_CREATE, "left alone");
inst = _nth_instance(insts, 1);
do_test(inst->state == SX_INSTANCE_STATE_REMINDER, "as we asked for");
for (i = 2; i < 4; i++)
{
inst = _nth_instance(insts, i);
do_test(inst->state == SX_INSTANCE_STATE_REMINDER, "changed as well");
}
}
g_object_unref(model);
remove_sx(foo);
}
int
main(int argc, char **argv)
{
g_type_init();
qof_init();
gnc_engine_init(0, NULL);
test_empty();
{
int i;
for (i = 0; i < 10; i++)
test_once();
}
test_basic();
test_state_changes();
print_test_results();
exit(get_rv());
}

View File

@ -237,16 +237,19 @@ add_transaction_local(sixtp_gdv2 *data, Transaction *trn)
static gboolean
add_schedXaction_local(sixtp_gdv2 *data, SchedXaction *sx)
{
GList *list;
list = gnc_book_get_schedxactions (data->book);
list = g_list_append(list, sx);
gnc_book_set_schedxactions(data->book, list);
data->counter.schedXactions_loaded++;
run_callback(data, "schedXactions");
return TRUE;
SchedXactions *sxes;
#if 0 // old
GList *list;
list = gnc_book_get_schedxactions (data->book);
list = g_list_append(list, sx);
gnc_book_set_schedxactions(data->book, list);
#endif // 0
sxes = gnc_book_get_schedxactions(data->book);
gnc_sxes_add_sx(sxes, sx);
data->counter.schedXactions_loaded++;
run_callback(data, "schedXactions");
return TRUE;
}
static gboolean
@ -932,7 +935,7 @@ write_book(FILE *out, QofBook *book, sixtp_gdv2 *gd)
"transaction",
gnc_book_count_transactions(book),
"schedxaction",
g_list_length( gnc_book_get_schedxactions(book) ),
g_list_length(gnc_book_get_schedxactions(book)->sx_list),
"budget", qof_collection_count(
qof_book_get_collection(book, GNC_ID_BUDGET)),
NULL);
@ -1067,25 +1070,24 @@ write_template_transaction_data( FILE *out, QofBook *book, sixtp_gdv2 *gd )
static void
write_schedXactions( FILE *out, QofBook *book, sixtp_gdv2 *gd)
{
GList *schedXactions;
SchedXaction *tmpSX;
xmlNodePtr node;
GList *schedXactions;
SchedXaction *tmpSX;
xmlNodePtr node;
schedXactions = gnc_book_get_schedxactions(book)->sx_list;
/* get list of scheduled transactions from QofBook */
schedXactions = gnc_book_get_schedxactions( book );
if ( schedXactions == NULL )
return;
if ( schedXactions == NULL )
return;
do {
tmpSX = schedXactions->data;
node = gnc_schedXaction_dom_tree_create( tmpSX );
xmlElemDump( out, NULL, node );
fprintf( out, "\n" );
xmlFreeNode( node );
gd->counter.schedXactions_loaded++;
run_callback(gd, "schedXactions");
} while ( (schedXactions = schedXactions->next) );
do {
tmpSX = schedXactions->data;
node = gnc_schedXaction_dom_tree_create( tmpSX );
xmlElemDump( out, NULL, node );
fprintf( out, "\n" );
xmlFreeNode( node );
gd->counter.schedXactions_loaded++;
run_callback(gd, "schedXactions");
} while ( (schedXactions = schedXactions->next) );
}
static void
@ -1175,7 +1177,7 @@ gnc_book_write_to_xml_filehandle_v2(QofBook *book, FILE *out)
xaccGroupGetNumSubAccounts(gnc_book_get_group(book));
gd->counter.transactions_total = gnc_book_count_transactions(book);
gd->counter.schedXactions_total =
g_list_length( gnc_book_get_schedxactions(book));
g_list_length(gnc_book_get_schedxactions(book)->sx_list);
gd->counter.budgets_total = qof_collection_count(
qof_book_get_collection(book, GNC_ID_BUDGET));

View File

@ -27,7 +27,7 @@
#include "gnc-glib-utils.h"
int
int
safe_utf8_collate (const char * da, const char * db)
{
if (da && !(*da))
@ -222,3 +222,31 @@ gnc_utf8_strip_invalid_strdup(const gchar* str)
gnc_utf8_strip_invalid (result);
return result;
}
GList*
gnc_g_list_map(GList* list, GncGMapFunc fn, gpointer user_data)
{
GList *rtn = NULL;
for (; list != NULL; list = list->next)
{
rtn = g_list_append(rtn, (*fn)(list->data, user_data));
}
return rtn;
}
void
gnc_g_list_cut(GList **list, GList *cut_point)
{
if (list == NULL || *list == NULL)
return;
// if it's the first element.
if (cut_point->prev == NULL)
{
*list = NULL;
return;
}
cut_point->prev->next = NULL;
cut_point->prev = NULL;
}

View File

@ -81,6 +81,19 @@ void gnc_utf8_strip_invalid (gchar *str);
* caller. */
gchar *gnc_utf8_strip_invalid_strdup (const gchar* str);
typedef gpointer (*GncGMapFunc)(gpointer data, gpointer user_data);
/**
* @return Caller-owned GList* of results of apply `fn` to `list` in order.
**/
GList* gnc_g_list_map(GList* list, GncGMapFunc fn, gpointer user_data);
/**
* Cut a GList into two parts; the {@param cut_point} is the beginning of the
* new list; {@param list} may need to be modified, but will be the list
* before the {@param cut_point}.
**/
void gnc_g_list_cut(GList **list, GList *cut_point);
/** @} */

312
src/doc/sx.rst Normal file
View File

@ -0,0 +1,312 @@
Scheduled Transactions
===============================================================
Overview
--------------
- SX List
- CRUD operations on SXes
- Show Next "year"s worth of SX instances
- gnc_sx_get_instances({now + 1yr})
- SX Editor
- SinceLastRun
- Last .. present (+ create-in-advance, reminder) instances
- gnc_sx_get_instances(now)
TODO
----------
- meta
- [x] move files around
- [ ] GncSxListTreeModelAdapter: s/real/adapted/
- [ ] generic tree model adapter setup code
- core
- [x] sx list -> qof collection
- [x] sx engine events
- [x] sx list collection add/remove -- sx-list GNC_EVENT_ITEM_ADDED, _REMOVED
- [x] sx modified -- QOF_EVENT_MODIFY
- [x] sx upcoming instance model
! - [x] implement sort model
- [x] rename, re-home gnc-sx-instance-model:sxsl_get_sx_vars
- [x] rename, re-home gnc-sx-instance-model:parse_vars_from_formula
- [ ] after updating/merging new instances, enforce state (+variable) consistency.
- unit testing
- [ ] model updating in the face of change
- [ ] insert sx
- [ ] remove sx
- [ ] update sx
- [ ] add instances
- [ ] remove instances
- [ ] make "weird"
- [x] ensure state consistency model is upheld
- [ ] check variables-unbound logic
- [ ] verify summary counts
- [ ] check "since last run" states
- [ ] -autocreate[, ±notify]
- [ ] +autocreate, -notify
- [ ] +autocreate, +notify
- [ ] +autocreate, -notify, w/postponed
- [ ] +autocreate, +notify, w/postponed
- [ ] bugs
- [?] Expired scheduled transactions never run - <http://bugzilla.gnome.org/show_bug.cgi?id=375892>
- bugs
- [ ] with SLR open (with instances), add variables to SX; only newly-created instances will have appropriate variable tables.
! - [ ] crash with two sx lists open and SX mutation
- I'm pretty sure this is due to SX lists not getting cleaned up on page close, somehow.
[[[
(gnucash:17610): GLib-GObject-WARNING **: invalid unclassed pointer in cast to `GncSxListTreeModelAdapterType'
sx list tree model adapter update
(gnucash:17610): Gtk-CRITICAL **: gtk_tree_store_clear: assertion `GTK_IS_TREE_STORE (tree_store)' failed ]]]
- [x] Scheduled Transactions on 31st/last put in following month - <http://bugzilla.gnome.org/show_bug.cgi?id=104844>
- sx list page
- [ ] use gnc-tree-view
- [ ] save/restore state
- [/] make into split panel
- [ ] fix slider position
- [ ] {0, 1, 2, 4, 8, 12} month selection for dense calendar
- sx editor
- [ ] clean up, reformat
- [ ] model-ize
- (check_consistent, especially...)
- gnc-frequency
- [ ] clean up, reformat
- gnc_dense_cal
- [ ] font handling: gdk -> pango
- [ ] change number-of-month properties to display-named properties (width, length)
- [?] better transient/floating window
- [/] (re-format file)
- [x] set_model(GncTemporalInstancesModel *mdl)
- [x] new interface creation.
- [x] register callbacks for signals
- [x] remove clist usage
- sx-from-trans
- [?] convert to GObject
- [x] hookup destroy/finalize
- FreqSpec
- [ ] type+ui-type -> type
- use Recurrence instead of FreqSpec
- [ ] XML migration, handling
- since-last-run
! - [x] rewrite adapter (re-)population logic
- [x] move "effect_change" up to app-utils/, test.
- [x] move state-change up to app-utils
- [x] move variable-setting up to app-utils
- [x] move summarization up to app-utils
- [x] add reminders, postponed to SxInstanceModel
- [x] add mutation support to sx instance model
- [x] state machine
- [x] add variable state to sx instance model
- [x] handle (hidden/system not for editing) variables.
- [x] add sx_upcoming_instance_model()
- [x] add effect_auto_create()
- [x] add some sort of "ready to go" flag and api
- [x] variable setting, primarily
- [x] some sort of commit_changes()
- [x] add variable table to instances
- [x] ui: add 'review created transactions' checkbox to SLR dialog
using txn search.
- destroy/cleanup
- notes
- dispose: should no longer hold references to other objects; callable
multiple times; chain up at end
- finalize: complete destruction; just before free; only called once;
chain up at end.
Pedantic Todo
----------------------
- s/SchedXaction/Scheduled/
- s/temporal_state/instance_sequence_context/
- change instance variable from 'i' to '__i' or something
============================================================
(eventually real documentation... (?))
Since Last Run
----------------------
+------------------+------------------+------------------+
| Thing | State | Value |
+------------------+------------------+------------------+
| - Foo | | |
+------------------+------------------+------------------+
| - 2006-08-27 | [Postponed|v] | |
+------------------+------------------+------------------+
| - variable-a | | 42 |
+------------------+------------------+------------------+
| - variable-b | | 75 |
+------------------+------------------+------------------+
| - 2006-08-27 | [To-Create|v] | |
+------------------+------------------+------------------+
| - variable-a | | 31 |
+------------------+------------------+------------------+
| - variable-b | | (value needed) |
+------------------+------------------+------------------+
The since-last-run dialog is a key user interface. More frequently than the
SX list or editor, the user will be in the process of creating transaction
instances through this interface.
The old SLR dialog has the following stages:
- Reminders
- can be promoted to "to-create"
- Auto-created, with notification
- To-Create
- postponed, to-create
- ignore state.
- Created review
- Obsolete SX cleanup
The new SLR dialog will have the following:
- Creation
(treemodel consisting of)
- auto-created
- reminder
- postponed
- to-create
- [obsolete SX]?
There is no separate to-review page, however the user may (optionally) want
to see the created transactions. This is done using the transaction-search
functionality over the created transactions by ID.
Upcoming instance states
---------------------------------------
reminder -> to-create
postponed -> to-create
to-create -> postponed
to-create -> ignore
to-create -> created [terminal]
Definitions:
reminder: a transient upcoming transaction that will not be created.
postponed: a historical to-create transaction that the user has
explicitly deferred.
to-create: an upcoming SX instance that should be created.
ignore: a scheduled instance the user has explicitly prevented the
instantiation of.
created: the instance has been created in this interaction cycle.
Formula Parsing
------------------------
A SXes formula is parsed in the context of:
- the template transaction
- the accounts of the splits
- the sequence number
- the date of the transaction
- a variable-binding table.
Testing Notes
---------------------
- auto-create
- auto-create with postponed instances shouldn't destroy postponed
instances
- basic sequence stuff
dialog-sxsincelast.c: ~L1241:
"Handle an interesting corner case of postponing or
ignoring the first instance. We only want to increment the
counters for newly-discovered-as-to-be-created SXes."
- auto-create
- auto-create transactions can be created w/o user interaction
- their state is transitioned to 'created', which is not modifiable
- auto-create (+notify) transactions should be displayed, even if they are
the only transactions created.
- auto-create (-notify) transactions should not be displayed, unless there
are other transactions.
- Scenarios
- only auto-create (-notify): no SLR, info dialog w/count (***)
- only auto-create (+notify): SLR dialog, already created
- others, auto-create (-notify): SLR dialog, incl. created
- others, auto-create (+notify): SLR dialog, incl. created
Bugs to close after merge
--------------------------------------
- With many auto-create transactions but none with notify option, "Auto-Created Transactions Notification" druid page lists every existing transaction - http://bugzilla.gnome.org/show_bug.cgi?id=347116
- Since last run dialog does not allow for early finish, an... - http://bugzilla.gnome.org/show_bug.cgi?id=329384
- Since Last Run druid changes data before Apply - http://bugzilla.gnome.org/show_bug.cgi?id=333849
- Resize the "Since Last Run" window is incorrect - http://bugzilla.gnome.org/show_bug.cgi?id=353563
- Transaction reminders page has slightly incorrect instructions - http://bugzilla.gnome.org/show_bug.cgi?id=331069
- Transaction not highlighted in "Transaction Preparation" window - http://bugzilla.gnome.org/show_bug.cgi?id=342658
- Scrolling through variables list does not work - http://bugzilla.gnome.org/show_bug.cgi?id=343190
- Gnucash thinks the file has changed after cancelling out of the Since Last Run dialog and making no changes - http://bugzilla.gnome.org/show_bug.cgi?id=344494
- Transaction reminder with variable amount doesn't display value field - http://bugzilla.gnome.org/show_bug.cgi?id=147946
------------------------------------------------------------
Release Notes
=============
Major overhaul
--------------
The core application-side SX code was overhauled for clarity, modularity, correctness, testability, &c.
SXList Plugin Page
-------------------
The SX list and upcoming-instances calendar moved from a top-level window to being a plugin page in the normal application container.
Since Last Run
--------------
The Since Last Run (SLR) dialog received a functional overhaul as well. The previous druid-based approach led to a huge bookkeeping headache, as transitioning between pages required partially-processed SXes to be maintained and transactions to be created and destroyed. As well, the multi-stage dialog approach was just too involved and ill-suited to the task at hand, especially as some stages were conditional on the state of the data. It made me sad.
The new Since Last Run dialog is a single treeview of upcoming instances and variable bindings. There's a checkbox to have all created transactions presented after they are.
It's easier to describe via screenshot: <http://asynchronous.org/tmp/sx-cleanup-eg.png>.
Updating/signaling
------------------
Part of the overhaul is a better use of QOF and GObject signaling for updates. The SX list and SLR update in response to changes in each other; for instance, you can change the frequency or start-range of an SX while the SLR dialog is open, and it will update in place.
Known Issues
------------
(as of 2007-01-14)
- The SX List plugin page doesn't save/restore its state.
- Updating the variables in a formula with the SLR dialog open isn't consistent.
- Closing an sx list plugin page leads to corrupted state.
Licensing
---------
In new files (and old files related to this code that I hold copyright on), I've removed the "or any later version" clause. I have problems licensing under a license that I haven't read, or that can change in ways I disagree with. At some point I'll make this change for all source files I hold copyright on, and I intend to not use the clause on sources I (re)write in the future.
Testing
-------
The key areas I think need testing are the new plugin page and the SLR dialog. It, at least, shouldn't do anything worse than the 1.8/2.0 SX code. :)

View File

@ -35,23 +35,16 @@
#define GNC_SX_BOOK_P_H
#include "qof.h"
#include "SX-book.h"
/* ====================================================================== */
struct xaccSchedXactionsDef {
GList *sx_list;
gboolean sx_notsaved;
};
void gnc_book_set_schedxactions( QofBook *book, GList *newList );
void gnc_collection_set_schedxactions( QofCollection *col, GList *newList );
SchedXactions* gnc_collection_get_schedxactions(const QofCollection *col);
/* Associate the given template group with a book */
void gnc_book_set_template_group (QofBook *book, AccountGroup *templateGroup);
void gnc_collection_set_template_group (QofCollection *col, AccountGroup *templateGroup);
gboolean gnc_sxtt_register (void);
#endif /* GNC_SX_BOOK_P_H */

View File

@ -44,6 +44,7 @@
#include "SchedXaction.h"
#include "SX-book.h"
#include "SX-book-p.h"
#include "gnc-event.h"
static QofLogModule log_module = GNC_MOD_SX;
@ -116,7 +117,6 @@ sxtg_book_end (QofBook *book)
gnc_book_set_template_group (book, NULL);
}
static gboolean
sxtg_is_dirty(const QofCollection *col)
{
@ -144,78 +144,45 @@ static QofObject sxtg_object_def =
/* ====================================================================== */
SchedXactions *
gnc_collection_get_schedxaction_list(const QofCollection *col)
{
return qof_collection_get_data (col);
}
GList *
SchedXactions*
gnc_collection_get_schedxactions(const QofCollection *col)
{
SchedXactions *list;
list = qof_collection_get_data (col);
if (list) return list->sx_list;
return NULL;
SchedXactions *rtn = qof_collection_get_data(col);
// @@assert(rtn != null);
return rtn;
}
GList *
SchedXactions*
gnc_book_get_schedxactions(QofBook *book)
{
QofCollection *col;
col = qof_book_get_collection (book, GNC_ID_SXTT);
return gnc_collection_get_schedxactions (col);
col = qof_book_get_collection(book, GNC_ID_SCHEDXACTION);
return gnc_collection_get_schedxactions(col);
}
void
gnc_collection_set_schedxactions( QofCollection *col, GList *newList )
gnc_sxes_add_sx(SchedXactions *sxes, SchedXaction *sx)
{
SchedXactions *old_list, *new_list;
if ( col == NULL ) return;
old_list = qof_collection_get_data (col);
if (old_list && old_list->sx_list == newList)
{
/* Assume the worst, that any 'set' means the data has
* changed, and needs to be saved. */
old_list->sx_notsaved = TRUE;
return;
}
new_list = g_new (SchedXactions, 1);
new_list->sx_list = newList;
new_list->sx_notsaved = TRUE;
if (NULL == newList) new_list->sx_notsaved = FALSE;
qof_collection_set_data (col, new_list);
g_free (old_list);
if (g_list_find(sxes->sx_list, sx) != NULL)
return;
sxes->sx_list = g_list_append(sxes->sx_list, sx);
qof_event_gen(&sxes->inst.entity, GNC_EVENT_ITEM_ADDED, (gpointer)sx);
}
void
gnc_book_set_schedxactions( QofBook *book, GList *newList )
gnc_sxes_del_sx(SchedXactions *sxes, SchedXaction *sx)
{
QofCollection *col;
if ( book == NULL ) return;
col = qof_book_get_collection (book, GNC_ID_SXTT);
gnc_collection_set_schedxactions (col, newList);
GList *to_remove;
to_remove = g_list_find(sxes->sx_list, sx);
if (to_remove == NULL)
return;
sxes->sx_list = g_list_delete_link(sxes->sx_list, to_remove);
qof_event_gen(&sxes->inst.entity, GNC_EVENT_ITEM_REMOVED, (gpointer)sx);
}
/* ====================================================================== */
/* SX-trans stuff */
static void
sxtt_book_begin (QofBook *book)
{
gnc_book_set_schedxactions (book, NULL);
}
static void
sxtt_book_end (QofBook *book)
{
gnc_book_set_schedxactions (book, NULL);
}
static void
mark_sx_clean(gpointer data, gpointer user_data)
{
@ -223,14 +190,29 @@ mark_sx_clean(gpointer data, gpointer user_data)
qof_instance_mark_clean (QOF_INSTANCE(sx));
}
static void
book_sxes_setup(QofBook *book)
{
QofCollection *col;
SchedXactions *sxes;
col = qof_book_get_collection(book, GNC_ID_SCHEDXACTION);
sxes = g_new (SchedXactions, 1);
qof_instance_init(&sxes->inst, GNC_ID_SXES, book);
sxes->sx_list = NULL;
sxes->sx_notsaved = TRUE;
qof_collection_set_data(col, sxes);
}
static void
book_sxns_mark_saved(QofCollection *col)
{
SchedXactions *sxl;
sxl = gnc_collection_get_schedxaction_list (col);
if (sxl) sxl->sx_notsaved = FALSE;
g_list_foreach(gnc_collection_get_schedxactions(col),
sxl = gnc_collection_get_schedxactions(col);
if (!sxl)
return;
sxl->sx_notsaved = FALSE;
g_list_foreach(sxl->sx_list,
mark_sx_clean,
NULL);
}
@ -241,10 +223,11 @@ book_sxlist_notsaved(const QofCollection *col)
GList *sxlist;
SchedXactions *sxl;
sxl = gnc_collection_get_schedxaction_list (col);
sxl = gnc_collection_get_schedxactions(col);
if (!sxl) return FALSE;
if((sxl && sxl->sx_notsaved)) return TRUE;
for(sxlist = gnc_collection_get_schedxactions(col);
for(sxlist = sxl->sx_list;
sxlist != NULL;
sxlist = g_list_next(sxlist))
{
@ -256,6 +239,21 @@ book_sxlist_notsaved(const QofCollection *col)
return FALSE;
}
static QofObject sxes_object_def =
{
interface_version: QOF_OBJECT_VERSION,
e_type: GNC_ID_SXES,
type_label: "Scheduled Transactions List",
create: NULL,
book_begin: book_sxes_setup,
book_end: NULL,
is_dirty: book_sxlist_notsaved,
mark_clean: book_sxns_mark_saved,
foreach: NULL,
printable: NULL,
version_cmp: NULL
};
static QofObject sxtt_object_def =
{
@ -263,10 +261,10 @@ static QofObject sxtt_object_def =
e_type: GNC_ID_SXTT,
type_label: "Scheduled Transaction Templates",
create: NULL,
book_begin: sxtt_book_begin,
book_end: sxtt_book_end,
is_dirty: book_sxlist_notsaved,
mark_clean: book_sxns_mark_saved,
book_begin: NULL,
book_end: NULL,
is_dirty: NULL,
mark_clean: NULL,
foreach: NULL,
printable: NULL,
version_cmp: NULL,
@ -275,8 +273,11 @@ static QofObject sxtt_object_def =
gboolean
gnc_sxtt_register (void)
{
return qof_object_register (&sxtg_object_def);
return qof_object_register (&sxtt_object_def);
if (!qof_object_register(&sxes_object_def))
return FALSE;
if (!qof_object_register(&sxtg_object_def))
return FALSE;
return qof_object_register(&sxtt_object_def);
}
GList*
@ -284,7 +285,7 @@ gnc_sx_get_sxes_referencing_account(QofBook *book, Account *acct)
{
GList *rtn = NULL;
const GUID *acct_guid = xaccAccountGetGUID(acct);
GList *sx_list = gnc_book_get_schedxactions(book);
GList *sx_list = gnc_book_get_schedxactions(book)->sx_list;
for (; sx_list != NULL; sx_list = sx_list->next)
{
SchedXaction *sx = (SchedXaction*)sx_list->data;

View File

@ -30,6 +30,7 @@
* @brief Anchor Scheduled Transaction info in a book.
* See src/doc/books.txt for design overview.
* @author Copyright (c) 2003 Linas Vepstas <linas@linas.org>
* @author Copyright (c) 2006 Joshua Sled <jsled@asynchronous.org>
*
* XXX currently, this is crufty, it should be modified to use
* entities a bit more whole-heartedly than it does.
@ -39,17 +40,28 @@
#define GNC_SX_BOOK_H
#include <glib.h>
#include "SchedXaction.h"
#include "qof.h"
typedef struct xaccSchedXactionsDef SchedXactions;
SchedXactions * gnc_collection_get_schedxaction_list(const QofCollection *col);
GList * gnc_collection_get_schedxactions(const QofCollection *col);
GList * gnc_book_get_schedxactions(QofBook *book);
struct xaccSchedXactionsDef {
QofInstance inst;
GList* sx_list;
gboolean sx_notsaved;
};
#define GNC_IS_SXES(obj) (QOF_CHECK_TYPE((obj), GNC_ID_SXES))
#define GNC_SXES(obj) (QOF_CHECK_CAST((obj), GNC_ID_SXES, SchedXactions))
SchedXactions* gnc_book_get_schedxactions(QofBook* book);
void gnc_sxes_add_sx(SchedXactions* sxes, SchedXaction* sx);
void gnc_sxes_del_sx(SchedXactions* sxes, SchedXaction* sx);
/** Returns the template group from the book. **/
AccountGroup * gnc_book_get_template_group(QofBook *book);
AccountGroup * gnc_collection_get_template_group(const QofCollection *col);
AccountGroup* gnc_book_get_template_group(QofBook* book);
AccountGroup* gnc_collection_get_template_group(const QofCollection *col);
/** @return The list of SXes which reference the given Account. Caller should free this list. **/
GList* gnc_sx_get_sxes_referencing_account(QofBook *book, Account *acct);

View File

@ -192,13 +192,18 @@ static void commit_err (QofInstance *inst, QofBackendError errcode)
PERR ("Failed to commit: %d", errcode);
}
static void noop (QofInstance *inst) {}
static void commit_done(QofInstance *inst)
{
qof_event_gen (&inst->entity, QOF_EVENT_MODIFY, NULL);
}
static void noop(QofInstance *inst) {}
void
gnc_sx_commit_edit (SchedXaction *sx)
{
if (!qof_commit_edit (QOF_INSTANCE(sx))) return;
qof_commit_edit_part2 (&sx->inst, commit_err, noop, noop);
qof_commit_edit_part2 (&sx->inst, commit_err, commit_done, noop);
}
/* ============================================================ */
@ -375,8 +380,10 @@ xaccSchedXactionGetAutoCreate( SchedXaction *sx,
gboolean *outAutoCreate,
gboolean *outNotify )
{
*outAutoCreate = sx->autoCreateOption;
*outNotify = sx->autoCreateNotify;
if (outAutoCreate != NULL)
*outAutoCreate = sx->autoCreateOption;
if (outNotify != NULL)
*outNotify = sx->autoCreateNotify;
return;
}

View File

@ -42,6 +42,9 @@
#include "FreqSpec.h"
#include "gnc-engine.h"
#define GNC_IS_SX(obj) (QOF_CHECK_TYPE((obj), GNC_ID_SCHEDXACTION))
#define GNC_SX(obj) (QOF_CHECK_CAST((obj), GNC_ID_SCHEDXACTION, SchedXaction))
/**
* The SchedXaction data.
*/
@ -196,7 +199,7 @@ void gnc_sx_remove_defer_instance( SchedXaction *sx, void *deferStateData );
This is a date-sorted state-data instance list.
The list should not be modified by the caller; use the
gnc_sx_{add,remove}_defer_instance() functions to modifiy the list.
gnc_sx_{add,remove}_defer_instance() functions to modify the list.
*/
GList *gnc_sx_get_defer_instances( SchedXaction *sx );

View File

@ -98,8 +98,9 @@
#define GNC_ID_PRICE "Price"
#define GNC_ID_PRICEDB "PriceDB"
#define GNC_ID_SPLIT "Split"
#define GNC_ID_SCHEDXACTION "SchedXaction"
#define GNC_ID_BUDGET "Budget"
#define GNC_ID_SCHEDXACTION "SchedXaction"
#define GNC_ID_SXES "SchedXactions"
#define GNC_ID_SXTG "SXTGroup"
#define GNC_ID_SXTT "SXTTrans"
#define GNC_ID_TRANS "Trans"

View File

@ -32,8 +32,12 @@
#include "Group.h"
#include "GroupP.h"
#include "gnc-engine.h"
#include "gnc-session.h"
#include "Transaction.h"
#include "TransactionP.h"
#include "FreqSpec.h"
#include "SchedXaction.h"
#include "SX-book.h"
#include "test-engine-stuff.h"
#include "test-stuff.h"
@ -2160,3 +2164,61 @@ make_trans_query (Transaction *trans, TestQueryTypes query_types)
return q;
}
static FreqSpec*
daily_freq(GDate* start, int multiplier)
{
QofBook *book = qof_session_get_book(gnc_get_current_session());
FreqSpec *freq = xaccFreqSpecMalloc(book);
xaccFreqSpecSetDaily(freq, start, multiplier);
xaccFreqSpecSetUIType(freq, UIFREQ_DAILY);
return freq;
}
static FreqSpec*
once_freq(GDate *when)
{
QofBook *book = qof_session_get_book(gnc_get_current_session());
FreqSpec *freq = xaccFreqSpecMalloc(book);
xaccFreqSpecSetOnceDate(freq, when);
xaccFreqSpecSetUIType(freq, UIFREQ_ONCE);
return freq;
}
static SchedXaction*
add_sx(gchar *name, GDate *start, GDate *end, GDate *last_occur, FreqSpec *fs)
{
QofBook *book = qof_session_get_book(gnc_get_current_session());
SchedXaction *sx = xaccSchedXactionMalloc(book);
xaccSchedXactionSetName(sx, name);
xaccSchedXactionSetStartDate(sx, start);
if (end != NULL)
xaccSchedXactionSetEndDate(sx, end);
if (last_occur != NULL)
xaccSchedXactionSetLastOccurDate(sx, last_occur);
xaccSchedXactionSetFreqSpec(sx, fs);
gnc_sxes_add_sx(gnc_book_get_schedxactions(book), sx);
return sx;
}
SchedXaction*
add_daily_sx(gchar *name, GDate *start, GDate *end, GDate *last_occur)
{
return add_sx(name, start, end, last_occur, daily_freq(start, 1));
}
SchedXaction*
add_once_sx(gchar *name, GDate *when)
{
return add_sx(name, when, NULL, NULL, once_freq(when));
}
void
remove_sx(SchedXaction *sx)
{
QofBook *book = qof_session_get_book(gnc_get_current_session());
SchedXactions *sxes = gnc_book_get_schedxactions(book);
gnc_sxes_del_sx(sxes, sx);
}

View File

@ -11,6 +11,7 @@
#include "qof.h"
#include "Query.h"
#include "gnc-pricedb.h"
#include "SchedXaction.h"
Timespec* get_random_timespec(void);
void random_timespec_zero_nsec (gboolean zero_nsec);
@ -88,4 +89,8 @@ void make_random_changes_to_group (QofBook *book, AccountGroup *group);
void make_random_changes_to_book (QofBook *book);
void make_random_changes_to_session (QofSession *session);
SchedXaction* add_daily_sx(gchar *name, GDate *start, GDate *end, GDate *last_occur);
SchedXaction* add_once_sx(gchar *name, GDate *when);
void remove_sx(SchedXaction *sx);
#endif

View File

@ -559,6 +559,50 @@ test_composite (void)
xaccFreqSpecFree(fs);
}
static void
test_monthly_31st_bug_104844()
{
gchar date_buf[128];
GDate start, next, expected;
FreqSpec *fs = xaccFreqSpecMalloc(book);
g_date_clear(&next, 1);
g_date_clear(&start, 1);
g_date_set_dmy(&start, 31, 1, 2003);
xaccFreqSpecSetMonthly(fs, &start, 1);
//g_date_add_days(&start, 1);
xaccFreqSpecGetNextInstance(fs, &start, &next);
g_date_clear(&expected, 1);
g_date_set_dmy(&expected, 28, 2, 2003);
g_date_strftime(date_buf, 128, "%c", &next);
do_test(g_date_compare(&expected, &next) == 0, date_buf);
start = next;
xaccFreqSpecGetNextInstance(fs, &start, &next);
g_date_set_dmy(&expected, 31, 3, 2003);
g_date_strftime(date_buf, 128, "%c", &next);
do_test(g_date_compare(&expected, &next) == 0, date_buf);
// test...
g_date_set_dmy(&start, 31, 1, 2003);
xaccFreqSpecSetMonthly(fs, &start, 1);
g_date_set_dmy(&start, 31, 1, 2007);
xaccFreqSpecGetNextInstance(fs, &start, &next);
g_date_set_dmy(&expected, 28, 2, 2007);
g_date_strftime(date_buf, 128, "%c", &next);
do_test(g_date_compare(&expected, &next) == 0, date_buf);
start = next;
xaccFreqSpecGetNextInstance(fs, &start, &next);
g_date_set_dmy(&expected, 31, 3, 2007);
g_date_strftime(date_buf, 128, "%c", &next);
do_test(g_date_compare(&expected, &next) == 0, date_buf);
xaccFreqSpecFree(fs);
}
int
main (int argc, char **argv)
{
@ -568,6 +612,9 @@ main (int argc, char **argv)
g_return_val_if_fail(cashobjects_register(), -1);
session = qof_session_new ();
book = qof_session_get_book(session);
test_monthly_31st_bug_104844();
test_once();
test_caseA();
test_daily();
@ -575,6 +622,7 @@ main (int argc, char **argv)
test_monthly();
test_month_relative();
test_composite();
print_test_results();
qof_session_end(session);
qof_close();

View File

@ -193,14 +193,14 @@ run_test (void)
int
main (int argc, char **argv)
{
qof_init();
if(cashobjects_register())
{
xaccLogDisable ();
run_test ();
success("transaction voiding seems OK");
print_test_results();
}
qof_close();
qof_init();
if(cashobjects_register())
{
xaccLogDisable ();
run_test ();
success("transaction voiding seems OK");
print_test_results();
}
qof_close();
return get_rv();
}

View File

@ -49,6 +49,8 @@ libgncmod_gnome_utils_la_SOURCES = \
gnc-date-edit.c \
gnc-date-format.c \
gnc-dense-cal.c \
gnc-dense-cal-model.c \
gnc-dense-cal-store.c \
gnc-druid-gnome.c \
gnc-druid-provider-edge-gnome.c \
gnc-druid-provider-file-gnome.c \
@ -74,6 +76,7 @@ libgncmod_gnome_utils_la_SOURCES = \
gnc-period-select.c \
gnc-query-list.c \
gnc-splash.c \
gnc-sx-instance-dense-cal-adapter.c \
gnc-tree-model.c \
gnc-tree-model-account-types.c \
gnc-tree-model-account.c \
@ -117,6 +120,8 @@ gncinclude_HEADERS = \
gnc-date-edit.h \
gnc-date-format.h \
gnc-dense-cal.h \
gnc-dense-cal-model.h \
gnc-dense-cal-store.h \
gnc-druid-gnome-ui.h \
gnc-embedded-window.h \
gnc-file.h \
@ -139,6 +144,7 @@ gncinclude_HEADERS = \
gnc-period-select.h \
gnc-query-list.h \
gnc-splash.h \
gnc-sx-instance-dense-cal-adapter.h \
gnc-tree-model.h \
gnc-tree-model-account-types.h \
gnc-tree-model-account.h \

View File

@ -0,0 +1,132 @@
/*
* gnc-dense-cal-model.c
*
* Copyright (C) 2006 Joshua Sled <jsled@asynchronous.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License*
* along with this program; if not, contact:
*
* Free Software Foundation Voice: +1-617-542-5942
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
* Boston, MA 02110-1301, USA gnu@gnu.org
*/
#include "config.h"
#include <glib.h>
#include <glib-object.h>
#include "gnc-dense-cal.h"
#include "gnc-dense-cal-model.h"
enum { GDCM_ADDED, GDCM_UPDATE, GDCM_REMOVE, LAST_SIGNAL };
static guint gnc_dense_cal_model_signals[LAST_SIGNAL] = { 0 };
static void
gnc_dense_cal_model_base_init(gpointer g_class)
{
static gboolean initialized = FALSE;
if (!initialized)
{
gnc_dense_cal_model_signals[GDCM_ADDED]
= g_signal_new("added",
G_TYPE_FROM_CLASS(g_class),
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
0 /* default offset */,
NULL /* accumulator */,
NULL /* accum. data */,
g_cclosure_marshal_VOID__UINT,
G_TYPE_NONE /* return */,
1 /* n_params */,
G_TYPE_UINT /* param types */
);
gnc_dense_cal_model_signals[GDCM_UPDATE]
= g_signal_new("update",
G_TYPE_FROM_CLASS(g_class),
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
0 /* default offset */,
NULL /* accumulator */,
NULL /* accum. data */,
g_cclosure_marshal_VOID__UINT,
G_TYPE_NONE /* return */,
1 /* n_params */,
G_TYPE_UINT /* param types */
);
gnc_dense_cal_model_signals[GDCM_REMOVE]
= g_signal_new("removing",
G_TYPE_FROM_CLASS(g_class),
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
0 /* default offset */,
NULL /* accumulator */,
NULL /* accum. data */,
g_cclosure_marshal_VOID__UINT,
G_TYPE_NONE /* return */,
1 /* n_params */,
G_TYPE_UINT /* param types */
);
initialized = TRUE;
}
}
GType
gnc_dense_cal_model_get_type(void)
{
static GType type = 0;
if (type == 0) {
static const GTypeInfo info = {
sizeof(GncDenseCalModelIface),
gnc_dense_cal_model_base_init, /* base_init */
NULL, /* base_finalize */
NULL, /* class_init */
NULL, /* class_finalize */
NULL, /* class_data */
0,
0, /* n_preallocs */
NULL /* instance_init */
};
type = g_type_register_static(G_TYPE_INTERFACE, "GncDenseCalModel", &info, 0);
}
return type;
}
GList*
gnc_dense_cal_model_get_contained(GncDenseCalModel *model)
{
return (*GNC_DENSE_CAL_MODEL_GET_INTERFACE(model)->get_contained)(model);
}
gchar*
gnc_dense_cal_model_get_name(GncDenseCalModel *model, guint tag)
{
return (*GNC_DENSE_CAL_MODEL_GET_INTERFACE(model)->get_name)(model, tag);
}
gchar*
gnc_dense_cal_model_get_info(GncDenseCalModel *model, guint tag)
{
return (*GNC_DENSE_CAL_MODEL_GET_INTERFACE(model)->get_info)(model, tag);
}
gint
gnc_dense_cal_model_get_instance_count(GncDenseCalModel *model, guint tag)
{
return (*GNC_DENSE_CAL_MODEL_GET_INTERFACE(model)->get_instance_count)(model, tag);
}
void
gnc_dense_cal_model_get_instance(GncDenseCalModel *model, guint tag, gint instance_index, GDate *date)
{
return (*GNC_DENSE_CAL_MODEL_GET_INTERFACE(model)->get_instance)(model, tag, instance_index, date);
}

View File

@ -0,0 +1,66 @@
/*
* gnc-dense-cal-model.h
*
* Copyright (C) 2006 Joshua Sled <jsled@asynchronous.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License*
* along with this program; if not, contact:
*
* Free Software Foundation Voice: +1-617-542-5942
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
* Boston, MA 02110-1301, USA gnu@gnu.org
*/
#ifndef _GNC_DENSE_CAL_MODEL_H
#define _GNC_DENSE_CAL_MODEL_H
#include "config.h"
#include <glib.h>
#include <glib-object.h>
G_BEGIN_DECLS
#define GNC_TYPE_DENSE_CAL_MODEL (gnc_dense_cal_model_get_type())
#define GNC_DENSE_CAL_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNC_TYPE_DENSE_CAL_MODEL, GncDenseCalModel))
#define GNC_IS_DENSE_CAL_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNC_TYPE_DENSE_CAL_MODEL))
#define GNC_DENSE_CAL_MODEL_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GNC_TYPE_DENSE_CAL_MODEL, GncDenseCalModelIface))
typedef struct _GncDenseCalModel GncDenseCalModel; /* non existant */
typedef struct _GncDenseCalModelIface
{
GTypeInterface parent;
/* signals */
void (*insert)(GncDenseCalModel *mdl, gint tag);
void (*update)(GncDenseCalModel *mdl, gint tag);
void (*remove)(GncDenseCalModel *mdl, gint tag);
/* virtual table */
GList* (*get_contained)(GncDenseCalModel *model);
gchar* (*get_name)(GncDenseCalModel *model, guint tag);
gchar* (*get_info)(GncDenseCalModel *model, guint tag);
gint (*get_instance_count)(GncDenseCalModel *model, guint tag);
void (*get_instance)(GncDenseCalModel *model, guint tag, gint instance_index, GDate *date);
} GncDenseCalModelIface;
GType gnc_dense_cal_model_get_type(void);
/* @fixme: glist mem alloc policy... ? */
GList* gnc_dense_cal_model_get_contained(GncDenseCalModel *model);
gchar* gnc_dense_cal_model_get_name(GncDenseCalModel *model, guint tag);
gchar* gnc_dense_cal_model_get_info(GncDenseCalModel *model, guint tag);
gint gnc_dense_cal_model_get_instance_count(GncDenseCalModel *model, guint tag);
void gnc_dense_cal_model_get_instance(GncDenseCalModel *model, guint tag, gint instance_index, GDate *date);
G_END_DECLS
#endif // _GNC_DENSE_CAL_MODEL_H

View File

@ -0,0 +1,290 @@
/*
* gnc-dense-cal-store.h
*
* Copyright (C) 2006 Joshua Sled <jsled@asynchronous.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License*
* along with this program; if not, contact:
*
* Free Software Foundation Voice: +1-617-542-5942
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
* Boston, MA 02110-1301, USA gnu@gnu.org
*/
#include "config.h"
#include <glib.h>
#include <glib-object.h>
#include "gnc-dense-cal.h"
#include "gnc-dense-cal-model.h"
#include "gnc-dense-cal-store.h"
struct _GncDenseCalStore
{
GObject parent;
GDate start_date;
gdcs_end_type end_type;
GDate end_date;
gint n_occurrences;
gchar *name;
gchar *info;
int num_marks;
int num_real_marks;
GDate **cal_marks;
};
struct _GncDenseCalStoreClass
{
GObjectClass parent_class;
};
static GObjectClass *parent_class = NULL;
static void gnc_dense_cal_store_class_init(GncDenseCalStoreClass *klass);
static void gnc_dense_cal_store_finalize(GObject *obj);
static GList* gdcs_get_contained(GncDenseCalModel *model);
static gchar* gdcs_get_name(GncDenseCalModel *model, guint tag);
static gchar* gdcs_get_info(GncDenseCalModel *model, guint tag);
static gint gdcs_get_instance_count(GncDenseCalModel *model, guint tag);
static void gdcs_get_instance(GncDenseCalModel *model, guint tag, gint instance_index, GDate *date);
static void
gnc_dense_cal_store_class_init(GncDenseCalStoreClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS(klass);
parent_class = g_type_class_peek_parent(klass);
object_class->finalize = gnc_dense_cal_store_finalize;
}
static void
gnc_dense_cal_store_iface_init(gpointer g_iface, gpointer iface_data)
{
GncDenseCalModelIface *iface = (GncDenseCalModelIface*)g_iface;
iface->get_contained = gdcs_get_contained;
iface->get_name = gdcs_get_name;
iface->get_info = gdcs_get_info;
iface->get_instance_count = gdcs_get_instance_count;
iface->get_instance = gdcs_get_instance;
}
GType
gnc_dense_cal_store_get_type(void)
{
static GType type = 0;
if (type == 0)
{
static const GTypeInfo info = {
sizeof (GncDenseCalStoreClass),
NULL, /* base_init */
NULL, /* base_finalize */
(GClassInitFunc)gnc_dense_cal_store_class_init, /* class_init */
NULL, /* class_finalize */
NULL, /* class_data */
sizeof(GncDenseCalStore),
0, /* n_preallocs */
NULL /* instance_init */
};
static const GInterfaceInfo iDenseCalModelInfo = {
(GInterfaceInitFunc)gnc_dense_cal_store_iface_init,
NULL, /* interface finalize */
NULL, /* interface data */
};
type = g_type_register_static(G_TYPE_OBJECT, "GncDenseCalStore", &info, 0);
g_type_add_interface_static(type,
GNC_TYPE_DENSE_CAL_MODEL,
&iDenseCalModelInfo);
}
return type;
}
GncDenseCalStore*
gnc_dense_cal_store_new(int num_marks)
{
GncDenseCalStore *model = g_object_new(GNC_TYPE_DENSE_CAL_STORE, NULL);
model->num_marks = num_marks;
model->cal_marks = g_new0(GDate*, num_marks);
{
int i = 0;
for (i = 0; i < model->num_marks; i++)
{
model->cal_marks[i] = g_date_new();
}
}
model->num_real_marks = 0;
g_date_clear(&model->start_date, 1);
g_date_set_time_t(&model->start_date, time(NULL));
model->end_type = NEVER_END;
g_date_clear(&model->end_date, 1);
g_date_set_time_t(&model->end_date, time(NULL));
model->n_occurrences = 0;
return model;
}
void
gnc_dense_cal_store_clear(GncDenseCalStore *model)
{
model->num_real_marks = 0;
g_signal_emit_by_name(model, "update", GUINT_TO_POINTER(1));
}
void
gnc_dense_cal_store_update_name(GncDenseCalStore *model, gchar *name)
{
if (model->name != NULL)
{
g_free(model->name);
}
model->name = g_strdup(name);
g_signal_emit_by_name(model, "update", GUINT_TO_POINTER(1));
}
void
gnc_dense_cal_store_update_info(GncDenseCalStore *model, gchar *info)
{
if (model->info != NULL)
{
g_free(model->info);
}
model->info = g_strdup(info);
g_signal_emit_by_name(model, "update", GUINT_TO_POINTER(1));
}
static void
gdcs_generic_update(GncDenseCalStore *trans, GDate *start, FreqSpec *fs)
{
int i;
GDate date;
date = *start;
/* go one day before what's in the box so we can get the correct start
* date. */
g_date_subtract_days(&date, 1);
xaccFreqSpecGetNextInstance(fs, &date, &date);
i = 0;
while ((i < trans->num_marks)
&& g_date_valid(&date)
/* Do checking against end restriction. */
&& ((trans->end_type == NEVER_END)
|| (trans->end_type == END_ON_DATE
&& g_date_compare(&date, &trans->end_date) <= 0)
|| (trans->end_type == END_AFTER_N_OCCS
&& i < trans->n_occurrences)))
{
*trans->cal_marks[i++] = date;
xaccFreqSpecGetNextInstance(fs, &date, &date);
}
trans->num_real_marks = (i-1);
g_signal_emit_by_name(trans, "update", GUINT_TO_POINTER(1));
}
void
gnc_dense_cal_store_update_no_end(GncDenseCalStore *model, GDate *start, FreqSpec *fs)
{
model->end_type = NEVER_END;
gdcs_generic_update(model, start, fs);
}
void
gnc_dense_cal_store_update_count_end(GncDenseCalStore *model, GDate *start, FreqSpec *fs, int num_occur)
{
model->end_type = END_AFTER_N_OCCS;
model->n_occurrences = num_occur;
gdcs_generic_update(model, start, fs);
}
void
gnc_dense_cal_store_update_date_end(GncDenseCalStore *model, GDate *start, FreqSpec *fs, GDate *end_date)
{
model->end_type = END_ON_DATE;
model->end_date = *end_date;
gdcs_generic_update(model, start, fs);
}
static GList*
gdcs_get_contained(GncDenseCalModel *model)
{
GList *rtn = NULL;
rtn = g_list_append(rtn, GUINT_TO_POINTER(1));
return rtn;
}
static gchar*
gdcs_get_name(GncDenseCalModel *model, guint tag)
{
GncDenseCalStore *mdl = GNC_DENSE_CAL_STORE(model);
// assert(tag == 1)
return mdl->name;
}
static gchar*
gdcs_get_info(GncDenseCalModel *model, guint tag)
{
GncDenseCalStore *mdl = GNC_DENSE_CAL_STORE(model);
// assert(tag == 1)
return mdl->info;
}
static gint
gdcs_get_instance_count(GncDenseCalModel *model, guint tag)
{
GncDenseCalStore *mdl = GNC_DENSE_CAL_STORE(model);
// assert(tag == 1)
return mdl->num_real_marks;
}
static void
gdcs_get_instance(GncDenseCalModel *model, guint tag, gint instance_index, GDate *date)
{
GncDenseCalStore *mdl = GNC_DENSE_CAL_STORE(model);
// assert(tag == 1)
// assert 0 < instance_index < model->num_marks;
*date = *mdl->cal_marks[instance_index];
}
static void
gnc_dense_cal_store_finalize(GObject *obj)
{
int i;
GncDenseCalStore *store;
g_return_if_fail(obj != NULL);
store = GNC_DENSE_CAL_STORE(obj);
if (store->name != NULL)
{
g_free(store->name);
store->name = NULL;
}
if (store->info != NULL)
{
g_free(store->info);
store->info = NULL;
}
for (i = 0; i < store->num_marks; i++)
{
g_free(store->cal_marks[i]);
store->cal_marks[i] = NULL;
}
if (store->cal_marks != NULL)
{
g_free(store->cal_marks);
store->cal_marks = NULL;
}
G_OBJECT_CLASS(parent_class)->finalize(obj);
}

View File

@ -0,0 +1,57 @@
/*
* gnc-dense-cal-store.h
*
* Copyright (C) 2006 Joshua Sled <jsled@asynchronous.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License*
* along with this program; if not, contact:
*
* Free Software Foundation Voice: +1-617-542-5942
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
* Boston, MA 02110-1301, USA gnu@gnu.org
*/
#ifndef _GNC_DENSE_CAL_STORE_H
#define _GNC_DENSE_CAL_STORE_H
#include "config.h"
#include <glib.h>
#include <glib-object.h>
#include "gnc-dense-cal-model.h"
#include "gnc-dense-cal.h"
G_BEGIN_DECLS
#define GNC_TYPE_DENSE_CAL_STORE (gnc_dense_cal_store_get_type())
#define GNC_DENSE_CAL_STORE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNC_TYPE_DENSE_CAL_STORE, GncDenseCalStore))
#define GNC_DENSE_CAL_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNC_TYPE_DENSE_CAL_STORE, GncDenseCalStoreClass))
#define GNC_IS_DENSE_CAL_STORE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNC_TYPE_DENSE_CAL_STORE))
#define GNC_IS_DENSE_CAL_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNC_TYPE_DENSE_CAL_STORE))
#define GNC_DENSE_CAL_STORE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNC_TYPE_DENSE_CAL_STORE, GncDenseCalStore))
typedef enum { NEVER_END, END_ON_DATE, END_AFTER_N_OCCS, BAD_END } gdcs_end_type;
typedef struct _GncDenseCalStore GncDenseCalStore;
typedef struct _GncDenseCalStoreClass GncDenseCalStoreClass;
GType gnc_dense_cal_store_get_type(void);
GncDenseCalStore* gnc_dense_cal_store_new(int num_marks);
void gnc_dense_cal_store_clear(GncDenseCalStore *model);
void gnc_dense_cal_store_update_name(GncDenseCalStore *model, gchar* name);
void gnc_dense_cal_store_update_info(GncDenseCalStore *model, gchar* info);
void gnc_dense_cal_store_update_no_end(GncDenseCalStore *model, GDate *start, FreqSpec *fs);
void gnc_dense_cal_store_update_count_end(GncDenseCalStore *model, GDate *start, FreqSpec *fs, int num_occur);
void gnc_dense_cal_store_update_date_end(GncDenseCalStore *model, GDate *start, FreqSpec *fs, GDate *end_date);
G_END_DECLS
#endif // _GNC_DENSE_CAL_STORE_H

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +1,11 @@
#ifndef _DENSECAL_H_
#define _DENSECAL_H_
/********************************************************************\
* gnc-dense-cal.h : a custom densely-dispalyed calendar widget *
* Copyright (C) 2002 Joshua Sled <jsled@asynchronous.org> *
* Copyright (C) 2002,2006 Joshua Sled <jsled@asynchronous.org> *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation; either version 2 of *
* the License, or (at your option) any later version. *
* published by the Free Software Foundation, under version 2 of *
* the License. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
@ -23,14 +20,17 @@
* Boston, MA 02110-1301, USA gnu@gnu.org *
\********************************************************************/
#include <gdk/gdk.h>
#include <gtk/gtkadjustment.h>
#include <gtk/gtkwidget.h>
#include <glib.h>
#ifndef _GNC_DENSE_CAL_H
#define _GNC_DENSE_CAL_H
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#include "config.h"
#include <FreqSpec.h>
#include <glib.h>
#include "gnc-dense-cal-model.h"
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define GNC_TYPE_DENSE_CAL (gnc_dense_cal_get_type ())
#define GNC_DENSE_CAL(obj) GTK_CHECK_CAST (obj, gnc_dense_cal_get_type (), GncDenseCal)
@ -40,92 +40,97 @@ extern "C" {
typedef struct _GncDenseCal GncDenseCal;
typedef struct _GncDenseCalClass GncDenseCalClass;
typedef struct _gdc_month_coords {
gint x, y;
typedef struct _gdc_month_coords
{
gint x, y;
} gdc_month_coords;
enum GDC_COLORS {
MONTH_THIS = 0,
MONTH_THAT,
MAX_COLORS
enum GDC_COLORS
{
MONTH_THIS = 0,
MONTH_THAT,
MAX_COLORS
};
struct _GncDenseCal
{
GtkWidget widget;
GtkWidget widget;
GdkPixmap *drawbuf;
GdkPixmap *drawbuf;
gboolean initialized;
gboolean initialized;
gboolean showPopup;
GtkWindow *transPopup;
gboolean showPopup;
GtkWindow *transPopup;
gint min_x_scale;
gint min_y_scale;
gint min_x_scale;
gint min_y_scale;
gint x_scale;
gint y_scale;
gint x_scale;
gint y_scale;
gint numMonths;
gint monthsPerCol;
gint num_weeks; /* computed */
gint numMonths;
gint monthsPerCol;
gint num_weeks; /* computed */
GDateMonth month;
gint year;
gint firstOfMonthOffset;
GDateMonth month;
gint year;
gint firstOfMonthOffset;
gint leftPadding;
gint topPadding;
gint leftPadding;
gint topPadding;
gboolean needInitMonthLabels;
gdc_month_coords monthPositions[12];
GdkFont *monthLabelFont;
GdkFont *dayLabelFont;
GdkPixmap *monthLabels[12];
gboolean needInitMonthLabels;
gdc_month_coords monthPositions[12];
GdkFont *monthLabelFont;
GdkFont *dayLabelFont;
GdkPixmap *monthLabels[12];
GdkColor weekColors[MAX_COLORS];
GdkColor weekColors[MAX_COLORS];
guint label_lbearing;
guint label_ascent;
guint label_width;
guint label_height;
guint dayLabelHeight;
guint label_lbearing;
guint label_ascent;
guint label_width;
guint label_height;
guint dayLabelHeight;
guint lastMarkTag;
GncDenseCalModel *model;
/**
* A GList of gdc_mark_data structs, one for each active/valid markTag.
**/
GList *markData;
int numMarks;
/* array of GList*s of per-cell markings. */
GList **marks;
guint lastMarkTag;
int disposed; /* private */
/**
* A GList of gdc_mark_data structs, one for each active/valid markTag.
**/
GList *markData;
int numMarks;
/* array of GList*s of per-cell markings. */
GList **marks;
int disposed; /* private */
};
struct _GncDenseCalClass
{
GtkWidgetClass parent_class;
void (*marks_lost_cb)( GncDenseCal *dcal, gpointer user_data );
GtkWidgetClass parent_class;
};
typedef struct _gdc_mark_data {
gchar *name;
gchar *info;
guint tag;
/* GdkColor markStyle; */
/**
* A GList of the dcal->marks indexes containing this mark.
**/
GList *ourMarks;
typedef struct _gdc_mark_data
{
gchar *name;
gchar *info;
guint tag;
/**
* A GList of the dcal->marks indexes containing this mark.
**/
GList *ourMarks;
} gdc_mark_data;
GtkWidget* gnc_dense_cal_new (void);
GtkWidget* gnc_dense_cal_new_with_model (GncDenseCalModel *model);
GType gnc_dense_cal_get_type (void);
void gnc_dense_cal_set_month( GncDenseCal *dcal, GDateMonth mon );
void gnc_dense_cal_set_model(GncDenseCal *cal, GncDenseCalModel *model);
void gnc_dense_cal_set_month(GncDenseCal *dcal, GDateMonth mon);
/**
* @param year Julian year: 2000 = 2000AD.
**/
@ -137,13 +142,6 @@ guint gnc_dense_cal_get_num_months( GncDenseCal *dcal );
GDateMonth gnc_dense_cal_get_month( GncDenseCal *dcal );
GDateYear gnc_dense_cal_get_year( GncDenseCal *dcal );
guint gnc_dense_cal_mark( GncDenseCal *dcal,
guint size, GDate **daysArray,
gchar *name, gchar *info );
void gnc_dense_cal_mark_remove( GncDenseCal *dcal, guint markToRemove );
G_END_DECLS
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* _DENSECAL_H_ */
#endif /* _GNC_DENSE_CAL_H */

View File

@ -638,12 +638,12 @@ gnc_frequency_save_state( GNCFrequency *gf, FreqSpec *fs, GDate *outDate )
gint tmpInt;
int i;
GDate gd;
time_t tmpTimeT;
time_t start_tt;
tmpTimeT = gnc_date_edit_get_date( gf->startDate );
start_tt = gnc_date_edit_get_date( gf->startDate );
if ( NULL != outDate )
{
g_date_set_time_t( outDate, tmpTimeT );
g_date_set_time_t( outDate, start_tt );
}
if (NULL == fs) return;
@ -656,7 +656,7 @@ gnc_frequency_save_state( GNCFrequency *gf, FreqSpec *fs, GDate *outDate )
gnc_suspend_gui_refresh();
g_date_clear (&gd, 1);
g_date_set_time_t( &gd, tmpTimeT );
g_date_set_time_t( &gd, start_tt );
/*uift = xaccFreqSpecGetUIType( fs );*/
uift = PAGES[page].uiFTVal;
@ -667,6 +667,7 @@ gnc_frequency_save_state( GNCFrequency *gf, FreqSpec *fs, GDate *outDate )
/* hmmm... shouldn't really be allowed. */
break;
case UIFREQ_ONCE:
xaccFreqSpecSetOnceDate(fs, &gd);
xaccFreqSpecSetUIType( fs, uift );
break;
case UIFREQ_DAILY:
@ -771,8 +772,8 @@ gnc_frequency_save_state( GNCFrequency *gf, FreqSpec *fs, GDate *outDate )
o = glade_xml_get_widget( gf->gxml, "semimonthly_second" );
day = gtk_combo_box_get_active( GTK_COMBO_BOX(o) )+1;
tmpFS = xaccFreqSpecMalloc(gnc_get_current_book ());
tmpTimeT = gnc_date_edit_get_date( gf->startDate );
g_date_set_time_t( &gd, tmpTimeT );
start_tt = gnc_date_edit_get_date( gf->startDate );
g_date_set_time_t( &gd, start_tt );
g_date_to_struct_tm( &gd, &stm);
if ( day >= stm.tm_mday ) {
/* next month */
@ -788,15 +789,19 @@ gnc_frequency_save_state( GNCFrequency *gf, FreqSpec *fs, GDate *outDate )
}
case UIFREQ_MONTHLY:
{
struct tm stm;
o = glade_xml_get_widget( gf->gxml, "monthly_spin" );
tmpInt = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(o));
g_date_to_struct_tm( &gd, &stm);
o = glade_xml_get_widget( gf->gxml, "monthly_day" );
day = gtk_combo_box_get_active( GTK_COMBO_BOX(o) ) + 1;
stm.tm_mday = day;
g_date_set_time_t( &gd, mktime( &stm ) );
g_date_set_time_t(&gd, time(NULL));
g_date_set_month(&gd, 1);
g_date_set_day(&gd, day);
{
gchar buf[128];
g_date_strftime(buf, 127, "%c", &gd);
printf("monthly date [%s]\n", buf);
}
xaccFreqSpecSetMonthly( fs, &gd, tmpInt );
xaccFreqSpecSetUIType( fs, uift );
break;

View File

@ -0,0 +1,254 @@
/*
* gnc-sx-instance-dense-cal-adapter.c
*
* Copyright (C) 2006 Josh Sled <jsled@asynchronous.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact:
*
* Free Software Foundation Voice: +1-617-542-5942
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
* Boston, MA 02110-1301, USA gnu@gnu.org
*/
#include "config.h"
#include <glib.h>
#include "gnc-sx-instance-dense-cal-adapter.h"
#include "gnc-dense-cal.h"
static void gnc_sx_instance_dense_cal_adapter_dispose(GObject *obj);
static void gnc_sx_instance_dense_cal_adapter_finalize(GObject *obj);
static GList* gsidca_get_contained(GncDenseCalModel *model);
static gchar* gsidca_get_name(GncDenseCalModel *model, guint tag);
static gchar* gsidca_get_info(GncDenseCalModel *model, guint tag);
static gint gsidca_get_instance_count(GncDenseCalModel *model, guint tag);
static void gsidca_get_instance(GncDenseCalModel *model, guint tag, gint instance_index, GDate *date);
static GObjectClass *parent_class = NULL;
struct _GncSxInstanceDenseCalAdapterClass
{
GObjectClass parent;
};
struct _GncSxInstanceDenseCalAdapter
{
GObject parent;
gboolean disposed;
GncSxInstanceModel *instances;
};
static void
gnc_sx_instance_dense_cal_adapter_class_init(GncSxInstanceDenseCalAdapterClass *klass)
{
GObjectClass *obj_class = G_OBJECT_CLASS(klass);
obj_class->dispose = gnc_sx_instance_dense_cal_adapter_dispose;
obj_class->finalize = gnc_sx_instance_dense_cal_adapter_finalize;
parent_class = g_type_class_peek_parent(klass);
}
static void
gnc_sx_instance_dense_cal_adapter_init(GTypeInstance *instance, gpointer klass)
{
/*GncSxInstanceDenseCalAdapter *adapter = GNC_SX_INSTANCE_DENSE_CAL_ADAPTER(instance);*/
; /* nop */
}
static void
gnc_sx_instance_dense_cal_adapter_interface_init(gpointer g_iface, gpointer iface_data)
{
GncDenseCalModelIface *iface = (GncDenseCalModelIface*)g_iface;
iface->get_contained = gsidca_get_contained;
iface->get_name = gsidca_get_name;
iface->get_info = gsidca_get_info;
iface->get_instance_count = gsidca_get_instance_count;
iface->get_instance = gsidca_get_instance;
}
static void
gsidca_instances_added_cb(GncSxInstanceModel *model, SchedXaction *sx_added, gpointer user_data)
{
GncSxInstanceDenseCalAdapter *adapter = GNC_SX_INSTANCE_DENSE_CAL_ADAPTER(user_data);
printf("instance added\n");
g_signal_emit_by_name(adapter, "added", GPOINTER_TO_UINT(sx_added));
}
static void
gsidca_instances_updated_cb(GncSxInstanceModel *model, SchedXaction *sx_updated, gpointer user_data)
{
GncSxInstanceDenseCalAdapter *adapter = GNC_SX_INSTANCE_DENSE_CAL_ADAPTER(user_data);
gnc_sx_instance_model_update_sx_instances(model, sx_updated);
printf("instances updated\n");
g_signal_emit_by_name(adapter, "update", GPOINTER_TO_UINT((gpointer)sx_updated));
}
static void
gsidca_instances_removing_cb(GncSxInstanceModel *model, SchedXaction *sx_to_be_removed, gpointer user_data)
{
GncSxInstanceDenseCalAdapter *adapter = GNC_SX_INSTANCE_DENSE_CAL_ADAPTER(user_data);
printf("removing instance...\n");
g_signal_emit_by_name(adapter, "removing", GPOINTER_TO_UINT(sx_to_be_removed));
gnc_sx_instance_model_remove_sx_instances(model, sx_to_be_removed);
}
GncSxInstanceDenseCalAdapter*
gnc_sx_instance_dense_cal_adapter_new(GncSxInstanceModel *instances)
{
GncSxInstanceDenseCalAdapter *adapter = g_object_new(GNC_TYPE_SX_INSTANCE_DENSE_CAL_ADAPTER, NULL);
adapter->instances = instances;
g_object_ref(G_OBJECT(adapter->instances));
g_signal_connect(instances, "added", (GCallback)gsidca_instances_added_cb, adapter);
g_signal_connect(instances, "updated", (GCallback)gsidca_instances_updated_cb, adapter);
g_signal_connect(instances, "removing", (GCallback)gsidca_instances_removing_cb, adapter);
return adapter;
}
GType
gnc_sx_instance_dense_cal_adapter_get_type(void)
{
static GType type = 0;
if (type == 0)
{
static const GTypeInfo info = {
sizeof (GncSxInstanceDenseCalAdapterClass),
NULL, /* base init */
NULL, /* base finalize */
(GClassInitFunc)gnc_sx_instance_dense_cal_adapter_class_init,
NULL, /* class finalize */
NULL, /* class data */
sizeof(GncSxInstanceDenseCalAdapter),
0, /* n_preallocs */
(GInstanceInitFunc)gnc_sx_instance_dense_cal_adapter_init
};
static const GInterfaceInfo iDenseCalModelInfo = {
(GInterfaceInitFunc)gnc_sx_instance_dense_cal_adapter_interface_init,
NULL, /* interface finalize */
NULL, /* interface data */
};
type = g_type_register_static (G_TYPE_OBJECT,
"GncSxInstanceDenseCalAdapterType",
&info, 0);
g_type_add_interface_static(type,
GNC_TYPE_DENSE_CAL_MODEL,
&iDenseCalModelInfo);
}
return type;
}
static gint
gsidca_find_sx_with_tag(gconstpointer list_data,
gconstpointer find_data)
{
GncSxInstances *sx_instances = (GncSxInstances*)list_data;
return (GUINT_TO_POINTER(GPOINTER_TO_UINT(sx_instances->sx)) == find_data ? 0 : 1);
}
// @@ fixme this list is leaked.
static GList*
gsidca_get_contained(GncDenseCalModel *model)
{
GncSxInstanceDenseCalAdapter *adapter = GNC_SX_INSTANCE_DENSE_CAL_ADAPTER(model);
//"removing return gnc_g_list_map(instances->sxes, sx_to_tag, null);
GList *list = NULL, *sxes;
for (sxes = adapter->instances->sx_instance_list; sxes != NULL; sxes = sxes->next)
{
GncSxInstances *sx_instances = (GncSxInstances*)sxes->data;
list = g_list_append(list, GUINT_TO_POINTER(GPOINTER_TO_UINT(sx_instances->sx)));
}
return list;
}
static gchar*
gsidca_get_name(GncDenseCalModel *model, guint tag)
{
GncSxInstanceDenseCalAdapter *adapter = GNC_SX_INSTANCE_DENSE_CAL_ADAPTER(model);
GncSxInstances *insts
= (GncSxInstances*)g_list_find_custom(adapter->instances->sx_instance_list, GUINT_TO_POINTER(tag), gsidca_find_sx_with_tag)->data;
if (insts == NULL)
return NULL;
return xaccSchedXactionGetName(insts->sx);
}
static gchar*
gsidca_get_info(GncDenseCalModel *model, guint tag)
{
GncSxInstanceDenseCalAdapter *adapter = GNC_SX_INSTANCE_DENSE_CAL_ADAPTER(model);
// g_list_find(instances->sxes, {sx_to_tag, tag}).get_freq_spec().get_freq_str();
FreqSpec *spec;
GString *info;
gchar *info_str;
GncSxInstances *insts
= (GncSxInstances*)g_list_find_custom(adapter->instances->sx_instance_list, GUINT_TO_POINTER(tag), gsidca_find_sx_with_tag)->data;
if (insts == NULL)
return NULL;
spec = xaccSchedXactionGetFreqSpec(insts->sx);
info = g_string_sized_new(16);
xaccFreqSpecGetFreqStr(spec, info);
info_str = info->str; // @fixme leaked... :/
g_string_free(info, FALSE);
return info_str;
}
static gint
gsidca_get_instance_count(GncDenseCalModel *model, guint tag)
{
GncSxInstanceDenseCalAdapter *adapter = GNC_SX_INSTANCE_DENSE_CAL_ADAPTER(model);
// g_list_find(instances->sxes, {sx_to_tag, tag}).length();
GncSxInstances *insts
= (GncSxInstances*)g_list_find_custom(adapter->instances->sx_instance_list, GUINT_TO_POINTER(tag), gsidca_find_sx_with_tag)->data;
if (insts == NULL)
return 0;
return g_list_length(insts->list);
}
static void
gsidca_get_instance(GncDenseCalModel *model, guint tag, gint instance_index, GDate *date)
{
GncSxInstanceDenseCalAdapter *adapter = GNC_SX_INSTANCE_DENSE_CAL_ADAPTER(model);
GncSxInstance *inst;
GncSxInstances *insts
= (GncSxInstances*)g_list_find_custom(adapter->instances->sx_instance_list, GUINT_TO_POINTER(tag), gsidca_find_sx_with_tag)->data;
if (insts == NULL)
return;
inst = (GncSxInstance*)g_list_nth_data(insts->list, instance_index);
g_date_valid(&inst->date);
*date = inst->date;
g_date_valid(date);
}
static void
gnc_sx_instance_dense_cal_adapter_dispose(GObject *obj)
{
GncSxInstanceDenseCalAdapter *adapter;
g_return_if_fail(obj != NULL);
adapter = GNC_SX_INSTANCE_DENSE_CAL_ADAPTER(obj);
// g_return_if_fail(!adapter->disposed);
if (adapter->disposed) return;
adapter->disposed = TRUE;
g_object_unref(G_OBJECT(adapter->instances));
adapter->instances = NULL;
G_OBJECT_CLASS(parent_class)->dispose(obj);
}
static void gnc_sx_instance_dense_cal_adapter_finalize(GObject *obj)
{
g_return_if_fail(obj != NULL);
// nop
G_OBJECT_CLASS(parent_class)->finalize(obj);
}

View File

@ -0,0 +1,43 @@
/*
* gnc-sx-instance-dense-cal-adapter.h
*
* Copyright (C) 2006 Josh Sled <jsled@asynchronous.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact:
*
* Free Software Foundation Voice: +1-617-542-5942
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
* Boston, MA 02110-1301, USA gnu@gnu.org
*/
#ifndef _GNC_SX_INSTANCE_DENSE_CAL_ADAPTER_H
#define _GNC_SX_INSTANCE_DENSE_CAL_ADAPTER_H
#include "config.h"
#include <glib.h>
#include "gnc-sx-instance-model.h"
typedef struct _GncSxInstanceDenseCalAdapterClass GncSxInstanceDenseCalAdapterClass;
typedef struct _GncSxInstanceDenseCalAdapter GncSxInstanceDenseCalAdapter;
#define GNC_TYPE_SX_INSTANCE_DENSE_CAL_ADAPTER (gnc_sx_instance_dense_cal_adapter_get_type ())
#define GNC_SX_INSTANCE_DENSE_CAL_ADAPTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNC_TYPE_SX_INSTANCE_DENSE_CAL_ADAPTER, GncSxInstanceDenseCalAdapter))
#define GNC_SX_INSTANCE_DENSE_CAL_ADAPTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNC_TYPE_SX_INSTANCE_DENSE_CAL_ADAPTER, GncSxInstanceDenseCalAdapterClass))
#define GNC_IS_SX_INSTANCE_DENSE_CAL_ADAPTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNC_TYPE_SX_INSTANCE_DENSE_CAL_ADAPTER))
#define GNC_IS_SX_INSTANCE_DENSE_CAL_ADAPTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNC_TYPE_SX_INSTANCE_DENSE_CAL_ADAPTER))
#define GNC_SX_INSTANCE_DENSE_CAL_ADAPTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNC_TYPE_SX_INSTANCE_DENSE_CAL_ADAPTER, GncSxInstanceDenseCalAdapterClass))
GncSxInstanceDenseCalAdapter* gnc_sx_instance_dense_cal_adapter_new(GncSxInstanceModel *instances);
GType gnc_sx_instance_dense_cal_adapter_get_type(void);
#endif // _GNC_SX_INSTANCE_DENSE_CAL_ADAPTER_H

View File

@ -1,5 +1,5 @@
TESTS = \
test-link-module test-load-module
test-link-module test-load-module test-sx
# The following tests are nice, but have absolutely no place in an
# automated testing system.
@ -26,29 +26,30 @@ TESTS_ENVIRONMENT := \
$(shell ${top_srcdir}/src/gnc-test-env --no-exports ${GNC_TEST_DEPS})
check_PROGRAMS = \
test-link-module test-gnc-recurrence test-gnc-dialog
test-link-module test-gnc-recurrence test-gnc-dialog test-sx
INCLUDES= \
-I${top_srcdir}/src \
-I${top_srcdir}/src/engine \
-I${top_srcdir}/src/engine/test-core \
-I${top_srcdir}/src/gnome-utils \
-I${top_srcdir}/src/gnc-module \
-I${top_srcdir}/src/app-utils \
-I${top_srcdir}/src/test-core \
${GLIB_CFLAGS} ${GUILE_INCS} ${GNOME_CFLAGS} ${GLADE_CFLAGS} ${QOF_CFLAGS}
test_gnc_recurrence_SOURCES=test-gnc-recurrence.c
test_gnc_recurrence_LDADD = ${GNOME_LIBS} \
LDADD = \
${GNOME_LIBS} \
${top_builddir}/src/app-utils/libgncmod-app-utils.la \
${top_builddir}/src/gnome-utils/libgncmod-gnome-utils.la \
${top_builddir}/src/engine/libgncmod-engine.la
${top_builddir}/src/engine/libgncmod-engine.la \
${top_builddir}/src/engine/test-core/libgncmod-test-engine.la \
${top_builddir}/src/test-core/libgncmod-test.la
test_gnc_dialog_LDADD = ${GNOME_LIBS} \
${top_builddir}/src/app-utils/libgncmod-app-utils.la \
${top_builddir}/src/gnome-utils/libgncmod-gnome-utils.la \
${top_builddir}/src/engine/libgncmod-engine.la
test_gnc_recurrence_SOURCES=test-gnc-recurrence.c
test_link_module_SOURCES=test-link-module.c
test_link_module_LDADD= \
test_link_module_LDADD = \
${GUILE_LIBS} \
${top_builddir}/src/gnc-module/libgnc-module.la

View File

@ -0,0 +1,82 @@
#include "config.h"
#include <glib.h>
#include "qof.h"
#include "gnc-engine.h"
#include "gnc-sx-instance-model.h"
#include "gnc-sx-instance-dense-cal-adapter.h"
#include "gnc-dense-cal.h"
#include "gnc-dense-cal-model.h"
#include "test-stuff.h"
#include "test-engine-stuff.h"
static void
_removing(GObject *obj, SchedXaction *removing, gpointer unused_user_data)
{
gnc_sx_instance_model_remove_sx_instances(GNC_SX_INSTANCE_MODEL(obj), removing);
}
static void
setup_default_handlers(GncSxInstanceModel *model)
{
g_signal_connect(model, "removing", (GCallback)_removing, NULL);
}
static void
test()
{
GDate *start, *end;
GncSxInstanceModel *model;
GncSxInstanceDenseCalAdapter *dense_cal_model;
GncDenseCal *cal;
SchedXaction *foo, *bar;
start = g_date_new();
g_date_clear(start, 1);
g_date_set_time_t(start, time(NULL));
end = g_date_new();
g_date_clear(end, 1);
g_date_set_time_t(end, time(NULL));
g_date_add_years(end, 1);
foo = add_daily_sx("foo", start, NULL, NULL);
model = gnc_sx_get_instances(end);
setup_default_handlers(model);
do_test(g_list_length(model->sx_instance_list) == 1, "1 instances");
dense_cal_model = gnc_sx_instance_dense_cal_adapter_new(model);
cal = GNC_DENSE_CAL(gnc_dense_cal_new_with_model(GNC_DENSE_CAL_MODEL(dense_cal_model)));
// gobject-2.10: g_object_ref_sink(cal);
g_object_ref(G_OBJECT(cal));
gtk_object_sink(GTK_OBJECT(cal));
bar = add_daily_sx("bar", start, NULL, NULL);
do_test(g_list_length(model->sx_instance_list) == 2, "2 instances");
remove_sx(foo);
do_test(g_list_length(model->sx_instance_list) == 1, "1 instance");
g_object_unref(cal);
success("freed calendar");
g_object_unref(dense_cal_model);
success("freed dense-cal model");
g_object_unref(model);
success("freed instances");
}
int
main(int argc, char **argv)
{
g_type_init();
gnc_engine_init(argc, argv);
gtk_init(&argc, &argv);
test();
print_test_results();
exit(get_rv());
}

View File

@ -29,11 +29,11 @@ libgnc_gnome_la_SOURCES = \
dialog-price-edit-db.c \
dialog-print-check.c \
dialog-progress.c \
dialog-sx-editor.c \
dialog-sx-from-trans.c \
dialog-sxsincelast.c \
dialog-sx-since-last-run.c \
dialog-tax-info.c \
dialog-userpass.c \
dialog-scheduledxaction.c \
druid-acct-period.c \
druid-hierarchy.c \
druid-merge.c \
@ -45,8 +45,10 @@ libgnc_gnome_la_SOURCES = \
gnc-plugin-register.c \
gnc-plugin-page-account-tree.c \
gnc-plugin-page-budget.c \
gnc-plugin-page-sx-list.c \
gnc-plugin-page-register.c \
gnc-split-reg.c \
gnc-sx-list-tree-model-adapter.c \
lot-viewer.c \
reconcile-list.c \
top-level.c \
@ -67,9 +69,9 @@ noinst_HEADERS = \
dialog-new-user.h \
dialog-print-check.h \
dialog-progress.h \
dialog-sx-editor.h \
dialog-sx-from-trans.h \
dialog-sxsincelast.h \
dialog-scheduledxaction.h \
dialog-sx-since-last-run.h \
druid-acct-period.h \
druid-hierarchy.h \
druid-merge.h \
@ -81,8 +83,10 @@ noinst_HEADERS = \
gnc-plugin-register.h \
gnc-plugin-page-account-tree.h \
gnc-plugin-page-budget.h \
gnc-plugin-page-sx-list.h \
gnc-plugin-page-register.h \
gnc-split-reg.h \
gnc-sx-list-tree-model-adapter.h \
lot-viewer.h \
reconcile-list.h \
top-level.h \

View File

@ -1,11 +1,10 @@
/********************************************************************\
* dialog-scheduledxaction.h : dialogs for scheduled transactions *
* Copyright (C) 2001 Joshua Sled <jsled@asynchronous.org> *
* dialog-sx-editor.h : dialog for scheduled transaction editing *
* Copyright (C) 2001,2006 Joshua Sled <jsled@asynchronous.org> *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation; either version 2 of *
* the License, or (at your option) any later version. *
* modify it under the terms of version 2 of the GNU General Public *
* License as published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
@ -20,8 +19,8 @@
* Boston, MA 02110-1301, USA gnu@gnu.org *
\********************************************************************/
#ifndef DIALOG_SCHEDULEDXACTION_H
#define DIALOG_SCHEDULEDXACTION_H
#ifndef DIALOG_SX_EDITOR_H
#define DIALOG_SX_EDITOR_H
#include "SchedXaction.h"
@ -34,29 +33,12 @@
#define KEY_CREATE_DAYS "create_days"
#define KEY_REMIND_DAYS "remind_days"
struct _SchedXactionDialog;
struct _SchedXactionEditorDialog;
typedef struct _GncSxEditorDialog GncSxEditorDialog;
typedef struct _SchedXactionDialog SchedXactionDialog;
typedef struct _SchedXactionEditorDialog SchedXactionEditorDialog;
GncSxEditorDialog* gnc_ui_scheduled_xaction_editor_dialog_create(SchedXaction *sx,
gboolean newSX);
SchedXactionDialog * gnc_ui_scheduled_xaction_dialog_create(void);
void gnc_ui_scheduled_xaction_dialog_destroy(SchedXactionDialog *sxd);
#ifdef __GTK_CLIST_H__
void row_select_handler( GtkCList *clist, gint row, gint col,
GdkEventButton *event, gpointer d );
void row_unselect_handler( GtkCList *clist, gint row, gint col,
GdkEventButton *event, gpointer d );
#endif
void gnc_sxd_list_refresh( SchedXactionDialog *sxd );
SchedXactionEditorDialog *
gnc_ui_scheduled_xaction_editor_dialog_create( SchedXactionDialog *sxd,
SchedXaction *sx,
gboolean newSX );
void gnc_ui_scheduled_xaction_editor_dialog_destroy( SchedXactionEditorDialog *sxd );
void gnc_ui_scheduled_xaction_editor_dialog_destroy(GncSxEditorDialog *sxd);
/**
* Sets up a book opened hook. The function called may open a "since

View File

@ -25,26 +25,25 @@
#include "config.h"
#include <stdlib.h>
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include "glib-compat.h"
#include "gnc-engine.h"
#include "SX-book.h"
#include "SX-book-p.h"
#include "SX-ttinfo.h"
#include "SchedXaction.h"
#include "gnc-component-manager.h"
#include "dialog-scheduledxaction.h"
#include "dialog-sx-editor.h"
#include "dialog-sx-from-trans.h"
#include "dialog-utils.h"
#include "glib-compat.h"
#include "gnc-component-manager.h"
#include "gnc-date-edit.h"
#include "qof.h"
#include "gnc-gconf-utils.h"
#include "gnc-ui.h"
#include "gnc-ui-util.h"
#include "gnc-dense-cal-store.h"
#include "gnc-dense-cal.h"
#include "gnc-engine.h"
#include "gnc-gconf-utils.h"
#include "gnc-ui-util.h"
#include "gnc-ui.h"
#include "qof.h"
#include "SchedXaction.h"
#include "SX-book.h"
#include "SX-ttinfo.h"
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#include <stdlib.h>
#define SX_GLADE_FILE "sched-xact.glade"
#define SXFTD_DIALOG_GLADE_NAME "sx_from_real_trans"
@ -78,11 +77,8 @@ static void gnc_sx_trans_window_response_cb(GtkDialog *dialog, gint response, gp
static void sxftd_destroy( GtkWidget *w, gpointer user_data );
typedef enum { NEVER_END, END_ON_DATE, END_AFTER_N_OCCS, BAD_END } endType;
typedef enum { FREQ_DAILY = 0, /* I know the =0 is redundant, but I'm using
* the numeric equivalences explicitly here
*/
* the numeric equivalences explicitly here */
FREQ_WEEKLY,
FREQ_BIWEEKLY,
FREQ_MONTHLY,
@ -97,11 +93,8 @@ typedef struct
Transaction *trans;
SchedXaction *sx;
GncDenseCalStore *dense_cal_model;
GncDenseCal *example_cal;
/** Storage for the maximum possible number of marks we could put on the
* calendar. */
GDate **cal_marks;
gint mark_id;
GNCDateEdit *startDateGDE, *endDateGDE;
@ -109,7 +102,7 @@ typedef struct
typedef struct
{
endType type;
gdcs_end_type type;
GDate end_date;
guint n_occurrences;
} getEndTuple;
@ -117,9 +110,6 @@ typedef struct
static void sxftd_update_example_cal( SXFromTransInfo *sxfti );
static void sxftd_update_excal_adapt( GObject *o, gpointer ud );
/* Stolen from jsled - nice and neat, actually (if a little light on
* for typechecking, but we'll be careful) . . .
*/
typedef struct
{
gchar *name;
@ -127,11 +117,9 @@ typedef struct
void (*handlerFn)();
} widgetSignalHandlerTuple;
static void sxftd_ok_clicked(SXFromTransInfo *sxfti);
static void sxftd_advanced_clicked(SXFromTransInfo *sxfti);
static void
sxfti_attach_callbacks(SXFromTransInfo *sxfti)
{
@ -144,7 +132,6 @@ sxfti_attach_callbacks(SXFromTransInfo *sxfti)
{ SXFTD_END_ON_DATE_BUTTON, "clicked", sxftd_update_excal_adapt },
{ SXFTD_N_OCCURRENCES_BUTTON, "clicked", sxftd_update_excal_adapt },
{ SXFTD_N_OCCURRENCES_ENTRY, "changed", sxftd_update_excal_adapt },
{ NULL, NULL, NULL }
};
@ -370,20 +357,19 @@ sxftd_init( SXFromTransInfo *sxfti )
/* Setup the example calendar and related data structures. */
{
int i;
int num_marks = SXFTD_EXCAL_NUM_MONTHS * 31;
w = GTK_WIDGET(glade_xml_get_widget( sxfti->gxml, SXFTD_EX_CAL_FRAME ));
sxfti->example_cal = GNC_DENSE_CAL(gnc_dense_cal_new());
sxfti->dense_cal_model = gnc_dense_cal_store_new(num_marks);
sxfti->example_cal = GNC_DENSE_CAL(gnc_dense_cal_new_with_model(GNC_DENSE_CAL_MODEL(sxfti->dense_cal_model)));
// gobject-2.10: g_object_ref_sink(sxfti->example_cal);
g_object_ref(G_OBJECT(sxfti->example_cal));
gtk_object_sink(GTK_OBJECT(sxfti->example_cal));
g_assert( sxfti->example_cal );
gnc_dense_cal_set_num_months( sxfti->example_cal, SXFTD_EXCAL_NUM_MONTHS );
gnc_dense_cal_set_months_per_col( sxfti->example_cal, SXFTD_EXCAL_MONTHS_PER_COL );
gtk_container_add( GTK_CONTAINER(w), GTK_WIDGET(sxfti->example_cal) );
sxfti->mark_id = -1;
sxfti->cal_marks = g_new0( GDate*, (SXFTD_EXCAL_NUM_MONTHS * 31) );
for ( i=0; i < SXFTD_EXCAL_NUM_MONTHS * 31; i++ ) {
sxfti->cal_marks[i] = g_date_new();
}
}
/* Setup the start and end dates as GNCDateEdits */
@ -551,7 +537,7 @@ static void
sxftd_ok_clicked(SXFromTransInfo *sxfti)
{
QofBook *book;
GList *sx_list;
SchedXactions *sxes;
guint sx_error = sxftd_compute_sx(sxfti);
if (sx_error != 0
@ -559,23 +545,14 @@ sxftd_ok_clicked(SXFromTransInfo *sxfti)
PERR( "Error in sxftd_compute_sx after ok_clicked [%d]", sx_error );
}
else {
SchedXactionDialog *sxd;
if ( sx_error == SXFTD_ERRNO_UNBALANCED_XACTION ) {
gnc_error_dialog( gnc_ui_get_toplevel(),
_( "The Scheduled Transaction is unbalanced. "
"You are strongly encouraged to correct this situation." ) );
}
book = gnc_get_current_book ();
sx_list = gnc_book_get_schedxactions(book);
sx_list = g_list_append(sx_list, sxfti->sx);
gnc_book_set_schedxactions(book, sx_list);
sxd = (SchedXactionDialog*)
gnc_find_first_gui_component(
DIALOG_SCHEDXACTION_CM_CLASS, NULL, NULL );
if ( sxd ) {
gnc_sxd_list_refresh( sxd );
}
sxes = gnc_book_get_schedxactions(book);
gnc_sxes_add_sx(sxes, sxfti->sx);
}
sxftd_close(sxfti, FALSE);
@ -617,8 +594,7 @@ static void
sxftd_advanced_clicked(SXFromTransInfo *sxfti)
{
guint sx_error = sxftd_compute_sx(sxfti);
SchedXactionDialog *adv_dlg;
SchedXactionEditorDialog *adv_edit_dlg;
GncSxEditorDialog *adv_edit_dlg;
GMainContext *context;
if ( sx_error != 0
@ -634,10 +610,8 @@ sxftd_advanced_clicked(SXFromTransInfo *sxfti)
context = g_main_context_default();
while (g_main_context_iteration(context, FALSE));
adv_dlg = gnc_ui_scheduled_xaction_dialog_create();
adv_edit_dlg =
gnc_ui_scheduled_xaction_editor_dialog_create(adv_dlg,
sxfti->sx,
gnc_ui_scheduled_xaction_editor_dialog_create(sxfti->sx,
TRUE /* newSX */);
/* close ourself, since advanced editing entails us, and there are sync
* issues otherwise. */
@ -647,28 +621,21 @@ sxftd_advanced_clicked(SXFromTransInfo *sxfti)
static void
sxftd_destroy( GtkWidget *w, gpointer user_data )
{
int i;
SXFromTransInfo *sxfti = (SXFromTransInfo*)user_data;
for ( i=0; i<SXFTD_EXCAL_NUM_MONTHS*31; i++ ) {
g_date_free( sxfti->cal_marks[i] );
}
g_free( sxfti->cal_marks );
if ( sxfti->sx ) {
xaccSchedXactionFree(sxfti->sx);
sxfti->sx = NULL;
}
g_object_unref(G_OBJECT(sxfti->dense_cal_model));
g_object_unref(G_OBJECT(sxfti->example_cal));
/* FIXME: do we need to clean up the GladeXML pointer? */
g_free(sxfti);
}
/**
*
**/
static void
gnc_sx_trans_window_response_cb (GtkDialog *dialog,
gint response,
@ -696,7 +663,6 @@ gnc_sx_trans_window_response_cb (GtkDialog *dialog,
LEAVE(" ");
}
/**
* Update the example calendar; make sure to take into account the end
* specification.
@ -707,11 +673,8 @@ sxftd_update_example_cal( SXFromTransInfo *sxfti )
struct tm *tmpTm;
time_t tmp_tt;
GDate date, startDate;
unsigned int i;
FreqSpec *fs;
getEndTuple get;
gchar *name;
GString *info;
fs = xaccFreqSpecMalloc( gnc_get_current_book() );
get = sxftd_get_end_info( sxfti );
@ -733,42 +696,26 @@ sxftd_update_example_cal( SXFromTransInfo *sxfti )
xaccFreqSpecGetNextInstance( fs, &date, &date );
startDate = date;
i = 0;
while ( (i < (SXFTD_EXCAL_NUM_MONTHS * 31))
&& g_date_valid( &date )
/* Do checking against end restriction. */
&& ( ( get.type == NEVER_END )
|| ( get.type == END_ON_DATE
&& g_date_compare( &date, &(get.end_date) ) <= 0 )
|| ( get.type == END_AFTER_N_OCCS
&& i < get.n_occurrences ) ) ) {
switch (get.type)
{
case NEVER_END:
gnc_dense_cal_store_update_no_end(sxfti->dense_cal_model, &startDate, fs);
break;
case END_ON_DATE:
gnc_dense_cal_store_update_date_end(sxfti->dense_cal_model, &startDate, fs, &get.end_date);
break;
case END_AFTER_N_OCCS:
gnc_dense_cal_store_update_count_end(sxfti->dense_cal_model, &startDate, fs, get.n_occurrences);
break;
default:
printf("unknown get.type [%d]\n", get.type);
break;
}
*sxfti->cal_marks[i++] = date;
xaccFreqSpecGetNextInstance( fs, &date, &date );
}
/* remove old marks */
if ( sxfti->mark_id != -1 ) {
gnc_dense_cal_mark_remove( sxfti->example_cal, sxfti->mark_id );
sxfti->mark_id = -1;
}
if ( i > 0 ) {
GtkWidget *w;
gnc_dense_cal_set_month( sxfti->example_cal,
g_date_get_month( &startDate ) );
gnc_dense_cal_set_year( sxfti->example_cal,
g_date_get_year( &startDate ) );
w = glade_xml_get_widget( sxfti->gxml, SXFTD_NAME_ENTRY );
name = gtk_editable_get_chars( GTK_EDITABLE(w), 0, -1 );
info = g_string_sized_new( 16 );
xaccFreqSpecGetFreqStr( fs, info );
sxfti->mark_id =
gnc_dense_cal_mark( sxfti->example_cal,
i, sxfti->cal_marks,
name, info->str );
gtk_widget_queue_draw( GTK_WIDGET(sxfti->example_cal) );
g_free( name );
g_string_free( info, TRUE );
}
gnc_dense_cal_set_month( sxfti->example_cal,
g_date_get_month( &startDate ) );
gnc_dense_cal_set_year( sxfti->example_cal,
g_date_get_year( &startDate ) );
xaccFreqSpecFree( fs );
}
@ -784,10 +731,6 @@ sxftd_update_excal_adapt( GObject *o, gpointer ud )
sxftd_update_example_cal( sxfti );
}
/**
*
**/
void
gnc_sx_create_from_trans( Transaction *trans )
{

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,52 @@
/********************************************************************\
* dialog-sx-since-last-run.h : dialog for scheduled transaction *
* since-last-run processing. *
* Copyright (C) 2006 Joshua Sled <jsled@asynchronous.org> *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of version 2 of the GNU General Public *
* License as published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License*
* along with this program; if not, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
* Boston, MA 02110-1301, USA gnu@gnu.org *
\********************************************************************/
#ifndef DIALOG_SX_SINCE_LAST_RUN_H
#define DIALOG_SX_SINCE_LAST_RUN_H
#include "config.h"
#include <gtk/gtk.h>
#include "gnc-sx-instance-model.h"
#include "gnc-plugin-page-sx-list.h"
typedef struct _GncSxSlrTreeModelAdapter GncSxSlrTreeModelAdapter;
typedef struct _GncSxSinceLastRunDialog GncSxSinceLastRunDialog;
/**
* This encapsulates the "run when file opened" application logic. As such,
* it should probably move to a non-ui file.
**/
void gnc_sx_sxsincelast_book_opened(void);
/**
* Create the since-last-run dialog.
**/
GncSxSinceLastRunDialog* gnc_ui_sx_since_last_run_dialog(GncSxInstanceModel *sx_instances);
// eliminate...
void gnc_ui_sxsincelast_dialog_create(void);
//void gnc_sx_slr_model_effect_change(GncSxSlrTreeModelAdapter *model, gboolean auto_create_only, GList **created_transaction_guids, GList **creation_errors);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,60 +0,0 @@
/********************************************************************\
* dialog-sxsincelast.h - SchedXaction "Since-Last-Run" dialog *
* Copyright (c) 2001 Joshua Sled <jsled@asynchronous.org> *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation; either version 2 of *
* the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License*
* along with this program; if not, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
* Boston, MA 02110-1301, USA gnu@gnu.org *
\********************************************************************/
#ifndef DIALOG_SXSINCELAST_H
#define DIALOG_SXSINCELAST_H
/**
* @return The magnitude of the return value is the number of auto-created,
* no-notification scheduled transactions created. This value is positive if
* there are additionally other SXes which need user interaction and the
* Druid has been displayed, or negative if there are not, and no Druid
* window was realized. In the case where there the dialog has been
* displayed but no auto-create-no-notify transactions have been created,
* INT_MAX [limits.h] is returned. 0 is treated as negative, with no
* transactions created and no dialog displayed. The caller can use this
* value as appropriate to inform the user.
*
* [e.g., for book-open-hook: do nothing; for menu-selection: display an info
* dialog stating there's nothing to do.]
**/
gint gnc_ui_sxsincelast_dialog_create( void );
void gnc_sx_sxsincelast_book_opened (void);
/**
* Returns the varaibles from the Splits of the given SchedXaction as the
* keys of the given GHashTable.
**/
void sxsl_get_sx_vars( SchedXaction *sx, GHashTable *varHash );
/**
* Returns the variables from the given formula [free-form non-numeric
* character strings] as the keys of the given GHashTable.
* @param result can be NULL if you're not interested in the result
**/
int parse_vars_from_formula( const char *formula,
GHashTable *varHash,
gnc_numeric *result );
void print_vars_helper( gpointer key, gpointer value, gpointer user_data );
#endif // !defined(DIALOG_SXSINCELAST_H)

View File

@ -35,7 +35,6 @@
#include "SchedXaction.h"
#include "SX-book.h"
#include "SX-book-p.h"
#include "SX-ttinfo.h"
#include "druid-utils.h"
#include "gnc-book.h"
@ -1982,7 +1981,8 @@ void
ld_create_sx_from_tcSX( LoanDruidData *ldd, toCreateSX *tcSX )
{
SchedXaction *sx;
GList *ttxnList, *sxList;
SchedXactions *sxes;
GList *ttxnList;
sx = xaccSchedXactionMalloc( gnc_get_current_book() );
xaccSchedXactionSetName( sx, tcSX->name );
@ -2003,9 +2003,8 @@ ld_create_sx_from_tcSX( LoanDruidData *ldd, toCreateSX *tcSX )
xaccSchedXactionSetTemplateTrans( sx, ttxnList,
gnc_get_current_book() );
sxList = gnc_book_get_schedxactions( gnc_get_current_book() );
sxList = g_list_append( sxList, sx );
gnc_book_set_schedxactions( gnc_get_current_book(), sxList );
sxes = gnc_book_get_schedxactions(gnc_get_current_book());
gnc_sxes_add_sx(sxes, sx);
g_list_free( ttxnList );
ttxnList = NULL;

View File

@ -77,7 +77,7 @@
</child>
<child>
<widget class="GtkVBox" id="vbox105">
<widget class="GtkVBox" id="editor-vbox">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
@ -2983,360 +2983,6 @@ December</property>
</child>
</widget>
<widget class="GtkDialog" id="Scheduled Transaction List">
<property name="visible">True</property>
<property name="title" translatable="yes">Scheduled Transactions</property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
<property name="window_position">GTK_WIN_POS_NONE</property>
<property name="modal">False</property>
<property name="default_width">640</property>
<property name="default_height">480</property>
<property name="resizable">True</property>
<property name="destroy_with_parent">False</property>
<property name="decorated">True</property>
<property name="skip_taskbar_hint">False</property>
<property name="skip_pager_hint">False</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
<property name="has_separator">True</property>
<child internal-child="vbox">
<widget class="GtkVBox" id="dialog-vbox18">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">8</property>
<child internal-child="action_area">
<widget class="GtkHButtonBox" id="dialog-action_area18">
<property name="visible">True</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
<widget class="GtkButton" id="close_button">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-close</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<property name="response_id">0</property>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">GTK_PACK_END</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label847992">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;b&gt;Transactions&lt;/b&gt;</property>
<property name="use_underline">False</property>
<property name="use_markup">True</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkAlignment" id="alignment34">
<property name="visible">True</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xscale">1</property>
<property name="yscale">1</property>
<property name="top_padding">0</property>
<property name="bottom_padding">0</property>
<property name="left_padding">12</property>
<property name="right_padding">0</property>
<child>
<widget class="GtkHBox" id="hbox123">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">12</property>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow1">
<property name="visible">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
<widget class="GtkCList" id="sched_xact_list">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="n_columns">3</property>
<property name="column_widths">127,140,80</property>
<property name="selection_mode">GTK_SELECTION_SINGLE</property>
<property name="show_titles">True</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<child>
<widget class="GtkLabel" id="label847750">
<property name="label" translatable="yes">Name</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_CENTER</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label847751">
<property name="label" translatable="yes">Frequency</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_CENTER</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label847752">
<property name="label" translatable="yes">Next Occurrence</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_CENTER</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkVButtonBox" id="vbuttonbox1">
<property name="visible">True</property>
<property name="layout_style">GTK_BUTTONBOX_SPREAD</property>
<property name="spacing">10</property>
<child>
<widget class="GtkButton" id="new_button">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-new</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
</widget>
</child>
<child>
<widget class="GtkButton" id="edit_button">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<child>
<widget class="GtkAlignment" id="alignment24">
<property name="visible">True</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xscale">0</property>
<property name="yscale">0</property>
<property name="top_padding">0</property>
<property name="bottom_padding">0</property>
<property name="left_padding">0</property>
<property name="right_padding">0</property>
<child>
<widget class="GtkHBox" id="hbox179">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">2</property>
<child>
<widget class="GtkImage" id="image1">
<property name="visible">True</property>
<property name="stock">gtk-properties</property>
<property name="icon_size">4</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label847980">
<property name="visible">True</property>
<property name="label" translatable="yes">_Edit</property>
<property name="use_underline">True</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkButton" id="delete_button">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-delete</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label847993">
<property name="visible">True</property>
<property name="label" translatable="yes"></property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label847970">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;b&gt;Upcoming&lt;/b&gt;</property>
<property name="use_underline">False</property>
<property name="use_markup">True</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkAlignment" id="alignment35">
<property name="visible">True</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xscale">1</property>
<property name="yscale">1</property>
<property name="top_padding">0</property>
<property name="bottom_padding">0</property>
<property name="left_padding">12</property>
<property name="right_padding">0</property>
<child>
<widget class="GtkHBox" id="upcoming_cal_hbox">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<child>
<placeholder/>
</child>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
</child>
</widget>
<widget class="GtkDialog" id="sx_from_real_trans">
<property name="visible">True</property>
<property name="title" translatable="yes">Make Scheduled Transaction</property>
@ -6717,4 +6363,364 @@ Custom</property>
</child>
</widget>
<widget class="GtkWindow" id="sx list plugin page content">
<property name="visible">True</property>
<property name="title" translatable="yes">window1</property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
<property name="window_position">GTK_WIN_POS_NONE</property>
<property name="modal">False</property>
<property name="resizable">True</property>
<property name="destroy_with_parent">False</property>
<property name="decorated">True</property>
<property name="skip_taskbar_hint">False</property>
<property name="skip_pager_hint">False</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
<child>
<widget class="GtkVPaned" id="sx-list-vbox">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="position">0</property>
<child>
<widget class="GtkVBox" id="vbox183">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<child>
<widget class="GtkLabel" id="label847992">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;b&gt;Transactions&lt;/b&gt;</property>
<property name="use_underline">False</property>
<property name="use_markup">True</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkAlignment" id="alignment34">
<property name="visible">True</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xscale">1</property>
<property name="yscale">1</property>
<property name="top_padding">0</property>
<property name="bottom_padding">0</property>
<property name="left_padding">12</property>
<property name="right_padding">0</property>
<child>
<widget class="GtkHBox" id="hbox123">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">12</property>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow1">
<property name="visible">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
<widget class="GtkTreeView" id="sx_list">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">True</property>
<property name="rules_hint">False</property>
<property name="reorderable">True</property>
<property name="enable_search">True</property>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
<packing>
<property name="shrink">True</property>
<property name="resize">False</property>
</packing>
</child>
<child>
<widget class="GtkVBox" id="upcoming mumble">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<child>
<widget class="GtkLabel" id="label847993">
<property name="visible">True</property>
<property name="label" translatable="yes"></property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label847970">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;b&gt;Upcoming&lt;/b&gt;</property>
<property name="use_underline">False</property>
<property name="use_markup">True</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkAlignment" id="alignment35">
<property name="visible">True</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xscale">1</property>
<property name="yscale">1</property>
<property name="top_padding">0</property>
<property name="bottom_padding">0</property>
<property name="left_padding">12</property>
<property name="right_padding">0</property>
<child>
<widget class="GtkHBox" id="upcoming_cal_hbox">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<child>
<placeholder/>
</child>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
<packing>
<property name="shrink">True</property>
<property name="resize">True</property>
</packing>
</child>
</widget>
</child>
</widget>
<widget class="GtkDialog" id="since-last-run-dialog">
<property name="visible">True</property>
<property name="title" translatable="yes">Since Last Run...</property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
<property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
<property name="modal">False</property>
<property name="default_width">640</property>
<property name="default_height">480</property>
<property name="resizable">True</property>
<property name="destroy_with_parent">True</property>
<property name="decorated">True</property>
<property name="skip_taskbar_hint">False</property>
<property name="skip_pager_hint">False</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
<property name="has_separator">True</property>
<child internal-child="vbox">
<widget class="GtkVBox" id="dialog-vbox25">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<child internal-child="action_area">
<widget class="GtkHButtonBox" id="dialog-action_area25">
<property name="visible">True</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
<widget class="GtkButton" id="cancelbutton1">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="has_default">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-cancel</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<property name="response_id">-6</property>
</widget>
</child>
<child>
<widget class="GtkButton" id="okbutton2">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-ok</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<property name="response_id">-5</property>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">GTK_PACK_END</property>
</packing>
</child>
<child>
<widget class="GtkVBox" id="vbox182">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<child>
<widget class="GtkVPaned" id="paned">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="position">240</property>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow21">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_ALWAYS</property>
<property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
<widget class="GtkTreeView" id="instance_view">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="has_focus">True</property>
<property name="headers_visible">True</property>
<property name="rules_hint">False</property>
<property name="reorderable">False</property>
<property name="enable_search">True</property>
</widget>
</child>
</widget>
<packing>
<property name="shrink">True</property>
<property name="resize">False</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox179">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<child>
<widget class="GtkFixed" id="fixed1">
<property name="visible">True</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkCheckButton" id="review_txn_toggle">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">_Review created transactions</property>
<property name="use_underline">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<property name="active">False</property>
<property name="inconsistent">False</property>
<property name="draw_indicator">True</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View File

@ -41,8 +41,7 @@
#include "dialog-chart-export.h"
#include "dialog-fincalc.h"
#include "dialog-find-transactions.h"
#include "dialog-scheduledxaction.h"
#include "dialog-sxsincelast.h"
#include "dialog-sx-since-last-run.h"
#include "dialog-totd.h"
#include "druid-acct-period.h"
#include "druid-loan.h"
@ -55,6 +54,8 @@
#include "gnc-window.h"
#include "gnc-session.h"
#include "gnc-plugin-page-sx-list.h"
/* This static indicates the debugging module that this .o belongs to. */
static QofLogModule log_module = GNC_MOD_GUI;
@ -434,35 +435,50 @@ gnc_main_window_cmd_edit_tax_options (GtkAction *action, GncMainWindowActionData
static void
gnc_main_window_cmd_actions_scheduled_transaction_editor (GtkAction *action, GncMainWindowActionData *data)
{
gnc_ui_scheduled_xaction_dialog_create ();
GncPluginPage *page = gnc_plugin_page_sx_list_new();
gnc_main_window_open_page(NULL, page);
}
static void
gnc_main_window_cmd_actions_since_last_run (GtkAction *action, GncMainWindowActionData *data)
{
GncMainWindow *window;
gint ret;
GncSxInstanceModel *sx_instances;
GncSxSummary summary;
const char *nothing_to_do_msg =
_( "There are no Scheduled Transactions to be entered at this time." );
g_return_if_fail (data != NULL);
window = data->window;
ret = gnc_ui_sxsincelast_dialog_create ();
if ( ret == 0 ) {
gnc_info_dialog (GTK_WIDGET(&window->gtk_window), nothing_to_do_msg);
} else if ( ret < 0 ) {
gnc_info_dialog (GTK_WIDGET(&window->gtk_window), ngettext
/* Translators: %d is the number of transactions. This is a
ngettext(3) message. */
("There are no Scheduled Transactions to be entered at this time. "
"(%d transaction automatically created)",
"There are no Scheduled Transactions to be entered at this time. "
"(%d transactions automatically created)",
-(ret)),
-(ret));
} /* else { this else [>0 means dialog was created] intentionally left
* blank. } */
sx_instances = gnc_sx_get_current_instances();
gnc_sx_instance_model_summarize(sx_instances, &summary);
gnc_sx_instance_model_effect_change(sx_instances, TRUE, NULL, NULL);
if (summary.need_dialog)
{
gnc_ui_sx_since_last_run_dialog(sx_instances);
}
else
{
if (summary.num_auto_create_no_notify_instances == 0)
{
gnc_info_dialog(GTK_WIDGET(&window->gtk_window), nothing_to_do_msg);
}
else
{
gnc_info_dialog(GTK_WIDGET(&window->gtk_window), ngettext
/* Translators: %d is the number of transactions. This is a
ngettext(3) message. */
("There are no Scheduled Transactions to be entered at this time. "
"(%d transaction automatically created)",
"There are no Scheduled Transactions to be entered at this time. "
"(%d transactions automatically created)",
summary.num_auto_create_no_notify_instances),
summary.num_auto_create_no_notify_instances);
}
}
g_object_unref(G_OBJECT(sx_instances));
}
static void

View File

@ -0,0 +1,628 @@
/*
* gnc-plugin-page-sx-list.c
*
* Copyright (C) 2006 Josh Sled <jsled@asynchronous.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact:
*
* Free Software Foundation Voice: +1-617-542-5942
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
* Boston, MA 02110-1301, USA gnu@gnu.org
*/
/* todo:
* - ui, actions, menus
* - icon
*/
/** @addtogroup ContentPlugins
@{ */
/** @addtogroup GncPluginPageSxList A SX List Plugin Page
@{ */
/** @brief Functions providing the SX List as a plugin page.
@author Josh Sled <jsled@asynchronous.org>
*/
#include "config.h"
#include <gtk/gtk.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <glade/glade-xml.h>
#ifndef HAVE_GLIB26
#include "gkeyfile.h"
#endif
#include "gnc-exp-parser.h"
#include "gnc-engine.h"
#include "Transaction.h"
#include "Split.h"
#include "gnc-commodity.h"
#include "gnc-event.h"
#include "gnc-dense-cal.h"
#include "gnc-glib-utils.h"
#include "gnc-icons.h"
#include "gnc-plugin-page-sx-list.h"
#include "gnc-sx-instance-model.h"
#include "gnc-sx-instance-dense-cal-adapter.h"
#include "gnc-sx-list-tree-model-adapter.h"
#include "gnc-ui-util.h"
#include "gnc-main-window.h"
#include "dialog-utils.h"
#include "gnc-component-manager.h"
#include "SX-book.h"
#include "gnc-book.h"
#include "dialog-sx-editor.h"
/* This static indicates the debugging module that this .o belongs to. */
static QofLogModule log_module = GNC_MOD_GUI;
#define PLUGIN_PAGE_SX_LIST_CM_CLASS "plugin-page-sx-list"
#define GCONF_SECTION "window/pages/sx_list"
typedef struct GncPluginPageSxListPrivate
{
gboolean disposed;
GtkWidget* widget;
gint gnc_component_id;
GladeXML* gxml;
GncSxInstanceDenseCalAdapter *dense_cal_model;
GncDenseCal* gdcal;
GncSxInstanceModel* instances;
GncSxListTreeModelAdapter* tree_model;
GtkTreeView* tree_view;
} GncPluginPageSxListPrivate;
#define GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), GNC_TYPE_PLUGIN_PAGE_SX_LIST, GncPluginPageSxListPrivate))
static GObjectClass *parent_class = NULL;
/************************************************************
* Prototypes *
************************************************************/
/* Plugin Actions */
static void gnc_plugin_page_sx_list_class_init (GncPluginPageSxListClass *klass);
static void gnc_plugin_page_sx_list_init (GncPluginPageSxList *plugin_page);
static void gnc_plugin_page_sx_list_dispose(GObject *object);
static void gnc_plugin_page_sx_list_finalize(GObject *object);
static GtkWidget *gnc_plugin_page_sx_list_create_widget (GncPluginPage *plugin_page);
static void gnc_plugin_page_sx_list_destroy_widget (GncPluginPage *plugin_page);
static void gnc_plugin_page_sx_list_save_page (GncPluginPage *plugin_page, GKeyFile *file, const gchar *group);
static GncPluginPage *gnc_plugin_page_sx_list_recreate_page (GtkWidget *window, GKeyFile *file, const gchar *group);
static void gppsl_row_activated_cb(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data);
/* Callbacks */
static void gnc_plugin_page_sx_list_cmd_new(GtkAction *action, GncPluginPageSxList *page);
static void gnc_plugin_page_sx_list_cmd_edit(GtkAction *action, GncPluginPageSxList *page);
static void gnc_plugin_page_sx_list_cmd_delete(GtkAction *action, GncPluginPageSxList *page);
/* Command callbacks */
static GtkActionEntry gnc_plugin_page_sx_list_actions [] = {
{ "SxListAction", NULL, N_("Scheduled"), NULL, NULL, NULL },
{ "SxListNewAction", GNC_STOCK_NEW_ACCOUNT, N_("New"), NULL,
N_("Create a new scheduled transaction"), G_CALLBACK(gnc_plugin_page_sx_list_cmd_new) },
{ "SxListEditAction", GNC_STOCK_EDIT_ACCOUNT, N_("Edit"), NULL,
N_("Edit the selected scheduled transaction"), G_CALLBACK(gnc_plugin_page_sx_list_cmd_edit) },
{ "SxListDeleteAction", GNC_STOCK_DELETE_ACCOUNT, N_("Delete"), NULL,
N_("Delete the selected scheduled transaction"), G_CALLBACK(gnc_plugin_page_sx_list_cmd_delete) },
};
/** The number of actions provided by this plugin. */
static guint gnc_plugin_page_sx_list_n_actions = G_N_ELEMENTS (gnc_plugin_page_sx_list_actions);
GType
gnc_plugin_page_sx_list_get_type (void)
{
static GType gnc_plugin_page_sx_list_type = 0;
if (gnc_plugin_page_sx_list_type == 0) {
static const GTypeInfo our_info = {
sizeof (GncPluginPageSxListClass),
NULL,
NULL,
(GClassInitFunc) gnc_plugin_page_sx_list_class_init,
NULL,
NULL,
sizeof (GncPluginPageSxList),
0,
(GInstanceInitFunc) gnc_plugin_page_sx_list_init
};
gnc_plugin_page_sx_list_type = g_type_register_static (GNC_TYPE_PLUGIN_PAGE,
GNC_PLUGIN_PAGE_SX_LIST_NAME,
&our_info, 0);
}
return gnc_plugin_page_sx_list_type;
}
GncPluginPage *
gnc_plugin_page_sx_list_new (void)
{
GncPluginPageSxList *plugin_page;
plugin_page = g_object_new(GNC_TYPE_PLUGIN_PAGE_SX_LIST, NULL);
return GNC_PLUGIN_PAGE(plugin_page);
}
static void
gnc_plugin_page_sx_list_class_init (GncPluginPageSxListClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS(klass);
GncPluginPageClass *gnc_plugin_class = GNC_PLUGIN_PAGE_CLASS(klass);
parent_class = g_type_class_peek_parent(klass);
object_class->dispose = gnc_plugin_page_sx_list_dispose;
object_class->finalize = gnc_plugin_page_sx_list_finalize;
gnc_plugin_class->tab_icon = GNC_STOCK_ACCOUNT;
gnc_plugin_class->plugin_name = GNC_PLUGIN_PAGE_SX_LIST_NAME;
gnc_plugin_class->create_widget = gnc_plugin_page_sx_list_create_widget;
gnc_plugin_class->destroy_widget = gnc_plugin_page_sx_list_destroy_widget;
gnc_plugin_class->save_page = gnc_plugin_page_sx_list_save_page;
gnc_plugin_class->recreate_page = gnc_plugin_page_sx_list_recreate_page;
g_type_class_add_private(klass, sizeof(GncPluginPageSxListPrivate));
}
static void
gnc_plugin_page_sx_list_init (GncPluginPageSxList *plugin_page)
{
GtkActionGroup *action_group;
GncPluginPageSxListPrivate *priv;
GncPluginPage *parent;
ENTER("page %p", plugin_page);
priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(plugin_page);
/* Init parent declared variables */
parent = GNC_PLUGIN_PAGE(plugin_page);
g_object_set(G_OBJECT(plugin_page),
"page-name", _("Scheduled Transactions"),
"page-uri", "default:",
"ui-description", "gnc-plugin-page-sx-list-ui.xml",
NULL);
/* change me when the system supports multiple books */
gnc_plugin_page_add_book(parent, gnc_get_current_book());
/* Create menu and toolbar information */
action_group =
gnc_plugin_page_create_action_group(parent,
"GncPluginPageSxListActions");
gtk_action_group_add_actions(action_group,
gnc_plugin_page_sx_list_actions,
gnc_plugin_page_sx_list_n_actions,
plugin_page);
/* gnc_plugin_init_short_names (action_group, toolbar_labels); */
LEAVE("page %p, priv %p, action group %p",
plugin_page, priv, action_group);
}
static void
gnc_plugin_page_sx_list_dispose(GObject *object)
{
GncPluginPageSxList *page;
GncPluginPageSxListPrivate *priv;
ENTER("object %p", object);
page = GNC_PLUGIN_PAGE_SX_LIST (object);
g_return_if_fail(GNC_IS_PLUGIN_PAGE_SX_LIST (page));
priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
g_return_if_fail(priv != NULL);
g_return_if_fail(!priv->disposed);
priv->disposed = TRUE;
g_object_unref(G_OBJECT(priv->dense_cal_model));
priv->dense_cal_model = NULL;
gtk_widget_unref(GTK_WIDGET(priv->gdcal));
priv->gdcal = NULL;
g_object_unref(G_OBJECT(priv->instances));
priv->instances = NULL;
g_object_unref(G_OBJECT(priv->tree_model));
priv->tree_model = NULL;
G_OBJECT_CLASS (parent_class)->dispose(object);
LEAVE(" ");
}
static void
gnc_plugin_page_sx_list_finalize (GObject *object)
{
GncPluginPageSxList *page;
GncPluginPageSxListPrivate *priv;
ENTER("object %p", object);
page = GNC_PLUGIN_PAGE_SX_LIST (object);
g_return_if_fail(GNC_IS_PLUGIN_PAGE_SX_LIST (page));
priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
g_return_if_fail(priv != NULL);
// by virtue of being a g_type_instance_..._private, does the private
// data get freed somewhere else?
G_OBJECT_CLASS (parent_class)->finalize (object);
LEAVE(" ");
}
/* Virtual Functions */
static void
gnc_plugin_page_sx_list_refresh_cb (GHashTable *changes, gpointer user_data)
{
GncPluginPageSxList *page = user_data;
GncPluginPageSxListPrivate *priv;
g_return_if_fail(GNC_IS_PLUGIN_PAGE_SX_LIST(page));
/* We're only looking for forced updates here. */
if (changes)
return;
priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
gtk_widget_queue_draw(priv->widget);
}
static void
gnc_plugin_page_sx_list_close_cb (gpointer user_data)
{
GncPluginPage *plugin_page;
GncPluginPageSxList *page;
plugin_page = GNC_PLUGIN_PAGE(user_data);
page = GNC_PLUGIN_PAGE_SX_LIST (plugin_page);
gnc_main_window_close_page(plugin_page);
}
static void
gppsl_selection_changed_cb(GtkTreeSelection *selection, gpointer user_data)
{
GncPluginPage *page;
GtkAction *edit_action, *delete_action;
gboolean selection_state = TRUE;
page = GNC_PLUGIN_PAGE(user_data);
edit_action = gnc_plugin_page_get_action(page, "SxListEditAction");
delete_action = gnc_plugin_page_get_action(page, "SxListDeleteAction");
selection_state
= gtk_tree_selection_count_selected_rows(selection) == 0
? FALSE
: TRUE;
gtk_action_set_sensitive(edit_action, selection_state);
gtk_action_set_sensitive(delete_action, selection_state);
}
static GtkWidget *
gnc_plugin_page_sx_list_create_widget (GncPluginPage *plugin_page)
{
GncPluginPageSxList *page;
GncPluginPageSxListPrivate *priv;
ENTER("page %p", plugin_page);
page = GNC_PLUGIN_PAGE_SX_LIST(plugin_page);
priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
if (priv->widget != NULL) {
LEAVE("widget = %p", priv->widget);
return priv->widget;
}
priv->gxml = gnc_glade_xml_new("sched-xact.glade", "sx-list-vbox");
priv->widget = glade_xml_get_widget(priv->gxml, "sx-list-vbox");
{
//gint half_way;
// half_way = plugin_page->notebook_page->allocation.height * 0.5;
// fixme; get a real value:
gtk_paned_set_position(GTK_PANED(priv->widget), 160);
}
{
GDate end;
g_date_clear(&end, 1);
g_date_set_time_t(&end, time(NULL));
g_date_add_years(&end, 1);
priv->instances = GNC_SX_INSTANCE_MODEL(gnc_sx_get_instances(&end));
}
{
GtkAction *edit_action, *delete_action;
edit_action = gnc_plugin_page_get_action(GNC_PLUGIN_PAGE(page), "SxListEditAction");
delete_action = gnc_plugin_page_get_action(GNC_PLUGIN_PAGE(page), "SxListDeleteAction");
gtk_action_set_sensitive(edit_action, FALSE);
gtk_action_set_sensitive(delete_action, FALSE);
}
{
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
GtkTreeSelection *selection;
priv->tree_model = gnc_sx_list_tree_model_adapter_new(priv->instances);
priv->tree_view = GTK_TREE_VIEW(glade_xml_get_widget(priv->gxml, "sx_list"));
gtk_tree_view_set_model(priv->tree_view, GTK_TREE_MODEL(priv->tree_model));
gtk_tree_view_set_headers_clickable(priv->tree_view, TRUE);
renderer = gtk_cell_renderer_text_new();
column = gtk_tree_view_column_new_with_attributes("Name", renderer, "text", SXLTMA_COL_NAME, NULL);
gtk_tree_view_column_set_sort_column_id(column, SXLTMA_COL_NAME);
gtk_tree_view_append_column(priv->tree_view, column);
renderer = gtk_cell_renderer_text_new();
column = gtk_tree_view_column_new_with_attributes("Frequency", renderer, "text", SXLTMA_COL_FREQUENCY, NULL);
gtk_tree_view_column_set_sort_column_id(column, SXLTMA_COL_FREQUENCY);
gtk_tree_view_append_column(priv->tree_view, column);
renderer = gtk_cell_renderer_text_new();
column = gtk_tree_view_column_new_with_attributes("Last Occur", renderer, "text", SXLTMA_COL_LAST_OCCUR, NULL);
gtk_tree_view_column_set_sort_column_id(column, SXLTMA_COL_LAST_OCCUR);
gtk_tree_view_append_column(priv->tree_view, column);
renderer = gtk_cell_renderer_text_new();
column = gtk_tree_view_column_new_with_attributes("Next Occur", renderer, "text", SXLTMA_COL_NEXT_OCCUR, NULL);
gtk_tree_view_column_set_sort_column_id(column, SXLTMA_COL_NEXT_OCCUR);
gtk_tree_view_append_column(priv->tree_view, column);
selection = gtk_tree_view_get_selection(priv->tree_view);
gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
g_signal_connect(G_OBJECT(selection), "changed", (GCallback)gppsl_selection_changed_cb, (gpointer)page);
g_signal_connect(G_OBJECT(priv->tree_view), "row-activated", (GCallback)gppsl_row_activated_cb, (gpointer)page);
}
{
GtkWidget *w;
priv->dense_cal_model = gnc_sx_instance_dense_cal_adapter_new(GNC_SX_INSTANCE_MODEL(priv->instances));
priv->gdcal = GNC_DENSE_CAL(gnc_dense_cal_new_with_model(GNC_DENSE_CAL_MODEL(priv->dense_cal_model)));
// gobject-2.10: g_object_ref_sink(G_OBJECT(priv->gdcal));
g_object_ref(G_OBJECT(priv->gdcal));
gtk_object_sink(GTK_OBJECT(priv->gdcal));
gnc_dense_cal_set_months_per_col(priv->gdcal, 4);
gnc_dense_cal_set_num_months(priv->gdcal, 12);
w = glade_xml_get_widget(priv->gxml, "upcoming_cal_hbox");
gtk_container_add(GTK_CONTAINER(w), GTK_WIDGET(priv->gdcal));
gtk_widget_show_all(w);
}
priv->gnc_component_id = gnc_register_gui_component("plugin-page-sx-list",
gnc_plugin_page_sx_list_refresh_cb,
gnc_plugin_page_sx_list_close_cb,
page);
/* @@fixme */
/* gnc_restore_window_size(SX_LIST_GCONF_SECTION, GTK_WINDOW(priv->widget)); */
return priv->widget;
}
static void
gnc_plugin_page_sx_list_destroy_widget (GncPluginPage *plugin_page)
{
GncPluginPageSxList *page;
GncPluginPageSxListPrivate *priv;
ENTER("page %p", plugin_page);
page = GNC_PLUGIN_PAGE_SX_LIST (plugin_page);
priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
if (priv->widget) {
g_object_unref(G_OBJECT(priv->widget));
priv->widget = NULL;
}
if (priv->gnc_component_id) {
gnc_unregister_gui_component(priv->gnc_component_id);
priv->gnc_component_id = 0;
}
LEAVE("widget destroyed");
}
/**
* Save enough information about this page that it can be recreated next time
* the user starts gnucash.
* @param page The page to save.
* @param key_file A pointer to the GKeyFile data structure where the
* page information should be written.
* @param group_name The group name to use when saving data.
**/
static void
gnc_plugin_page_sx_list_save_page (GncPluginPage *plugin_page,
GKeyFile *key_file,
const gchar *group_name)
{
GncPluginPageSxList *page;
GncPluginPageSxListPrivate *priv;
g_return_if_fail(GNC_IS_PLUGIN_PAGE_SX_LIST(plugin_page));
g_return_if_fail(key_file != NULL);
g_return_if_fail(group_name != NULL);
ENTER("page %p, key_file %p, group_name %s", plugin_page, key_file,
group_name);
page = GNC_PLUGIN_PAGE_SX_LIST(plugin_page);
priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
#if 0
gnc_tree_view_account_save(GNC_TREE_VIEW_ACCOUNT(priv->tree_view),
&priv->fd, key_file, group_name);
#endif /* 0 */
LEAVE(" ");
}
/**
* Create a new sx list page based on the information saved during a previous
* instantiation of gnucash.
* @param window The window where this page should be installed.
* @param key_file A pointer to the GKeyFile data structure where the
* page information should be read.
* @param group_name The group name to use when restoring data.
**/
static GncPluginPage *
gnc_plugin_page_sx_list_recreate_page (GtkWidget *window,
GKeyFile *key_file,
const gchar *group_name)
{
GncPluginPageSxList *page;
GncPluginPageSxListPrivate *priv;
g_return_val_if_fail(key_file, NULL);
g_return_val_if_fail(group_name, NULL);
ENTER("key_file %p, group_name %s", key_file, group_name);
/* Create the new page. */
page = GNC_PLUGIN_PAGE_SX_LIST(gnc_plugin_page_sx_list_new());
priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
/* Install it now so we can them manipulate the created widget */
gnc_main_window_open_page(GNC_MAIN_WINDOW(window), GNC_PLUGIN_PAGE(page));
#if 0
gnc_tree_view_account_restore(GNC_TREE_VIEW_ACCOUNT(priv->tree_view),
&priv->fd, key_file, group_name);
#endif /* 0 */
LEAVE(" ");
return GNC_PLUGIN_PAGE(page);
}
/* Callbacks */
static SchedXaction*
_sx_for_path(gpointer data, gpointer user_data)
{
GtkTreeIter iter;
GncSxListTreeModelAdapter *model = GNC_SX_LIST_TREE_MODEL_ADAPTER(user_data);
gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, (GtkTreePath*)data);
return gnc_sx_list_tree_model_adapter_get_sx_instances(model, &iter)->sx;
}
static void
gnc_plugin_page_sx_list_cmd_new(GtkAction *action, GncPluginPageSxList *page)
{
FreqSpec *fs;
SchedXaction *new_sx;
gboolean new_sx_flag = TRUE;
new_sx = xaccSchedXactionMalloc(gnc_get_current_book());
/* Give decent initial FreqSpec for SX */
fs = xaccSchedXactionGetFreqSpec(new_sx);
{
GDate *now;
now = g_date_new();
g_date_set_time_t(now, time(NULL));
xaccFreqSpecSetMonthly(fs, now, 1);
xaccFreqSpecSetUIType(fs, UIFREQ_MONTHLY);
g_date_free(now);
}
gnc_ui_scheduled_xaction_editor_dialog_create(new_sx, new_sx_flag);
}
static void
_edit_sx(gpointer data, gpointer user_data)
{
gnc_ui_scheduled_xaction_editor_dialog_create((SchedXaction*)data, FALSE);
}
static void
gnc_plugin_page_sx_list_cmd_edit(GtkAction *action, GncPluginPageSxList *page)
{
GncPluginPageSxListPrivate *priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
GtkTreeSelection *selection;
GList *selected_paths, *to_edit;
GtkTreeModel *model;
selection = gtk_tree_view_get_selection(priv->tree_view);
selected_paths = gtk_tree_selection_get_selected_rows(selection, &model);
if (g_list_length(selected_paths) == 0)
{
PERR("no selection edit.");
return;
}
to_edit = gnc_g_list_map(selected_paths, (GncGMapFunc)_sx_for_path, model);
g_list_foreach(to_edit, (GFunc)_edit_sx, NULL);
g_list_free(to_edit);
g_list_foreach(selected_paths, (GFunc)gtk_tree_path_free, NULL);
g_list_free(selected_paths);
}
static void
gppsl_row_activated_cb(GtkTreeView *tree_view,
GtkTreePath *path,
GtkTreeViewColumn *column,
gpointer user_data)
{
GncPluginPageSxList *page = GNC_PLUGIN_PAGE_SX_LIST(user_data);
GncPluginPageSxListPrivate *priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
SchedXaction *sx = _sx_for_path(path, priv->tree_model);
gnc_ui_scheduled_xaction_editor_dialog_create(sx, FALSE);
}
static void
_destroy_sx(gpointer data, gpointer user_data)
{
SchedXactions *sxes;
SchedXaction *sx = (SchedXaction*)data;
GNCBook *book;
book = gnc_get_current_book();
sxes = gnc_book_get_schedxactions(book);
gnc_sxes_del_sx(sxes, sx);
xaccSchedXactionFree(sx);
}
static void
gnc_plugin_page_sx_list_cmd_delete(GtkAction *action, GncPluginPageSxList *page)
{
GncPluginPageSxListPrivate *priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
GtkTreeSelection *selection;
GList *selected_paths, *to_delete = NULL;
GtkTreeModel *model;
/* @@fixme -- add (suppressible?) confirmation dialog */
selection = gtk_tree_view_get_selection(priv->tree_view);
selected_paths = gtk_tree_selection_get_selected_rows(selection, &model);
if (g_list_length(selected_paths) == 0)
{
PERR("no selection for delete.");
return;
}
to_delete = gnc_g_list_map(selected_paths, (GncGMapFunc)_sx_for_path, model);
{
GList *list;
for (list = to_delete; list != NULL; list = list->next)
{
DEBUG("to-delete [%s]\n", xaccSchedXactionGetName((SchedXaction*)list->data));
}
}
g_list_foreach(to_delete, (GFunc)_destroy_sx, NULL);
g_list_free(to_delete);
g_list_foreach(selected_paths, (GFunc)gtk_tree_path_free, NULL);
g_list_free(selected_paths);
}
/** @} */
/** @} */

View File

@ -0,0 +1,81 @@
/*
* gnc-plugin-page-sx-list.h
*
* Copyright (C) 2006 Josh Sled <jsled@asynchronous.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact:
*
* Free Software Foundation Voice: +1-617-542-5942
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
* Boston, MA 02110-1301, USA gnu@gnu.org
*/
/** @addtogroup ContentPlugins
@{ */
/** @addtogroup GncPluginPageSxList An Plugin Page for the SX List.
@{ */
/** @brief Functions providing a list of scheduled transactions as a plugin page.
@author Josh Sled <jsled@asynchronous.org>
*/
#ifndef __GNC_PLUGIN_PAGE_SX_LIST_H
#define __GNC_PLUGIN_PAGE_SX_LIST_H
#include "config.h"
#include <glib/gi18n.h>
#include <gtk/gtkwindow.h>
#include "SchedXaction.h"
#include "gnc-plugin-page.h"
G_BEGIN_DECLS
/* type macros */
#define GNC_TYPE_PLUGIN_PAGE_SX_LIST (gnc_plugin_page_sx_list_get_type ())
#define GNC_PLUGIN_PAGE_SX_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNC_TYPE_PLUGIN_PAGE_SX_LIST, GncPluginPageSxList))
#define GNC_PLUGIN_PAGE_SX_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNC_TYPE_PLUGIN_PAGE_SX_LIST, GncPluginPageSxListClass))
#define GNC_IS_PLUGIN_PAGE_SX_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNC_TYPE_PLUGIN_PAGE_SX_LIST))
#define GNC_IS_PLUGIN_PAGE_SX_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNC_TYPE_PLUGIN_PAGE_SX_LIST))
#define GNC_PLUGIN_PAGE_SX_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNC_TYPE_PLUGIN_PAGE_SX_LIST, GncPluginPageSxListClass))
#define GNC_PLUGIN_PAGE_SX_LIST_NAME "GncPluginPageSxList"
/* typedefs & structures */
typedef struct
{
GncPluginPage gnc_plugin_page;
} GncPluginPageSxList;
typedef struct
{
GncPluginPageClass gnc_plugin_page;
} GncPluginPageSxListClass;
/* function prototypes */
/**
* Retrieve the type number for an "sx list" plugin page.
* @return The type number.
*/
GType gnc_plugin_page_sx_list_get_type(void);
/**
* @return The newly created plugin page.
**/
GncPluginPage *gnc_plugin_page_sx_list_new(void);
G_END_DECLS
#endif /* __GNC_PLUGIN_PAGE_SX_LIST_H */
/** @} */
/** @} */

View File

@ -5,7 +5,7 @@
* Copyright (C) 1998 Rob Browning <rlb@cs.utexas.edu> *
* Copyright (C) 1999-2000 Dave Peticolas <dave@krondo.com> *
* Copyright (C) 2001 Gnumatic, Inc. *
* Copyright (C) 2002 Joshua Sled <jsled@asynchronous.org> *
* Copyright (C) 2002,2006 Joshua Sled <jsled@asynchronous.org> *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
@ -37,7 +37,7 @@
#include "QueryNew.h"
#include "SX-book.h"
#include "dialog-account.h"
#include "dialog-scheduledxaction.h"
#include "dialog-sx-editor.h"
#include "dialog-sx-from-trans.h"
#include "gnc-component-manager.h"
#include "gnc-date-edit.h"
@ -1227,7 +1227,7 @@ gsr_default_schedule_handler( GNCSplitReg *gsr, gpointer data )
GList *sxElts;
/* Get the correct SX */
for ( sxElts = gnc_book_get_schedxactions( gnc_get_current_book() );
for ( sxElts = gnc_book_get_schedxactions(gnc_get_current_book())->sx_list;
(!theSX) && sxElts;
sxElts = sxElts->next ) {
SchedXaction *sx = (SchedXaction*)sxElts->data;
@ -1237,8 +1237,7 @@ gsr_default_schedule_handler( GNCSplitReg *gsr, gpointer data )
}
if ( theSX ) {
SchedXactionDialog *sxd = gnc_ui_scheduled_xaction_dialog_create();
gnc_ui_scheduled_xaction_editor_dialog_create( sxd, theSX, FALSE );
gnc_ui_scheduled_xaction_editor_dialog_create(theSX, FALSE);
return;
}
}

View File

@ -0,0 +1,600 @@
/*
* gnc-sx-list-tree-model-adapter.c
*
* Copyright (C) 2006 Josh Sled <jsled@asynchronous.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact:
*
* Free Software Foundation Voice: +1-617-542-5942
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
* Boston, MA 02110-1301, USA gnu@gnu.org
*/
#include "config.h"
#include <glib.h>
#include <glib-object.h>
#include "gnc-sx-instance-model.h"
#include "gnc-sx-list-tree-model-adapter.h"
#include <gtk/gtk.h>
struct _GncSxListTreeModelAdapter
{
GObject parent;
/* protected */
gboolean disposed;
GncSxInstanceModel *instances;
GtkTreeStore *orig;
GtkTreeModelSort *real;
};
struct _GncSxListTreeModelAdapterClass
{
GObjectClass parent;
};
static GObjectClass *parent_class = NULL;
static void gnc_sx_list_tree_model_adapter_class_init(GncSxListTreeModelAdapterClass *klass);
static void gsltma_tree_model_interface_init(gpointer g_iface, gpointer iface_data);
static void gsltma_tree_sortable_interface_init(gpointer g_iface, gpointer iface_data);
static void gnc_sx_list_tree_model_adapter_init(GTypeInstance *instance, gpointer klass);
static void gnc_sx_list_tree_model_adapter_dispose(GObject *obj);
static void gnc_sx_list_tree_model_adapter_finalize(GObject *obj);
static GncSxInstances* gsltma_get_sx_instances_from_orig_iter(GncSxListTreeModelAdapter *model, GtkTreeIter *orig_iter);
GType
gnc_sx_list_tree_model_adapter_get_type(void)
{
static GType type = 0;
if (type == 0) {
static const GTypeInfo info = {
sizeof (GncSxListTreeModelAdapterClass),
NULL, /* base_init */
NULL, /* base_finalize */
(GClassInitFunc)gnc_sx_list_tree_model_adapter_class_init, /* class_init */
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (GncSxListTreeModelAdapter),
0, /* n_preallocs */
(GInstanceInitFunc)gnc_sx_list_tree_model_adapter_init /* instance_init */
};
static const GInterfaceInfo itree_model_info = {
(GInterfaceInitFunc) gsltma_tree_model_interface_init, /* interface_init */
NULL, /* interface_finalize */
NULL /* interface_data */
};
static const GInterfaceInfo itree_sortable_info = {
(GInterfaceInitFunc) gsltma_tree_sortable_interface_init, /* interface_init */
NULL, /* interface_finalize */
NULL /* interface_data */
};
type = g_type_register_static (G_TYPE_OBJECT,
"GncSxListTreeModelAdapterType",
&info, 0);
g_type_add_interface_static(type,
GTK_TYPE_TREE_MODEL,
&itree_model_info);
g_type_add_interface_static(type,
GTK_TYPE_TREE_SORTABLE,
&itree_sortable_info);
}
return type;
}
static void
gnc_sx_list_tree_model_adapter_class_init(GncSxListTreeModelAdapterClass *klass)
{
GObjectClass *obj_class = G_OBJECT_CLASS(klass);
parent_class = g_type_class_peek_parent(klass);
obj_class->dispose = gnc_sx_list_tree_model_adapter_dispose;
obj_class->finalize = gnc_sx_list_tree_model_adapter_finalize;
}
static GtkTreeModelFlags
gsltma_get_flags(GtkTreeModel *tree_model)
{
return gtk_tree_model_get_flags(GTK_TREE_MODEL(GNC_SX_LIST_TREE_MODEL_ADAPTER(tree_model)->real));
}
static gint
gsltma_get_n_columns(GtkTreeModel *tree_model)
{
return gtk_tree_model_get_n_columns(GTK_TREE_MODEL(GNC_SX_LIST_TREE_MODEL_ADAPTER(tree_model)->real));
}
static GType
gsltma_get_column_type(GtkTreeModel *tree_model, gint index)
{
return gtk_tree_model_get_column_type(GTK_TREE_MODEL(GNC_SX_LIST_TREE_MODEL_ADAPTER(tree_model)->real), index);
}
static gboolean
gsltma_get_iter(GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreePath *path)
{
return gtk_tree_model_get_iter(GTK_TREE_MODEL(GNC_SX_LIST_TREE_MODEL_ADAPTER(tree_model)->real), iter, path);
}
static GtkTreePath*
gsltma_get_path(GtkTreeModel *tree_model,
GtkTreeIter *iter)
{
return gtk_tree_model_get_path(GTK_TREE_MODEL(GNC_SX_LIST_TREE_MODEL_ADAPTER(tree_model)->real), iter);
}
static void
gsltma_get_value(GtkTreeModel *tree_model,
GtkTreeIter *iter,
gint column,
GValue *value)
{
gtk_tree_model_get_value(GTK_TREE_MODEL(GNC_SX_LIST_TREE_MODEL_ADAPTER(tree_model)->real), iter, column, value);
}
static gboolean
gsltma_iter_next(GtkTreeModel *tree_model,
GtkTreeIter *iter)
{
return gtk_tree_model_iter_next(GTK_TREE_MODEL(GNC_SX_LIST_TREE_MODEL_ADAPTER(tree_model)->real), iter);
}
static gboolean
gsltma_iter_children(GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreeIter *parent)
{
return gtk_tree_model_iter_children(GTK_TREE_MODEL(GNC_SX_LIST_TREE_MODEL_ADAPTER(tree_model)->real), iter, parent);
}
static gboolean
gsltma_iter_has_child(GtkTreeModel *tree_model,
GtkTreeIter *iter)
{
return gtk_tree_model_iter_has_child(GTK_TREE_MODEL(GNC_SX_LIST_TREE_MODEL_ADAPTER(tree_model)->real), iter);
}
static gint
gsltma_iter_n_children(GtkTreeModel *tree_model,
GtkTreeIter *iter)
{
return gtk_tree_model_iter_n_children(GTK_TREE_MODEL(GNC_SX_LIST_TREE_MODEL_ADAPTER(tree_model)->real), iter);
}
static gboolean
gsltma_iter_nth_child(GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreeIter *parent,
gint n)
{
return gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(GNC_SX_LIST_TREE_MODEL_ADAPTER(tree_model)->real), iter, parent, n);
}
static gboolean
gsltma_iter_parent(GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreeIter *child)
{
return gtk_tree_model_iter_parent(GTK_TREE_MODEL(GNC_SX_LIST_TREE_MODEL_ADAPTER(tree_model)->real), iter, child);
}
static void
gsltma_ref_node(GtkTreeModel *tree_model,
GtkTreeIter *iter)
{
gtk_tree_model_ref_node(GTK_TREE_MODEL(GNC_SX_LIST_TREE_MODEL_ADAPTER(tree_model)->real), iter);
}
static void
gsltma_unref_node(GtkTreeModel *tree_model,
GtkTreeIter *iter)
{
gtk_tree_model_unref_node(GTK_TREE_MODEL(GNC_SX_LIST_TREE_MODEL_ADAPTER(tree_model)->real), iter);
}
static void
gsltma_tree_model_interface_init(gpointer g_iface, gpointer iface_data)
{
GtkTreeModelIface *tree_model = (GtkTreeModelIface*)g_iface;
tree_model->get_flags = gsltma_get_flags;
tree_model->get_n_columns = gsltma_get_n_columns;
tree_model->get_column_type = gsltma_get_column_type;
tree_model->get_iter = gsltma_get_iter;
tree_model->get_path = gsltma_get_path;
tree_model->get_value = gsltma_get_value;
tree_model->iter_next = gsltma_iter_next;
tree_model->iter_children = gsltma_iter_children;
tree_model->iter_has_child = gsltma_iter_has_child;
tree_model->iter_n_children = gsltma_iter_n_children;
tree_model->iter_nth_child = gsltma_iter_nth_child;
tree_model->iter_parent = gsltma_iter_parent;
tree_model->ref_node = gsltma_ref_node;
tree_model->unref_node = gsltma_unref_node;
}
static gboolean
gsltma_get_sort_column_id(GtkTreeSortable *sortable,
gint *sort_column_id,
GtkSortType *order)
{
return gtk_tree_sortable_get_sort_column_id(GTK_TREE_SORTABLE(GNC_SX_LIST_TREE_MODEL_ADAPTER(sortable)->real),
sort_column_id,
order);
}
static void
gsltma_set_sort_column_id(GtkTreeSortable *sortable,
gint sort_column_id,
GtkSortType order)
{
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(GNC_SX_LIST_TREE_MODEL_ADAPTER(sortable)->real),
sort_column_id,
order);
}
static void
gsltma_set_sort_func(GtkTreeSortable *sortable,
gint sort_column_id,
GtkTreeIterCompareFunc func,
gpointer data,
GtkDestroyNotify destroy)
{
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(GNC_SX_LIST_TREE_MODEL_ADAPTER(sortable)->real),
sort_column_id,
func,
data,
destroy);
}
static void
gsltma_set_default_sort_func(GtkTreeSortable *sortable,
GtkTreeIterCompareFunc func,
gpointer data,
GtkDestroyNotify destroy)
{
gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(GNC_SX_LIST_TREE_MODEL_ADAPTER(sortable)->real),
func, data, destroy);
}
static gboolean
gsltma_has_default_sort_func(GtkTreeSortable *sortable)
{
return gtk_tree_sortable_has_default_sort_func(GTK_TREE_SORTABLE(GNC_SX_LIST_TREE_MODEL_ADAPTER(sortable)->real));
}
static void
gsltma_tree_sortable_interface_init(gpointer g_iface, gpointer iface_data)
{
GtkTreeSortableIface *tree_sortable = (GtkTreeSortableIface*)g_iface;
tree_sortable->get_sort_column_id = gsltma_get_sort_column_id;
tree_sortable->set_sort_column_id = gsltma_set_sort_column_id;
tree_sortable->set_sort_func = gsltma_set_sort_func;
tree_sortable->set_default_sort_func = gsltma_set_default_sort_func;
tree_sortable->has_default_sort_func = gsltma_has_default_sort_func;
tree_sortable->get_sort_column_id = gsltma_get_sort_column_id;
tree_sortable->set_sort_column_id = gsltma_set_sort_column_id;
tree_sortable->set_sort_func = gsltma_set_sort_func;
tree_sortable->set_default_sort_func = gsltma_set_default_sort_func;
tree_sortable->has_default_sort_func = gsltma_has_default_sort_func;
}
static void
gsltma_proxy_row_changed(GtkTreeModel *treemodel,
GtkTreePath *arg1,
GtkTreeIter *arg2,
gpointer user_data)
{
g_signal_emit_by_name(user_data, "row-changed", arg1, arg2);
}
static void
gsltma_proxy_row_deleted(GtkTreeModel *treemodel,
GtkTreePath *arg1,
gpointer user_data)
{
g_signal_emit_by_name(user_data, "row-deleted", arg1);
}
static void
gsltma_proxy_row_has_child_toggled(GtkTreeModel *treemodel,
GtkTreePath *arg1,
GtkTreeIter *arg2,
gpointer user_data)
{
g_signal_emit_by_name(user_data, "row-has-child-toggled", arg1, arg2);
}
static void
gsltma_proxy_row_inserted(GtkTreeModel *treemodel,
GtkTreePath *arg1,
GtkTreeIter *arg2,
gpointer user_data)
{
g_signal_emit_by_name(user_data, "row-inserted", arg1, arg2);
}
static void
gsltma_proxy_rows_reordered(GtkTreeModel *treemodel,
GtkTreePath *arg1,
GtkTreeIter *arg2,
gpointer arg3,
gpointer user_data)
{
g_signal_emit_by_name(user_data, "rows-reordered", arg1, arg2, arg3);
}
static void
gsltma_proxy_sort_column_changed(GtkTreeSortable *sortable, gpointer user_data)
{
g_signal_emit_by_name(user_data, "sort-column-changed");
}
static gint
_name_comparator(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data)
{
gint rtn;
GncSxListTreeModelAdapter *adapter = GNC_SX_LIST_TREE_MODEL_ADAPTER(user_data);
GncSxInstances *a_inst, *b_inst;
gchar *a_caseless, *b_caseless;
a_inst = gsltma_get_sx_instances_from_orig_iter(adapter, a);
b_inst = gsltma_get_sx_instances_from_orig_iter(adapter, b);
if (a_inst == NULL && b_inst == NULL) return 0;
if (a_inst == NULL) return 1;
if (b_inst == NULL) return -1;
a_caseless = g_utf8_casefold(xaccSchedXactionGetName(a_inst->sx), -1);
b_caseless = g_utf8_casefold(xaccSchedXactionGetName(b_inst->sx), -1);
rtn = safe_strcmp(a_caseless, b_caseless);
g_free(a_caseless);
g_free(b_caseless);
return rtn;
}
static gint
_freq_comparator(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data)
{
GncSxListTreeModelAdapter *adapter = GNC_SX_LIST_TREE_MODEL_ADAPTER(user_data);
GncSxInstances *a_inst, *b_inst;
a_inst = gsltma_get_sx_instances_from_orig_iter(adapter, a);
b_inst = gsltma_get_sx_instances_from_orig_iter(adapter, b);
if (a_inst == NULL && b_inst == NULL) return 0;
if (a_inst == NULL) return 1;
if (b_inst == NULL) return -1;
return gnc_freq_spec_compare(xaccSchedXactionGetFreqSpec(a_inst->sx),
xaccSchedXactionGetFreqSpec(b_inst->sx));
}
static gint
_safe_invalidable_date_compare(GDate *a, GDate *b)
{
if (!g_date_valid(a) && !g_date_valid(b))
{
return 0;
}
if (!g_date_valid(a))
{
return 1;
}
if (!g_date_valid(b))
{
return -1;
}
return g_date_compare(a, b);
}
static gint
_last_occur_comparator(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data)
{
GncSxListTreeModelAdapter *adapter = GNC_SX_LIST_TREE_MODEL_ADAPTER(user_data);
GncSxInstances *a_inst, *b_inst;
a_inst = gsltma_get_sx_instances_from_orig_iter(adapter, a);
b_inst = gsltma_get_sx_instances_from_orig_iter(adapter, b);
return _safe_invalidable_date_compare(xaccSchedXactionGetLastOccurDate(a_inst->sx),
xaccSchedXactionGetLastOccurDate(b_inst->sx));
}
static gint
_next_occur_comparator(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data)
{
GncSxListTreeModelAdapter *adapter = GNC_SX_LIST_TREE_MODEL_ADAPTER(user_data);
GncSxInstances *a_inst, *b_inst;
a_inst = gsltma_get_sx_instances_from_orig_iter(adapter, a);
b_inst = gsltma_get_sx_instances_from_orig_iter(adapter, b);
return _safe_invalidable_date_compare(&a_inst->next_instance_date,
&b_inst->next_instance_date);
}
static void
gnc_sx_list_tree_model_adapter_init(GTypeInstance *instance, gpointer klass)
{
GncSxListTreeModelAdapter *adapter = GNC_SX_LIST_TREE_MODEL_ADAPTER(instance);
adapter->orig = gtk_tree_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
adapter->real = GTK_TREE_MODEL_SORT(gtk_tree_model_sort_new_with_model(GTK_TREE_MODEL(adapter->orig)));
// setup sorting
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(adapter->real), SXLTMA_COL_NAME, _name_comparator, adapter, NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(adapter->real), SXLTMA_COL_FREQUENCY, _freq_comparator, adapter, NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(adapter->real), SXLTMA_COL_LAST_OCCUR, _last_occur_comparator, adapter, NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(adapter->real), SXLTMA_COL_NEXT_OCCUR, _next_occur_comparator, adapter, NULL);
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(adapter->real), SXLTMA_COL_NEXT_OCCUR, GTK_SORT_ASCENDING);
g_signal_connect(adapter->real, "row-changed", G_CALLBACK(gsltma_proxy_row_changed), adapter);
g_signal_connect(adapter->real, "row-deleted", G_CALLBACK(gsltma_proxy_row_deleted), adapter);
g_signal_connect(adapter->real, "row-has-child-toggled", G_CALLBACK(gsltma_proxy_row_has_child_toggled), adapter);
g_signal_connect(adapter->real, "row-inserted", G_CALLBACK(gsltma_proxy_row_inserted), adapter);
g_signal_connect(adapter->real, "rows-reordered", G_CALLBACK(gsltma_proxy_rows_reordered), adapter);
g_signal_connect(adapter->real, "sort-column-changed", G_CALLBACK(gsltma_proxy_sort_column_changed), adapter);
}
static void
gsltma_populate_tree_store(GncSxListTreeModelAdapter *model)
{
GtkTreeIter iter;
GList *list;
for (list = model->instances->sx_instance_list; list != NULL; list = list->next)
{
GncSxInstances *instances = (GncSxInstances*)list->data;
FreqSpec *fs;
GString *frequency_str;
char last_occur_date_buf[MAX_DATE_LENGTH+1];
char next_occur_date_buf[MAX_DATE_LENGTH+1];
frequency_str = g_string_sized_new(32);
fs = xaccSchedXactionGetFreqSpec(instances->sx);
xaccFreqSpecGetFreqStr(fs, frequency_str);
{
GDate *last_occur = xaccSchedXactionGetLastOccurDate(instances->sx);
if (last_occur == NULL || !g_date_valid(last_occur))
{
g_stpcpy(last_occur_date_buf, "never");
}
else
{
qof_print_gdate(last_occur_date_buf,
MAX_DATE_LENGTH,
last_occur);
}
}
qof_print_gdate(next_occur_date_buf, MAX_DATE_LENGTH, &instances->next_instance_date);
gtk_tree_store_append(model->orig, &iter, NULL);
gtk_tree_store_set(model->orig, &iter,
SXLTMA_COL_NAME, xaccSchedXactionGetName(instances->sx),
SXLTMA_COL_FREQUENCY, frequency_str->str,
SXLTMA_COL_LAST_OCCUR, last_occur_date_buf,
SXLTMA_COL_NEXT_OCCUR, next_occur_date_buf,
-1);
g_string_free(frequency_str, TRUE);
}
}
static void
gsltma_added_cb(GncSxInstanceModel *instances, SchedXaction *sx_added, gpointer user_data)
{
GncSxListTreeModelAdapter *model = GNC_SX_LIST_TREE_MODEL_ADAPTER(user_data);
gtk_tree_store_clear(model->orig);
gsltma_populate_tree_store(model);
}
static void
gsltma_updated_cb(GncSxInstanceModel *instances, SchedXaction *sx_updated, gpointer user_data)
{
GncSxListTreeModelAdapter *model = GNC_SX_LIST_TREE_MODEL_ADAPTER(user_data);
gnc_sx_instance_model_update_sx_instances(instances, sx_updated);
gtk_tree_store_clear(model->orig);
gsltma_populate_tree_store(model);
}
static void
gsltma_removing_cb(GncSxInstanceModel *instances, SchedXaction *sx_removing, gpointer user_data)
{
GncSxListTreeModelAdapter *model = GNC_SX_LIST_TREE_MODEL_ADAPTER(user_data);
gnc_sx_instance_model_remove_sx_instances(instances, sx_removing);
gtk_tree_store_clear(model->orig);
gsltma_populate_tree_store(model);
}
GncSxListTreeModelAdapter*
gnc_sx_list_tree_model_adapter_new(GncSxInstanceModel *instances)
{
GncSxListTreeModelAdapter *rtn;
rtn = GNC_SX_LIST_TREE_MODEL_ADAPTER(g_object_new(GNC_TYPE_SX_LIST_TREE_MODEL_ADAPTER, NULL));
rtn->instances = instances;
g_object_ref(G_OBJECT(rtn->instances));
gsltma_populate_tree_store(rtn);
g_signal_connect(G_OBJECT(rtn->instances), "added", (GCallback)gsltma_added_cb, (gpointer)rtn);
g_signal_connect(G_OBJECT(rtn->instances), "updated", (GCallback)gsltma_updated_cb, (gpointer)rtn);
g_signal_connect(G_OBJECT(rtn->instances), "removing", (GCallback)gsltma_removing_cb, (gpointer)rtn);
return rtn;
}
GncSxInstances*
gsltma_get_sx_instances_from_orig_iter(GncSxListTreeModelAdapter *model, GtkTreeIter *orig_iter)
{
GtkTreePath *path;
gint *indices;
gint index;
path = gtk_tree_model_get_path(GTK_TREE_MODEL(model->orig), orig_iter);
if (gtk_tree_path_get_depth(path) > 1)
{
gtk_tree_path_free(path);
return NULL;
}
indices = gtk_tree_path_get_indices(path);
index = indices[0];
gtk_tree_path_free(path);
return (GncSxInstances*)g_list_nth_data(model->instances->sx_instance_list, index);
}
GncSxInstances*
gnc_sx_list_tree_model_adapter_get_sx_instances(GncSxListTreeModelAdapter *model, GtkTreeIter *sort_iter)
{
GtkTreeIter translated_iter;
gtk_tree_model_sort_convert_iter_to_child_iter(model->real,
&translated_iter,
sort_iter);
return gsltma_get_sx_instances_from_orig_iter(model, &translated_iter);
}
static void
gnc_sx_list_tree_model_adapter_dispose(GObject *obj)
{
GncSxListTreeModelAdapter *adapter;
g_return_if_fail(obj != NULL);
adapter = GNC_SX_LIST_TREE_MODEL_ADAPTER(obj);
g_return_if_fail(adapter->disposed);
adapter->disposed = TRUE;
g_object_unref(G_OBJECT(adapter->instances));
adapter->instances = NULL;
g_object_unref(G_OBJECT(adapter->real));
adapter->real = NULL;
g_object_unref(G_OBJECT(adapter->orig));
adapter->orig = NULL;
G_OBJECT_CLASS(parent_class)->dispose(obj);
}
static void
gnc_sx_list_tree_model_adapter_finalize(GObject *obj)
{
g_return_if_fail(obj != NULL);
G_OBJECT_CLASS(parent_class)->finalize(obj);
}

View File

@ -0,0 +1,59 @@
/*
* gnc-sx-list-tree-model-adapter.h
*
* Copyright (C) 2006 Josh Sled <jsled@asynchronous.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact:
*
* Free Software Foundation Voice: +1-617-542-5942
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
* Boston, MA 02110-1301, USA gnu@gnu.org
*/
#ifndef _GNC_SX_LIST_TREE_MODEL_ADAPTER_H
#define _GNC_SX_LIST_TREE_MODEL_ADAPTER_H
#include "config.h"
#include <glib.h>
#include <glib-object.h>
#include <gtk/gtk.h>
#include "gnc-sx-instance-model.h"
G_BEGIN_DECLS
#define GNC_TYPE_SX_LIST_TREE_MODEL_ADAPTER (gnc_sx_list_tree_model_adapter_get_type ())
#define GNC_SX_LIST_TREE_MODEL_ADAPTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNC_TYPE_SX_LIST_TREE_MODEL_ADAPTER, GncSxListTreeModelAdapter))
#define GNC_SX_LIST_TREE_MODEL_ADAPTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNC_TYPE_SX_LIST_TREE_MODEL_ADAPTER, GncSxListTreeModelAdapterClass))
#define GNC_IS_SX_LIST_TREE_MODEL_ADAPTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNC_TYPE_SX_LIST_TREE_MODEL_ADAPTER))
#define GNC_IS_SX_LIST_TREE_MODEL_ADAPTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNC_TYPE_SX_LIST_TREE_MODEL_ADAPTER))
#define GNC_SX_LIST_TREE_MODEL_ADAPTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNC_TYPE_SX_LIST_TREE_MODEL_ADAPTER, GncSxListTreeModelAdapterClass))
typedef struct _GncSxListTreeModelAdapter GncSxListTreeModelAdapter;
typedef struct _GncSxListTreeModelAdapterClass GncSxListTreeModelAdapterClass;
// model columns
enum {
SXLTMA_COL_NAME = 0,
SXLTMA_COL_FREQUENCY,
SXLTMA_COL_LAST_OCCUR,
SXLTMA_COL_NEXT_OCCUR
};
GType gnc_sx_list_tree_model_adapter_get_type(void);
GncSxListTreeModelAdapter* gnc_sx_list_tree_model_adapter_new(GncSxInstanceModel *instances);
GncSxInstances* gnc_sx_list_tree_model_adapter_get_sx_instances(GncSxListTreeModelAdapter *model, GtkTreeIter *iter);
G_END_DECLS
#endif // _GNC_SX_LIST_TREE_MODEL_ADAPTER_H

View File

@ -33,7 +33,7 @@
#include "dialog-account.h"
#include "dialog-commodity.h"
#include "dialog-options.h"
#include "dialog-scheduledxaction.h"
#include "dialog-sx-editor.h"
#include "dialog-transfer.h"
#include "dialog-totd.h"
#include "druid-hierarchy.h"

View File

@ -8,6 +8,7 @@ ui_DATA = \
gnc-plugin-file-history-ui.xml \
gnc-plugin-register-ui.xml \
gnc-plugin-page-register-ui.xml \
gnc-plugin-page-sx-list-ui.xml \
gnc-plugin-page-sxregister-ui.xml \
gnc-sxed-to-create-window-ui.xml \
gnc-reconcile-window-ui.xml \

View File

@ -0,0 +1,20 @@
<ui>
<menubar>
<placeholder name="AdditionalMenusPlaceholder">
<menu action="SxListAction">
<menuitem name="SxListNew" action="SxListNewAction"/>
<menuitem name="SxListEdit" action="SxListEditAction"/>
<menuitem name="SxListDelete" action="SxListDeleteAction"/>
</menu>
</placeholder>
</menubar>
<toolbar name="DefaultToolbar">
<placeholder name="DefaultToolbarPlaceholder">
<separator name="ToolbarSep2" />
<toolitem name="SxListToolbarNew" action="SxListNewAction"/>
<toolitem name="SxListToolbarEdit" action="SxListEditAction"/>
<toolitem name="SxListToolbarDelete" action="SxListDeleteAction"/>
</placeholder>
</toolbar>
</ui>