-
Notifications
You must be signed in to change notification settings - Fork 762
/
dialog-totd.c
391 lines (328 loc) · 12.2 KB
/
dialog-totd.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
/********************************************************************\
* dialog-totd.c : dialog to display a "tip of the day" *
* *
* Initial copyright not recorded. *
* Copyright (c) 2006 David Hampton <hampton@employees.org> *
* Copyright (c) 2011 Robert Fewell *
* *
* 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 *
\********************************************************************/
#include <config.h>
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include "dialog-totd.h"
#include "dialog-utils.h"
#include "gnc-component-manager.h"
#include "gnc-filepath-utils.h"
#include "gnc-prefs.h"
#include "gnc-gnome-utils.h"
#include "gnc-engine.h"
#include "gnc-ui.h"
#define GNC_PREFS_GROUP "dialogs.totd"
#define GNC_PREF_CURRENT_TIP "current-tip"
#define GNC_PREF_SHOW_TIPS "show-at-startup"
#define DIALOG_TOTD_CM_CLASS "dialog-totd"
#define GNC_RESPONSE_FORWARD 1
#define GNC_RESPONSE_BACK 2
/* Callbacks */
void gnc_totd_dialog_response_cb (GtkDialog *dialog, gint response, gpointer user_data);
void gnc_totd_dialog_startup_toggled_cb (GtkToggleButton *button, gpointer user_data);
/* The Tips */
static gchar **tip_list;
static gint tip_count = -1;
static gint current_tip_number = -1;
/* This static indicates the debugging module that this .o belongs to. */
static QofLogModule log_module = GNC_MOD_GUI;
typedef struct
{
GtkWidget *dialog;
GtkTextView *textview;
GtkWidget *showcheck_button;
} TotdDialog;
/***********************************************************************
* This function should be called to change the tip number. It
* handles clamping the number to the range of tips available, saving
* the number in the preferences database, and updating the dialog window
* with the text of the newly selected tip.
*
* @param Tip of the day structure. This points to the dialog and
* the GtkTextView widget that holds the text of the tip.
*
* @param offset Which tip to show. If the value is zero then the
* current tip will be shown. If the value is negative the previous
* tip will be shown. If the value is positive the next tip will be
* shown.
************************************************************************/
static void
gnc_new_tip_number (TotdDialog *totd_dialog, gint offset)
{
gchar **tip_components = NULL;
gchar *tip;
ENTER("TotdDialog %p, offset %d", totd_dialog, offset);
g_return_if_fail (tip_list != NULL);
current_tip_number += offset;
DEBUG("clamp %d to '0 <= x < %d'", current_tip_number, tip_count);
if (current_tip_number < 0)
current_tip_number = tip_count - 1;
if (current_tip_number >= tip_count)
current_tip_number = 0;
gnc_prefs_set_int(GNC_PREFS_GROUP, GNC_PREF_CURRENT_TIP, current_tip_number);
/* A tip consists of a translatable string, which might contain a %s
* placeholder, optionally followed by a | and a (non-translated)
* string to put in the placeholder. For example:
*
* Welcome to GnuCash version %s|2.4
*/
if (tip_list[current_tip_number])
tip_components = g_strsplit(tip_list[current_tip_number], "|", 0);
/* If the tip is empty, g_strsplit will return an empty list. This
* shouldn't normally happen, but make sure we don't crash just in
* case */
if (tip_components == NULL)
{
tip = g_strdup("");
}
else
{
/* Use printf to do the substitution. Note that if there is no |
* in the tip, tip_components[1] will be the terminating NULL,
* so this will never cause an out-of-bounds array access.
*/
tip = g_strdup_printf( _(tip_components[0]), tip_components[1]);
}
g_strfreev(tip_components);
gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(totd_dialog->textview)),
tip, -1);
g_free(tip);
LEAVE("");
}
/***************************/
/* Dialog Callbacks */
/***************************/
void gnc_totd_dialog_response_cb (GtkDialog *dialog,
gint response,
gpointer user_data)
{
TotdDialog *totd_dialog = user_data;
ENTER("dialog %p, response %d, user_data %p", dialog, response, user_data);
switch (response)
{
case GNC_RESPONSE_FORWARD:
gnc_new_tip_number(totd_dialog, 1);
break;
case GNC_RESPONSE_BACK:
gnc_new_tip_number(totd_dialog, -1);
break;
case GTK_RESPONSE_CLOSE:
gnc_save_window_size(GNC_PREFS_GROUP, GTK_WINDOW(totd_dialog->dialog));
/* fall through */
default:
gnc_unregister_gui_component_by_data(DIALOG_TOTD_CM_CLASS, totd_dialog);
gtk_widget_destroy(GTK_WIDGET(totd_dialog->dialog));
break;
}
LEAVE("");
}
void
gnc_totd_dialog_startup_toggled_cb (GtkToggleButton *button,
gpointer user_data)
{
gboolean active;
active = gtk_toggle_button_get_active(button);
gnc_prefs_set_bool(GNC_PREFS_GROUP, GNC_PREF_SHOW_TIPS, active);
}
/***********************************/
/* Tip of the Day Parser */
/***********************************/
static gboolean
gnc_totd_initialize (void)
{
gchar *filename = NULL, *contents = NULL, *new_str = NULL;
gsize length;
GError *error = NULL;
int tip;
/* Find the file */
filename = gnc_filepath_locate_data_file("tip_of_the_day.list");
if (!filename)
return FALSE;
/* Read it */
if (!g_file_get_contents(filename, &contents, &length, &error))
{
printf("Unable to read file: %s\n", error->message);
g_error_free(error);
g_free(filename);
return FALSE;
}
g_free(filename);
/* Split into multiple strings. Due to the nature of the
* tip list file, this can contain empty strings */
if (contents)
{
tip_list = g_strsplit(contents, "\n", 0);
g_free(contents);
contents = NULL;
}
tip_count = g_strv_length (tip_list);
/* Remove the empty strings */
for (tip = 0; tip < tip_count; ++tip)
{
if (*tip_list[tip] != '\0')
{
g_strstrip(tip_list[tip]);
if (!contents)
contents = g_strdup (tip_list[tip]);
else
{
new_str = g_strjoin ("\n", contents, tip_list[tip], NULL);
g_free (contents);
contents = new_str;
}
}
}
/* Split cleaned up contents into multiple strings again */
g_strfreev (tip_list);
tip_list = NULL;
if (contents)
{
tip_list = g_strsplit(contents, "\n", 0);
tip_count = g_strv_length (tip_list);
/* Convert any escaped characters while counting the strings */
for (tip = 0; tip < tip_count; ++tip)
{
new_str = g_strcompress(tip_list[tip]);
g_free(tip_list[tip]);
tip_list[tip] = new_str;
}
}
if (tip_count < 1)
return FALSE;
return TRUE;
}
/***********************************************************************
* Raise the totd dialog to the top of the window stack. This
* function is called if the user attempts to create a second totd
* dialog.
*
* @internal
*
* @param class_name Unused.
*
* @param component_id Unused.
*
* @param user_data A pointer to the totd structure.
*
* @param iter_data Unused.
***********************************************************************/
static gboolean
show_handler (const char *class_name, gint component_id,
gpointer user_data, gpointer iter_data)
{
TotdDialog *totd_dialog = user_data;
ENTER(" ");
if (!totd_dialog)
{
LEAVE("no data structure");
return(FALSE);
}
gtk_window_set_transient_for (GTK_WINDOW (totd_dialog->dialog),
gnc_ui_get_main_window (NULL));
LEAVE(" ");
return(TRUE);
}
/****************************************************
* Close the totd dialog.
*
* @internal
*
* @param user_data A pointer to the totd structure.
****************************************************/
static void
close_handler (gpointer user_data)
{
TotdDialog *totd_dialog = user_data;
ENTER(" ");
gnc_unregister_gui_component_by_data(DIALOG_TOTD_CM_CLASS, totd_dialog);
LEAVE(" ");
}
/*************************************/
/* Create the TotD Dialog */
/*************************************/
void
gnc_totd_dialog (GtkWindow *parent, gboolean startup)
{
TotdDialog *totd_dialog;
GtkBuilder *builder;
GtkWidget *dialog, *button;
GtkTextView *textview;
gboolean show_tips;
show_tips = gnc_prefs_get_bool(GNC_PREFS_GROUP, GNC_PREF_SHOW_TIPS);
if (startup && !show_tips)
return;
if (tip_count == -1)
{
if (!gnc_totd_initialize())
return;
current_tip_number = gnc_prefs_get_int(GNC_PREFS_GROUP, GNC_PREF_CURRENT_TIP);
}
/* Don't continue when no tips were found, to prevent
* gnc_new_tip_number doesn't handle that case (it would try to
* display the terminating NULL). There's nothing to show
* anyway...*/
if (tip_count < 1)
{
PWARN("No tips found - Tips of the day window won't be displayed.");
return;
}
if (gnc_forall_gui_components(DIALOG_TOTD_CM_CLASS, show_handler, NULL))
{
return;
}
builder = gtk_builder_new();
gnc_builder_add_from_file (builder, "dialog-totd.glade", "totd_dialog");
dialog = GTK_WIDGET(gtk_builder_get_object (builder, "totd_dialog"));
gtk_window_set_transient_for(GTK_WINDOW (dialog), parent);
// Set the name for this dialog so it can be easily manipulated with css
gtk_widget_set_name (GTK_WIDGET(dialog), "gnc-id-tip-of-the-day");
totd_dialog = g_new0 (TotdDialog, 1);
totd_dialog->dialog = dialog;
ENTER("totd_dialog %p, dialog %p", totd_dialog, dialog);
gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, totd_dialog);
button = GTK_WIDGET(gtk_builder_get_object (builder, "show_checkbutton"));
totd_dialog->showcheck_button = button;
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (button), show_tips);
textview = GTK_TEXT_VIEW(gtk_builder_get_object (builder, "tip_textview"));
totd_dialog->textview = textview;
gnc_new_tip_number(totd_dialog, 1);
gnc_restore_window_size(GNC_PREFS_GROUP, GTK_WINDOW(totd_dialog->dialog), parent);
gtk_widget_show(GTK_WIDGET (totd_dialog->dialog));
gnc_register_gui_component(DIALOG_TOTD_CM_CLASS,
NULL, close_handler, totd_dialog);
g_object_unref(G_OBJECT(builder));
LEAVE("");
}
/****************************************************
* Set the totd dialog transient for the currently
* active main window. This will prevent the totd
* dialog from accidentally hiding behind a main
* window.
****************************************************/
void
gnc_totd_dialog_reparent (void)
{
gnc_forall_gui_components(DIALOG_TOTD_CM_CLASS, show_handler, NULL);
}