Listing: gtkclock.c

/* Gtk Widget Factory generated widget
 * http://gwf.sourceforge.net
 * Copyright (c) 1999-2000 Jeroen Benckhuijsen
*/

/* This file is Copyrighted (C) 1999-2000 by Ishan Chattopadhyaya (ishanchattopadhyaya@hclinfinet.com)
 * Please read the license agreement below
*/

/*  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.
*/

#ifdef HAVE_CONFIG_H
  #include <config.h>
#endif

#include <gtk/gtk.h>
#include "gtkclock.h"

#include <math.h>

static GtkObjectClass *parent_class = NULL;

static void	gtk_clock_class_init	(GtkClockClass *klass);
static void	gtk_clock_init		(GtkClock *clock);
static void	gtk_clock_destroy	(GtkObject *object);
static void gtk_clock_draw (GtkWidget *widget, GdkRectangle *area);

static void gtk_clock_size_request (GtkWidget *widget, GtkRequisition *req);
static void gtk_clock_realize (GtkWidget *widget);

int get_current_time (int *hr, int *mn, int *sc);
void gtk_clock_obtain_coordinates (int *xcood, int *ycood, int h, int k, int value, int max_radius, int max_val);

guint
gtk_clock_get_type (void)
{
  static guint gtk_clock_type = 0;

  if (!gtk_clock_type)
    {
      GtkTypeInfo gtk_clock_info =
      {
        "GtkClock",
        sizeof (GtkClock),
        sizeof (GtkClockClass),
        (GtkClassInitFunc) gtk_clock_class_init,
        (GtkObjectInitFunc) gtk_clock_init,
        /* reserved_1 */ NULL,
        /* reserved_2 */ NULL,
        (GtkClassInitFunc) NULL,
      };

      gtk_clock_type = gtk_type_unique (gtk_widget_get_type (), &gtk_clock_info);
    }

  return gtk_clock_type;
}

static void
gtk_clock_class_init (GtkClockClass *klass)
{
  GtkObjectClass *object_class;
  GtkWidgetClass *widget_class;

  object_class = (GtkObjectClass*) klass;
  widget_class = (GtkWidgetClass*) klass;

  object_class->destroy = gtk_clock_destroy;
  parent_class = gtk_type_class (gtk_widget_get_type());

  widget_class->realize = gtk_clock_realize;
  widget_class->size_request = gtk_clock_size_request;
}

static void
gtk_clock_init (GtkClock *clock)
{
	/* Start the clock, refresh every 1ms */
    gtk_timeout_add (1, gtk_clock_construct, (gpointer)(clock));
}

static void
gtk_clock_destroy (GtkObject *object)
{
  parent_class->destroy(object);
}

GtkWidget*
gtk_clock_new(void)
{
  GtkWidget *clock;
  clock = GTK_WIDGET( gtk_type_new ( gtk_clock_get_type()));
  return clock;
}

static void gtk_clock_size_request (GtkWidget *widget,
                                    GtkRequisition *req)
{
    req->width =  200;	// Default value
    req->height = 200;	// Default value
}

static void gtk_clock_realize (GtkWidget *widget)
{
	GtkClock *darea;
	GdkWindowAttr attributes;
	gint attributes_mask;

	g_return_if_fail (widget != NULL);
	g_return_if_fail (GTK_IS_CLOCK (widget));

	darea = GTK_CLOCK (widget);
	GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);

	attributes.window_type = GDK_WINDOW_CHILD;
	attributes.x = widget->allocation.x;
	attributes.y = widget->allocation.y;
	attributes.width = widget->allocation.width;
	attributes.height = widget->allocation.height;
	attributes.wclass = GDK_INPUT_OUTPUT;
	attributes.visual = gtk_widget_get_visual (widget);
	attributes.colormap = gtk_widget_get_colormap (widget);
	attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;

	attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;

	widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
	gdk_window_set_user_data (widget->window, darea);

	widget->style = gtk_style_attach (widget->style, widget->window);
	gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
}

gint
gtk_clock_construct (gpointer data)
{
    GtkClock *clock = data;
    if (!GTK_WIDGET_DRAWABLE (clock))
        return 0;

	get_current_time ( &clock->hour, &clock->min, &clock->sec );
	gtk_clock_draw (GTK_WIDGET(clock), NULL);

    return 1;
}

