/* GKrellM
|  Copyright (C) 1999-2005 Bill Wilson
|
|  Author:  Bill Wilson    bill@gkrellm.net
|  Latest versions might be found at:  http://gkrellm.net
|
|  This program is free software which I release under the GNU General Public
|  License. You may redistribute and/or modify this program under the terms
|  of that 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. Version 2 is in the
|  COPYRIGHT file in the top level directory of this distribution.
| 
|  To get a copy of the GNU General Puplic License, write to the Free Software
|  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "gkrellm.h"
#include "gkrellm-private.h"

#if defined(WIN32)
#define	STRFTIME_24_HOUR	"%#H:%M"
#define STRFTIME_12_HOUR	"%#I:%M"
#else
#define	STRFTIME_24_HOUR	"%k:%M"
#define STRFTIME_12_HOUR	"%l:%M"
#endif


static GkrellmMonitor
				*mon_clock,
				*mon_cal;

static GkrellmPanel
				*pclock,
				*pcal;

static GkrellmDecal
				*d_wday,
				*d_mday,
				*d_month,
				*d_clock,
				*d_seconds;


static GkrellmLauncher
				clock_launch,
				cal_launch;

typedef struct
	{
	GkrellmTextstyle ts;
	gint		width,
				height,
				baseline,
				y_ink;
	}
	Extents;

static gboolean	cal_enable,
				clock_enable,
				clock_12hr_sec_enable,
				clock_24hr_sec_enable,
				clock_24hr_mode,
				loop_chime_enable;

static gint		x_clock_seconds,
				x_clock_no_seconds;


static gchar	*hour_chime_command,
				*quarter_chime_command;

typedef struct
	{
	gchar	*command;
	gint	count;
	}
	ChimeData;

static Extents	wday_extents,
				mday_extents,
				month_extents,
				time_extents,
				sec_extents,
				ampm_extents;

static gint		clock_style_id,
				cal_style_id;
static gboolean	chime_block;

struct tm		gkrellm_current_tm;


struct tm *
gkrellm_get_current_time(void)
	{
	return &gkrellm_current_tm;
	}

static void
string_extents(gchar *text, Extents *ext)
	{
	gkrellm_text_extents(ext->ts.font, text, strlen(text),
			&ext->width, &ext->height, &ext->baseline, &ext->y_ink);
	}

static gpointer
chime_func(gpointer data)
	{
	ChimeData	*chime = (ChimeData *)data;
	gint		counter;

	if (strlen(chime->command)) 
		{
		if (chime->count > 12) 
			chime->count -= 12;

		for (counter = 0; counter < chime -> count; counter ++)
			g_spawn_command_line_sync(chime->command,
						NULL, NULL, NULL, NULL /* GError */);
		}
	g_free(chime->command);
	g_free(chime);
	return NULL;
	}

static gchar *
utf8_from_strftime(gchar *buf)
	{
	gchar	*utf8 = NULL;

	if (   g_utf8_validate(buf, -1, NULL)
		|| (utf8 = g_locale_to_utf8(buf, -1, NULL, NULL, NULL)) == NULL
	   )
		utf8 = g_strdup(buf);

	return utf8;
	}


static void
draw_cal(void)
	{
	struct tm 	*t;
	gchar		*utf8, buf[32];
	gint		w;

	if (!cal_enable)
		return;
	if (_GK.client_mode)
		t = gkrellm_client_server_time();
	else
		t = &gkrellm_current_tm;

	strftime(buf, sizeof(buf), "%a", t);  /* Abbreviated weekday name */
	utf8 = utf8_from_strftime(buf);
	w = gkrellm_gdk_string_width(d_wday->text_style.font, utf8);
	d_wday->x_off = (w < d_wday->w) ? (d_wday->w - w) / 2 : 0;
	gkrellm_draw_decal_text(pcal, d_wday, utf8, -1);
	g_free(utf8);

	/*strftime(buf, sizeof(buf), "%e", t);*/  /* Day in month 1-31 */
	snprintf(buf, sizeof(buf), "%d", t->tm_mday);
	w = gkrellm_gdk_string_width(d_mday->text_style.font, buf);
	d_mday->x_off = (w < d_mday->w) ? (d_mday->w - w) / 2 : 0;
	gkrellm_draw_decal_text(pcal, d_mday, buf, -1);

	strftime(buf, sizeof(buf), "%b", t); /* Abbreviated month name */
	utf8 = utf8_from_strftime(buf);
	gkrellm_draw_decal_text(pcal, d_month, utf8, -1);
	g_free(utf8);

	gkrellm_draw_panel_layers(pcal);
	}


