diff options
author | Jonathan Blandford <jrb@gnome.org> | 2003-06-26 11:40:10 +0000 |
---|---|---|
committer | Jonathan Blandford <jrb@src.gnome.org> | 2003-06-26 11:40:10 +0000 |
commit | 56057239d528687acf95ad294d6872ab6437f1d5 (patch) | |
tree | 44112f75bacecdabcde7b9e645f099e4e2117d8e /typing-break/drw-break-window.c | |
parent | 3ed3f34c81497b7fb09194e0610ffdb677c823be (diff) | |
download | gnome-control-center-56057239d528687acf95ad294d6872ab6437f1d5.tar.gz |
added the typing break to CVS from drwright.
Thu Jun 26 07:38:45 2003 Jonathan Blandford <jrb@gnome.org>
* typing-break/drwright.c: added the typing break to CVS from
drwright.
Diffstat (limited to 'typing-break/drw-break-window.c')
-rw-r--r-- | typing-break/drw-break-window.c | 689 |
1 files changed, 689 insertions, 0 deletions
diff --git a/typing-break/drw-break-window.c b/typing-break/drw-break-window.c new file mode 100644 index 000000000..1d228c11b --- /dev/null +++ b/typing-break/drw-break-window.c @@ -0,0 +1,689 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2002 CodeFactory AB + * Copyright (C) 2002 Richard Hult <rhult@codefactory.se> + * + * 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 "drw-break-window.h" +#include "drw-intl.h" + +struct _DrwBreakWindowPriv { + GtkWidget *clock_label; + GtkWidget *break_label; + GtkWidget *image; + + GtkWidget *unlock_entry; + GtkWidget *unlock_button; + + GTimer *timer; + + gint break_time; + + gchar *break_text; + guint clock_timeout_id; + guint unlock_timeout_id; +}; + +#define UNLOCK_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 unlock_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_unlock; + + priv = g_new0 (DrwBreakWindowPriv, 1); + window->priv = priv; + + priv->break_time = 60 * gconf_client_get_int (gconf_client_get_default (), + "/desktop/gnome/typing_break/break_time", + NULL); + + allow_unlock = gconf_client_get_bool (gconf_client_get_default (), + "/desktop/gnome/typing_break/allow_unlock", + 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_unlock) { + button_box = gtk_hbox_new (FALSE, 0); + gtk_widget_show (button_box); + + gtk_container_set_border_width (GTK_CONTAINER (button_box), 12); + + priv->unlock_button = gtk_button_new_with_label (_("Postpone break")); + gtk_widget_show (priv->unlock_button); + + g_signal_connect (priv->unlock_button, + "clicked", + G_CALLBACK (unlock_clicked_cb), + window); + + gtk_box_pack_end (GTK_BOX (button_box), priv->unlock_button, FALSE, TRUE, 0); + + priv->unlock_entry = gtk_entry_new (); + gtk_entry_set_has_frame (GTK_ENTRY (priv->unlock_entry), FALSE); + + gtk_box_pack_end (GTK_BOX (button_box), priv->unlock_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->unlock_timeout_id != 0) { + g_source_remove (priv->unlock_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 +unlock_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 (), + "/desktop/gnome/typing_break/unlock_phrase", + NULL); + + if (!strcmp (str, phrase)) { + g_signal_emit (window, signals[POSTPONE], 0, NULL); + //gtk_widget_destroy (GTK_WIDGET (window)); + 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 +unlock_cancel_cb (DrwBreakWindow *window) +{ + DrwBreakWindowPriv *priv; + + priv = window->priv; + + gtk_entry_set_text (GTK_ENTRY (priv->unlock_entry), ""); + gtk_widget_hide (priv->unlock_entry); + + priv->unlock_timeout_id = 0; + + return FALSE; +} + +static gboolean +unlock_entry_key_press_event_cb (GtkEntry *entry, + GdkEventKey *event, + DrwBreakWindow *window) +{ + DrwBreakWindowPriv *priv; + + priv = window->priv; + + if (event->keyval == GDK_Escape) { + if (priv->unlock_timeout_id) { + g_source_remove (priv->unlock_timeout_id); + } + + unlock_cancel_cb (window); + + return TRUE; + } + + g_source_remove (priv->unlock_timeout_id); + + priv->unlock_timeout_id = g_timeout_add (UNLOCK_CANCEL, (GSourceFunc) unlock_cancel_cb, window); + + return FALSE; +} + +static void +unlock_clicked_cb (GtkWidget *button, + GtkWidget *window) +{ + DrwBreakWindow *bw = DRW_BREAK_WINDOW (window); + DrwBreakWindowPriv *priv = bw->priv; + gchar *phrase; + + phrase = gconf_client_get_string (gconf_client_get_default (), + "/desktop/gnome/typing_break/unlock_phrase", + NULL); + + if (!phrase || !phrase[0]) { + g_signal_emit (window, signals[POSTPONE], 0, NULL); + + //gtk_widget_destroy (window); + return; + } + + if (GTK_WIDGET_VISIBLE (priv->unlock_entry)) { + gtk_widget_activate (priv->unlock_entry); + return; + } + + gtk_widget_show (priv->unlock_entry); + + priv->unlock_timeout_id = g_timeout_add (UNLOCK_CANCEL, (GSourceFunc) unlock_cancel_cb, bw); + + grab_on_window (priv->unlock_entry->window, gtk_get_current_event_time ()); + + gtk_widget_grab_focus (priv->unlock_entry); + + g_signal_connect (priv->unlock_entry, + "activate", + G_CALLBACK (unlock_entry_activate_cb), + bw); + + g_signal_connect (priv->unlock_entry, + "key_press_event", + G_CALLBACK (unlock_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; +} |