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