static void
draw_clock(gboolean update_minutes)
	{
	struct tm 	*t;
	gchar		buf[32], sec_ampm[32], *utf8;
	gint		w;

	if (!clock_enable)
		return;

	if (_GK.client_mode)
		t = gkrellm_client_server_time();
	else
		t = &gkrellm_current_tm;

	if (update_minutes)
		{
		if (clock_24hr_mode)
			strftime(buf, sizeof(buf), STRFTIME_24_HOUR, t);
		else
			strftime(buf, sizeof(buf), STRFTIME_12_HOUR, t);
		w = gkrellm_gdk_string_width(d_clock->text_style.font, buf);
		d_clock->x_off = (w < d_clock->w) ? (d_clock->w - w) / 2 : 0;
		gkrellm_draw_decal_text(pclock, d_clock, buf, -1);
		}
	if (   (!clock_24hr_mode && clock_12hr_sec_enable)
		|| (clock_24hr_mode && clock_24hr_sec_enable)
	   )
		{
		strftime(sec_ampm, sizeof(sec_ampm), "%S", t);	/* seconds 00-59 */
		gkrellm_draw_decal_text(pclock, d_seconds, sec_ampm, -1);
		}
	else if (! clock_24hr_mode)
		{
		strftime(sec_ampm, sizeof(sec_ampm), "%p", t);	/* AM/PM */
		if (sec_ampm[0] == '\0')		/* locale doesn't support AM/PM */
			strcpy(sec_ampm, "--");
		if (   g_utf8_validate(sec_ampm, -1, NULL)
			|| (utf8 = g_locale_to_utf8(sec_ampm, -1, NULL, NULL,NULL)) == NULL
		   )
			utf8 = g_strdup(sec_ampm);
		gkrellm_draw_decal_text(pclock, d_seconds, utf8, -1);
		g_free(utf8);
		}
	gkrellm_draw_panel_layers(pclock);
	}

static void
update_clock(void)
	{
	struct tm		*ptm;
	gboolean		update_minutes = FALSE;
	static gint		min_prev, hour_prev = -1, sec_prev = -1;
	ChimeData		*chime;

	if (_GK.client_mode)
		{
		ptm = gkrellm_client_server_time();
		if (sec_prev == ptm->tm_sec)
			return;
		sec_prev = ptm->tm_sec;
		}
	else
		{
		if (!GK.second_tick)
			return;
		ptm = &gkrellm_current_tm;
		}

	if (   ptm->tm_min != min_prev
		|| ptm->tm_hour != hour_prev
	   )
		{
		update_minutes = TRUE;
		draw_cal();
		if (ptm->tm_hour != hour_prev && hour_prev != -1)
			{
			if (!chime_block && hour_chime_command && *hour_chime_command) 
				{
				chime = g_new0(ChimeData, 1);
				chime -> command = strdup(hour_chime_command);
				chime -> count = loop_chime_enable ? ptm->tm_hour : 1;
				g_thread_create(chime_func, chime, FALSE, NULL);
				}
			}
		else
			{
			if (   !chime_block && (ptm->tm_min % 15) == 0
				&& quarter_chime_command && *quarter_chime_command
			   ) 
				{
				chime = g_new0(ChimeData, 1);
				chime -> command = strdup(quarter_chime_command);
				chime -> count = 1;
				g_thread_create(chime_func, chime, FALSE, NULL);
				}
			}
		}
	draw_clock(update_minutes);
	min_prev = ptm->tm_min;
	hour_prev = ptm->tm_hour;
	}

static gint
expose_event(GtkWidget *widget, GdkEventExpose *ev)
	{
	GdkPixmap	*pixmap = NULL;

	if (widget == pcal->drawing_area)
		pixmap = pcal->pixmap;
	else if (widget == pclock->drawing_area)
		pixmap = pclock->pixmap;
	if (pixmap)
		gdk_draw_drawable(widget->window, gkrellm_draw_GC(1), pixmap,
				ev->area.x, ev->area.y, ev->area.x, ev->area.y,
				ev->area.width, ev->area.height);
	return FALSE;
	}

