------------------------------------------------------------------------------
--                                                                          --
--                An Ada2005 binding to the Qt C++ framework                --
--                                                                          --
------------------------------------------------------------------------------
--                                                                          --
-- Copyright © 2008, 2009 Vadim Godunko <vgodunko@gmail.com>                --
--                                                                          --
-- QtAda is free software;  you can  redistribute it and/or modify it under --
-- terms of the  GNU General Public License as published  by the Free Soft- --
-- ware  Foundation;  either version 2,  or (at your option) any later ver- --
-- sion. QtAda is distributed in the hope that it will be useful, but WITH- --
-- OUT 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 distributed with QtAda;  see file COPYING.  If not, write --
-- to  the  Free Software Foundation,  51  Franklin  Street,  Fifth  Floor, --
-- Boston, MA 02110-1301, USA.                                              --
--                                                                          --
------------------------------------------------------------------------------
--  $Revision: 3225 $ $Date: 2009-04-03 17:20:32 +0400 (Fri, 03 Apr 2009) $
------------------------------------------------------------------------------
with Ada.Numerics.Elementary_Functions;
with Ada.Numerics.Float_Random;

with Qt4.Brushes;
with Qt4.Colors;
with Qt4.Fonts;
with Qt4.Linear_Gradients.Constructors;
with Qt4.List_Of_Graphics_Items;
with Qt4.Matrices;
with Qt4.Strings;

with Qt4.Graphics_Scenes;
pragma Unreferenced (Qt4.Graphics_Scenes);

