gpe-expenses
0.1.9
|
00001 /*************************************************************************** 00002 * expenses-gtk.c 00003 * 00004 * Mon Nov 21 22:56:18 2005 00005 * Copyright 2005 Neil Williams <linux@codehelp.co.uk> 00006 * Copyright (C) 2002, 2003, 2004 Philip Blundell <philb@gnu.org> 00007 * Copyright (C) 2005, 2006 Florian Boor <florian@kernelconcepts.de> 00008 ****************************************************************************/ 00009 /* 00010 This package is free software; you can redistribute it and/or modify 00011 it under the terms of the GNU General Public License as published by 00012 the Free Software Foundation; either version 3 of the License, or 00013 (at your option) any later version. 00014 00015 This program is distributed in the hope that it will be useful, 00016 but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00018 GNU General Public License for more details. 00019 00020 You should have received a copy of the GNU General Public License 00021 along with this program. If not, see <http://www.gnu.org/licenses/>. 00022 */ 00023 00024 #include "config.h" 00025 #include <stdlib.h> 00026 #include <glib.h> 00027 #include <glib/gi18n.h> 00028 #include <glib/gprintf.h> 00029 #include <qof.h> 00030 #include <math.h> 00031 #include <locale.h> 00032 /* GTK and GPE includes */ 00033 #include <gtk/gtkmain.h> 00034 #include <gpe/pixmaps.h> 00035 #include <gpe/init.h> 00036 #include <gpe/pim-categories.h> 00037 #include <gpe/pim-categories-ui.h> 00038 #include <gpe/spacing.h> 00039 #include "expenses-gtk.h" 00040 #include "qof-main.h" 00041 #include "gpe-expenses.h" 00042 #include "qof-expenses.h" 00043 00044 #define _(String) gettext (String) 00045 00047 #define SHOW_DECIMAL_PLACES 2 00048 00049 static QofLogModule log_module = GPE_MOD_GUI; 00050 00051 enum { 00052 COL_EXP_DATE, 00053 COL_EXP_TYPE, 00054 COL_EXP_SYMBOL, 00055 COL_EXP_AMOUNT, 00056 COL_ENTITY, 00057 COL_MAX 00058 }; 00059 static void exp_refresh_list (GpeExpenseData *context); 00060 00074 static inline void 00075 compare_cache (gchar * text_entry, QofEntity * ent, const QofParam * param) 00076 { 00077 gchar * check; 00078 check = qof_util_param_to_string (ent, param); 00079 g_return_if_fail (check); 00080 if (safe_strcmp(check, text_entry)) 00081 { 00082 qof_util_param_edit ((QofInstance*)ent, param); 00083 qof_util_param_set_string (ent, param, text_entry); 00084 qof_util_param_commit ((QofInstance*)ent, param); 00085 } 00086 if (text_entry) 00087 g_free (text_entry); 00088 text_entry = NULL; 00089 } 00090 00097 static void 00098 edit_ok_clicked(GtkWidget G_GNUC_UNUSED *widget, gpointer data) 00099 { 00100 GpeExpenseData *context; 00101 QofCurrency * currency; 00102 GtkTextBuffer *buf; 00103 GtkTextIter start, end; 00104 QofEntity *ent; 00105 gchar * mnemonic; 00106 00107 context = (GpeExpenseData*)data; 00108 g_return_if_fail(context); 00109 ent = context->entity; 00110 00111 buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (context->text_view)); 00112 gtk_text_buffer_get_bounds (buf, &start, &end); 00113 /* exp_note */ 00114 compare_cache (g_strdup(gtk_text_buffer_get_text (GTK_TEXT_BUFFER (buf), 00115 &start, &end, FALSE)), ent, 00116 qof_class_get_parameter (GPE_QOF_EXPENSES, EXP_NOTE)); 00117 /* exp_vendor */ 00118 compare_cache ( g_strdup (gtk_entry_get_text 00119 (GTK_ENTRY (context->vendor_entry))), ent, 00120 qof_class_get_parameter(GPE_QOF_EXPENSES, EXP_VENDOR)); 00121 /* exp_city */ 00122 compare_cache (g_strdup (gtk_entry_get_text 00123 (GTK_ENTRY (context->city_entry))), ent, 00124 qof_class_get_parameter(GPE_QOF_EXPENSES, EXP_CITY)); 00125 /* exp_attendees */ 00126 compare_cache (g_strdup (gtk_entry_get_text 00127 (GTK_ENTRY (context->attendees_entry))), ent, 00128 qof_class_get_parameter(GPE_QOF_EXPENSES, EXP_ATTENDEES)); 00129 /* exp_type */ 00130 compare_cache (g_strdup (ExpenseTypeasString 00131 (gtk_combo_box_get_active(context->edit_type_list))), ent, 00132 qof_class_get_parameter(GPE_QOF_EXPENSES, EXP_TYPE)); 00133 /* exp_payment */ 00134 compare_cache (g_strdup (ExpensePaymentasString 00135 (gtk_combo_box_get_active(context->payment_list))), ent, 00136 qof_class_get_parameter(GPE_QOF_EXPENSES, EXP_PAYMENT)); 00137 /* exp_category */ 00138 compare_cache (g_strdup (gtk_label_get_text (GTK_LABEL(context->cat_label))), 00139 ent, qof_class_get_parameter (GPE_QOF_EXPENSES, EXP_CATEGORY)); 00140 /* exp_currency */ 00141 mnemonic = gtk_combo_box_get_active_text (context->currency_list); 00142 currency = qof_currency_lookup_name ((QofInstance*)ent, mnemonic); 00143 /* if currency is not found, preserve the cache version. */ 00144 if (currency) 00145 compare_cache (g_strdup_printf ("%d", currency->pq_code), ent, 00146 qof_class_get_parameter (GPE_QOF_EXPENSES, EXP_CURRENCY)); 00147 gtk_widget_destroy(context->window); 00148 context->entity = NULL; 00149 exp_refresh_list(context); 00150 } 00151 00152 static void 00153 edit_cancel_clicked(GtkWidget G_GNUC_UNUSED *widget, gpointer data) 00154 { 00155 GpeExpenseData *context; 00156 00157 context = (GpeExpenseData*)data; 00158 g_return_if_fail(context); 00159 exp_refresh_list(context); 00160 gtk_widget_destroy(context->window); 00161 context->entity = NULL; 00162 } 00163 00164 static void 00165 edit_delete_clicked(GtkWidget G_GNUC_UNUSED *widget, gpointer data) 00166 { 00167 GpeExpenseData *context; 00168 00169 context = (GpeExpenseData*)data; 00170 g_return_if_fail(context); 00171 qof_event_gen (context->entity, QOF_EVENT_DESTROY, NULL); 00172 qof_entity_release(context->entity); 00173 exp_refresh_list(context); 00174 gtk_widget_destroy(context->window); 00175 context->entity = NULL; 00176 } 00177 00178 static void 00179 create_currency_list(gpointer G_GNUC_UNUSED key, gpointer value, gpointer user_data) 00180 { 00181 QofCurrency *curr; 00182 GpeExpenseData *context; 00183 00184 curr = (QofCurrency*)value; 00185 context = (GpeExpenseData*)user_data; 00186 gtk_combo_box_insert_text (context->currency_list, curr->pq_code, 00187 curr->mnemonic); 00188 } 00189 00190 /* ported from gpe-contacts */ 00191 static gchar * 00192 build_categories_string (GSList * catlist) 00193 { 00194 guint id; 00195 GSList * iter; 00196 const gchar *cat; 00197 gchar *s = NULL; 00198 00199 if (!catlist) { return g_strdup(""); } 00200 for (iter = catlist; iter; iter = iter->next) 00201 { 00202 id = GPOINTER_TO_INT(iter->data); 00203 cat = gpe_pim_category_name (id); 00204 00205 if (cat) 00206 { 00207 if (s) 00208 { 00209 /* append as a comma-separated list */ 00210 gchar *ns = g_strdup_printf ("%s, %s", s, cat); 00211 g_free (s); 00212 s = ns; 00213 } 00214 else 00215 s = g_strdup (cat); 00216 } 00217 } 00218 return s; 00219 } 00220 00221 static void 00222 set_selected_category (GtkWidget G_GNUC_UNUSED *ui, GSList *selected, gpointer user_data) 00223 { 00224 GpeExpenseData * context; 00225 gchar * str; 00226 00227 context = (GpeExpenseData*)user_data; 00228 g_return_if_fail (context); 00229 00230 str = build_categories_string (selected); 00231 if (str) 00232 { 00233 gtk_label_set_markup (GTK_LABEL (context->cat_label), str); 00234 g_free (str); 00235 } 00236 } 00237 00238 /* based on gpe-contacts */ 00239 static void 00240 on_categories_clicked (GtkButton G_GNUC_UNUSED *button, gpointer user_data) 00241 { 00242 GtkWidget * G_GNUC_UNUSED w; 00243 00244 w = gpe_pim_categories_dialog (gpe_pim_categories_list (), 00245 G_CALLBACK (set_selected_category), user_data); 00246 } 00247 00248 static gint 00249 cat_compare (gconstpointer gpe_cat, gconstpointer qof_name) 00250 { 00251 struct gpe_pim_category * c; 00252 00253 c = (struct gpe_pim_category *) gpe_cat; 00254 return safe_strcasecmp (gpe_pim_category_name (c->id), qof_name); 00255 } 00256 00257 /* merge the QOF category list into the GPE PIM category list */ 00258 static void 00259 cat_populate (const gchar * cat_name) 00260 { 00261 GSList * cat_list, *match; 00262 gint max_list; 00263 00264 match = NULL; 00265 cat_list = gpe_pim_categories_list(); 00266 if (cat_list) 00267 { 00268 /* if the category name exists, leave it alone */ 00269 match = g_slist_find_custom (cat_list, cat_name, cat_compare); 00270 } 00271 if (!match) 00272 { 00273 DEBUG ("'%s' not found in GPE category list, adding.", cat_name); 00274 gpe_pim_category_new (cat_name, &max_list); 00275 } 00276 } 00277 00278 static void 00279 edit_expense (GtkWidget G_GNUC_UNUSED *w, GpeExpenseData *context) 00280 { 00281 GtkWidget *table, *top_vbox; 00282 GtkWidget *viewport, *scrolled_window; 00283 GtkWidget *buttonbox, *buttonok, *buttoncancel, *buttondelete; 00284 GtkWidget *type_label, *payment_label, *currency_label; 00285 GtkWidget *city_label, *vendor_label, *note_label, *attendees_label; 00286 GtkTextBuffer *buf; 00287 guint gpe_spacing, pos, i; 00288 const QofParam *param; 00289 gboolean mileage; 00290 gchar *text; 00291 00292 g_return_if_fail(context); 00293 if(!context->entity) { return; } 00294 ENTER (" "); 00295 gpe_spacing = 0; 00296 pos = 0; 00297 mileage = FALSE; 00298 buttonok = gtk_button_new_from_stock (GTK_STOCK_SAVE); 00299 buttoncancel = gtk_button_new_from_stock (GTK_STOCK_CANCEL); 00300 buttondelete = gtk_button_new_from_stock (GTK_STOCK_DELETE); 00301 00302 table = gtk_table_new(10, 6, FALSE); 00303 top_vbox = gtk_vbox_new (FALSE, 0); 00304 context->text_view = gtk_text_view_new (); 00305 buttonbox = gtk_hbox_new (FALSE, 0); 00306 type_label = gtk_label_new (_("Type:")); 00307 payment_label = gtk_label_new (_("Payment:")); 00308 currency_label = gtk_label_new (_("Currency:")); 00309 vendor_label = gtk_label_new(_("Vendor:")); 00310 city_label = gtk_label_new(_("City:")); 00311 note_label = gtk_label_new(_("Note:")); 00312 attendees_label = gtk_label_new(_("Attendees")); 00313 context->categories = gtk_button_new_with_label (_("Category")); 00314 context->cat_label = gtk_label_new (""); 00315 context->edit_type_list = GTK_COMBO_BOX(gtk_combo_box_new_text()); 00316 context->currency_list = GTK_COMBO_BOX(gtk_combo_box_new_text()); 00317 context->payment_list = GTK_COMBO_BOX(gtk_combo_box_new_text()); 00318 context->vendor_entry = GTK_ENTRY (gtk_entry_new()); 00319 context->city_entry = GTK_ENTRY (gtk_entry_new()); 00320 context->attendees_entry = GTK_ENTRY (gtk_entry_new()); 00321 scrolled_window = gtk_scrolled_window_new (NULL, NULL); 00322 viewport = gtk_viewport_new(NULL, NULL); 00323 00324 context->window = gtk_window_new (GTK_WINDOW_TOPLEVEL); 00325 00326 gtk_table_set_col_spacings (GTK_TABLE(table), gpe_spacing); 00327 gtk_table_set_row_spacings (GTK_TABLE(table), gpe_spacing); 00328 gtk_box_set_spacing (GTK_BOX(top_vbox), gpe_spacing); 00329 00330 gtk_window_set_default_size (GTK_WINDOW (context->window), 240, 320); 00331 gtk_window_set_title (GTK_WINDOW (context->window), _("Expenses")); 00332 00333 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), 00334 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); 00335 gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE); 00336 gtk_container_add (GTK_CONTAINER (viewport), table); 00337 gtk_container_add (GTK_CONTAINER (scrolled_window), viewport); 00338 00339 gtk_container_set_border_width(GTK_CONTAINER(buttonbox), 0); 00340 00341 gtk_table_set_col_spacings (GTK_TABLE(table), gpe_spacing); 00342 gtk_table_set_row_spacings (GTK_TABLE(table), gpe_spacing); 00343 gtk_box_set_spacing (GTK_BOX(top_vbox), gpe_spacing); 00344 00345 gtk_signal_connect (GTK_OBJECT (buttonok), "clicked", 00346 GTK_SIGNAL_FUNC (edit_ok_clicked), context); 00347 gtk_signal_connect (GTK_OBJECT (buttoncancel), "clicked", 00348 GTK_SIGNAL_FUNC (edit_cancel_clicked), context); 00349 gtk_signal_connect (GTK_OBJECT (buttondelete), "clicked", 00350 GTK_SIGNAL_FUNC (edit_delete_clicked), context); 00351 00352 gtk_box_pack_start (GTK_BOX (buttonbox), buttondelete, TRUE, FALSE, 0); 00353 gtk_box_pack_start (GTK_BOX (buttonbox), buttoncancel, TRUE, FALSE, 0); 00354 gtk_box_pack_start (GTK_BOX (buttonbox), buttonok, TRUE, FALSE, 0); 00355 00356 gtk_misc_set_alignment (GTK_MISC (context->cat_label), 0.0, 0.5); 00357 gtk_misc_set_alignment (GTK_MISC (type_label), 0.0, 0.5); 00358 gtk_misc_set_alignment (GTK_MISC (payment_label), 0.0, 0.5); 00359 gtk_misc_set_alignment (GTK_MISC (currency_label), 0.0, 0.5); 00360 gtk_misc_set_alignment (GTK_MISC (vendor_label), 0.0, 0.5); 00361 gtk_misc_set_alignment (GTK_MISC (city_label), 0.0, 0.5); 00362 gtk_misc_set_alignment (GTK_MISC (attendees_label), 0.0, 0.5); 00363 00364 i = 0; 00365 param = qof_class_get_parameter(context->entity->e_type, EXP_TYPE); 00366 i = ExpenseTypefromString(param->param_getfcn(context->entity, param)); 00367 gtk_combo_box_set_active(context->edit_type_list, i); 00368 /* Currency and PaymentType disabled for mileage */ 00369 if(i == Mileage) { mileage = TRUE; } 00370 00371 /* Category */ 00372 pos++; 00373 gtk_table_attach (GTK_TABLE(table), context->categories, 0, 1, pos, pos+1, 00374 GTK_FILL, GTK_FILL, 0, 0); 00375 /* the label comes after the button as the label *is* the data. */ 00376 gtk_table_attach (GTK_TABLE(table), context->cat_label, 2, 3, pos, pos+1, 00377 GTK_FILL, GTK_FILL, 0, 0); 00378 pos++; 00379 /* Type of expense */ 00380 i = 0; 00381 gtk_table_attach(GTK_TABLE(table), type_label, 0, 1, pos, pos+1, 00382 GTK_FILL, GTK_FILL, 0, 0); 00383 while(0 != safe_strcmp(ExpenseTypeasString(i), "")) 00384 { 00385 gchar *check; 00386 00387 gtk_combo_box_append_text (context->edit_type_list, 00388 dgettext(LIBRARY_GETTEXT_PACKAGE, ExpenseTypeasString(i))); 00389 check = qof_util_param_to_string(context->entity, param); 00390 if(0 == safe_strcmp(check, ExpenseTypeasString(i))) 00391 { 00392 gtk_combo_box_set_active(context->edit_type_list, i); 00393 } 00394 g_free(check); 00395 i++; 00396 } 00397 gtk_table_attach(GTK_TABLE(table), GTK_WIDGET(context->edit_type_list), 1, 00398 3, pos, pos+1, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); 00399 00400 pos++; 00401 /* Payment method */ 00402 i = 0; 00403 gtk_table_attach(GTK_TABLE(table), payment_label, 0, 1, pos, pos+1, 00404 GTK_FILL, GTK_FILL, 0, 0); 00405 00406 while((0 != safe_strcmp(ExpensePaymentasString(i), "")) && 00407 (!mileage)) 00408 { 00409 gtk_combo_box_append_text(context->payment_list, 00410 dgettext(LIBRARY_GETTEXT_PACKAGE, ExpensePaymentasString(i))); 00411 i++; 00412 } 00413 gtk_table_attach(GTK_TABLE(table), GTK_WIDGET(context->payment_list), 1, 00414 3, pos, pos+1, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); 00415 pos++; 00416 /* Currency (to be replaced when using mileage) */ 00417 if(!mileage) 00418 { 00419 gtk_table_attach(GTK_TABLE(table), currency_label, 0, 1, pos, pos+1, 00420 GTK_FILL, GTK_FILL, 0, 0); 00421 qof_currency_foreach(create_currency_list, context); 00422 gtk_table_attach(GTK_TABLE(table), GTK_WIDGET(context->currency_list), 1, 00423 3, pos, pos+1, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); 00424 pos++; 00425 } 00426 /* Vendor */ 00427 gtk_table_attach(GTK_TABLE(table), vendor_label, 0, 1, pos, pos+1, 00428 GTK_FILL, GTK_FILL, 0, 0); 00429 gtk_table_attach(GTK_TABLE(table), GTK_WIDGET (context->vendor_entry), 00430 1, 5, pos, pos+1, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); 00431 pos++; 00432 /* City */ 00433 gtk_table_attach(GTK_TABLE(table), city_label, 0, 1, pos, pos+1, 00434 GTK_FILL, GTK_FILL, 0, 0); 00435 gtk_table_attach(GTK_TABLE(table), GTK_WIDGET (context->city_entry), 00436 1, 5, pos, pos+1, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); 00437 pos++; 00438 /* Attendees */ 00439 gtk_table_attach(GTK_TABLE(table), attendees_label, 0, 1, pos, pos+1, 00440 GTK_FILL, GTK_FILL, 0, 0); 00441 gtk_table_attach(GTK_TABLE(table), GTK_WIDGET (context->attendees_entry), 00442 1, 5, pos, pos+1, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); 00443 pos++; 00444 /* Note */ 00445 gtk_table_attach(GTK_TABLE(table), note_label, 0, 1, pos, pos+1, 00446 GTK_FILL, GTK_FILL, 0, 0); 00447 gtk_table_attach(GTK_TABLE(table), context->text_view, 1, 5, pos, pos+1, 00448 GTK_FILL, GTK_FILL | GTK_EXPAND, 0, 0); 00449 pos++; 00450 00451 gtk_text_view_set_editable (GTK_TEXT_VIEW (context->text_view), TRUE); 00452 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (context->text_view), GTK_WRAP_WORD_CHAR); 00453 00454 gtk_box_pack_start (GTK_BOX (top_vbox), scrolled_window, TRUE, TRUE, 0); 00455 gtk_box_pack_start (GTK_BOX (top_vbox), buttonbox, FALSE, FALSE, 0); 00456 00457 gtk_container_add (GTK_CONTAINER (context->window), top_vbox); 00458 00459 /* use entity values to preset the edit window */ 00460 i = 0; 00461 param = qof_class_get_parameter(context->entity->e_type, EXP_CATEGORY); 00462 text = param->param_getfcn(context->entity, param); 00463 if(text) { 00464 /* check the QOF categories against the GPE categories 00465 and add the OQF ones if they do not exist in GPE */ 00466 cat_populate (text); 00467 PINFO ("setting cat_label=%s", text); 00468 gtk_label_set_markup (GTK_LABEL(context->cat_label), text); 00469 } 00470 00471 param = qof_class_get_parameter(context->entity->e_type, EXP_PAYMENT); 00472 i = ExpensePaymentfromString(param->param_getfcn(context->entity, param)); 00473 gtk_combo_box_set_active(context->payment_list, i); 00474 00475 param = qof_class_get_parameter(context->entity->e_type, EXP_VENDOR); 00476 text = param->param_getfcn(context->entity, param); 00477 if(text) { gtk_entry_set_text(context->vendor_entry, text); } 00478 00479 param = qof_class_get_parameter(context->entity->e_type, EXP_CITY); 00480 text = param->param_getfcn(context->entity, param); 00481 if(text) { gtk_entry_set_text(context->city_entry, text); } 00482 00483 param = qof_class_get_parameter(context->entity->e_type, EXP_CURRENCY); 00484 { 00485 QofCurrency * currency; 00486 gint32 check, (*int32_getter) (QofEntity*, const QofParam*); 00487 int32_getter = (gint32 (*)(QofEntity*, const QofParam*)) param->param_getfcn; 00488 check = int32_getter(context->entity, param); 00489 currency = qof_currency_lookup ((QofInstance*)context->entity, check); 00490 if (currency) 00491 PINFO (" currency=%d mnemonic=%s", check, currency->mnemonic); 00492 gtk_combo_box_set_active(context->currency_list, check); 00493 } 00494 00495 param = qof_class_get_parameter(context->entity->e_type, EXP_ATTENDEES); 00496 text = param->param_getfcn(context->entity, param); 00497 if(text) { gtk_entry_set_text(context->attendees_entry, text); } 00498 00499 param = qof_class_get_parameter(context->entity->e_type, EXP_NOTE); 00500 buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (context->text_view)); 00501 text = param->param_getfcn(context->entity, param); 00502 if(text) 00503 { 00504 gtk_text_buffer_set_text(buf, text, strlen(text)); 00505 gtk_text_view_set_buffer(GTK_TEXT_VIEW (context->text_view), buf); 00506 } 00507 00508 g_signal_connect (G_OBJECT (context->categories), "clicked", 00509 G_CALLBACK (on_categories_clicked), context); 00510 gpe_set_window_icon (context->window, "icon"); 00511 gtk_widget_show_all(context->window); 00512 LEAVE (" "); 00513 } 00514 00515 static void 00516 open_about_dialog (void) 00517 { 00518 GtkAboutDialog* ab; 00519 /* If you modify gpe-expenses, add your details here. */ 00520 const gchar * authors[4] = {"Neil Williams <linux@codehelp.co.uk>\n" 00521 "Philip Blundell <philb@gnu.org>\n" 00522 "Florian Boor <florian@kernelconcepts.de>\n" 00523 , NULL }; 00524 00525 ab = GTK_ABOUT_DIALOG( gtk_about_dialog_new() ); 00526 gtk_about_dialog_set_copyright(ab, 00527 "Copyright 2005-2007 Neil Williams <linux@codehelp.co.uk>"); 00528 gtk_about_dialog_set_version(ab, VERSION); 00529 gtk_about_dialog_set_comments(ab, 00530 /* Translators: line breaks allowed here. */ 00531 _("Expenses records for GPE. Supports payment types, " 00532 "categories, expense types (mileage, meals, parking, etc.), " 00533 "notes and currency selection.")); 00534 gtk_about_dialog_set_license (ab, 00535 " This package is free software; you can redistribute it and/or modify\n" 00536 " it under the terms of the GNU General Public License as published by\n" 00537 " the Free Software Foundation; either version 3 of the License, or\n" 00538 " (at your option) any later version.\n" 00539 "\n" 00540 " This program is distributed in the hope that it will be useful,\n" 00541 " but WITHOUT ANY WARRANTY; without even the implied warranty of\n" 00542 " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" 00543 " GNU General Public License for more details.\n" 00544 "\n" 00545 " You should have received a copy of the GNU General Public License\n" 00546 " along with this program. If not, see <http://www.gnu.org/licenses/>.\n"); 00547 gtk_about_dialog_set_website (ab, "http://gpe-expenses.sourceforge.net/"); 00548 gtk_about_dialog_set_authors (ab, authors); 00549 gtk_about_dialog_set_translator_credits (ab, _("translator-credits")); 00550 gtk_about_dialog_set_logo (ab, gpe_try_find_icon("icon", NULL)); 00551 gpe_set_window_icon(GTK_WIDGET(ab), "icon"); 00552 gtk_dialog_run(GTK_DIALOG(ab)); 00553 gtk_widget_destroy (GTK_WIDGET(ab)); 00554 } 00555 00556 /* Receives the selected expense, then list each 00557 column parameter. Use data selection for column1, 00558 string selection from list for column2, digit entry for column4. 00559 Column3 is set via selection of column2. 00560 */ 00561 static void 00562 exp_show_entities(QofEntity *ent, gpointer data) 00563 { 00564 GpeExpenseData *context; 00565 QofTime *qt; 00566 QofDate *qd; 00567 QofNumeric amount, (*numeric_getter) (QofEntity*, const QofParam*); 00568 GtkTreeIter ent_data; 00569 const QofParam *param; 00570 gchar *type, *symbol, *date_string, *dis_string; 00571 gdouble d_amount; 00572 00573 context = (GpeExpenseData*)data; 00574 g_return_if_fail(context); 00575 symbol = ""; 00576 /* param_getfcn each parameter */ 00577 param = qof_class_get_parameter(GPE_QOF_EXPENSES, EXP_DATE); 00578 qt = (QofTime*)param->param_getfcn (ent, param); 00579 qd = qof_date_from_qtime (qt); 00580 date_string = qof_date_print (qd, QOF_DATE_FORMAT_CE); 00581 qof_date_free (qd); 00582 param = qof_class_get_parameter(GPE_QOF_EXPENSES, EXP_TYPE); 00583 dis_string = param->param_getfcn(ent, param); 00584 type = dgettext(LIBRARY_GETTEXT_PACKAGE, dis_string); 00585 /* If Mileage, use ExpenseDistance, 00586 else use ExpenseCustomCurrency->symbol */ 00587 if(0 == safe_strcmp(dis_string, "Mileage")) 00588 { 00589 gint unit; 00590 /* EXP_DISTANCE */ 00591 param = qof_class_get_parameter(GPE_QOF_EXPENSES, EXP_DISTANCE); 00592 unit = ExpenseDistancefromString(param->param_getfcn(ent, param)); 00593 switch (unit) 00594 { 00595 /* Translators: short form of 'miles' */ 00596 case 0 : { symbol = _("mi"); break; } 00597 /* Translators: short form of 'kilometres' */ 00598 case 1 : { symbol = _("km"); break; } 00599 } 00600 } 00601 else 00602 { 00603 QofCurrency * pqc; 00604 gint32 curr_code, (*int32_getter) (QofEntity *, const QofParam *); 00605 param = qof_class_get_parameter (GPE_QOF_EXPENSES, EXP_CURRENCY); 00606 int32_getter = (gint32 (*)(QofEntity *, const QofParam *)) 00607 param->param_getfcn; 00608 curr_code = int32_getter (ent, param); 00609 pqc = qof_currency_lookup ((QofInstance*)ent, curr_code); 00610 if (pqc) 00611 symbol = g_strdup(pqc->symbol); 00612 } 00613 param = qof_class_get_parameter(GPE_QOF_EXPENSES, EXP_AMOUNT); 00614 numeric_getter = (QofNumeric (*)(QofEntity*, const QofParam*)) param->param_getfcn; 00615 amount = numeric_getter(ent, param); 00616 d_amount = qof_numeric_to_double(amount); 00617 /* Columns: Date Type symbol Amount */ 00618 gtk_list_store_append(context->list_store, &ent_data); 00619 gtk_list_store_set(context->list_store, &ent_data, 00620 COL_EXP_DATE, date_string, 00621 COL_EXP_TYPE, type, 00622 COL_EXP_SYMBOL, symbol, 00623 COL_EXP_AMOUNT, d_amount, 00624 COL_ENTITY, ent, 00625 -1 ); 00626 g_free(date_string); 00627 } 00628 00629 /* button == 1 left mouse button, 3 = right click. */ 00630 static gboolean 00631 button_press_event (GtkWidget *widget, GdkEventButton *b, gpointer data) 00632 { 00633 GtkTreeViewColumn *col; 00634 GtkTreePath *path; 00635 GtkTreeIter iter; 00636 QofEntity *ent; 00637 gdouble amount; 00638 GpeExpenseData *context; 00639 00640 context = (GpeExpenseData*)data; 00641 g_return_val_if_fail(context, FALSE); 00642 amount = 0.00; 00643 ent = NULL; 00644 switch (b->button) { 00645 case 1 : { 00646 if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget), 00647 b->x, b->y, &path, &col, NULL, NULL)) 00648 { 00649 GtkTreeView * treeview; 00650 00651 treeview = GTK_TREE_VIEW 00652 (gtk_tree_view_new_with_model 00653 (GTK_TREE_MODEL(context->list_store))); 00654 gtk_tree_view_set_cursor (treeview, path, NULL, TRUE); 00655 gtk_widget_grab_focus (GTK_WIDGET (treeview)); 00656 gtk_tree_model_get_iter (GTK_TREE_MODEL (context->list_store), 00657 &iter, path); 00658 gtk_tree_model_get(GTK_TREE_MODEL (context->list_store), 00659 &iter, COL_ENTITY, &ent, -1); 00660 gtk_tree_model_get(GTK_TREE_MODEL (context->list_store), 00661 &iter, COL_EXP_AMOUNT, &amount, -1); 00662 context->entity = ent; 00663 gtk_tree_path_free (path); 00664 } 00665 break; 00666 } 00667 default : { break; } 00668 } 00669 return FALSE; 00670 } 00671 00681 static void 00682 change_date (GtkCellRendererText G_GNUC_UNUSED *cell, 00683 gchar G_GNUC_UNUSED *path_string, gchar *new_text, gpointer data) 00684 { 00685 void (*time_setter) (QofEntity*, QofTime*); 00686 GpeExpenseData *context; 00687 const QofParam *param; 00688 QofTime *qt; 00689 QofDate *qd; 00690 00691 context = (GpeExpenseData*)data; 00692 g_return_if_fail(context); 00693 ENTER (" new_text=%s", new_text); 00694 param = qof_class_get_parameter(GPE_QOF_EXPENSES, EXP_DATE); 00695 time_setter = (void(*)(QofEntity*, QofTime*))param->param_setfcn; 00696 /* convert the string to a QofTime */ 00697 qd = qof_date_parse (new_text, QOF_DATE_FORMAT_CE); 00698 qt = qof_date_to_qtime (qd); 00699 qof_util_param_edit ((QofInstance*)context->entity, param); 00700 if ((time_setter && qof_time_is_valid (qt))) 00701 time_setter (context->entity, qt); 00702 qof_util_param_commit ((QofInstance*)context->entity, param); 00703 exp_refresh_list(context); 00704 LEAVE (" "); 00705 } 00706 00707 static void 00708 change_amount (GtkCellRendererText G_GNUC_UNUSED *cell, 00709 gchar G_GNUC_UNUSED *path_string, gchar *new_text, gpointer data) 00710 { 00711 GpeExpenseData *context; 00712 QofNumeric amount; 00713 void (*numeric_setter) (QofEntity*, QofNumeric); 00714 void (*i32_setter) (QofEntity*, gint32); 00715 const QofParam *param; 00716 gchar *numeric_char; 00717 gdouble d_amount; 00718 gint precision; 00719 /* detect local currency */ 00720 QofCurrency * c; 00721 struct lconv lc; 00722 gchar * sy; 00723 guint def_pq_code; 00724 00725 context = (GpeExpenseData*)data; 00726 g_return_if_fail(context); 00727 ENTER (" "); 00728 def_pq_code = -1; 00729 param = qof_class_get_parameter(GPE_QOF_EXPENSES, EXP_CURRENCY); 00730 i32_setter = (void(*)(QofEntity*, gint32))param->param_setfcn; 00731 /* use locale currency */ 00732 lc = *localeconv(); 00733 DEBUG (" sym=%s", lc.currency_symbol); 00734 sy = g_strdup (lc.int_curr_symbol); 00735 sy = g_strstrip (sy); 00736 c = qof_currency_lookup_name ((QofInstance*)context->entity, sy); 00737 if (c) 00738 def_pq_code = c->pq_code; 00739 g_free (sy); 00740 if(i32_setter != NULL) { i32_setter(context->entity, def_pq_code); } 00741 param = qof_class_get_parameter(GPE_QOF_EXPENSES, EXP_AMOUNT); 00742 numeric_setter = (void(*)(QofEntity*, QofNumeric))param->param_setfcn; 00743 d_amount = 0.00; 00744 precision = pow(10, SHOW_DECIMAL_PLACES); 00745 amount = qof_numeric_zero(); 00746 d_amount = strtod(new_text, NULL); 00747 amount = qof_numeric_from_double (d_amount, precision, 00748 QOF_HOW_DENOM_EXACT | QOF_HOW_RND_ROUND); 00749 numeric_char = qof_numeric_to_string(amount); 00750 DEBUG (" numeric_char=%s", numeric_char); 00751 g_free(numeric_char); 00752 00753 if ((qof_numeric_check (amount) == QOF_ERROR_OK) && (numeric_setter)) 00754 { 00755 qof_util_param_edit ((QofInstance*)context->entity, param); 00756 numeric_setter(context->entity, amount); 00757 qof_util_param_commit ((QofInstance*)context->entity, param); 00758 } 00759 else 00760 { 00761 qof_util_param_edit ((QofInstance*)context->entity, param); 00762 numeric_setter(context->entity, qof_numeric_zero ()); 00763 qof_util_param_commit ((QofInstance*)context->entity, param); 00764 } 00765 exp_refresh_list(context); 00766 LEAVE (" "); 00767 } 00768 00769 static void 00770 rounding_func (GtkTreeViewColumn G_GNUC_UNUSED *tree_column, 00771 GtkCellRenderer *cell, GtkTreeModel *tree_model, 00772 GtkTreeIter *iter, gpointer data) 00773 { 00774 GtkCellRendererText *cell_text; 00775 gdouble d; 00776 gchar *s; 00777 00778 cell_text = (GtkCellRendererText *)cell; 00779 d = 0.00; 00780 s = NULL; 00781 g_free(cell_text->text); 00782 gtk_tree_model_get(tree_model, iter, GPOINTER_TO_INT(data), &d, -1); 00783 s = g_strdup_printf("%%.%i", SHOW_DECIMAL_PLACES); 00784 s = g_strconcat(s, "f", NULL); 00785 cell_text->text = g_strdup_printf(s, d); 00786 g_free(s); 00787 } 00788 00789 static gint 00790 type_compare (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, 00791 gpointer G_GNUC_UNUSED user_data) 00792 { 00793 gint result; 00794 gchar *str_a, *str_b; 00795 00796 result = 0; 00797 gtk_tree_model_get(model, a, COL_EXP_TYPE, &str_a, -1); 00798 gtk_tree_model_get(model, b, COL_EXP_TYPE, &str_b, -1); 00799 result = safe_strcmp(str_a, str_b); 00800 g_free(str_a); 00801 g_free(str_b); 00802 return result; 00803 } 00804 00811 static gint 00812 date_compare (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, 00813 gpointer G_GNUC_UNUSED user_data) 00814 { 00815 QofTime *qt_a, *qt_b; 00816 QofEntity * ent_a, * ent_b; 00817 const QofParam * param; 00818 gint result; 00819 00820 ent_a = ent_b = NULL; 00821 result = 0; 00822 gtk_tree_model_get(model, a, COL_ENTITY, &ent_a, -1); 00823 gtk_tree_model_get(model, b, COL_ENTITY, &ent_b, -1); 00824 param = qof_class_get_parameter (GPE_QOF_EXPENSES, EXP_DATE); 00825 qt_a = param->param_getfcn (ent_a, param); 00826 qt_b = param->param_getfcn (ent_b, param); 00827 result = qof_time_cmp (qt_a, qt_b); 00828 qof_time_free (qt_a); 00829 qof_time_free (qt_b); 00830 return result; 00831 } 00832 00833 static gint 00834 amount_compare (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, 00835 gpointer G_GNUC_UNUSED user_data) 00836 { 00837 gint result; 00838 gdouble *dbl_a, *dbl_b; 00839 00840 result = 0; 00841 gtk_tree_model_get(model, a, COL_EXP_AMOUNT, &dbl_a, -1); 00842 gtk_tree_model_get(model, b, COL_EXP_AMOUNT, &dbl_b, -1); 00843 if(dbl_a != dbl_b) 00844 result = (dbl_a > dbl_b) ? 1 : -1; 00845 return result; 00846 } 00847 00848 static GtkWidget* 00849 set_list_view(GpeExpenseData *context) 00850 { 00851 GtkTreeView *view; 00852 GtkTreeSortable *sort; 00853 GtkWidget *scrolled; 00854 GtkCellRenderer *date_rend, *type_rend, *symbol_rend, *amount_rend; 00855 GtkTreeViewColumn *col; 00856 00857 scrolled = gtk_scrolled_window_new (NULL, NULL); 00858 context->list_store = gtk_list_store_new(COL_MAX, G_TYPE_STRING, 00859 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_DOUBLE, G_TYPE_POINTER); 00860 sort = GTK_TREE_SORTABLE (context->list_store); 00861 view = GTK_TREE_VIEW (gtk_tree_view_new_with_model 00862 (GTK_TREE_MODEL (sort))); 00863 00864 date_rend = gtk_cell_renderer_text_new (); 00865 col = gtk_tree_view_column_new_with_attributes ((_("Date")), date_rend, 00866 "text", COL_EXP_DATE, NULL); 00867 g_object_set(date_rend, "editable", TRUE, NULL); 00868 g_object_set(col, "reorderable", TRUE, NULL); 00869 g_object_set(col, "sort-indicator", TRUE, NULL); 00870 gtk_tree_view_column_set_sort_column_id(col, COL_EXP_DATE); 00871 gtk_tree_sortable_set_sort_func (sort, COL_EXP_DATE, date_compare, NULL, NULL); 00872 gtk_tree_view_column_set_expand(col, TRUE); 00873 gtk_tree_view_column_set_clickable(col, TRUE); 00874 gtk_tree_view_insert_column (GTK_TREE_VIEW (view), col, -1); 00875 g_signal_connect(date_rend, "edited", 00876 (GCallback) change_date, context); 00877 00878 type_rend = gtk_cell_renderer_text_new (); 00879 col = gtk_tree_view_column_new_with_attributes (_("Type"), type_rend, 00880 "text", COL_EXP_TYPE, NULL); 00881 gtk_tree_view_column_set_sort_column_id(col, COL_EXP_TYPE); 00882 gtk_tree_sortable_set_sort_func (sort, COL_EXP_TYPE, type_compare, NULL, NULL); 00883 gtk_tree_view_column_set_expand(col, TRUE); 00884 gtk_tree_view_column_set_clickable(col, TRUE); 00885 g_object_set(col, "reorderable", TRUE, NULL); 00886 g_object_set(col, "sort-indicator", TRUE, NULL); 00887 gtk_tree_view_insert_column (GTK_TREE_VIEW (view), col, -1); 00888 00889 symbol_rend = gtk_cell_renderer_text_new (); 00890 col = gtk_tree_view_column_new_with_attributes ("", symbol_rend, 00891 "text", COL_EXP_SYMBOL, NULL); 00892 gtk_tree_view_column_set_expand(col, TRUE); 00893 gtk_tree_view_column_set_clickable(col, TRUE); 00894 gtk_tree_view_insert_column (GTK_TREE_VIEW (view), col, -1); 00895 00896 amount_rend = gtk_cell_renderer_text_new (); 00897 col = gtk_tree_view_column_new_with_attributes (_("Amount"), amount_rend, 00898 "text", COL_EXP_AMOUNT, NULL); 00899 g_object_set(amount_rend, "editable", TRUE, NULL); 00900 g_object_set(col, "reorderable", TRUE, NULL); 00901 g_object_set(col, "sort-indicator", TRUE, NULL); 00902 gtk_tree_view_column_set_sort_column_id(col, COL_EXP_AMOUNT); 00903 gtk_tree_sortable_set_sort_func (sort, COL_EXP_AMOUNT, amount_compare, NULL, NULL); 00904 gtk_tree_view_column_set_expand(col, TRUE); 00905 gtk_tree_view_column_set_clickable(col, TRUE); 00906 gtk_tree_view_insert_column (GTK_TREE_VIEW (view), col, -1); 00907 gtk_tree_view_column_set_cell_data_func(col, amount_rend, 00908 rounding_func, GINT_TO_POINTER(COL_EXP_AMOUNT), NULL); 00909 g_signal_connect(amount_rend, "edited", 00910 (GCallback) change_amount, context); 00911 00912 g_signal_connect (G_OBJECT (view), "button_press_event", 00913 G_CALLBACK (button_press_event), context); 00914 00915 gtk_container_add (GTK_CONTAINER (scrolled), GTK_WIDGET(view)); 00916 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), 00917 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); 00918 gtk_tree_sortable_set_sort_column_id (sort, COL_EXP_DATE, 00919 GTK_SORT_DESCENDING); 00920 return GTK_WIDGET(scrolled); 00921 } 00922 00923 static void 00924 exp_refresh_list (GpeExpenseData *context) 00925 { 00926 gtk_list_store_clear(context->list_store); 00927 /* Populate the list from qof_object_foreach */ 00928 qof_object_foreach(GPE_QOF_EXPENSES, context->book, exp_show_entities, context); 00929 } 00930 00931 static void 00932 open_new_expense (GtkWidget G_GNUC_UNUSED *w, GpeExpenseData *context) 00933 { 00934 GtkTreeIter ent_data; 00935 QofInstance *inst; 00936 QofBook *book; 00937 const QofParam *param; 00938 void (*i32_setter) (QofEntity*, gint32); 00939 /* detect local currency */ 00940 QofCurrency * c; 00941 struct lconv lc; 00942 gchar * sy; 00943 guint def_pq_code; 00944 00945 g_return_if_fail(context); 00946 def_pq_code = -1; 00947 book = qof_session_get_book(context->qof.input_session); 00948 inst = (QofInstance*)qof_object_new_instance(GPE_QOF_EXPENSES, book); 00949 context->entity = &inst->entity; 00950 /* use locale currency */ 00951 lc = *localeconv(); 00952 DEBUG (" sym=%s", lc.currency_symbol); 00953 sy = g_strdup (lc.int_curr_symbol); 00954 sy = g_strstrip (sy); 00955 c = qof_currency_lookup_name (inst, sy); 00956 if (c) 00957 def_pq_code = c->pq_code; 00958 g_free (sy); 00959 param = qof_class_get_parameter (GPE_QOF_EXPENSES, EXP_CURRENCY); 00960 i32_setter = (void(*)(QofEntity*, gint32))param->param_setfcn; 00961 qof_util_param_edit ((QofInstance*)context->entity, param); 00962 i32_setter(context->entity, def_pq_code); 00963 qof_util_param_commit ((QofInstance*)context->entity, param); 00964 gtk_list_store_append(context->list_store, &ent_data); 00965 exp_refresh_list(context); 00966 } 00967 00968 static GtkWidget* 00969 set_toolbar (GpeExpenseData *context) 00970 { 00971 GtkWidget *toolbar; 00972 GtkToolItem *new_exp, *quit_exp, *about_exp, *item, *edit_exp; 00973 toolbar = gtk_toolbar_new (); 00974 gtk_toolbar_set_orientation (GTK_TOOLBAR (toolbar), GTK_ORIENTATION_HORIZONTAL); 00975 00976 new_exp = gtk_tool_button_new_from_stock(GTK_STOCK_NEW); 00977 g_signal_connect(G_OBJECT(new_exp), "clicked", 00978 G_CALLBACK (open_new_expense), context); 00979 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), new_exp, -1); 00980 00981 edit_exp = gtk_tool_button_new_from_stock(GTK_STOCK_PROPERTIES); 00982 g_signal_connect(G_OBJECT(edit_exp), "clicked", 00983 G_CALLBACK (edit_expense), context); 00984 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), edit_exp, -1); 00985 00986 item = gtk_separator_tool_item_new(); 00987 gtk_tool_item_set_expand(GTK_TOOL_ITEM(item), FALSE); 00988 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1); 00989 00990 about_exp = gtk_tool_button_new_from_stock(GTK_STOCK_ABOUT); 00991 g_signal_connect(G_OBJECT(about_exp), "clicked", 00992 G_CALLBACK (open_about_dialog), NULL); 00993 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), about_exp, -1); 00994 00995 item = gtk_separator_tool_item_new(); 00996 gtk_tool_item_set_expand(GTK_TOOL_ITEM(item), FALSE); 00997 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1); 00998 00999 quit_exp = gtk_tool_button_new_from_stock(GTK_STOCK_QUIT); 01000 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), quit_exp, -1); 01001 g_signal_connect (G_OBJECT (quit_exp), "clicked", 01002 G_CALLBACK (gtk_main_quit), NULL); 01003 return GTK_WIDGET(toolbar); 01004 } 01005 01006 void 01007 open_expenses_window (GpeExpenseData *context) 01008 { 01009 GtkWidget *window; 01010 GtkWidget *vbox; 01011 01012 g_return_if_fail(context); 01013 ENTER (" "); 01014 vbox = gtk_vbox_new (FALSE, 0); 01015 01016 gpe_pim_categories_init (); 01017 window = gtk_window_new (GTK_WINDOW_TOPLEVEL); 01018 gtk_window_set_default_size (GTK_WINDOW (window), 240, 320); 01019 gtk_window_set_title (GTK_WINDOW (window), _("Expenses")); 01020 gpe_set_window_icon (window, "icon"); 01021 01022 gtk_box_pack_start (GTK_BOX (vbox), set_toolbar(context), FALSE, FALSE, 0); 01023 g_signal_connect (G_OBJECT (window), "delete-event", 01024 G_CALLBACK (gtk_main_quit), NULL); 01025 01026 gtk_box_pack_start (GTK_BOX (vbox), set_list_view(context), TRUE, TRUE, 0); 01027 01028 gtk_container_add (GTK_CONTAINER (window), vbox); 01029 gtk_widget_show_all (window); 01030 /* Populate the list from qof_object_foreach */ 01031 qof_object_foreach(GPE_QOF_EXPENSES, context->book, 01032 exp_show_entities, context); 01033 LEAVE (" "); 01034 }