static gint
cb_panel_press(GtkWidget *widget, GdkEventButton *ev)
	{
	if (ev->button == 3)
		gkrellm_open_config_window(mon_clock);
	return FALSE;
	}

static void
cal_visibility(void)
	{
	if (cal_enable)
		{
		gkrellm_panel_show(pcal);
		gkrellm_spacers_show(mon_cal);
		}
	else
		{
		gkrellm_panel_hide(pcal);
		gkrellm_spacers_hide(mon_cal);
		}
	}

static void
create_calendar(GtkWidget *vbox, gint first_create)
	{
	GkrellmStyle	*style;
	Extents			ext;
	gchar			buf[32], *utf8, *str = NULL;
	gint			m, d, w;
	struct tm		tm = *gkrellm_get_current_time();

	if (first_create)
		pcal = gkrellm_panel_new0();
	style = gkrellm_meter_style(cal_style_id);
	ext.ts = *gkrellm_meter_textstyle(cal_style_id);

	/* Get max weekday name height for current locale
	*/
	wday_extents.width = wday_extents.height = 0;
	wday_extents.baseline = wday_extents.y_ink = 0;
	for (d = 0; d < 6; ++d)
		{
		tm.tm_wday = d;
		strftime(buf, sizeof(buf), "%a", &tm);  /* Abbreviated weekday name */
		utf8 = utf8_from_strftime(buf);
		string_extents(utf8, &ext);
		wday_extents.baseline = ext.baseline;	/* not used */
		if (ext.width > wday_extents.width)
			wday_extents.width = ext.width;
		if (ext.height > wday_extents.height)
			{
			str = utf8;
			wday_extents.height = ext.height;	/* not used */
			wday_extents.y_ink = ext.y_ink;		/* not used */
			}
		else
			g_free(utf8);
		}
	wday_extents.ts = *gkrellm_meter_textstyle(cal_style_id);
	d_wday = gkrellm_create_decal_text(pcal, str, &wday_extents.ts,
				style, 0, -1 /*top margin*/, wday_extents.width + 2);
	g_free(str);

	mday_extents.ts = *gkrellm_meter_alt_textstyle(cal_style_id);
	string_extents("28", &mday_extents);
	d_mday = gkrellm_create_decal_text(pcal, "28", &mday_extents.ts,
				style, 0, -1 /*top margin*/, mday_extents.width + 2);

	/* Get max month name height for current locale
	*/
	month_extents.width = month_extents.height = 0;
	month_extents.baseline = month_extents.y_ink = 0;
	for (m = 0; m < 11; ++m)
		{
		tm.tm_mon = m;
		strftime(buf, sizeof(buf), "%b", &tm); /* Abbreviated month name */
		utf8 = utf8_from_strftime(buf);
		string_extents(utf8, &ext);
		month_extents.baseline = ext.baseline;	/* not used */
		if (ext.width > month_extents.width)
			month_extents.width = ext.width;
		if (ext.height > month_extents.height)
			{
			str = utf8;
			month_extents.height = ext.height;	/* not used */
			month_extents.y_ink = ext.y_ink;		/* not used */
			}
		else
			g_free(utf8);
		}
	month_extents.ts = *gkrellm_meter_textstyle(cal_style_id);
	d_month = gkrellm_create_decal_text(pcal, str, &month_extents.ts,
				style, 0, -1 /*top margin*/, month_extents.width + 2);
	g_free(str);

	w = d_wday->w + d_mday->w + d_month->w;
	d_wday->x = (gkrellm_chart_width() - w) / 2;
	d_mday->x = d_wday->x + d_wday->w;
	d_month->x = d_mday->x + d_mday->w;
	if (d_wday->h < d_mday->h)
		{
		d_wday->y = d_mday->y + d_mday->h - d_wday->h - 1;
		d_month->y = d_wday->y;
		}
	else if (d_mday->h < d_wday->h)
		d_mday->y = d_wday->y + d_wday->h - d_mday->h - 1;
	gkrellm_panel_configure(pcal, NULL, style);
	gkrellm_panel_create(vbox, mon_cal, pcal);

	if (first_create)
		{
		/* Help the motion out hack. If starting a move in host panel and mouse
		|  jerks into the pclock/pcal drawing areas, we stop moving unless:
		*/
		extern void gkrellm_motion(GtkWidget *, GdkEventMotion *, gpointer);

		g_signal_connect(G_OBJECT(pcal->drawing_area), "motion_notify_event",
				G_CALLBACK(gkrellm_motion), NULL);

		g_signal_connect(G_OBJECT (pcal->drawing_area), "expose_event",
				G_CALLBACK(expose_event), NULL);
		g_signal_connect(G_OBJECT(pcal->drawing_area), "button_press_event",
				G_CALLBACK(cb_panel_press), NULL);
		}
	gkrellm_setup_launcher(pcal, &cal_launch, METER_PANEL_TYPE, 0);

	cal_visibility();
	draw_cal();
	}

