This patch does 2 things: 1) Defines a DrwTimer that we use instead of GTimer. This is just a thin wrapper around g_get_current_time, and it means we can accurately track typing/idle periods based on real-world wall-clock time, which GTimer is apparently not intended to do. 2) The typing monitor has some complicated state handling where it transitions between an IDLE state and a TYPING state. This transition is based on running a callback once per second, and checking whether any keystrokes have been recorded since the last time the callback was called. The actual idle *time* is tracked separately, independently of these two states, but only when we are in the IDLE *state* was the idle *time* checked to see if we should reset the break timer. This leads to a race condition -- if we suspend while in the TYPING state, then eventually we will wake up, notice that no key press has happened in the last second, *reset the idle timer*, transition to the IDLE state, and then check the amount of time on the idle timer. We will thus never notice the amount of time that the computer was suspended. I considered making the IDLE/TYPING transition code smarter, but it turns out the desired behavior for the two states is entirely identical anyway, so rather than adding more complexity to this pointless code, I just diked it out and replaced them both by a single state called RUNNING. Closes bug #430797.
776 lines
17 KiB
C
776 lines
17 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
|
/*
|
|
* Copyright (C) 2003-2005 Imendio HB
|
|
* Copyright (C) 2002-2003 Richard Hult <richard@imendio.com>
|
|
* Copyright (C) 2002 CodeFactory AB
|
|
*
|
|
* 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 <glib/gi18n.h>
|
|
#include <gdk/gdk.h>
|
|
#include <gdk/gdkkeysyms.h>
|
|
#include <gtk/gtk.h>
|
|
#include <gconf/gconf-client.h>
|
|
|
|
#include "drwright.h"
|
|
#include "drw-break-window.h"
|
|
#include "drw-monitor.h"
|
|
#include "drw-utils.h"
|
|
#include "drw-timer.h"
|
|
|
|
#define BLINK_TIMEOUT 200
|
|
#define BLINK_TIMEOUT_MIN 120
|
|
#define BLINK_TIMEOUT_FACTOR 100
|
|
|
|
#define POPUP_ITEM_ENABLED 1
|
|
#define POPUP_ITEM_BREAK 2
|
|
|
|
typedef enum {
|
|
STATE_START,
|
|
STATE_RUNNING,
|
|
STATE_WARN,
|
|
STATE_BREAK_SETUP,
|
|
STATE_BREAK,
|
|
STATE_BREAK_DONE_SETUP,
|
|
STATE_BREAK_DONE
|
|
} DrwState;
|
|
|
|
struct _DrWright {
|
|
/* Widgets. */
|
|
GtkWidget *break_window;
|
|
GList *secondary_break_windows;
|
|
|
|
DrwMonitor *monitor;
|
|
|
|
GtkUIManager *ui_manager;
|
|
|
|
DrwState state;
|
|
DrwTimer *timer;
|
|
DrwTimer *idle_timer;
|
|
|
|
gint last_elapsed_time;
|
|
gint save_last_time;
|
|
|
|
/* Time settings. */
|
|
gint type_time;
|
|
gint break_time;
|
|
gint warn_time;
|
|
|
|
gboolean enabled;
|
|
|
|
guint clock_timeout_id;
|
|
guint blink_timeout_id;
|
|
|
|
gboolean blink_on;
|
|
|
|
GtkStatusIcon *icon;
|
|
|
|
GdkPixbuf *neutral_bar;
|
|
GdkPixbuf *red_bar;
|
|
GdkPixbuf *green_bar;
|
|
GdkPixbuf *disabled_bar;
|
|
GdkPixbuf *composite_bar;
|
|
|
|
GtkWidget *warn_dialog;
|
|
};
|
|
|
|
static void activity_detected_cb (DrwMonitor *monitor,
|
|
DrWright *drwright);
|
|
static gboolean maybe_change_state (DrWright *drwright);
|
|
static gboolean update_tooltip (DrWright *drwright);
|
|
static void break_window_done_cb (GtkWidget *window,
|
|
DrWright *dr);
|
|
static void break_window_postpone_cb (GtkWidget *window,
|
|
DrWright *dr);
|
|
static void break_window_destroy_cb (GtkWidget *window,
|
|
DrWright *dr);
|
|
static void popup_break_cb (GtkAction *action,
|
|
DrWright *dr);
|
|
static void popup_preferences_cb (GtkAction *action,
|
|
DrWright *dr);
|
|
static void popup_about_cb (GtkAction *action,
|
|
DrWright *dr);
|
|
static void init_tray_icon (DrWright *dr);
|
|
static GList * create_secondary_break_windows (void);
|
|
|
|
static const GtkActionEntry actions[] = {
|
|
{"Preferences", GTK_STOCK_PREFERENCES, NULL, NULL, NULL, G_CALLBACK (popup_preferences_cb)},
|
|
{"About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK (popup_about_cb)},
|
|
{"TakeABreak", NULL, N_("_Take a Break"), NULL, NULL, G_CALLBACK (popup_break_cb)}
|
|
};
|
|
|
|
extern gboolean debug;
|
|
|
|
static void
|
|
setup_debug_values (DrWright *dr)
|
|
{
|
|
dr->type_time = 5;
|
|
dr->warn_time = 4;
|
|
dr->break_time = 10;
|
|
}
|
|
|
|
static void
|
|
update_icon (DrWright *dr)
|
|
{
|
|
GdkPixbuf *pixbuf;
|
|
GdkPixbuf *tmp_pixbuf;
|
|
gint width, height;
|
|
gfloat r;
|
|
gint offset;
|
|
gboolean set_pixbuf;
|
|
|
|
if (!dr->enabled) {
|
|
gtk_status_icon_set_from_pixbuf (dr->icon,
|
|
dr->disabled_bar);
|
|
return;
|
|
}
|
|
|
|
tmp_pixbuf = gdk_pixbuf_copy (dr->neutral_bar);
|
|
|
|
width = gdk_pixbuf_get_width (tmp_pixbuf);
|
|
height = gdk_pixbuf_get_height (tmp_pixbuf);
|
|
|
|
set_pixbuf = TRUE;
|
|
|
|
switch (dr->state) {
|
|
case STATE_BREAK:
|
|
case STATE_BREAK_SETUP:
|
|
r = 1;
|
|
break;
|
|
|
|
case STATE_BREAK_DONE:
|
|
case STATE_BREAK_DONE_SETUP:
|
|
case STATE_START:
|
|
r = 0;
|
|
break;
|
|
|
|
default:
|
|
r = (float) (drw_timer_elapsed (dr->timer) + dr->save_last_time) /
|
|
(float) dr->type_time;
|
|
break;
|
|
}
|
|
|
|
offset = CLAMP ((height - 0) * (1.0 - r), 1, height - 0);
|
|
|
|
switch (dr->state) {
|
|
case STATE_WARN:
|
|
pixbuf = dr->red_bar;
|
|
set_pixbuf = FALSE;
|
|
break;
|
|
|
|
case STATE_BREAK_SETUP:
|
|
case STATE_BREAK:
|
|
pixbuf = dr->red_bar;
|
|
break;
|
|
|
|
default:
|
|
pixbuf = dr->green_bar;
|
|
}
|
|
|
|
gdk_pixbuf_composite (pixbuf,
|
|
tmp_pixbuf,
|
|
0,
|
|
offset,
|
|
width,
|
|
height - offset,
|
|
0,
|
|
0,
|
|
1.0,
|
|
1.0,
|
|
GDK_INTERP_BILINEAR,
|
|
255);
|
|
|
|
if (set_pixbuf) {
|
|
gtk_status_icon_set_from_pixbuf (dr->icon,
|
|
tmp_pixbuf);
|
|
}
|
|
|
|
if (dr->composite_bar) {
|
|
g_object_unref (dr->composite_bar);
|
|
}
|
|
|
|
dr->composite_bar = tmp_pixbuf;
|
|
}
|
|
|
|
static gboolean
|
|
blink_timeout_cb (DrWright *dr)
|
|
{
|
|
gfloat r;
|
|
gint timeout;
|
|
|
|
r = (dr->type_time - drw_timer_elapsed (dr->timer) - dr->save_last_time) / dr->warn_time;
|
|
timeout = BLINK_TIMEOUT + BLINK_TIMEOUT_FACTOR * r;
|
|
|
|
if (timeout < BLINK_TIMEOUT_MIN) {
|
|
timeout = BLINK_TIMEOUT_MIN;
|
|
}
|
|
|
|
if (dr->blink_on || timeout == 0) {
|
|
gtk_status_icon_set_from_pixbuf (dr->icon,
|
|
dr->composite_bar);
|
|
} else {
|
|
gtk_status_icon_set_from_pixbuf (dr->icon,
|
|
dr->neutral_bar);
|
|
}
|
|
|
|
dr->blink_on = !dr->blink_on;
|
|
|
|
if (timeout) {
|
|
dr->blink_timeout_id = g_timeout_add (timeout,
|
|
(GSourceFunc) blink_timeout_cb,
|
|
dr);
|
|
} else {
|
|
dr->blink_timeout_id = 0;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
start_blinking (DrWright *dr)
|
|
{
|
|
if (!dr->blink_timeout_id) {
|
|
dr->blink_on = TRUE;
|
|
blink_timeout_cb (dr);
|
|
}
|
|
|
|
/*gtk_widget_show (GTK_WIDGET (dr->icon));*/
|
|
}
|
|
|
|
static void
|
|
stop_blinking (DrWright *dr)
|
|
{
|
|
if (dr->blink_timeout_id) {
|
|
g_source_remove (dr->blink_timeout_id);
|
|
dr->blink_timeout_id = 0;
|
|
}
|
|
|
|
/*gtk_widget_hide (GTK_WIDGET (dr->icon));*/
|
|
}
|
|
|
|
static gboolean
|
|
grab_keyboard_on_window (GdkWindow *window,
|
|
guint32 activate_time)
|
|
{
|
|
GdkGrabStatus status;
|
|
|
|
status = gdk_keyboard_grab (window, TRUE, activate_time);
|
|
if (status == GDK_GRAB_SUCCESS) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
break_window_map_event_cb (GtkWidget *widget,
|
|
GdkEvent *event,
|
|
DrWright *dr)
|
|
{
|
|
grab_keyboard_on_window (dr->break_window->window, gtk_get_current_event_time ());
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
maybe_change_state (DrWright *dr)
|
|
{
|
|
gint elapsed_time;
|
|
gint elapsed_idle_time;
|
|
|
|
if (debug) {
|
|
drw_timer_start (dr->idle_timer);
|
|
}
|
|
|
|
elapsed_time = drw_timer_elapsed (dr->timer) + dr->save_last_time;
|
|
elapsed_idle_time = drw_timer_elapsed (dr->idle_timer);
|
|
|
|
if (elapsed_time > dr->last_elapsed_time + dr->warn_time) {
|
|
/* If the timeout is delayed by the amount of warning time, then
|
|
* we must have been suspended or stopped, so we just start
|
|
* over.
|
|
*/
|
|
dr->state = STATE_START;
|
|
}
|
|
|
|
switch (dr->state) {
|
|
case STATE_START:
|
|
if (dr->break_window) {
|
|
gtk_widget_destroy (dr->break_window);
|
|
dr->break_window = NULL;
|
|
}
|
|
|
|
gtk_status_icon_set_from_pixbuf (dr->icon,
|
|
dr->neutral_bar);
|
|
|
|
dr->save_last_time = 0;
|
|
|
|
drw_timer_start (dr->timer);
|
|
drw_timer_start (dr->idle_timer);
|
|
|
|
if (dr->enabled) {
|
|
dr->state = STATE_RUNNING;
|
|
}
|
|
|
|
update_tooltip (dr);
|
|
stop_blinking (dr);
|
|
break;
|
|
|
|
case STATE_RUNNING:
|
|
case STATE_WARN:
|
|
if (elapsed_idle_time >= dr->break_time) {
|
|
dr->state = STATE_BREAK_DONE_SETUP;
|
|
} else if (elapsed_time >= dr->type_time) {
|
|
dr->state = STATE_BREAK_SETUP;
|
|
} else if (dr->state != STATE_WARN
|
|
&& elapsed_time >= dr->type_time - dr->warn_time) {
|
|
dr->state = STATE_WARN;
|
|
start_blinking (dr);
|
|
}
|
|
break;
|
|
|
|
case STATE_BREAK_SETUP:
|
|
/* Don't allow more than one break window to coexist, can happen
|
|
* if a break is manually enforced.
|
|
*/
|
|
if (dr->break_window) {
|
|
dr->state = STATE_BREAK;
|
|
break;
|
|
}
|
|
|
|
stop_blinking (dr);
|
|
gtk_status_icon_set_from_pixbuf (dr->icon,
|
|
dr->red_bar);
|
|
|
|
drw_timer_start (dr->timer);
|
|
|
|
dr->break_window = drw_break_window_new ();
|
|
|
|
g_signal_connect (dr->break_window, "map_event",
|
|
G_CALLBACK (break_window_map_event_cb),
|
|
dr);
|
|
|
|
g_signal_connect (dr->break_window,
|
|
"done",
|
|
G_CALLBACK (break_window_done_cb),
|
|
dr);
|
|
|
|
g_signal_connect (dr->break_window,
|
|
"postpone",
|
|
G_CALLBACK (break_window_postpone_cb),
|
|
dr);
|
|
|
|
g_signal_connect (dr->break_window,
|
|
"destroy",
|
|
G_CALLBACK (break_window_destroy_cb),
|
|
dr);
|
|
|
|
dr->secondary_break_windows = create_secondary_break_windows ();
|
|
|
|
gtk_widget_show (dr->break_window);
|
|
|
|
dr->save_last_time = elapsed_time;
|
|
dr->state = STATE_BREAK;
|
|
break;
|
|
|
|
case STATE_BREAK:
|
|
if (elapsed_time - dr->save_last_time >= dr->break_time) {
|
|
dr->state = STATE_BREAK_DONE_SETUP;
|
|
}
|
|
break;
|
|
|
|
case STATE_BREAK_DONE_SETUP:
|
|
stop_blinking (dr);
|
|
gtk_status_icon_set_from_pixbuf (dr->icon,
|
|
dr->green_bar);
|
|
|
|
dr->state = STATE_BREAK_DONE;
|
|
break;
|
|
|
|
case STATE_BREAK_DONE:
|
|
dr->state = STATE_START;
|
|
if (dr->break_window) {
|
|
gtk_widget_destroy (dr->break_window);
|
|
dr->break_window = NULL;
|
|
}
|
|
break;
|
|
}
|
|
|
|
dr->last_elapsed_time = elapsed_time;
|
|
|
|
update_icon (dr);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
update_tooltip (DrWright *dr)
|
|
{
|
|
gint elapsed_time, min;
|
|
gchar *str;
|
|
|
|
if (!dr->enabled) {
|
|
gtk_status_icon_set_tooltip_text (dr->icon,
|
|
_("Disabled"));
|
|
return TRUE;
|
|
}
|
|
|
|
elapsed_time = drw_timer_elapsed (dr->timer);
|
|
|
|
min = floor (0.5 + (dr->type_time - elapsed_time - dr->save_last_time) / 60.0);
|
|
|
|
if (min >= 1) {
|
|
str = g_strdup_printf (ngettext("%d minute until the next break",
|
|
"%d minutes until the next break",
|
|
min), min);
|
|
} else {
|
|
str = g_strdup_printf (_("Less than one minute until the next break"));
|
|
}
|
|
|
|
gtk_status_icon_set_tooltip_text (dr->icon,
|
|
str);
|
|
|
|
g_free (str);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
activity_detected_cb (DrwMonitor *monitor,
|
|
DrWright *dr)
|
|
{
|
|
drw_timer_start (dr->idle_timer);
|
|
}
|
|
|
|
static void
|
|
gconf_notify_cb (GConfClient *client,
|
|
guint cnxn_id,
|
|
GConfEntry *entry,
|
|
gpointer user_data)
|
|
{
|
|
DrWright *dr = user_data;
|
|
GtkWidget *item;
|
|
|
|
if (!strcmp (entry->key, GCONF_PATH "/type_time")) {
|
|
if (entry->value->type == GCONF_VALUE_INT) {
|
|
dr->type_time = 60 * gconf_value_get_int (entry->value);
|
|
dr->warn_time = MIN (dr->type_time / 10, 5*60);
|
|
|
|
dr->state = STATE_START;
|
|
}
|
|
}
|
|
else if (!strcmp (entry->key, GCONF_PATH "/break_time")) {
|
|
if (entry->value->type == GCONF_VALUE_INT) {
|
|
dr->break_time = 60 * gconf_value_get_int (entry->value);
|
|
dr->state = STATE_START;
|
|
}
|
|
}
|
|
else if (!strcmp (entry->key, GCONF_PATH "/enabled")) {
|
|
if (entry->value->type == GCONF_VALUE_BOOL) {
|
|
dr->enabled = gconf_value_get_bool (entry->value);
|
|
dr->state = STATE_START;
|
|
|
|
item = gtk_ui_manager_get_widget (dr->ui_manager,
|
|
"/Pop/TakeABreak");
|
|
gtk_widget_set_sensitive (item, dr->enabled);
|
|
|
|
update_tooltip (dr);
|
|
}
|
|
}
|
|
|
|
maybe_change_state (dr);
|
|
}
|
|
|
|
static void
|
|
popup_break_cb (GtkAction *action, DrWright *dr)
|
|
{
|
|
if (dr->enabled) {
|
|
dr->state = STATE_BREAK_SETUP;
|
|
maybe_change_state (dr);
|
|
}
|
|
}
|
|
|
|
static void
|
|
popup_preferences_cb (GtkAction *action, DrWright *dr)
|
|
{
|
|
GdkScreen *screen;
|
|
GError *error = NULL;
|
|
GtkWidget *menu;
|
|
|
|
menu = gtk_ui_manager_get_widget (dr->ui_manager, "/Pop");
|
|
screen = gtk_widget_get_screen (menu);
|
|
|
|
if (!gdk_spawn_command_line_on_screen (screen, "gnome-keyboard-properties --typing-break", &error)) {
|
|
GtkWidget *error_dialog;
|
|
|
|
error_dialog = gtk_message_dialog_new (NULL, 0,
|
|
GTK_MESSAGE_ERROR,
|
|
GTK_BUTTONS_CLOSE,
|
|
_("Unable to bring up the typing break properties dialog with the following error: %s"),
|
|
error->message);
|
|
g_signal_connect (error_dialog,
|
|
"response",
|
|
G_CALLBACK (gtk_widget_destroy), NULL);
|
|
gtk_window_set_resizable (GTK_WINDOW (error_dialog), FALSE);
|
|
gtk_widget_show (error_dialog);
|
|
|
|
g_error_free (error);
|
|
}
|
|
}
|
|
|
|
static void
|
|
popup_about_cb (GtkAction *action, DrWright *dr)
|
|
{
|
|
gint i;
|
|
gchar *authors[] = {
|
|
N_("Written by Richard Hult <richard@imendio.com>"),
|
|
N_("Eye candy added by Anders Carlsson"),
|
|
NULL
|
|
};
|
|
|
|
for (i = 0; authors [i]; i++)
|
|
authors [i] = _(authors [i]);
|
|
|
|
gtk_show_about_dialog (NULL,
|
|
"authors", authors,
|
|
"comments", _("A computer break reminder."),
|
|
"logo-icon-name", "typing-monitor",
|
|
"translator-credits", _("translator-credits"),
|
|
"version", VERSION,
|
|
NULL);
|
|
}
|
|
|
|
static void
|
|
popup_menu_cb (GtkWidget *widget,
|
|
guint button,
|
|
guint activate_time,
|
|
DrWright *dr)
|
|
{
|
|
GtkWidget *menu;
|
|
|
|
menu = gtk_ui_manager_get_widget (dr->ui_manager, "/Pop");
|
|
|
|
gtk_menu_popup (GTK_MENU (menu),
|
|
NULL,
|
|
NULL,
|
|
gtk_status_icon_position_menu,
|
|
dr->icon,
|
|
0,
|
|
gtk_get_current_event_time());
|
|
}
|
|
|
|
static void
|
|
break_window_done_cb (GtkWidget *window,
|
|
DrWright *dr)
|
|
{
|
|
gtk_widget_destroy (dr->break_window);
|
|
|
|
dr->state = STATE_BREAK_DONE_SETUP;
|
|
dr->break_window = NULL;
|
|
|
|
update_tooltip (dr);
|
|
maybe_change_state (dr);
|
|
}
|
|
|
|
static void
|
|
break_window_postpone_cb (GtkWidget *window,
|
|
DrWright *dr)
|
|
{
|
|
gint elapsed_time;
|
|
|
|
gtk_widget_destroy (dr->break_window);
|
|
|
|
dr->state = STATE_RUNNING;
|
|
dr->break_window = NULL;
|
|
|
|
elapsed_time = drw_timer_elapsed (dr->timer);
|
|
|
|
if (elapsed_time + dr->save_last_time >= dr->type_time) {
|
|
/* Typing time has expired, but break was postponed.
|
|
* We'll warn again in (elapsed * sqrt (typing_time))^2 */
|
|
gfloat postpone_time = (((float) elapsed_time) / dr->break_time)
|
|
* sqrt (dr->type_time);
|
|
postpone_time *= postpone_time;
|
|
dr->save_last_time = dr->type_time - MAX (dr->warn_time, (gint) postpone_time);
|
|
}
|
|
|
|
drw_timer_start (dr->timer);
|
|
maybe_change_state (dr);
|
|
update_icon (dr);
|
|
update_tooltip (dr);
|
|
}
|
|
|
|
static void
|
|
break_window_destroy_cb (GtkWidget *window,
|
|
DrWright *dr)
|
|
{
|
|
GList *l;
|
|
|
|
for (l = dr->secondary_break_windows; l; l = l->next) {
|
|
gtk_widget_destroy (l->data);
|
|
}
|
|
|
|
g_list_free (dr->secondary_break_windows);
|
|
dr->secondary_break_windows = NULL;
|
|
}
|
|
|
|
static void
|
|
init_tray_icon (DrWright *dr)
|
|
{
|
|
dr->icon = gtk_status_icon_new_from_pixbuf (dr->neutral_bar);
|
|
|
|
update_tooltip (dr);
|
|
update_icon (dr);
|
|
|
|
g_signal_connect (dr->icon,
|
|
"popup_menu",
|
|
G_CALLBACK (popup_menu_cb),
|
|
dr);
|
|
}
|
|
|
|
static GList *
|
|
create_secondary_break_windows (void)
|
|
{
|
|
GdkDisplay *display;
|
|
GdkScreen *screen;
|
|
GtkWidget *window;
|
|
gint i;
|
|
GList *windows = NULL;
|
|
|
|
display = gdk_display_get_default ();
|
|
|
|
for (i = 0; i < gdk_display_get_n_screens (display); i++) {
|
|
screen = gdk_display_get_screen (display, i);
|
|
|
|
if (screen == gdk_screen_get_default ()) {
|
|
/* Handled by DrwBreakWindow. */
|
|
continue;
|
|
}
|
|
|
|
window = gtk_window_new (GTK_WINDOW_POPUP);
|
|
|
|
windows = g_list_prepend (windows, window);
|
|
|
|
gtk_window_set_screen (GTK_WINDOW (window), screen);
|
|
|
|
gtk_window_set_default_size (GTK_WINDOW (window),
|
|
gdk_screen_get_width (screen),
|
|
gdk_screen_get_height (screen));
|
|
|
|
gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
|
|
drw_setup_background (GTK_WIDGET (window));
|
|
gtk_window_stick (GTK_WINDOW (window));
|
|
gtk_widget_show (window);
|
|
}
|
|
|
|
return windows;
|
|
}
|
|
|
|
DrWright *
|
|
drwright_new (void)
|
|
{
|
|
DrWright *dr;
|
|
GtkWidget *item;
|
|
GConfClient *client;
|
|
GtkActionGroup *action_group;
|
|
|
|
static const char ui_description[] =
|
|
"<ui>"
|
|
" <popup name='Pop'>"
|
|
" <menuitem action='Preferences'/>"
|
|
" <menuitem action='About'/>"
|
|
" <separator/>"
|
|
" <menuitem action='TakeABreak'/>"
|
|
" </popup>"
|
|
"</ui>";
|
|
|
|
dr = g_new0 (DrWright, 1);
|
|
|
|
client = gconf_client_get_default ();
|
|
|
|
gconf_client_add_dir (client,
|
|
GCONF_PATH,
|
|
GCONF_CLIENT_PRELOAD_NONE,
|
|
NULL);
|
|
|
|
gconf_client_notify_add (client, GCONF_PATH,
|
|
gconf_notify_cb,
|
|
dr,
|
|
NULL,
|
|
NULL);
|
|
|
|
dr->type_time = 60 * gconf_client_get_int (
|
|
client, GCONF_PATH "/type_time", NULL);
|
|
|
|
dr->warn_time = MIN (dr->type_time / 12, 60*3);
|
|
|
|
dr->break_time = 60 * gconf_client_get_int (
|
|
client, GCONF_PATH "/break_time", NULL);
|
|
|
|
dr->enabled = gconf_client_get_bool (
|
|
client,
|
|
GCONF_PATH "/enabled",
|
|
NULL);
|
|
|
|
g_object_unref (client);
|
|
|
|
if (debug) {
|
|
setup_debug_values (dr);
|
|
}
|
|
|
|
dr->ui_manager = gtk_ui_manager_new ();
|
|
|
|
action_group = gtk_action_group_new ("MenuActions");
|
|
gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
|
|
gtk_action_group_add_actions (action_group, actions, G_N_ELEMENTS (actions), dr);
|
|
gtk_ui_manager_insert_action_group (dr->ui_manager, action_group, 0);
|
|
gtk_ui_manager_add_ui_from_string (dr->ui_manager, ui_description, -1, NULL);
|
|
|
|
item = gtk_ui_manager_get_widget (dr->ui_manager, "/Pop/TakeABreak");
|
|
gtk_widget_set_sensitive (item, dr->enabled);
|
|
|
|
dr->timer = drw_timer_new ();
|
|
dr->idle_timer = drw_timer_new ();
|
|
|
|
dr->state = STATE_START;
|
|
|
|
dr->monitor = drw_monitor_new ();
|
|
|
|
g_signal_connect (dr->monitor,
|
|
"activity",
|
|
G_CALLBACK (activity_detected_cb),
|
|
dr);
|
|
|
|
dr->neutral_bar = gdk_pixbuf_new_from_file (IMAGEDIR "/bar.png", NULL);
|
|
dr->red_bar = gdk_pixbuf_new_from_file (IMAGEDIR "/bar-red.png", NULL);
|
|
dr->green_bar = gdk_pixbuf_new_from_file (IMAGEDIR "/bar-green.png", NULL);
|
|
dr->disabled_bar = gdk_pixbuf_new_from_file (IMAGEDIR "/bar-disabled.png", NULL);
|
|
|
|
init_tray_icon (dr);
|
|
|
|
g_timeout_add_seconds (12,
|
|
(GSourceFunc) update_tooltip,
|
|
dr);
|
|
g_timeout_add_seconds (1,
|
|
(GSourceFunc) maybe_change_state,
|
|
dr);
|
|
|
|
return dr;
|
|
}
|