gnome-control-center/typing-break/drw-break-window.c

690 lines
17 KiB
C
Raw Normal View History

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Copyright (C) 2002 CodeFactory AB
* Copyright (C) 2002 Richard Hult <richard@imendio.com>
*
* 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, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <config.h>
#include <string.h>
#include <math.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <gconf/gconf-client.h>
#include <libgnome/gnome-i18n.h>
#include "drwright.h"
#include "drw-break-window.h"
struct _DrwBreakWindowPriv {
GtkWidget *clock_label;
GtkWidget *break_label;
GtkWidget *image;
GtkWidget *postpone_entry;
GtkWidget *postpone_button;
GTimer *timer;
gint break_time;
gchar *break_text;
guint clock_timeout_id;
guint postpone_timeout_id;
};
#define POSTPONE_CANCEL 30*1000
/* Signals */
enum {
DONE,
POSTPONE,
LAST_SIGNAL
};
static void drw_break_window_class_init (DrwBreakWindowClass *klass);
static void drw_break_window_init (DrwBreakWindow *window);
static void drw_break_window_finalize (GObject *object);
static GdkPixbuf * create_tile_pixbuf (GdkPixbuf *dest_pixbuf,
GdkPixbuf *src_pixbuf,
GdkRectangle *field_geom,
guint alpha,
GdkColor *bg_color);
static gboolean clock_timeout_cb (DrwBreakWindow *window);
static void postpone_clicked_cb (GtkWidget *button,
GtkWidget *window);
static gboolean label_expose_event_cb (GtkLabel *label,
GdkEventExpose *event,
gpointer user_data);
static void label_size_request_cb (GtkLabel *label,
GtkRequisition *requisition,
gpointer user_data);
static GObjectClass *parent_class;
static guint signals[LAST_SIGNAL];
GType
drw_break_window_get_type (void)
{
static GType object_type = 0;
if (!object_type) {
static const GTypeInfo object_info = {
sizeof (DrwBreakWindowClass),
NULL, /* base_init */
NULL, /* base_finalize */
(GClassInitFunc) drw_break_window_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (DrwBreakWindow),
0, /* n_preallocs */
(GInstanceInitFunc) drw_break_window_init,
};
object_type = g_type_register_static (GTK_TYPE_WINDOW,
"DrwBreakWindow",
&object_info,
0);
}
return object_type;
}
static void
drw_break_window_class_init (DrwBreakWindowClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
object_class->finalize = drw_break_window_finalize;
signals[POSTPONE] =
g_signal_new ("postpone",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[DONE] =
g_signal_new ("done",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}
static void
drw_break_window_init (DrwBreakWindow *window)
{
DrwBreakWindowPriv *priv;
GtkWidget *vbox;
GtkWidget *hbox;
GtkWidget *frame;
GtkWidget *align;
gchar *str;
GdkPixbuf *tmp_pixbuf, *pixbuf, *tile_pixbuf;
GdkPixmap *pixmap;
GdkRectangle rect;
GdkColor color;
GtkWidget *outer_vbox;
GtkWidget *button_box;
gboolean allow_postpone;
priv = g_new0 (DrwBreakWindowPriv, 1);
window->priv = priv;
priv->break_time = 60 * gconf_client_get_int (gconf_client_get_default (),
GCONF_PATH "/break_time",
NULL);
allow_postpone = gconf_client_get_bool (gconf_client_get_default (),
GCONF_PATH "/allow_postpone",
NULL);
GTK_WINDOW (window)->type = GTK_WINDOW_POPUP;
gtk_window_set_default_size (GTK_WINDOW (window),
gdk_screen_width (),
gdk_screen_height ());
gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
gtk_widget_realize (GTK_WIDGET (window));
tmp_pixbuf = gdk_pixbuf_get_from_drawable (NULL,
gdk_get_default_root_window (),
gdk_colormap_get_system (),
0,
0,
0,
0,
gdk_screen_width (),
gdk_screen_height ());
pixbuf = gdk_pixbuf_new_from_file (IMAGEDIR "/ocean-stripes.png", NULL);
rect.x = 0;
rect.y = 0;
rect.width = gdk_screen_width ();
rect.height = gdk_screen_height ();
color.red = 0;
color.blue = 0;
color.green = 0;
tile_pixbuf = create_tile_pixbuf (NULL,
pixbuf,
&rect,
155,
&color);
g_object_unref (pixbuf);
gdk_pixbuf_composite (tile_pixbuf,
tmp_pixbuf,
0,
0,
gdk_screen_width (),
gdk_screen_height (),
0,
0,
1,
1,
GDK_INTERP_NEAREST,
225);
g_object_unref (tile_pixbuf);
pixmap = gdk_pixmap_new (GTK_WIDGET (window)->window,
gdk_screen_width (),
gdk_screen_height (),
-1);
gdk_pixbuf_render_to_drawable_alpha (tmp_pixbuf,
pixmap,
0,
0,
0,
0,
gdk_screen_width (),
gdk_screen_height (),
GDK_PIXBUF_ALPHA_BILEVEL,
0,
GDK_RGB_DITHER_NONE,
0,
0);
g_object_unref (tmp_pixbuf);
gdk_window_set_back_pixmap (GTK_WIDGET (window)->window, pixmap, FALSE);
g_object_unref (pixmap);
frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE);
gtk_widget_show (frame);
align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
gtk_widget_show (align);
outer_vbox = gtk_vbox_new (FALSE, 0);
gtk_widget_show (outer_vbox);
gtk_container_add (GTK_CONTAINER (window), outer_vbox);
gtk_box_pack_start (GTK_BOX (outer_vbox), align, TRUE, TRUE, 0);
if (allow_postpone) {
button_box = gtk_hbox_new (FALSE, 0);
gtk_widget_show (button_box);
gtk_container_set_border_width (GTK_CONTAINER (button_box), 12);
priv->postpone_button = gtk_button_new_with_label (_("Postpone break"));
gtk_widget_show (priv->postpone_button);
g_signal_connect (priv->postpone_button,
"clicked",
G_CALLBACK (postpone_clicked_cb),
window);
gtk_box_pack_end (GTK_BOX (button_box), priv->postpone_button, FALSE, TRUE, 0);
priv->postpone_entry = gtk_entry_new ();
gtk_entry_set_has_frame (GTK_ENTRY (priv->postpone_entry), FALSE);
gtk_box_pack_end (GTK_BOX (button_box), priv->postpone_entry, FALSE, TRUE, 4);
gtk_box_pack_end (GTK_BOX (outer_vbox), button_box, FALSE, TRUE, 0);
}
vbox = gtk_vbox_new (FALSE, 0);
gtk_widget_show (vbox);
gtk_container_add (GTK_CONTAINER (align), frame);
gtk_container_add (GTK_CONTAINER (frame), vbox);
priv->break_label = gtk_label_new (NULL);
gtk_widget_show (priv->break_label);
g_signal_connect (priv->break_label,
"expose_event",
G_CALLBACK (label_expose_event_cb),
NULL);
g_signal_connect_after (priv->break_label,
"size_request",
G_CALLBACK (label_size_request_cb),
NULL);
str = g_strdup_printf ("<span size=\"xx-large\" foreground=\"white\"><b>%s</b></span>",
_("Take a break!"));
gtk_label_set_markup (GTK_LABEL (priv->break_label), str);
g_free (str);
gtk_box_pack_start (GTK_BOX (vbox), priv->break_label, FALSE, FALSE, 12);
hbox = gtk_hbox_new (FALSE, 0);
gtk_widget_show (hbox);
gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, FALSE, 0);
priv->image = gtk_image_new_from_file (IMAGEDIR "/stop.png");
gtk_misc_set_alignment (GTK_MISC (priv->image), 1, 0.5);
gtk_widget_show (priv->image);
gtk_box_pack_start (GTK_BOX (hbox), priv->image, TRUE, TRUE, 8);
priv->clock_label = gtk_label_new (NULL);
gtk_misc_set_alignment (GTK_MISC (priv->clock_label), 0, 0.5);
gtk_widget_show (priv->clock_label);
gtk_box_pack_start (GTK_BOX (hbox), priv->clock_label, TRUE, TRUE, 8);
g_signal_connect (priv->clock_label,
"expose_event",
G_CALLBACK (label_expose_event_cb),
NULL);
g_signal_connect_after (priv->clock_label,
"size_request",
G_CALLBACK (label_size_request_cb),
NULL);
gtk_window_stick (GTK_WINDOW (window));
priv->timer = g_timer_new ();
/* Make sure we have a valid time label from the start. */
clock_timeout_cb (window);
priv->clock_timeout_id = g_timeout_add (1000,
(GSourceFunc) clock_timeout_cb,
window);
}
static void
drw_break_window_finalize (GObject *object)
{
DrwBreakWindow *window = DRW_BREAK_WINDOW (object);
DrwBreakWindowPriv *priv;
priv = window->priv;
if (priv->clock_timeout_id != 0) {
g_source_remove (priv->clock_timeout_id);
}
if (priv->postpone_timeout_id != 0) {
g_source_remove (priv->postpone_timeout_id);
}
g_free (priv);
window->priv = NULL;
if (G_OBJECT_CLASS (parent_class)->finalize) {
(* G_OBJECT_CLASS (parent_class)->finalize) (object);
}
}
GtkWidget *
drw_break_window_new (void)
{
return g_object_new (DRW_TYPE_BREAK_WINDOW, NULL);
}
static GdkPixbuf *
create_tile_pixbuf (GdkPixbuf *dest_pixbuf,
GdkPixbuf *src_pixbuf,
GdkRectangle *field_geom,
guint alpha,
GdkColor *bg_color)
{
gboolean need_composite;
gboolean use_simple;
gdouble cx, cy;
gdouble colorv;
gint pwidth, pheight;
need_composite = (alpha < 255 || gdk_pixbuf_get_has_alpha (src_pixbuf));
use_simple = (dest_pixbuf == NULL);
if (dest_pixbuf == NULL)
dest_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, field_geom->width, field_geom->height);
if (need_composite && use_simple)
colorv = ((bg_color->red & 0xff00) << 8) |
(bg_color->green & 0xff00) |
((bg_color->blue & 0xff00) >> 8);
else
colorv = 0;
pwidth = gdk_pixbuf_get_width (src_pixbuf);
pheight = gdk_pixbuf_get_height (src_pixbuf);
for (cy = 0; cy < field_geom->height; cy += pheight) {
for (cx = 0; cx < field_geom->width; cx += pwidth) {
if (need_composite && !use_simple)
gdk_pixbuf_composite
(src_pixbuf, dest_pixbuf,
cx, cy,
MIN (pwidth, field_geom->width - cx),
MIN (pheight, field_geom->height - cy),
cx, cy,
1.0, 1.0,
GDK_INTERP_BILINEAR,
alpha);
else if (need_composite && use_simple)
gdk_pixbuf_composite_color
(src_pixbuf, dest_pixbuf,
cx, cy,
MIN (pwidth, field_geom->width - cx),
MIN (pheight, field_geom->height - cy),
cx, cy,
1.0, 1.0,
GDK_INTERP_BILINEAR,
alpha,
65536, 65536, 65536,
colorv, colorv);
else
gdk_pixbuf_copy_area
(src_pixbuf,
0, 0,
MIN (pwidth, field_geom->width - cx),
MIN (pheight, field_geom->height - cy),
dest_pixbuf,
cx, cy);
}
}
return dest_pixbuf;
}
static gboolean
clock_timeout_cb (DrwBreakWindow *window)
{
DrwBreakWindowPriv *priv;
gchar *txt;
gint minutes;
gint seconds;
g_return_val_if_fail (DRW_IS_BREAK_WINDOW (window), FALSE);
priv = window->priv;
seconds = 1 + priv->break_time - g_timer_elapsed (priv->timer, NULL);
seconds = MAX (0, seconds);
if (seconds == 0) {
/* Zero this out so the finalizer doesn't try to remove the
* source, which would be done in the timeout callback ==
* no-no.
*/
priv->clock_timeout_id = 0;
g_signal_emit (window, signals[DONE], 0, NULL);
//gtk_widget_destroy (GTK_WIDGET (window));
return FALSE;
}
minutes = seconds / 60;
seconds -= minutes * 60;
txt = g_strdup_printf ("<span size=\"25000\" foreground=\"white\"><b>%d:%02d</b></span>",
minutes,
seconds);
gtk_label_set_markup (GTK_LABEL (priv->clock_label), txt);
g_free (txt);
return TRUE;
}
static void
postpone_entry_activate_cb (GtkWidget *entry,
DrwBreakWindow *window)
{
const gchar *str;
const gchar *phrase;
str = gtk_entry_get_text (GTK_ENTRY (entry));
phrase = gconf_client_get_string (gconf_client_get_default (),
GCONF_PATH "/unlock_phrase",
NULL);
if (!strcmp (str, phrase)) {
g_signal_emit (window, signals[POSTPONE], 0, NULL);
return;
}
gtk_entry_set_text (GTK_ENTRY (entry), "");
}
static gboolean
grab_on_window (GdkWindow *window,
guint32 activate_time)
{
if ((gdk_pointer_grab (window, TRUE,
GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK |
GDK_POINTER_MOTION_MASK,
NULL, NULL, activate_time) == 0)) {
if (gdk_keyboard_grab (window, TRUE,
activate_time) == 0)
return TRUE;
else {
gdk_pointer_ungrab (activate_time);
return FALSE;
}
}
return FALSE;
}
static gboolean
postpone_cancel_cb (DrwBreakWindow *window)
{
DrwBreakWindowPriv *priv;
priv = window->priv;
gtk_entry_set_text (GTK_ENTRY (priv->postpone_entry), "");
gtk_widget_hide (priv->postpone_entry);
priv->postpone_timeout_id = 0;
return FALSE;
}
static gboolean
postpone_entry_key_press_event_cb (GtkEntry *entry,
GdkEventKey *event,
DrwBreakWindow *window)
{
DrwBreakWindowPriv *priv;
priv = window->priv;
if (event->keyval == GDK_Escape) {
if (priv->postpone_timeout_id) {
g_source_remove (priv->postpone_timeout_id);
}
postpone_cancel_cb (window);
return TRUE;
}
g_source_remove (priv->postpone_timeout_id);
priv->postpone_timeout_id = g_timeout_add (POSTPONE_CANCEL, (GSourceFunc) postpone_cancel_cb, window);
return FALSE;
}
static void
postpone_clicked_cb (GtkWidget *button,
GtkWidget *window)
{
DrwBreakWindow *bw = DRW_BREAK_WINDOW (window);
DrwBreakWindowPriv *priv = bw->priv;
gchar *phrase;
/* Disable the phrase for now. */
phrase = NULL; /*gconf_client_get_string (gconf_client_get_default (),
GCONF_PATH "/unlock_phrase",
NULL);*/
if (!phrase || !phrase[0]) {
g_signal_emit (window, signals[POSTPONE], 0, NULL);
return;
}
if (GTK_WIDGET_VISIBLE (priv->postpone_entry)) {
gtk_widget_activate (priv->postpone_entry);
return;
}
gtk_widget_show (priv->postpone_entry);
priv->postpone_timeout_id = g_timeout_add (POSTPONE_CANCEL, (GSourceFunc) postpone_cancel_cb, bw);
grab_on_window (priv->postpone_entry->window, gtk_get_current_event_time ());
gtk_widget_grab_focus (priv->postpone_entry);
g_signal_connect (priv->postpone_entry,
"activate",
G_CALLBACK (postpone_entry_activate_cb),
bw);
g_signal_connect (priv->postpone_entry,
"key_press_event",
G_CALLBACK (postpone_entry_key_press_event_cb),
bw);
}
static void
get_layout_location (GtkLabel *label,
gint *xp,
gint *yp)
{
GtkMisc *misc;
GtkWidget *widget;
gfloat xalign;
gint x, y;
misc = GTK_MISC (label);
widget = GTK_WIDGET (label);
if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) {
xalign = misc->xalign;
} else {
xalign = 1.0 - misc->xalign;
}
x = floor (widget->allocation.x + (int)misc->xpad
+ ((widget->allocation.width - widget->requisition.width - 1) * xalign)
+ 0.5);
y = floor (widget->allocation.y + (int)misc->ypad
+ ((widget->allocation.height - widget->requisition.height - 1) * misc->yalign)
+ 0.5);
if (xp) {
*xp = x;
}
if (yp) {
*yp = y;
}
}
static gboolean
label_expose_event_cb (GtkLabel *label,
GdkEventExpose *event,
gpointer user_data)
{
gint x, y;
GdkColor color;
GtkWidget *widget;
GdkGC *gc;
color.red = 0;
color.green = 0;
color.blue = 0;
color.pixel = 0;
get_layout_location (label, &x, &y);
widget = GTK_WIDGET (label);
gc = gdk_gc_new (widget->window);
gdk_gc_set_rgb_fg_color (gc, &color);
gdk_gc_set_clip_rectangle (gc, &event->area);
gdk_draw_layout_with_colors (widget->window,
gc,
x + 1,
y + 1,
label->layout,
&color,
NULL);
g_object_unref (gc);
gtk_paint_layout (widget->style,
widget->window,
GTK_WIDGET_STATE (widget),
FALSE,
&event->area,
widget,
"label",
x, y,
label->layout);
return TRUE;
}
static void
label_size_request_cb (GtkLabel *label,
GtkRequisition *requisition,
gpointer user_data)
{
requisition->width += 1;
requisition->height += 1;
}