static void
set_seconds_display()
	{
	if (   !clock_24hr_mode
		|| (clock_24hr_mode && clock_24hr_sec_enable)
	   )
		{
		gkrellm_move_decal(pclock, d_clock, x_clock_seconds, d_clock->y);
		gkrellm_make_decal_visible(pclock, d_seconds);
		}
	else
		{
		gkrellm_move_decal(pclock, d_clock, x_clock_no_seconds, d_clock->y);
		gkrellm_make_decal_invisible(pclock, d_seconds);
		}
	}

static void
clock_visibility(void)
	{
	if (clock_enable)
		{
		gkrellm_panel_show(pclock);
		gkrellm_spacers_show(mon_clock);
		}
	else
		{
		gkrellm_panel_hide(pclock);
		gkrellm_spacers_hide(mon_clock);
		}
	}

static void
create_clock(GtkWidget *vbox, gint first_create)
	{
	GkrellmStyle	*style;

	if (first_create)
		pclock = gkrellm_panel_new0();
	style = gkrellm_meter_style(clock_style_id);

	time_extents.ts = *gkrellm_meter_textstyle(clock_style_id);
	string_extents("00:00", &time_extents);
	sec_extents.ts = *gkrellm_meter_alt_textstyle(clock_style_id);
	string_extents("8M", &sec_extents);
	ampm_extents.ts = *gkrellm_meter_alt_textstyle(clock_style_id);
	string_extents("8M", &ampm_extents);

	d_clock = gkrellm_create_decal_text(pclock, "88", &time_extents.ts,
				style, 0, -1 /*top margin*/, time_extents.width + 2);
	d_seconds = gkrellm_create_decal_text(pclock, "88", &sec_extents.ts,
				style, 0, -1, sec_extents.width + 2);

	x_clock_seconds = (gkrellm_chart_width() - d_clock->w - d_seconds->w) / 2;
	x_clock_no_seconds = (gkrellm_chart_width() - d_clock->w) / 2;
	d_seconds->x = x_clock_seconds + d_clock->w + 2;
	d_seconds->y = d_clock->y + d_clock->h - d_seconds->h;

	gkrellm_panel_configure(pclock, NULL, style);
	gkrellm_panel_create(vbox, mon_clock, pclock);

	if (first_create)
		{
		g_signal_connect(G_OBJECT (pclock->drawing_area), "expose_event",
				G_CALLBACK(expose_event), NULL);
		g_signal_connect(G_OBJECT(pclock->drawing_area), "button_press_event",
				G_CALLBACK(cb_panel_press), NULL);
		}
	gkrellm_setup_launcher(pclock, &clock_launch, METER_PANEL_TYPE, 0);

	set_seconds_display();

	clock_visibility();
	draw_clock(TRUE);
	}

#define	CLOCK_CONFIG_KEYWORD	"clock_cal"

static void
save_clock_cal_config(FILE *f)
	{
	fprintf(f, "%s clock_launch %s\n", CLOCK_CONFIG_KEYWORD,
				clock_launch.command);
	fprintf(f, "%s clock_tooltip %s\n", CLOCK_CONFIG_KEYWORD,
				clock_launch.tooltip_comment);
	fprintf(f, "%s cal_launch %s\n", CLOCK_CONFIG_KEYWORD,
				cal_launch.command);
	fprintf(f, "%s cal_tooltip %s\n", CLOCK_CONFIG_KEYWORD,
				cal_launch.tooltip_comment);
	fprintf(f, "%s hour_chime_command %s\n", CLOCK_CONFIG_KEYWORD,
				hour_chime_command);
	fprintf(f, "%s quarter_chime_command %s\n", CLOCK_CONFIG_KEYWORD,
				quarter_chime_command);
    fprintf(f, "%s loop_chime_enable %d\n", CLOCK_CONFIG_KEYWORD,
               loop_chime_enable);
	fprintf(f, "%s clock_options %d %d %d %d\n", CLOCK_CONFIG_KEYWORD,
				clock_enable, clock_12hr_sec_enable, clock_24hr_mode, 
				clock_24hr_sec_enable);
	fprintf(f, "%s cal_options %d\n", CLOCK_CONFIG_KEYWORD,
				cal_enable);
	}

