Adding upstream version 0.5.5.
[debian/lxpanel.git] / src / plugins / dclock.c
CommitLineData
6cc5e1a6
DB
1/**
2 * Copyright (c) 2006 LxDE Developers, see the file AUTHORS for details.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19#include <time.h>
20#include <sys/time.h>
21#include <sys/types.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <glib/gi18n.h>
6cc5e1a6
DB
26
27#include "panel.h"
28#include "misc.h"
29#include "plugin.h"
6cc5e1a6
DB
30
31#include "dbg.h"
32
33#define DEFAULT_TIP_FORMAT "%A %x"
34#define DEFAULT_CLOCK_FORMAT "%R"
35
2ba86315 36/* Private context for digital clock plugin. */
6cc5e1a6 37typedef struct {
2ba86315
DB
38 Plugin * plugin; /* Back pointer to Plugin */
39 GtkWidget * clock_label; /* Label containing clock value */
40 GtkWidget * clock_icon; /* Icon when icon_only */
41 GtkWidget * calendar_window; /* Calendar window, if it is being displayed */
42 char * clock_format; /* Format string for clock value */
43 char * tooltip_format; /* Format string for tooltip value */
44 char * action; /* Command to execute on a click */
45 gboolean bold; /* True if bold font */
46 gboolean icon_only; /* True if icon only (no clock value) */
47 guint timer; /* Timer for periodic update */
48 char * prev_output; /* Previous value of clock */
49} DClockPlugin;
50
32a67dc7
DB
51static void dclock_popup_map(GtkWidget * widget, DClockPlugin * dc);
52static GtkWidget * dclock_create_calendar(DClockPlugin * dc);
2ba86315
DB
53static gboolean dclock_button_press_event(GtkWidget * widget, GdkEventButton * evt, Plugin * plugin);
54static gboolean dclock_update_display(DClockPlugin * dc);
55static int dclock_constructor(Plugin * p, char ** fp);
56static void dclock_destructor(Plugin * p);
57static void dclock_apply_configuration(Plugin * p);
58static void dclock_configure(Plugin * p, GtkWindow * parent);
59static void dclock_save_configuration(Plugin * p, FILE * fp);
60static void dclock_panel_configuration_changed(Plugin * p);
61
32a67dc7
DB
62/* Handler for "map" signal on popup window. */
63static void dclock_popup_map(GtkWidget * widget, DClockPlugin * dc)
64{
65 plugin_adjust_popup_position(widget, dc->plugin);
66}
67
2ba86315 68/* Display a window containing the standard calendar widget. */
32a67dc7 69static GtkWidget * dclock_create_calendar(DClockPlugin * dc)
6cc5e1a6 70{
2ba86315
DB
71 /* Create a new window. */
72 GtkWidget * win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
6cc5e1a6
DB
73 gtk_window_set_default_size(GTK_WINDOW(win), 180, 180);
74 gtk_window_set_decorated(GTK_WINDOW(win), FALSE);
2ba86315 75 gtk_window_set_resizable(GTK_WINDOW(win), FALSE);
6cc5e1a6
DB
76 gtk_container_set_border_width(GTK_CONTAINER(win), 5);
77 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(win), TRUE);
78 gtk_window_set_skip_pager_hint(GTK_WINDOW(win), TRUE);
2ba86315
DB
79 gtk_window_stick(GTK_WINDOW(win));
80
81 /* Create a vertical box as a child of the window. */
82 GtkWidget * box = gtk_vbox_new(FALSE, 0);
7486d297 83 gtk_container_add(GTK_CONTAINER(win), GTK_WIDGET(box));
6cc5e1a6 84
2ba86315
DB
85 /* Create a standard calendar widget as a child of the vertical box. */
86 GtkWidget * calendar = gtk_calendar_new();
87 gtk_calendar_display_options(
88 GTK_CALENDAR(calendar),
89 GTK_CALENDAR_SHOW_WEEK_NUMBERS | GTK_CALENDAR_SHOW_DAY_NAMES | GTK_CALENDAR_SHOW_HEADING);
90 gtk_box_pack_start_defaults(GTK_BOX(box), calendar);
6cc5e1a6 91
32a67dc7
DB
92 /* Connect signals. */
93 g_signal_connect(G_OBJECT(win), "map", G_CALLBACK(dclock_popup_map), dc);
94
2ba86315
DB
95 /* Return the widget. */
96 return win;
6cc5e1a6
DB
97}
98
2ba86315
DB
99/* Handler for "button-press-event" event from main widget. */
100static gboolean dclock_button_press_event(GtkWidget * widget, GdkEventButton * evt, Plugin * plugin)
6cc5e1a6 101{
2ba86315 102 DClockPlugin * dc = (DClockPlugin *) plugin->priv;
6cc5e1a6 103
2ba86315
DB
104 /* Standard right-click handling. */
105 if (plugin_button_press_event(widget, evt, plugin))
6cc5e1a6 106 return TRUE;
6cc5e1a6 107
2ba86315
DB
108 /* If an action is set, execute it. */
109 if (dc->action != NULL)
110 g_spawn_command_line_async(dc->action, NULL);
111
112 /* If no action is set, toggle the presentation of the calendar. */
113 else
114 {
115 if (dc->calendar_window == NULL)
116 {
32a67dc7 117 dc->calendar_window = dclock_create_calendar(dc);
2ba86315
DB
118 gtk_widget_show_all(dc->calendar_window);
119 }
120 else
121 {
122 gtk_widget_destroy(dc->calendar_window);
123 dc->calendar_window = NULL;
6cc5e1a6
DB
124 }
125 }
2ba86315 126 return TRUE;
6cc5e1a6
DB
127}
128
2ba86315
DB
129/* Periodic timer callback.
130 * Also used during initialization and configuration change to do a redraw. */
131static gboolean dclock_update_display(DClockPlugin * dc)
6cc5e1a6 132{
2ba86315 133 /* Determine the current time. */
6cc5e1a6 134 time_t now;
6cc5e1a6 135 time(&now);
2ba86315 136 struct tm * detail = localtime(&now);
6cc5e1a6 137
2ba86315
DB
138 /* Determine the content of the clock label. */
139 char output[64];
140 strftime(output, sizeof(output),
141 ((dc->clock_format != NULL) ? dc->clock_format : DEFAULT_CLOCK_FORMAT), detail);
6cc5e1a6 142
2ba86315
DB
143 /* When we write the clock value, it causes the panel to do a full relayout.
144 * Since this function is called once per second, we take the trouble to check if the string actually changed first. */
145 if (( ! dc->icon_only)
146 && ((dc->prev_output == NULL) || (strcmp(dc->prev_output, output) != 0)))
147 {
148 g_free(dc->prev_output);
149 dc->prev_output = g_strdup(output);
150
151 /* Convert "\n" escapes in the user's format string to newline characters. */
152 char * newlines_converted = NULL;
153 if (strstr(output, "\\n") != NULL)
154 {
155 newlines_converted = g_strdup(output); /* Just to get enough space for the converted result */
156 char * p;
157 char * q;
158 for (p = output, q = newlines_converted; *p != '\0'; p += 1)
159 {
160 if ((p[0] == '\\') && (p[1] == 'n'))
161 {
162 *q++ = '\n';
163 p += 1;
164 }
165 else
166 *q++ = *p;
6cc5e1a6 167 }
2ba86315
DB
168 *q = '\0';
169 }
170
171 gchar * utf8 = g_locale_to_utf8(((newlines_converted != NULL) ? newlines_converted : output), -1, NULL, NULL, NULL);
172 if (utf8 != NULL)
173 {
174 panel_draw_label_text(dc->plugin->panel, dc->clock_label, utf8, dc->bold, TRUE);
175 g_free(utf8);
176 }
177 g_free(newlines_converted);
6cc5e1a6
DB
178 }
179
2ba86315
DB
180 /* Determine the content of the tooltip. */
181 strftime(output, sizeof(output),
182 ((dc->tooltip_format != NULL) ? dc->tooltip_format : DEFAULT_TIP_FORMAT), detail);
183 gchar * utf8 = g_locale_to_utf8(output, -1, NULL, NULL, NULL);
184 if (utf8 != NULL)
185 {
186 gtk_widget_set_tooltip_text(dc->plugin->pwid, utf8);
187 g_free(utf8);
188 }
189 return TRUE;
6cc5e1a6
DB
190}
191
2ba86315
DB
192/* Plugin constructor. */
193static int dclock_constructor(Plugin * p, char ** fp)
6cc5e1a6 194{
2ba86315
DB
195 /* Allocate and initialize plugin context and set into Plugin private data pointer. */
196 DClockPlugin * dc = g_new0(DClockPlugin, 1);
6cc5e1a6 197 p->priv = dc;
2ba86315 198 dc->plugin = p;
6cc5e1a6 199
2ba86315
DB
200 /* Load parameters from the configuration file. */
201 line s;
6cc5e1a6 202 s.len = 256;
2ba86315 203 if (fp != NULL)
6cc5e1a6 204 {
2ba86315
DB
205 while (lxpanel_get_line(fp, &s) != LINE_BLOCK_END)
206 {
207 if (s.type == LINE_NONE)
208 {
6cc5e1a6 209 ERR( "dclock: illegal token %s\n", s.str);
2ba86315 210 return 0;
6cc5e1a6 211 }
2ba86315
DB
212 if (s.type == LINE_VAR)
213 {
214 if (g_ascii_strcasecmp(s.t[0], "ClockFmt") == 0)
215 dc->clock_format = g_strdup(s.t[1]);
216 else if (g_ascii_strcasecmp(s.t[0], "TooltipFmt") == 0)
217 dc->tooltip_format = g_strdup(s.t[1]);
218 else if (g_ascii_strcasecmp(s.t[0], "Action") == 0)
6cc5e1a6 219 dc->action = g_strdup(s.t[1]);
2ba86315 220 else if (g_ascii_strcasecmp(s.t[0], "BoldFont") == 0)
6cc5e1a6 221 dc->bold = str2num(bool_pair, s.t[1], 0);
2ba86315
DB
222 else if (g_ascii_strcasecmp(s.t[0], "IconOnly") == 0)
223 dc->icon_only = str2num(bool_pair, s.t[1], 0);
224 else
6cc5e1a6 225 ERR( "dclock: unknown var %s\n", s.t[0]);
2ba86315
DB
226 }
227 else
228 {
6cc5e1a6 229 ERR( "dclock: illegal in this context %s\n", s.str);
2ba86315 230 return 0;
6cc5e1a6
DB
231 }
232 }
233 }
6cc5e1a6 234
2ba86315
DB
235 /* Allocate top level widget and set into Plugin widget pointer. */
236 p->pwid = gtk_event_box_new();
6cc5e1a6 237
2ba86315
DB
238 /* Allocate a horizontal box as the child of the top level. */
239 GtkWidget * hbox = gtk_hbox_new(FALSE, 0);
240 gtk_container_add(GTK_CONTAINER(p->pwid), hbox);
241 gtk_widget_show(hbox);
6cc5e1a6 242
2ba86315
DB
243 /* Create a label and an image as children of the horizontal box.
244 * Only one of these is visible at a time, controlled by user preference. */
245 dc->clock_label = gtk_label_new(NULL);
246 gtk_misc_set_alignment(GTK_MISC(dc->clock_label), 0.5, 0.5);
247 gtk_misc_set_padding(GTK_MISC(dc->clock_label), 4, 0);
248 gtk_container_add(GTK_CONTAINER(hbox), dc->clock_label);
249 dc->clock_icon = gtk_image_new();
250 gtk_container_add(GTK_CONTAINER(hbox), dc->clock_icon);
6cc5e1a6 251
2ba86315
DB
252 /* Connect signals. */
253 g_signal_connect(G_OBJECT (p->pwid), "button_press_event", G_CALLBACK(dclock_button_press_event), (gpointer) p);
6cc5e1a6 254
2ba86315
DB
255 /* Initialize the clock display */
256 dclock_apply_configuration(p);
6cc5e1a6 257
2ba86315
DB
258 /* Start a timer to refresh the clock display. */
259 dc->timer = g_timeout_add(1000, (GSourceFunc) dclock_update_display, (gpointer) dc);
6cc5e1a6 260
2ba86315
DB
261 /* Show the widget and return. */
262 gtk_widget_show(p->pwid);
263 return 1;
264}
6cc5e1a6 265
2ba86315
DB
266/* Plugin destructor. */
267static void dclock_destructor(Plugin * p)
6cc5e1a6 268{
2ba86315 269 DClockPlugin * dc = (DClockPlugin *) p->priv;
6cc5e1a6 270
2ba86315
DB
271 /* Remove the timer. */
272 if (dc->timer != 0)
6cc5e1a6
DB
273 g_source_remove(dc->timer);
274
2ba86315
DB
275 /* Ensure that the calendar is dismissed. */
276 if (dc->calendar_window != NULL)
277 gtk_widget_destroy(dc->calendar_window);
7486d297 278
2ba86315
DB
279 /* Deallocate all memory. */
280 g_free(dc->clock_format);
281 g_free(dc->tooltip_format);
6cc5e1a6 282 g_free(dc->action);
2ba86315
DB
283 g_free(dc->prev_output);
284 g_free(dc);
6cc5e1a6
DB
285}
286
2ba86315
DB
287/* Callback when the configuration dialog has recorded a configuration change. */
288static void dclock_apply_configuration(Plugin * p)
6cc5e1a6 289{
2ba86315
DB
290 DClockPlugin * dc = (DClockPlugin *) p->priv;
291
292 /* Set up the icon or the label as the displayable widget. */
293 if (dc->icon_only)
294 {
295 panel_image_set_from_file(p->panel, dc->clock_icon, PACKAGE_DATA_DIR "/lxpanel/images/clock.png");
296 gtk_widget_show(dc->clock_icon);
297 gtk_widget_hide(dc->clock_label);
298 }
299 else
300 {
301 gtk_widget_show(dc->clock_label);
302 gtk_widget_hide(dc->clock_icon);
303 }
304
305 /* Update the display. */
306 g_free(dc->prev_output); /* Force the update of the clock display */
307 dc->prev_output = NULL;
308 dclock_update_display(dc);
32a67dc7
DB
309
310 /* Hide the calendar. */
311 if (dc->calendar_window != NULL)
312 {
313 gtk_widget_destroy(dc->calendar_window);
314 dc->calendar_window = NULL;
315 }
6cc5e1a6
DB
316}
317
2ba86315
DB
318/* Callback when the configuration dialog is to be shown. */
319static void dclock_configure(Plugin * p, GtkWindow * parent)
6cc5e1a6 320{
2ba86315
DB
321 DClockPlugin * dc = (DClockPlugin *) p->priv;
322 GtkWidget * dlg = create_generic_config_dlg(
323 _(p->class->name),
324 GTK_WIDGET(parent),
325 (GSourceFunc) dclock_apply_configuration, (gpointer) p,
326 _("Clock Format"), &dc->clock_format, CONF_TYPE_STR,
327 _("Tooltip Format"), &dc->tooltip_format, CONF_TYPE_STR,
328 _("Format codes: man 3 strftime; \\n for line break"), NULL, CONF_TYPE_TRIM,
329 _("Action when clicked (default: display calendar)"), &dc->action, CONF_TYPE_STR,
330 _("Bold font"), &dc->bold, CONF_TYPE_BOOL,
331 _("Tooltip only"), &dc->icon_only, CONF_TYPE_BOOL,
332 NULL);
333 gtk_window_present(GTK_WINDOW(dlg));
6cc5e1a6
DB
334}
335
2ba86315
DB
336/* Callback when the configuration is to be saved. */
337static void dclock_save_configuration(Plugin * p, FILE * fp)
6cc5e1a6 338{
2ba86315
DB
339 DClockPlugin * dc = (DClockPlugin *) p->priv;
340 lxpanel_put_str(fp, "ClockFmt", dc->clock_format);
341 lxpanel_put_str(fp, "TooltipFmt", dc->tooltip_format);
342 lxpanel_put_str(fp, "Action", dc->action);
343 lxpanel_put_int(fp, "BoldFont", dc->bold);
344 lxpanel_put_int(fp, "IconOnly", dc->icon_only);
6cc5e1a6
DB
345}
346
2ba86315
DB
347/* Callback when panel configuration changes. */
348static void dclock_panel_configuration_changed(Plugin * p)
6cc5e1a6 349{
2ba86315 350 dclock_apply_configuration(p);
6cc5e1a6
DB
351}
352
2ba86315 353/* Plugin descriptor. */
6cc5e1a6 354PluginClass dclock_plugin_class = {
2ba86315
DB
355
356 PLUGINCLASS_VERSIONING,
6cc5e1a6
DB
357
358 type : "dclock",
359 name : N_("Digital Clock"),
360 version: "1.0",
2ba86315 361 description : N_("Display digital clock and tooltip"),
6cc5e1a6
DB
362
363 constructor : dclock_constructor,
364 destructor : dclock_destructor,
2ba86315
DB
365 config : dclock_configure,
366 save : dclock_save_configuration,
367 panel_configuration_changed : dclock_panel_configuration_changed
6cc5e1a6 368};