package body Graph_Widgets is

   use type Qt4.Q_Integer;
   use type Qt4.Q_Real;

   Generator : Ada.Numerics.Float_Random.Generator;
   --  Random number generator.

   ---------------------
   -- Draw_Background --
   ---------------------

   overriding
   procedure Draw_Background
    (Self    : not null access Graph_Widget;
     Painter : in out Qt4.Painters.Q_Painter'Class;
     Rect    : Qt4.Rect_Fs.Q_Rect_F)
   is
      Scene_Rect    : constant Qt4.Rect_Fs.Q_Rect_F := Self.Scene_Rect;
      Rigth_Shadow  : constant Qt4.Rect_Fs.Q_Rect_F
        := Qt4.Rect_Fs.Create
            (Scene_Rect.Right, Scene_Rect.Top + 5.0, 5.0, Scene_Rect.Height);
      Bottom_Shadow : constant Qt4.Rect_Fs.Q_Rect_F
        := Qt4.Rect_Fs.Create
            (Scene_Rect.Left + 5.0, Scene_Rect.Bottom, Scene_Rect.Width, 5.0);
      Gradient      : constant Qt4.Linear_Gradients.Q_Linear_Gradient
        := Qt4.Linear_Gradients.Constructors.Create
            (Scene_Rect.Top_Left, Scene_Rect.Bottom_Right);
      Text_Rect     : constant Qt4.Rect_Fs.Q_Rect_F
        := Qt4.Rect_Fs.Create
            (Scene_Rect.Left + 4.0, Scene_Rect.Top + 4.0,
             Scene_Rect.Width - 4.0, Scene_Rect.Height - 4.0);
      Message       : constant Qt4.Strings.Q_String
        := Qt4.Strings.From_Utf_16
            ("Click and drag the nodes around, and zoom with the mouse "
               & "wheel or the '+' and '-' keys");
      Font          : Qt4.Fonts.Q_Font := Painter.Font;

   begin
      --  Shadow

      if Rigth_Shadow.Intersects (Rect) or Rigth_Shadow.Contains (Rect) then
         Painter.Fill_Rect (Rigth_Shadow, Qt4.Brushes.Create (Qt4.Dark_Gray));
      end if;

      if Bottom_Shadow.Intersects (Rect) or Bottom_Shadow.Contains (Rect) then
         Painter.Fill_Rect (Bottom_Shadow, Qt4.Brushes.Create (Qt4.Dark_Gray));
      end if;

      --  Fill

      Gradient.Set_Color_At (0.0, Qt4.Colors.Create (Qt4.White));
      Gradient.Set_Color_At (1.0, Qt4.Colors.Create (Qt4.Light_Gray));
      Painter.Fill_Rect
       (Rect.Intersected (Scene_Rect), Qt4.Brushes.Create (Gradient));
      Painter.Set_Brush (Qt4.No_Brush);
      Painter.Draw_Rect (Scene_Rect);

      --  Text

      Font.Set_Bold (True);
      Font.Set_Point_Size (14);
      Painter.Set_Font (Font);
      Painter.Set_Pen (Qt4.Colors.Create (Qt4.Light_Gray));
      Painter.Draw_Text (Text_Rect.Translated (2.0, 2.0), Message);
      Painter.Set_Pen (Qt4.Colors.Create (Qt4.Black));
      Painter.Draw_Text (Text_Rect, Message);
   end Draw_Background;

   ----------------
   -- Item_Moved --
   ----------------

   procedure Item_Moved (Self : not null access Graph_Widget'Class) is
   begin
      if Self.Timer_Id = 0 then
         Self.Timer_Id := Self.Start_Timer (0.04);
      end if;
   end Item_Moved;

   ---------------------
   -- Key_Press_Event --
   ---------------------

   overriding
   procedure Key_Press_Event
    (Self  : not null access Graph_Widget;
     Event : not null access Qt4.Key_Events.Q_Key_Event'Class)
   is
   begin
      case Event.Key is
         when Qt4.Key_Up =>
            Self.Center_Node.Move_By (0.0, -20.0);

         when Qt4.Key_Down =>
            Self.Center_Node.Move_By (0.0, 20.0);

         when Qt4.Key_Left =>
            Self.Center_Node.Move_By (-20.0, 0.0);

         when Qt4.Key_Right =>
            Self.Center_Node.Move_By (20.0, 0.0);

         when Qt4.Key_Plus =>
            Self.Scale_View (1.2);

         when Qt4.Key_Minus =>
            Self.Scale_View (1.0 / 1.2);

         when Qt4.Key_Space | Qt4.Key_Enter =>
            declare
               Items : Qt4.List_Of_Graphics_Items.Q_List_Of_Q_Graphics_Item
                 renames Self.Scene.Items;

            begin
               for J in 0 .. Items.Count - 1 loop
                  if Items.Value (J).all in Nodes.Node'Class then
                     declare
                        Item : constant access Nodes.Node'Class
                        := Nodes.Node'Class (Items.Value (J).all)'Access;

                     begin
                        Item.Set_Pos
                         (-150.0
                            + Qt4.Q_Real
                               (Ada.Numerics.Float_Random.Random (Generator))
                                  * 300.0,
                          -150.0
                            + Qt4.Q_Real
                               (Ada.Numerics.Float_Random.Random (Generator))
                                  * 300.0);
                     end;
                  end if;
               end loop;
            end;

         when others =>
            Qt4.Graphics_Views.Directors.Q_Graphics_View_Director
             (Self.all).Key_Press_Event (Event);
      end case;
   end Key_Press_Event;

   ----------------
   -- Scale_View --
   ----------------

   procedure Scale_View
    (Self         : not null access Graph_Widget'Class;
     Scale_Factor : Qt4.Q_Real)
   is
      Matrix : Qt4.Matrices.Q_Matrix := Self.Matrix;
      Factor : Qt4.Q_Real;

   begin
      Matrix.Scale (Scale_Factor, Scale_Factor);
      Factor :=
        Matrix.Map_Rect (Qt4.Rect_Fs.Create (0.0, 0.0, 1.0, 1.0)).Width;

      if Factor < 0.07 or Factor > 100.0 then
         return;
      end if;

      Self.Scale (Scale_Factor, Scale_Factor);
   end Scale_View;

   -----------------
   -- Timer_Event --
   -----------------

   overriding
   procedure Timer_Event
    (Self  : not null access Graph_Widget;
     Event : not null access Qt4.Timer_Events.Q_Timer_Event'Class)
   is
      pragma Unreferenced (Event);

      Items_Moved : Boolean := False;
      Items       : Qt4.List_Of_Graphics_Items.Q_List_Of_Q_Graphics_Item
        renames Self.Scene.Items;

   begin
      for J in 0 .. Items.Count - 1 loop
         if Items.Value (J).all in Nodes.Node'Class then
            Nodes.Node'Class (Items.Value (J).all).Calculate_Forces;
         end if;
      end loop;

      for J in 0 .. Items.Count - 1 loop
         if Items.Value (J).all in Nodes.Node'Class
           and then Nodes.Node'Class (Items.Value (J).all).Advance
         then
            Items_Moved := True;
         end if;
      end loop;

      if not Items_Moved then
         Self.Kill_Timer (Self.Timer_Id);
         Self.Timer_Id := 0;
      end if;
   end Timer_Event;

   -----------------
   -- Wheel_Event --
   -----------------

   overriding
   procedure Wheel_Event
    (Self  : not null access Graph_Widget;
     Event : not null access Qt4.Wheel_Events.Q_Wheel_Event'Class)
   is
     use Ada.Numerics.Elementary_Functions;

   begin
      Self.Scale_View
       (Qt4.Q_Real (2.0 ** ((0.0 - Float (Event.Get_Delta)) / 240.0)));
   end Wheel_Event;

end Graph_Widgets;