static void
load_clock_cal_config(gchar *arg)
	{
	gchar	config[32], item[CFG_BUFSIZE];
	gint	n;

	n = sscanf(arg, "%31s %[^\n]", config, item);
	if (n == 2)
		{
		if (!strcmp(config, "clock_launch"))
			clock_launch.command = g_strdup(item);
		else if (!strcmp(config, "clock_tooltip"))
			clock_launch.tooltip_comment = g_strdup(item);
		else if (!strcmp(config, "cal_launch"))
			cal_launch.command = g_strdup(item);
		else if (!strcmp(config, "cal_tooltip"))
			cal_launch.tooltip_comment = g_strdup(item);
		else if (!strcmp(config, "hour_chime_command"))
			gkrellm_dup_string(&hour_chime_command, item);
		else if (!strcmp(config, "quarter_chime_command"))
			gkrellm_dup_string(&quarter_chime_command, item);
		else if (!strcmp(config, "loop_chime_enable"))
			sscanf(item, "%d", &loop_chime_enable);
		else if (!strcmp(config, "clock_options"))
			sscanf(item, "%d %d %d %d", &clock_enable, &clock_12hr_sec_enable,
					&clock_24hr_mode, &clock_24hr_sec_enable);
		else if (!strcmp(config, "cal_options"))
			sscanf(item, "%d", &cal_enable);
		}
	}

/* --------------------------------------------------------------------- */
static GtkWidget	*cal_launch_entry,
					*cal_tooltip_entry,
					*clock_launch_entry,
					*clock_tooltip_entry,
                    *hour_chime_entry,
                    *quarter_chime_entry,
					*clock_enable_button,
					*cal_enable_button,
					*loop_chime_button,
					*clock_24hr_button,
					*clock_24hr_sec_button,
					*clock_12hr_sec_button;


static void
set_seconds_buttons_sensitivity(void)
	{
	if (clock_24hr_mode)
		{
		gtk_widget_set_sensitive(clock_24hr_sec_button, TRUE);
		gtk_widget_set_sensitive(clock_12hr_sec_button, FALSE);
		}
	else
		{
		gtk_widget_set_sensitive(clock_24hr_sec_button, FALSE);
		gtk_widget_set_sensitive(clock_12hr_sec_button, TRUE);
		}
	}


static void
cb_clock_cal(GtkWidget *widget, gpointer data)
	{
	loop_chime_enable = GTK_TOGGLE_BUTTON(loop_chime_button)->active;
	clock_24hr_mode = GTK_TOGGLE_BUTTON (clock_24hr_button)->active;
	clock_12hr_sec_enable = GTK_TOGGLE_BUTTON (clock_12hr_sec_button)->active;
	clock_24hr_sec_enable = GTK_TOGGLE_BUTTON (clock_24hr_sec_button)->active;
	set_seconds_buttons_sensitivity();

	clock_enable = GTK_TOGGLE_BUTTON(clock_enable_button)->active;
	clock_visibility();

	cal_enable = GTK_TOGGLE_BUTTON(cal_enable_button)->active;
	cal_visibility();

	set_seconds_display();
	draw_cal();
	draw_clock(TRUE);
	}

static void
cb_launch_entry(GtkWidget *widget, gpointer data)
	{
	gint	which = GPOINTER_TO_INT(data);

	if (which)
		gkrellm_apply_launcher(&cal_launch_entry, &cal_tooltip_entry,
					pcal, &cal_launch, gkrellm_launch_button_cb);
	else
		gkrellm_apply_launcher(&clock_launch_entry, &clock_tooltip_entry,
					pclock, &clock_launch, gkrellm_launch_button_cb);
	}

