1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3
* Copyright (C) 2002 CodeFactory AB
4
* Copyright (C) 2002-2003 Richard Hult <richard@imendio.com>
7
* This program is free software; you can redistribute it and/or
8
* modify it under the terms of the GNU General Public License as
9
* published by the Free Software Foundation; either version 2 of the
10
* License, or (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
* General Public License for more details.
17
* You should have received a copy of the GNU General Public
18
* License along with this program; if not, write to the
19
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20
* Boston, MA 02111-1307, USA.
26
#include <glib/gi18n.h>
28
#include <gdk/gdkkeysyms.h>
29
#include <gconf/gconf-client.h>
31
#ifdef HAVE_CANBERRA_GTK
32
#include <canberra-gtk.h>
36
#include "drw-utils.h"
37
#include "drw-break-window.h"
38
#include "drw-timer.h"
40
struct _DrwBreakWindowPrivate {
41
GtkWidget *clock_label;
42
GtkWidget *break_label;
45
GtkWidget *postpone_entry;
46
GtkWidget *postpone_button;
52
guint clock_timeout_id;
53
guint postpone_timeout_id;
54
guint postpone_sensitize_id;
57
#define DRW_BREAK_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), DRW_TYPE_BREAK_WINDOW, DrwBreakWindowPrivate))
59
#define POSTPONE_CANCEL 30
68
static void drw_break_window_class_init (DrwBreakWindowClass *klass);
69
static void drw_break_window_init (DrwBreakWindow *window);
70
static void drw_break_window_finalize (GObject *object);
71
static void drw_break_window_dispose (GObject *object);
72
static gboolean postpone_sensitize_cb (DrwBreakWindow *window);
73
static gboolean clock_timeout_cb (DrwBreakWindow *window);
74
static void postpone_clicked_cb (GtkWidget *button,
76
static gboolean label_expose_event_cb (GtkLabel *label,
77
GdkEventExpose *event,
79
static void label_size_request_cb (GtkLabel *label,
80
GtkRequisition *requisition,
83
G_DEFINE_TYPE (DrwBreakWindow, drw_break_window, GTK_TYPE_WINDOW)
85
static guint signals[LAST_SIGNAL];
88
drw_break_window_class_init (DrwBreakWindowClass *klass)
90
GObjectClass *object_class = G_OBJECT_CLASS (klass);
92
object_class->finalize = drw_break_window_finalize;
93
object_class->dispose = drw_break_window_dispose;
96
g_signal_new ("postpone",
97
G_TYPE_FROM_CLASS (klass),
101
g_cclosure_marshal_VOID__VOID,
105
g_signal_new ("done",
106
G_TYPE_FROM_CLASS (klass),
110
g_cclosure_marshal_VOID__VOID,
113
g_type_class_add_private (klass, sizeof (DrwBreakWindowPrivate));
117
drw_break_window_init (DrwBreakWindow *window)
119
DrwBreakWindowPrivate *priv;
123
GtkWidget *monitor_box;
125
GtkWidget *outer_vbox;
126
GtkWidget *button_box;
127
gboolean allow_postpone;
129
gint root_monitor = 0;
130
GdkScreen *screen = NULL;
131
GdkRectangle monitor;
136
priv = DRW_BREAK_WINDOW_GET_PRIVATE (window);
139
client = gconf_client_get_default ();
141
priv->break_time = 60 * gconf_client_get_int (client,
142
GCONF_PATH "/break_time",
145
allow_postpone = gconf_client_get_bool (client,
146
GCONF_PATH "/allow_postpone",
148
g_object_unref (client);
150
g_object_set (window, "type", GTK_WINDOW_POPUP, NULL);
151
gtk_window_set_keep_above (GTK_WINDOW (window), TRUE);
152
gtk_window_fullscreen (GTK_WINDOW (window));
153
gtk_window_set_modal (GTK_WINDOW (window), TRUE);
155
screen = gdk_screen_get_default ();
156
gdk_screen_get_monitor_geometry (screen, root_monitor, &monitor);
158
gtk_window_set_default_size (GTK_WINDOW (window),
159
gdk_screen_get_width (screen),
160
gdk_screen_get_height (screen));
162
gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
163
gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
164
drw_setup_background (GTK_WIDGET (window));
166
align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
167
gtk_widget_show (align);
169
outer_vbox = gtk_vbox_new (FALSE, 0);
170
gtk_widget_show (outer_vbox);
172
right_padding = gdk_screen_get_width (screen) - monitor.width - monitor.x;
173
bottom_padding = gdk_screen_get_height (screen) - monitor.height - monitor.y;
175
monitor_box = gtk_alignment_new (0.5, 0.5, 1, 1);
176
gtk_alignment_set_padding (GTK_ALIGNMENT (monitor_box),
181
gtk_widget_show (monitor_box);
183
gtk_container_add (GTK_CONTAINER (window), monitor_box);
184
gtk_container_add (GTK_CONTAINER (monitor_box), outer_vbox);
186
gtk_box_pack_start (GTK_BOX (outer_vbox), align, TRUE, TRUE, 0);
188
if (allow_postpone) {
189
button_box = gtk_hbox_new (FALSE, 0);
190
gtk_widget_show (button_box);
192
gtk_container_set_border_width (GTK_CONTAINER (button_box), 12);
194
priv->postpone_button = gtk_button_new_with_mnemonic (_("_Postpone Break"));
195
gtk_widget_show (priv->postpone_button);
197
gtk_widget_set_sensitive (priv->postpone_button, FALSE);
199
if (priv->postpone_sensitize_id) {
200
g_source_remove (priv->postpone_sensitize_id);
203
priv->postpone_sensitize_id = g_timeout_add_seconds (5,
204
(GSourceFunc) postpone_sensitize_cb,
207
g_signal_connect (priv->postpone_button,
209
G_CALLBACK (postpone_clicked_cb),
212
gtk_box_pack_end (GTK_BOX (button_box), priv->postpone_button, FALSE, TRUE, 0);
214
priv->postpone_entry = gtk_entry_new ();
215
gtk_entry_set_has_frame (GTK_ENTRY (priv->postpone_entry), FALSE);
217
gtk_box_pack_end (GTK_BOX (button_box), priv->postpone_entry, FALSE, TRUE, 4);
219
gtk_box_pack_end (GTK_BOX (outer_vbox), button_box, FALSE, TRUE, 0);
222
vbox = gtk_vbox_new (FALSE, 0);
223
gtk_widget_show (vbox);
225
gtk_container_add (GTK_CONTAINER (align), vbox);
227
hbox = gtk_hbox_new (FALSE, 0);
228
gtk_widget_show (hbox);
229
gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, FALSE, 0);
231
priv->image = gtk_image_new_from_stock (GTK_STOCK_STOP, GTK_ICON_SIZE_DIALOG);
232
gtk_misc_set_alignment (GTK_MISC (priv->image), 1, 0.5);
233
gtk_widget_show (priv->image);
234
gtk_box_pack_start (GTK_BOX (hbox), priv->image, TRUE, TRUE, 8);
236
priv->break_label = gtk_label_new (NULL);
237
gtk_widget_show (priv->break_label);
239
g_signal_connect (priv->break_label,
241
G_CALLBACK (label_expose_event_cb),
244
g_signal_connect_after (priv->break_label,
246
G_CALLBACK (label_size_request_cb),
249
str = g_strdup_printf ("<span size=\"xx-large\" foreground=\"white\"><b>%s</b></span>",
251
gtk_label_set_markup (GTK_LABEL (priv->break_label), str);
254
gtk_box_pack_start (GTK_BOX (hbox), priv->break_label, FALSE, FALSE, 12);
257
priv->clock_label = gtk_label_new (NULL);
258
gtk_misc_set_alignment (GTK_MISC (priv->clock_label), 0.5, 0.5);
259
gtk_widget_show (priv->clock_label);
260
gtk_box_pack_start (GTK_BOX (vbox), priv->clock_label, TRUE, TRUE, 8);
262
g_signal_connect (priv->clock_label,
264
G_CALLBACK (label_expose_event_cb),
267
g_signal_connect_after (priv->clock_label,
269
G_CALLBACK (label_size_request_cb),
272
gtk_window_stick (GTK_WINDOW (window));
274
priv->timer = drw_timer_new ();
276
/* Make sure we have a valid time label from the start. */
277
clock_timeout_cb (window);
279
priv->clock_timeout_id = g_timeout_add (1000,
280
(GSourceFunc) clock_timeout_cb,
282
#ifdef HAVE_CANBERRA_GTK
283
ca_context_play (ca_gtk_context_get (), 0, CA_PROP_EVENT_ID, "desktop-screen-lock", NULL);
288
drw_break_window_finalize (GObject *object)
290
DrwBreakWindow *window = DRW_BREAK_WINDOW (object);
291
DrwBreakWindowPrivate *priv;
295
if (priv->clock_timeout_id != 0) {
296
g_source_remove (priv->clock_timeout_id);
299
if (priv->postpone_timeout_id != 0) {
300
g_source_remove (priv->postpone_timeout_id);
303
if (priv->postpone_sensitize_id != 0) {
304
g_source_remove (priv->postpone_sensitize_id);
309
if (G_OBJECT_CLASS (drw_break_window_parent_class)->finalize) {
310
(* G_OBJECT_CLASS (drw_break_window_parent_class)->finalize) (object);
315
drw_break_window_dispose (GObject *object)
317
DrwBreakWindow *window = DRW_BREAK_WINDOW (object);
318
DrwBreakWindowPrivate *priv;
323
drw_timer_destroy (priv->timer);
327
if (priv->clock_timeout_id != 0) {
328
g_source_remove (priv->clock_timeout_id);
329
priv->clock_timeout_id = 0;
332
if (priv->postpone_timeout_id != 0) {
333
g_source_remove (priv->postpone_timeout_id);
334
priv->postpone_timeout_id = 0;
337
if (priv->postpone_sensitize_id != 0) {
338
g_source_remove (priv->postpone_sensitize_id);
341
if (G_OBJECT_CLASS (drw_break_window_parent_class)->dispose) {
342
(* G_OBJECT_CLASS (drw_break_window_parent_class)->dispose) (object);
347
drw_break_window_new (void)
351
object = g_object_new (DRW_TYPE_BREAK_WINDOW,
352
"type", GTK_WINDOW_POPUP,
353
"skip-taskbar-hint", TRUE,
354
"skip-pager-hint", TRUE,
355
"focus-on-map", TRUE,
358
return GTK_WIDGET (object);
362
postpone_sensitize_cb (DrwBreakWindow *window)
364
DrwBreakWindowPrivate *priv;
368
gtk_widget_set_sensitive (priv->postpone_button, TRUE);
370
priv->postpone_sensitize_id = 0;
375
clock_timeout_cb (DrwBreakWindow *window)
377
DrwBreakWindowPrivate *priv;
382
g_return_val_if_fail (DRW_IS_BREAK_WINDOW (window), FALSE);
386
seconds = 1 + priv->break_time - drw_timer_elapsed (priv->timer);
387
seconds = MAX (0, seconds);
390
/* Zero this out so the finalizer doesn't try to remove the
391
* source, which would be done in the timeout callback ==
394
priv->clock_timeout_id = 0;
396
#ifdef HAVE_CANBERRA_GTK
397
ca_context_play (ca_gtk_context_get (), 0, CA_PROP_EVENT_ID, "alarm-clock-elapsed", NULL);
399
g_signal_emit (window, signals[DONE], 0, NULL);
404
minutes = seconds / 60;
405
seconds -= minutes * 60;
407
txt = g_strdup_printf ("<span size=\"25000\" foreground=\"white\"><b>%d:%02d</b></span>",
410
gtk_label_set_markup (GTK_LABEL (priv->clock_label), txt);
417
postpone_entry_activate_cb (GtkWidget *entry,
418
DrwBreakWindow *window)
422
GConfClient *client = gconf_client_get_default();
424
str = gtk_entry_get_text (GTK_ENTRY (entry));
426
phrase = gconf_client_get_string (client,
427
GCONF_PATH "/unlock_phrase",
429
g_object_unref (client);
431
if (!strcmp (str, phrase)) {
432
g_signal_emit (window, signals[POSTPONE], 0, NULL);
438
gtk_entry_set_text (GTK_ENTRY (entry), "");
442
grab_on_window (GdkWindow *window,
443
guint32 activate_time)
445
if ((gdk_pointer_grab (window, TRUE,
446
GDK_BUTTON_PRESS_MASK |
447
GDK_BUTTON_RELEASE_MASK |
448
GDK_POINTER_MOTION_MASK,
449
NULL, NULL, activate_time) == 0)) {
450
if (gdk_keyboard_grab (window, TRUE,
454
gdk_pointer_ungrab (activate_time);
463
postpone_cancel_cb (DrwBreakWindow *window)
465
DrwBreakWindowPrivate *priv;
469
gtk_entry_set_text (GTK_ENTRY (priv->postpone_entry), "");
470
gtk_widget_hide (priv->postpone_entry);
472
priv->postpone_timeout_id = 0;
478
postpone_entry_key_press_event_cb (GtkEntry *entry,
480
DrwBreakWindow *window)
482
DrwBreakWindowPrivate *priv;
486
if (event->keyval == GDK_Escape) {
487
if (priv->postpone_timeout_id) {
488
g_source_remove (priv->postpone_timeout_id);
491
postpone_cancel_cb (window);
496
g_source_remove (priv->postpone_timeout_id);
498
priv->postpone_timeout_id = g_timeout_add_seconds (POSTPONE_CANCEL, (GSourceFunc) postpone_cancel_cb, window);
504
postpone_clicked_cb (GtkWidget *button,
507
DrwBreakWindow *bw = DRW_BREAK_WINDOW (window);
508
DrwBreakWindowPrivate *priv = bw->priv;
511
/* Disable the phrase for now. */
512
phrase = NULL; /*gconf_client_get_string (gconf_client_get_default (),
513
GCONF_PATH "/unlock_phrase",
516
if (!phrase || !phrase[0]) {
517
g_signal_emit (window, signals[POSTPONE], 0, NULL);
521
if (gtk_widget_get_visible (priv->postpone_entry)) {
522
gtk_widget_activate (priv->postpone_entry);
526
gtk_widget_show (priv->postpone_entry);
528
priv->postpone_timeout_id = g_timeout_add_seconds (POSTPONE_CANCEL, (GSourceFunc) postpone_cancel_cb, bw);
530
grab_on_window (gtk_widget_get_window (priv->postpone_entry), gtk_get_current_event_time ());
532
gtk_widget_grab_focus (priv->postpone_entry);
534
g_signal_connect (priv->postpone_entry,
536
G_CALLBACK (postpone_entry_activate_cb),
539
g_signal_connect (priv->postpone_entry,
541
G_CALLBACK (postpone_entry_key_press_event_cb),
546
get_layout_location (GtkLabel *label,
552
GtkAllocation widget_allocation;
553
GtkRequisition widget_requisition;
554
gfloat xalign, yalign;
558
misc = GTK_MISC (label);
559
widget = GTK_WIDGET (label);
561
gtk_misc_get_alignment (misc, &xalign, &yalign);
562
gtk_misc_get_padding (misc, &xpad, &ypad);
563
gtk_widget_get_allocation (widget, &widget_allocation);
564
gtk_widget_get_requisition (widget, &widget_requisition);
566
if (gtk_widget_get_direction (widget) != GTK_TEXT_DIR_LTR)
567
xalign = 1.0 - xalign;
569
x = floor (widget_allocation.x + (int)xpad
570
+ ((widget_allocation.width - widget_requisition.width - 1) * xalign)
573
y = floor (widget_allocation.y + (int)ypad
574
+ ((widget_allocation.height - widget_requisition.height - 1) * yalign)
587
label_expose_event_cb (GtkLabel *label,
588
GdkEventExpose *event,
602
get_layout_location (label, &x, &y);
604
widget = GTK_WIDGET (label);
605
window = gtk_widget_get_window (widget);
607
gc = gdk_gc_new (window);
608
gdk_gc_set_rgb_fg_color (gc, &color);
609
gdk_gc_set_clip_rectangle (gc, &event->area);
611
gdk_draw_layout_with_colors (window,
615
gtk_label_get_layout (label),
620
gtk_paint_layout (gtk_widget_get_style (widget),
622
gtk_widget_get_state (widget),
628
gtk_label_get_layout (label));
634
label_size_request_cb (GtkLabel *label,
635
GtkRequisition *requisition,
638
requisition->width += 1;
639
requisition->height += 1;