Rename panel, plugin, and plugin_class to Panel, Plugin, and
[lxde/lxpanel.git] / src / plugins / dclock.c
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>
26 #include <pthread.h>
27
28 #include "panel.h"
29 #include "misc.h"
30 #include "plugin.h"
31 #include "glib-mem.h"
32
33 #include "dbg.h"
34
35 #define DEFAULT_TIP_FORMAT "%A %x"
36 #define DEFAULT_CLOCK_FORMAT "%R"
37
38 typedef struct {
39 GtkWidget *eb;
40 GtkWidget *main;
41 GtkWidget *clockw;
42 GtkWidget *calwin;
43 GtkTooltips *tip;
44 char *tfmt;
45 char *cfmt;
46 char *action;
47 short lastDay;
48 int bold;
49 int timer;
50 int usefontcolor;
51 GdkColor *gfontcolor;
52 gboolean cal_show;
53 } dclock;
54
55 static void
56 update_label_orient( Plugin* p );
57
58 static GtkWidget *create_calendar()
59 {
60 GtkWidget *win;
61 GtkWidget *calendar;
62
63 /* create a new window */
64 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
65 gtk_window_set_default_size(GTK_WINDOW(win), 180, 180);
66 gtk_window_set_decorated(GTK_WINDOW(win), FALSE);
67 gtk_window_set_resizable (GTK_WINDOW(win), FALSE);
68 gtk_container_set_border_width(GTK_CONTAINER(win), 0);
69 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(win), TRUE);
70 gtk_window_set_skip_pager_hint(GTK_WINDOW(win), TRUE);
71 gtk_window_set_type_hint(GTK_WINDOW(win), GDK_WINDOW_TYPE_HINT_DOCK);
72 gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_MOUSE);
73 gtk_window_stick (GTK_WINDOW(win));
74
75 GtkVBox* box = gtk_vbox_new(FALSE, 0);
76
77 /* calendar */
78 calendar = gtk_calendar_new();
79 gtk_calendar_display_options(GTK_CALENDAR(calendar),
80 GTK_CALENDAR_SHOW_WEEK_NUMBERS |
81 GTK_CALENDAR_SHOW_DAY_NAMES |
82 GTK_CALENDAR_SHOW_HEADING);
83 // gtk_container_add(GTK_CONTAINER(win), calendar);
84 gtk_box_pack_start_defaults( box, calendar );
85 gtk_container_add(GTK_CONTAINER(win), box);
86
87 gtk_widget_show_all(win);
88
89 RET(win);
90 }
91
92 static int
93 actionProcess( void *arg )
94 {
95 ENTER;
96 RET(system((char *) arg));
97 }
98
99 static gboolean
100 clicked( GtkWidget *widget, gpointer dummy, dclock *dc)
101 {
102 ENTER2;
103 if( dc->action ) {
104 pthread_t actionThread;
105 pthread_create(&actionThread, NULL, actionProcess, dc->action);
106 } else {
107 if (!dc->cal_show) {
108 dc->cal_show = TRUE;
109 dc->calwin = create_calendar();
110 } else {
111 dc->cal_show = FALSE;
112 gtk_widget_destroy(dc->calwin);
113 dc->calwin = NULL;
114 }
115 }
116 RET2(TRUE);
117 }
118
119 static gint
120 clock_update(gpointer data )
121 {
122 char output[64], str[64];
123 time_t now;
124 struct tm * detail;
125 dclock *dc;
126 gchar *utf8;
127
128 ENTER;
129 g_assert(data != NULL);
130 dc = (dclock *)data;
131
132 time(&now);
133 detail = localtime(&now);
134 strftime(output, sizeof(output),
135 (dc->cfmt ? dc->cfmt : DEFAULT_CLOCK_FORMAT), detail);
136
137 if (dc->bold&&dc->usefontcolor)
138 g_snprintf(str, 64, "<span color=\"#%06x\"><b>%s</b></span>", gcolor2rgb24(dc->gfontcolor), output);
139 else if (dc->bold)
140 g_snprintf(str, 64, "<b>%s</b>", output);
141 else if (dc->usefontcolor)
142 g_snprintf(str, 64, "<span color=\"#%06x\">%s</span>", gcolor2rgb24(dc->gfontcolor), output);
143 else
144 g_snprintf(str, 64, "%s", output);
145
146 gtk_label_set_markup (GTK_LABEL(dc->clockw), str);
147
148 if (detail->tm_mday != dc->lastDay) {
149 dc->lastDay = detail->tm_mday ;
150 strftime (output, sizeof(output),
151 (dc->tfmt ? dc->tfmt : DEFAULT_TIP_FORMAT), detail);
152 if ((utf8 = g_locale_to_utf8(output, -1, NULL, NULL, NULL))) {
153 gtk_tooltips_set_tip(dc->tip, dc->main, utf8, NULL);
154 g_free(utf8);
155 }
156 }
157
158 RET(TRUE);
159 }
160
161
162 static int
163 dclock_constructor(Plugin *p, char** fp)
164 {
165 line s;
166 dclock *dc;
167
168 ENTER;
169 dc = g_slice_new0(dclock);
170 g_return_val_if_fail(dc != NULL, 0);
171 p->priv = dc;
172
173 s.len = 256;
174 dc->cfmt = dc->tfmt = dc->action = dc->bold = 0;
175 dc->bold = TRUE;
176 dc->cal_show = FALSE;
177 if( fp )
178 {
179 while (lxpanel_get_line(fp, &s) != LINE_BLOCK_END) {
180 if (s.type == LINE_NONE) {
181 ERR( "dclock: illegal token %s\n", s.str);
182 goto error;
183 }
184 if (s.type == LINE_VAR) {
185 if (!g_ascii_strcasecmp(s.t[0], "ClockFmt"))
186 dc->cfmt = g_strdup(s.t[1]);
187 else if (!g_ascii_strcasecmp(s.t[0], "TooltipFmt"))
188 dc->tfmt = g_strdup(s.t[1]);
189 else if (!g_ascii_strcasecmp(s.t[0], "Action"))
190 dc->action = g_strdup(s.t[1]);
191 else if (!g_ascii_strcasecmp(s.t[0], "BoldFont"))
192 dc->bold = str2num(bool_pair, s.t[1], 0);
193 else {
194 ERR( "dclock: unknown var %s\n", s.t[0]);
195 goto error;
196 }
197 } else {
198 ERR( "dclock: illegal in this context %s\n", s.str);
199 goto error;
200 }
201 }
202 }
203 dc->main = gtk_event_box_new();
204
205 g_signal_connect (G_OBJECT (dc->main), "button_press_event",
206 G_CALLBACK (clicked), (gpointer) dc);
207 dc->clockw = gtk_label_new("");
208 gtk_misc_set_alignment(GTK_MISC(dc->clockw), 0.5, 0.5);
209 gtk_misc_set_padding(GTK_MISC(dc->clockw), 4, 0);
210 update_label_orient( p );
211 gtk_container_add(GTK_CONTAINER(dc->main), dc->clockw);
212 gtk_widget_show_all(dc->main);
213
214 dc->tip = p->panel->tooltips;
215
216 dc->tip = gtk_tooltips_new();
217 #if GLIB_CHECK_VERSION( 2, 10, 0 )
218 g_object_ref_sink( dc->tip );
219 #else
220 g_object_ref( dc->tip );
221 gtk_object_sink( dc->tip );
222 #endif
223
224 dc->timer = g_timeout_add(1000, (GSourceFunc) clock_update, (gpointer)dc);
225
226 /* font color */
227 if (p->panel->usefontcolor)
228 dc->gfontcolor = &p->panel->gfontcolor;
229
230 dc->usefontcolor = p->panel->usefontcolor;
231
232 clock_update( dc );
233
234 /* store the created Plugin widget in Plugin->pwid */
235 p->pwid = dc->main;
236
237 RET(1);
238
239 error:
240 g_free(dc->cfmt);
241 g_free(dc->tfmt);
242 g_free(dc->action);
243 g_slice_free(dclock, dc);
244 RET(0);
245 }
246
247
248 static void
249 dclock_destructor(Plugin *p)
250 {
251 dclock *dc = (dclock *)p->priv;
252
253 ENTER;
254 dc = (dclock *) p->priv;
255 if (dc->timer)
256 g_source_remove(dc->timer);
257
258 /* g_object_unref( dc->tip ); */
259 gtk_widget_destroy(dc->calwin);
260 g_free(dc->cfmt);
261 g_free(dc->tfmt);
262 g_free(dc->action);
263 g_slice_free(dclock, dc);
264 RET();
265 }
266
267 static void apply_config( Plugin* p )
268 {
269 /* NOTE: This dirty hack is used to force the update of tooltip
270 because tooltip will be updated when dc->lastDay != today.
271 */
272 dclock* dc = (dclock*)p->priv;
273 --dc->lastDay;
274 clock_update( dc );
275 }
276
277 static void dclock_config( Plugin *p, GtkWindow* parent )
278 {
279 GtkWidget* dlg;
280 dclock *dc = (dclock *)p->priv;
281 dlg = create_generic_config_dlg( _(p->class->name),
282 GTK_WIDGET(parent),
283 (GSourceFunc) apply_config, (gpointer) p,
284 _("Clock Format"), &dc->cfmt, G_TYPE_STRING,
285 _("Tooltip Format"), &dc->tfmt, G_TYPE_STRING,
286 _("Action"), &dc->action, G_TYPE_STRING,
287 _("Bold font"), &dc->bold, G_TYPE_BOOLEAN,
288 NULL );
289 gtk_window_present( GTK_WINDOW(dlg) );
290 }
291
292 static void save_config( Plugin* p, FILE* fp )
293 {
294 dclock *dc = (dclock *)p->priv;
295 lxpanel_put_str( fp, "ClockFmt", dc->cfmt );
296 lxpanel_put_str( fp, "TooltipFmt", dc->tfmt );
297 lxpanel_put_str( fp, "Action", dc->action );
298 lxpanel_put_int( fp, "BoldFont", dc->bold );
299 }
300
301 static void
302 update_label_orient( Plugin* p )
303 {
304 dclock *dc = (dclock *)p->priv;
305 GtkLabel* label = GTK_LABEL(dc->clockw);
306 /* FIXME: gtk+ has only limited support for this, sigh! */
307 gdouble angle;
308 if( p->panel->edge == EDGE_LEFT )
309 angle = 90.0;
310 else if( p->panel->edge == EDGE_RIGHT )
311 angle = 270.0;
312 else
313 angle = 0.0;
314 gtk_label_set_angle( GTK_LABEL(label), angle );
315 }
316
317 PluginClass dclock_plugin_class = {
318 fname: NULL,
319 count: 0,
320
321 type : "dclock",
322 name : N_("Digital Clock"),
323 version: "1.0",
324 description : N_("Display Digital clock and Tooltip"),
325
326 constructor : dclock_constructor,
327 destructor : dclock_destructor,
328 config : dclock_config,
329 save : save_config,
330 orientation : update_label_orient
331 };