static void
cb_chime_entry(GtkWidget *widget, gpointer data)
	{
	gint	which    = (GPOINTER_TO_INT(data)) & 0x1;
	gint	activate = (GPOINTER_TO_INT(data)) & 0x10;

	/* If editing the chime commands, block them until config is destroyed
	|  or we get a "activate".
	*/
	chime_block = activate ? FALSE : TRUE;
	if (which)
	    gkrellm_dup_string(&hour_chime_command,
				gkrellm_gtk_entry_get_text(&hour_chime_entry));
	else
	    gkrellm_dup_string(&quarter_chime_command,
				gkrellm_gtk_entry_get_text(&quarter_chime_entry));
	}

static void
config_destroyed(void)
	{
	chime_block = FALSE;
	}

static void
create_clock_tab(GtkWidget *tab_vbox)
	{
	GtkWidget	*tabs;
	GtkWidget	*table, *vbox, *vbox1, *hbox;

	tabs = gtk_notebook_new();
	gtk_notebook_set_tab_pos(GTK_NOTEBOOK(tabs), GTK_POS_TOP);
	gtk_box_pack_start(GTK_BOX(tab_vbox), tabs, TRUE, TRUE, 0);
	g_signal_connect(G_OBJECT(tabs),"destroy",
				G_CALLBACK(config_destroyed), NULL);

	vbox = gkrellm_gtk_framed_notebook_page(tabs, _("Options"));

	vbox1 = gkrellm_gtk_framed_vbox(vbox, _("Calendar"), 4, FALSE, 0, 2);
	gkrellm_gtk_check_button_connected(vbox1, &cal_enable_button,
				cal_enable, FALSE, FALSE, 2,
				cb_clock_cal, NULL,
				_("Enable"));

	vbox1 = gkrellm_gtk_framed_vbox_end(vbox, _("Clock"), 4, FALSE, 0, 2);

	gkrellm_gtk_check_button_connected(vbox1, &clock_enable_button,
				clock_enable, FALSE, FALSE, 6,
				cb_clock_cal, NULL,
				_("Enable"));

	gkrellm_gtk_check_button_connected(vbox1, &clock_24hr_button,
				clock_24hr_mode, FALSE, FALSE, 0,
				cb_clock_cal, NULL, 
				_("Display 24 hour instead of 12 hour time"));

	hbox = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(vbox1), hbox, FALSE, FALSE, 0);
	gkrellm_gtk_check_button_connected(hbox, &clock_12hr_sec_button,
			clock_12hr_sec_enable, FALSE, FALSE, 20,
			cb_clock_cal, NULL,
			_("Show seconds instead of am/pm in 12 hour time mode"));

	hbox = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(vbox1), hbox, FALSE, FALSE, 0);
	gkrellm_gtk_check_button_connected(hbox, &clock_24hr_sec_button,
			clock_24hr_sec_enable, FALSE, FALSE, 20,
			cb_clock_cal, NULL,
			_("Show seconds in 24 hour time mode"));

	set_seconds_buttons_sensitivity();

