File : klokka_gui.adb


----------------------------------------------------------------------------
--                             Klokka                                     --
--                        Copyright (C) 2002                              --
--                          Preben Randhol                                --
--              License: Gnat Modified GNU Public License                 --
--                                                                        --
--   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.                                                     --
--                                                                        --
--   As a special exception, if other files instantiate generics from     --
--   this unit, or you link this unit with other files to produce an      --
--   executable, this unit does not by itself cause the resulting         --
--   executable to be covered by the GNU General Public License. This     --
--   exception does not however invalidate any other reasons why the      --
--   executable file might be covered by the GNU Public License.          --
--                                                                        --
----------------------------------------------------------------------------

with Glib;             use Glib;
with Gtk.Enums;        use Gtk.Enums;
with Gtk;              use Gtk;
with Gdk.Types;        use Gdk.Types;
with Gdk.Bitmap;       use Gdk.Bitmap;
with Gdk.Drawable;     use Gdk.Drawable;
with Gtk.Widget;       use Gtk.Widget;
with Gtk.Enums;        use Gtk.Enums;
with Gtkada.Handlers;  use Gtkada.Handlers;
with Callbacks_Klokka; use Callbacks_Klokka;
with Ada.Numerics;     use Ada.Numerics;
with Ada.Calendar;     use Ada.Calendar;
with Klokka_Pixmap;