static void
gtk_clock_draw (GtkWidget *widget, GdkRectangle *area)
{
	GtkClock *clock;

	int loop1;
	int max_radius;
	int min=0, hour=0, sec=0;
	int yc, xc;
	int alloc_width, alloc_height;
	GdkColor colors[4];
	GdkGC	*clock_gc = gdk_gc_new (widget->window);

    if (GTK_WIDGET_DRAWABLE (widget))
    {
		clock = GTK_CLOCK (widget);

		alloc_width = widget->allocation.width - 1;
		alloc_height = widget->allocation.height - 1;

		gdk_window_clear_area (widget->window, 0, 0, alloc_width, alloc_height);

		gdk_color_parse("Red", &colors[0]);
		gdk_color_parse("DarkGreen", &colors[1]);
		gdk_color_parse("RoyalBlue", &colors[2]);
		gdk_color_parse("Black", &colors[3]);

		min = clock->min; hour = clock->hour; sec = clock->sec;
		max_radius = (alloc_width < alloc_height)? (alloc_width/2) : (alloc_height/2);
		for (loop1=0; loop1<60; loop1+=5)
        {
			gtk_clock_obtain_coordinates (&xc, &yc, max_radius, max_radius, loop1, max_radius-10, 60);
            gdk_gc_set_line_attributes      (clock_gc, 10, GDK_LINE_SOLID, 0, 0);
			gdk_draw_line (widget->window, widget->style->black_gc, xc, yc,xc+1,yc+1);
			gdk_draw_line (widget->window, widget->style->black_gc, xc+1, yc,xc,yc+1);
        }
        
        gdk_gc_set_line_attributes      (clock_gc, 2, GDK_LINE_SOLID, 0, 0);
        /* Obtain coordinates for minute and draw a line from center to that point */
		gtk_clock_obtain_coordinates (&xc, &yc, max_radius, max_radius, min, max_radius, 60);
        gdk_colormap_alloc_color (gdk_colormap_get_system(), &colors[0], FALSE, TRUE);
        gdk_gc_set_foreground (clock_gc, colors+0);          /* Red */
        gdk_draw_line (widget->window, clock_gc, xc, yc, max_radius, max_radius);

        /* Obtain coordinates for hour and draw a line from center to that point */
        gtk_clock_obtain_coordinates (&xc, &yc, max_radius, max_radius, (hour!=12)?hour*60+min:min, max_radius*0.65, 12*60);
        gdk_colormap_alloc_color (gdk_colormap_get_system(), &colors[1], FALSE, TRUE);
        gdk_gc_set_foreground (clock_gc, colors+1);          /* Green */
        gdk_draw_line (widget->window, clock_gc, xc, yc, max_radius, max_radius);

        /* Obtain coordinates for seconds and draw a line from center to that point */
        gtk_clock_obtain_coordinates (&xc, &yc, max_radius, max_radius, sec, max_radius*.85, 60);
        gdk_colormap_alloc_color (gdk_colormap_get_system(), &colors[2], FALSE, TRUE);
        gdk_gc_set_foreground (clock_gc, colors+2);          /* Blue */
        gdk_draw_line (widget->window, clock_gc, xc, yc, max_radius, max_radius);
    }
}


/* ----------------------------------------------------------------------------------*/
/* UTILITY FUNCTIONS */

// TODO: replace this function with a better one. Due to lack of time,
// this one is still buggy.
int get_current_time (int *hr, int *mn, int *sc)
{
     long hour = *hr, min =	 *mn, sec=   *sc;

     hour = time(NULL);
     min=hour; sec = hour; sec=hour%60;
     min=min-min%60; min=min/60; min=min%60;
     hour=hour/(60*60); hour=hour%12; hour = (hour+6)%12;
     if (min<30)  min = min+30;
     else min = min-30;
     if(!hour) hour=12; if (min>30)  hour--; if(hour<1) hour=12;

     *sc = sec; *mn = min; *hr = hour;
     return 1;
}

void gtk_clock_obtain_coordinates (int *xcood, int *ycood, int h, int k, int value, int max_radius, int max_val)
{
   	int yc=*ycood, xc=*xcood, f;
    float t=0, s=0;

   	t=(float)(value*360.0/(float)max_val)  * 3.14159/180;
   	s=sin(t/2);
   	yc=(int)(k+ 2*max_radius*(pow(s,2)) -max_radius);
   	f=-max_radius*max_radius+h*h+(yc-k)*(yc-k);
    if (value<max_val/2)
   		xc=h + (float)(pow(h*h-f, .5));
    else
   		xc=h - (float)(pow(h*h-f, .5));
    *ycood=yc; *xcood=xc;
}