/* -- Setup tab */
	vbox = gkrellm_gtk_framed_notebook_page(tabs, _("Setup"));

	vbox1 = gkrellm_gtk_framed_vbox(vbox, _("Clock Chime Commands"),
				4, FALSE, 0, 2);
	table = gkrellm_gtk_launcher_table_new(vbox1, 2);

	gkrellm_gtk_config_launcher(table, 0, &hour_chime_entry, NULL,
			_("Hour"), NULL);
	gtk_entry_set_text(GTK_ENTRY(hour_chime_entry), hour_chime_command);
	g_signal_connect(G_OBJECT(hour_chime_entry), "changed",
			G_CALLBACK(cb_chime_entry), GINT_TO_POINTER(1));
	g_signal_connect(G_OBJECT(hour_chime_entry), "activate",
			G_CALLBACK(cb_chime_entry), GINT_TO_POINTER(0x11));

	gkrellm_gtk_config_launcher(table, 1, &quarter_chime_entry, NULL,
			_("Quarter hour"), NULL);
    gtk_entry_set_text(GTK_ENTRY(quarter_chime_entry), quarter_chime_command);
	g_signal_connect(G_OBJECT(quarter_chime_entry), "changed",
			G_CALLBACK(cb_chime_entry), GINT_TO_POINTER(0));
	g_signal_connect(G_OBJECT(quarter_chime_entry), "activate",
			G_CALLBACK(cb_chime_entry), GINT_TO_POINTER(0x10));

	gkrellm_gtk_check_button_connected(vbox, &loop_chime_button,
			loop_chime_enable, FALSE, FALSE, 6,
			cb_clock_cal, NULL,
			_("Loop hour chime command"));

	vbox = gkrellm_gtk_framed_vbox_end(vbox, _("Launch Commands"),
			4, FALSE, 0, 2);
	table = gkrellm_gtk_launcher_table_new(vbox, 2);

	gkrellm_gtk_config_launcher(table, 0,  &cal_launch_entry,
				&cal_tooltip_entry, _("Calendar"), &cal_launch);
	g_signal_connect(G_OBJECT(cal_launch_entry), "changed",
			G_CALLBACK(cb_launch_entry), GINT_TO_POINTER(1));
	g_signal_connect(G_OBJECT(cal_tooltip_entry), "changed",
			G_CALLBACK(cb_launch_entry), GINT_TO_POINTER(1));
	
	gkrellm_gtk_config_launcher(table, 1,  &clock_launch_entry,
				&clock_tooltip_entry, _("Clock"), &clock_launch);
	g_signal_connect(G_OBJECT(clock_launch_entry), "changed",
			G_CALLBACK(cb_launch_entry), GINT_TO_POINTER(0));
	g_signal_connect(G_OBJECT(clock_tooltip_entry), "changed",
			G_CALLBACK(cb_launch_entry), GINT_TO_POINTER(0));
	}


static GkrellmMonitor	monitor_clock =
	{
	N_("Clock"),		/* Name, for config tab.	*/
	MON_CLOCK,			/* Id,  0 if a plugin		*/
	create_clock,		/* The create function		*/
	update_clock,		/* The update function		*/
	create_clock_tab,	/* The config tab create function	*/
	NULL,				/* Instant apply	*/

	save_clock_cal_config,	/* Save user conifg			*/
	load_clock_cal_config,	/* Load user config			*/
	CLOCK_CONFIG_KEYWORD, /* config keyword			*/

	NULL,				/* Undef 2	*/
	NULL,				/* Undef 1	*/
	NULL,				/* Undef 0	*/

	0,					/* insert_before_id - place plugin before this mon */

	NULL,				/* Handle if a plugin, filled in by GKrellM		*/
	NULL				/* path if a plugin, filled in by GKrellM		*/
	};

GkrellmMonitor *
gkrellm_init_clock_monitor(void)
	{
	clock_enable = TRUE;
    monitor_clock.name = _(monitor_clock.name);
	clock_12hr_sec_enable = TRUE;
	clock_24hr_sec_enable = TRUE;
    loop_chime_enable   = FALSE;
    hour_chime_command = g_strdup("");
    quarter_chime_command = g_strdup("");

	clock_style_id = gkrellm_add_meter_style(&monitor_clock, CLOCK_STYLE_NAME);
	mon_clock = &monitor_clock;
	return &monitor_clock;
	}

static GkrellmMonitor	monitor_cal =
	{
	N_("Calendar"),	/* Name, for config tab.	*/
	MON_CAL,		/* Id,  0 if a plugin		*/
	create_calendar, /* The create function		*/
	NULL,			/* The update function		*/
	NULL,			/* The config tab create function	*/
	NULL,			/* Apply the config function		*/

	NULL,			/* Save user conifg			*/
	NULL,			/* Load user config			*/
	NULL,			/* config keyword			*/

	NULL,			/* Undef 2	*/
	NULL,			/* Undef 1	*/
	NULL,			/* Undef 0	*/

	0,				/* insert_before_id - place plugin before this mon */

	NULL,			/* Handle if a plugin, filled in by GKrellM		*/
	NULL			/* path if a plugin, filled in by GKrellM		*/
	};

GkrellmMonitor *
gkrellm_init_cal_monitor(void)
	{
	cal_enable = TRUE;
    monitor_cal.name = _(monitor_cal.name);

	cal_style_id = gkrellm_add_meter_style(&monitor_cal, CAL_STYLE_NAME);
	mon_cal = &monitor_cal;
	return &monitor_cal;
	}