package body Klokka_Gui is

   procedure Make
   is
   begin
      Klokka_Gui.Gtk_New (Klokka_Gui.Window);
      Klokka_Gui.Show_All (Klokka_Gui.Window);
      Klokka_Gui.Clock_Init (Klokka_Gui.Window);
   end Make;

   procedure Gtk_New (Window : out Window_Access) is
   begin
      Window := new Window_Record;
      Klokka_Gui.Initialize (Window);
   end Gtk_New;

   procedure Clock_Init (Window : access Window_Record'Class) is
      Mask    : Gdk.Bitmap.Gdk_Bitmap;
      Success : Boolean;
   begin
      Window.Draw_Window := Gtk.Drawing_Area.Get_Window (Window.Drawing_Area);
      Set_Rgb (Window.Light_Grey_Color, 46_848, 44_056, 26_112);

      Alloc_Color (Colormap   => Gtk.Widget.Get_Default_Colormap,
                   Color      => Window.Light_Grey_Color,
                   Writeable  => False,
                   Best_Match => True,
                   Success    => Success);

      Set_Rgb (Window.Grey_Color, 22_016, 20_248, 12_544);
      Alloc_Color (Colormap   => Gtk.Widget.Get_Default_Colormap,
                   Color      => Window.Grey_Color,
                   Writeable  => False,
                   Best_Match => True,
                   Success    => Success);

      Set_Rgb (Window.Red_Color, 38_535, 0, 0);
      Alloc_Color (Colormap   => Gtk.Widget.Get_Default_Colormap,
                   Color      => Window.Red_Color,
                   Writeable  => False,
                   Best_Match => True,
                   Success    => Success);

      Gdk_New (Window.Clock_GC, Window.Draw_Window);

      Create_From_Xpm_D
         (Pixmap        => Window.Background_Pixmap,
          Window        => Window.Draw_Window,
          Mask          => Mask,
          Transparent   => Gdk.Color.Null_Color,
          Data          => Klokka_Pixmap.Klokka_Xpm);

      Gdk_New
        (Pixmap   => Window.Pixmap_Buffer,
         Window   => Window.Draw_Window,
         Width    => 71,
         Height   => 73);

      Gdk_New
        (Pixmap   => Window.Pixmap,
         Window   => Window.Draw_Window,
         Width    => 71,
         Height   => 73);
   end Clock_Init;

   procedure Draw_Clock is
   begin
      Draw_Pixmap
         (Drawable => Window.Draw_Window,
          Gc       => Window.Clock_GC,
          Src      => Window.Pixmap,
          Xsrc     => 0,
          Ysrc     => 0,
          Xdest    => 0,
          Ydest    => 0);

   end Draw_Clock;

   procedure Update_Clock is
      Deg_To_Rad       : constant := Pi / 180;
      Sec_To_Deg       : constant := 15;
      Hrs_To_Sec       : constant := 5.0;
      X                : constant := 35;
      Y                : constant := 36;
      Hour_Radius      : constant := 9.0;
      Min_Radius       : constant := Hour_Radius + 6.0;
      Sec_Radius       : constant := Min_Radius + 3.0;
      Handle_Thickness : constant := 3;
      Year             : Year_Number;
      Month            : Month_Number;
      Day              : Day_Number;
      Seconds          : Natural;
      S                : Day_Duration;
      Hour             : Float;
      Min              : Natural;
      Sec              : Natural;
      Hour_X2          : Gint;
      Hour_Y2          : Gint;
      Min_X2           : Gint;
      Min_Y2           : Gint;
      Sec_X2           : Gint;
      Sec_Y2           : Gint;
   begin

      Draw_Pixmap
         (Drawable => Window.Pixmap_Buffer,
          Gc       => Window.Clock_GC,
          Src      => Window.Background_Pixmap,
          Xsrc     => 0,
          Ysrc     => 0,
          Xdest    => 0,
          Ydest    => 0);

      Split (Date => Clock,
             Year => Year,
             Month => Month,
             Day => Day,
             Seconds => S);

      Seconds := Natural (S);

      Sec  := Seconds rem 60;
      Min  := (Seconds / 60) rem 60;
      Hour := (Float (Seconds) / 3600.0);
      --  Using Float for Hour because I need the hour-handle to move
      --  also between the hours otherwise it is even harder to read the
      --  time.

      Hour_X2 := X - Gint (Hour_Radius *
         Cos ((Hour * Hrs_To_Sec + Float (Sec_To_Deg)) * 6.0 * Deg_To_Rad));
      Hour_Y2 := Y - Gint (Hour_Radius *
         Sin ((Hour * Hrs_To_Sec + Float (Sec_To_Deg)) * 6.0 * Deg_To_Rad));

      Min_X2 := X - Gint (Min_Radius *
         Cos (Float ((Min + Sec_To_Deg) * 6) * Deg_To_Rad));
      Min_Y2 := Y - Gint (Min_Radius *
         Sin (Float ((Min + Sec_To_Deg) * 6) * Deg_To_Rad));

      Sec_X2 := X - Gint (Sec_Radius *
         Cos (Float ((Sec + Sec_To_Deg) * 6) * Deg_To_Rad));
      Sec_Y2 := Y - Gint (Sec_Radius *
         Sin (Float ((Sec + Sec_To_Deg) * 6) * Deg_To_Rad));

      --  First we draw the hour and minute handles with a light colour
      --  and 2 pixels wider than what we want the handles to be to get
      --  an antialias effect. If we don't do this the handles look
      --  ugly.

      Set_Line_Attributes
         (GC         => Window.Clock_GC,
          Line_Width => Handle_Thickness + 2,
          Line_Style => Line_Solid,
          Cap_Style  => Cap_Projecting,
          Join_Style => Join_Miter);

      Set_Foreground
         (GC    => Window.Clock_GC,
          Color => Window.Light_Grey_Color);

      Draw_Line
         (Drawable => Window.Pixmap_Buffer,
          Gc       => Window.Clock_GC,
          X1       => X,
          Y1       => Y,
          X2       => Min_X2,
          Y2       => Min_Y2);

      Draw_Line
         (Drawable => Window.Pixmap_Buffer,
          Gc       => Window.Clock_GC,
          X1       => X,
          Y1       => Y,
          X2       => Hour_X2,
          Y2       => Hour_Y2);

      --  Now we draw the hour and minute handles with a darker colour
      --  and 1 pixels wider for the antialias effect.

      Set_Line_Attributes
         (GC         => Window.Clock_GC,
          Line_Width => Handle_Thickness + 1,
          Line_Style => Line_Solid,
          Cap_Style  => Cap_Projecting,
          Join_Style => Join_Miter);

      Set_Foreground
         (GC    => Window.Clock_GC,
          Color => Window.Grey_Color);

      Draw_Line
         (Drawable => Window.Pixmap_Buffer,
          Gc       => Window.Clock_GC,
          X1       => X,
          Y1       => Y,
          X2       => Min_X2,
          Y2       => Min_Y2);

      Draw_Line
         (Drawable => Window.Pixmap_Buffer,
          Gc       => Window.Clock_GC,
          X1       => X,
          Y1       => Y,
          X2       => Hour_X2,
          Y2       => Hour_Y2);

      --  Finally we draw the hour and minute handles with a black
      --  colour.

      Set_Line_Attributes
         (GC          => Window.Clock_GC,
          Line_Width  => Handle_Thickness,
          Line_Style  => Line_Solid,
          Cap_Style   => Cap_Projecting,
          Join_Style  => Join_Miter);

      Set_Foreground
         (GC    => Window.Clock_GC,
          Color => Black (Gtk.Widget.Get_Default_Colormap));

      Draw_Line
         (Drawable => Window.Pixmap_Buffer,
          Gc       => Window.Clock_GC,
          X1       => X,
          Y1       => Y,
          X2       => Min_X2,
          Y2       => Min_Y2);

      Draw_Line
         (Drawable => Window.Pixmap_Buffer,
          Gc       => Window.Clock_GC,
          X1       => X,
          Y1       => Y,
          X2       => Hour_X2,
          Y2       => Hour_Y2);

      --  Drawing the second handle. This is only a pixel thick so we
      --  cannot use antialiasing here.

      Set_Line_Attributes
         (GC         => Window.Clock_GC,
          Line_Width => 1,
          Line_Style => Line_Solid,
          Cap_Style  => Cap_Projecting,
          Join_Style => Join_Miter);

      Set_Foreground
         (GC    => Window.Clock_GC,
          Color => Window.Red_Color);

      Draw_Line
         (Drawable => Window.Pixmap_Buffer,
          Gc       => Window.Clock_GC,
          X1       => X,
          Y1       => Y,
          X2       => Sec_X2,
          Y2       => Sec_Y2);

      Draw_Pixmap
         (Drawable => Window.Pixmap,
          Gc       => Window.Clock_GC,
          Src      => Window.Pixmap_Buffer,
          Xsrc     => 0,
          Ysrc     => 0,
          Xdest    => 0,
          Ydest    => 0);

   end Update_Clock;

   procedure Initialize (Window : access Window_Record'Class) is
   begin
      Gtk.Window.Initialize (Window, Window_Toplevel);

      Set_Title (Window, "Klokka");
      Set_Policy (Window, False, False, True);
      Set_Position (Window, Win_Pos_None);
      Set_Modal (Window, False);
      Set_Sensitive (Window, True);

      Gtk_New_Vbox (Window.Base_Vbox, False, 0);
      Add (Window, Window.Base_Vbox);

      Gtk_New (Window.Drawing_Area);
      Size (Window.Drawing_Area, 71, 73);
      Pack_Start (Window.Base_Vbox, Window.Drawing_Area, True, True, 0);

      Return_Callback.Connect
         (Gtk_Widget (Window), "delete_event",
            Callbacks_Klokka.On_Window_Delete_Event'Access);

      Event_Cb.Connect
         (Gtk_Widget (Window.Drawing_Area), "expose_event",
          Event_Cb.To_Marshaller (
             On_Window_Expose_Event'Access));

   end Initialize;

end Klokka_Gui;