--                                                                    --
--  package                         Copyright (c)  Dmitry A. Kazakov  --
--     Gtk.Tree_Model.Columned_Store               Luebeck            --
--  Implementation                                 Autumn, 2007       --
--                                                                    --
--                                Last revision :  11:45 29 Jul 2018  --
--                                                                    --
--  This  library  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 library  --
--  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 library; 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 Ada.Exceptions;  use Ada.Exceptions;
with GLib.Messages;   use GLib.Messages;
with Gtk.Missed;      use Gtk.Missed;
with GtkAda.Types;    use GtkAda.Types;
with System;          use System;

with Ada.Unchecked_Conversion;
with GNAT.Traceback.Symbolic;
with Interfaces.C.Pointers;

package body Gtk.Tree_Model.Columned_Store is

   GTK_Type     : GType := GType_Invalid;
   Root_Changed : constant String := "root-changed" & ASCII.NUL;

   function Where (Text : String) return String is
   begin
      return " in Gtk.Tree_Model.Columned_Store." & Text;
   end Where;

   function From_Addr is
      new Ada.Unchecked_Conversion (Address, GInt);

   function To_Addr is
      new Ada.Unchecked_Conversion (GInt, Address);

   procedure Emit_Root_Changed
             (  Store : access Gtk_Columned_Store_Record'Class
             )  is
      procedure Internal (Object : System.Address; Name : String);
      pragma Import (C, Internal, "ada_g_signal_emit_by_name");
   begin
      Internal (Get_Object (Store), Root_Changed);
   end Emit_Root_Changed;

   procedure Set_Path (Path : in out Gtk_Tree_Path; Row : GInt) is
   begin
      Prepend_Index (Path, Row);
      if Up (Path) then
         null;
      end if;
   end Set_Path;

   function Compose_Columned
            (  Model : not null access Gtk_Columned_Store_Record'Class;
               Row   : GInt
            )  return Gtk_Tree_Iter is
   begin
      if Row in 0..Model.Rows - 1 then
         return
         (  Model.Stamp,
            To_Addr (Row),
            Model.all'Address,
            Null_Address
         );
      else
         return Null_Iter;
      end if;
   end Compose_Columned;

   function Compose_Reference
            (  Model  : not null access Gtk_Columned_Store_Record'Class;
               Row    : GInt;
               Column : GInt
            )  return Gtk_Tree_Iter is
   begin
      if Row >= Model.Rows or else Column >= Model.Columns then
         return Null_Iter;
      else
         return
            Nth_Child
            (  Model.Reference,
               Model.Get_Root,
               Row + Column * Model.Rows
            );
      end if;
   end Compose_Reference;

   procedure Do_Set_Root
             (  Model : not null access Gtk_Columned_Store_Record'Class;
                Root  : Gtk_Tree_Iter
             )  is
      Path : Gtk_Tree_Path;
   begin
      if Root = Null_Iter then
         Gtk_New (Path);
      else
         Path := Get_Path (Model.Reference, Root);
      end if;
      if Model.Path = Null_Gtk_Tree_Path then
         Model.Stamp := Model.Stamp + 1;
         Model.Path  := Path;
         Changed_Root (Model);
         Emit_Root_Changed (Model);
      elsif Compare (Model.Path, Path) = 0 then
         Path_Free (Path);
      else
         Path_Free (Model.Path);
         Model.Stamp := Model.Stamp + 1;
         Model.Path  := Path;
         Changed_Root (Model);
         Emit_Root_Changed (Model);
      end if;
   end Do_Set_Root;

   function Get_Position
            (  Model : not null access Gtk_Columned_Store_Record'Class;
               Path  : Gtk_Tree_Path
            )  return GInt is
   begin
      if Model.Reference = Null_Gtk_Tree_Model then
         return -1;
      elsif Path = Null_Gtk_Tree_Path then
         Log
         (  GtkAda_Contributions_Domain,
            Log_Level_Critical,
            "Null path in the reference model" & Where ("Get_Position")
         );
         return -1;
      end if;
      declare
         Indices : GInt_Array renames Get_Indices (Path);
      begin
         if Model.Path = Null_Gtk_Tree_Path then
            if Indices'Length /= 1 then
               return - 1;
            end if;
         else
            if (  Get_Indices (Model.Path)
               /= Indices (Indices'First..Indices'Last - 1)
               )
            then
               return -1;
            end if;
         end if;
         return Indices (Indices'Last);
      end;
   end Get_Position;

   procedure Split_Columned
             (  Model : not null access Gtk_Columned_Store_Record'Class;
                Iter  : Gtk_Tree_Iter;
                Row   : out GInt
             )  is
   begin
      if Model.Reference = Null_Gtk_Tree_Model then
         Row := -1;
      elsif Model.all'Address /= Iter.User_Data2 then
         Row := -1;
         Log
         (  GtkAda_Contributions_Domain,
            Log_Level_Critical,
            "Model mismatch" & Where ("Split_Columned")
         );
      elsif Model.Stamp /= Iter.Stamp then
         Row := -1;
         Log
         (  GtkAda_Contributions_Domain,
            Log_Level_Critical,
            "Stamp mismatch" & Where ("Split_Columned")
         );
      else
         Row := From_Addr (Iter.User_Data);
         if Row not in 0..Model.Rows - 1 then
            Row := -1;
            Log
            (  GtkAda_Contributions_Domain,
               Log_Level_Critical,
               "Illegal row in columned iterator"
            );
         end if;
      end if;
   end Split_Columned;

   procedure Split_Reference
             (  Model  : access Gtk_Columned_Store_Record'Class;
                Path   : Gtk_Tree_Path;
                Row    : out GInt;
                Column : out GInt
             )  is
   begin
      Row := Get_Position (Model, Path);
      if Row >= 0 then
         if 0 /= Model.Rows then
            Column := Row / Model.Rows;
            Row    := Row mod Model.Rows;
            if Column >= Model.Columns then
               Log
               (  GtkAda_Contributions_Domain,
                  Log_Level_Critical,
                  "Unexpected reference row" & Where ("Split_Reference")
               );
               Row    := -1;
               Column := 0;
            end if;
         end if;
      else
         Column := 0;
      end if;
   end Split_Reference;

   function From_Columned
            (  Model  : not null access Gtk_Columned_Store_Record;
               Iter   : Gtk_Tree_Iter;
               Column : Positive
            )  return Gtk_Tree_Iter is
      Row : GInt;
   begin
      Split_Columned (Model, Iter, Row);
      return Compose_Reference (Model, Row, GInt (Column) - 1);
   end From_Columned;

   function From_Columned
            (  Model  : not null access Gtk_Columned_Store_Record;
               Path   : Gtk_Tree_Path;
               Column : Positive
            )  return Gtk_Tree_Path is
      Row : constant Gtk_Tree_Iter :=
               From_Columned (Model, Get_Iter (Model, Path), Column);
   begin
      if Row = Null_Iter then
         return Null_Gtk_Tree_Path;
      else
         return Get_Path (Model.Reference, Row);
      end if;
   end From_Columned;

   procedure Changed_Root
             (  Model : not null access Gtk_Columned_Store_Record
             )  is
      Path : Gtk_Tree_Path;
   begin
      Gtk_New_First (Path);
      Model.Rows :=
         (  (  N_Children (Model.Reference, Model.Get_Root)
            +  Model.Columns
            -  1
            )
         /  Model.Columns
         );
      -- Simulation of children insertion
      for Row in 0..Model.Rows - 1 loop
         Set_Path (Path, Row);
         Row_Inserted
         (  To_Interface (Model),
            Path,
            Compose_Columned (Model, Row)
         );
      end loop;
      Path_Free (Path);
   end Changed_Root;

   function Children
            (  Model  : not null access Gtk_Columned_Store_Record;
               Parent : Gtk_Tree_Iter
            )  return Gtk_Tree_Iter is
   begin
      if Parent = Null_Iter then
         return Compose_Columned (Model, 0);
      else
         return Null_Iter;
      end if;
   end Children;

   procedure Erase
             (  Model : not null access Gtk_Columned_Store_Record
             )  is
      Item_Path : Gtk_Tree_Path;
      Last_Row  : constant GInt := Model.Rows - 1;
   begin
      Gtk_New_First (Item_Path);
      for Child in reverse 0..Last_Row loop
         Model.Rows := Child;
         Set_Path (Item_Path, Child);
         Row_Deleted (To_Interface (Model), Item_Path);
      end loop;
      Path_Free (Item_Path);
      if Model.Path /= Null_Gtk_Tree_Path then
         Path_Free (Model.Path);
         Model.Path := Null_Gtk_Tree_Path;
      end if;
   end Erase;

   procedure Finalize
             (  Model : not null access Gtk_Columned_Store_Record
             )  is
   begin
      if Model.Path /= Null_Gtk_Tree_Path then
         Path_Free (Model.Path);
      end if;
      if Model.Reference /= Null_Gtk_Tree_Model then
         Unref (-Model.Reference);
      end if;
   end Finalize;

   function Get_Column_Height
            (  Model  : not null access Gtk_Columned_Store_Record;
               Column : Positive
            )  return Natural is
   begin
      if GInt (Column) < Model.Columns then
         return Natural (Model.Rows);
      elsif GInt (Column) > Model.Columns or else Model.Rows = 0 then
         return 0;
      end if;
      declare
         Last : constant GInt :=
                (  N_Children (Model.Reference, Model.Get_Root)
                mod
                   Model.Rows
                );
      begin
         if Last = 0 then
            return Natural (Model.Rows);
         else
            return Natural (Last);
         end if;
      end;
   end Get_Column_Height;

   function Get_Column_Type
            (  Model : not null access Gtk_Columned_Store_Record;
               Index : GInt
            )  return GType is
   begin
      if Model.Reference = Null_Gtk_Tree_Model then
         Log
         (  GtkAda_Contributions_Domain,
            Log_Level_Critical,
            "Null reference model" & Where ("Get_Column_Type")
         );
         return GType_Invalid;
      else
         return
            Get_Column_Type
            (  Model.Reference,
               Index mod Get_N_Columns (Model.Reference)
            );
      end if;
   end Get_Column_Type;

   function Get_Flags
            (  Model : not null access Gtk_Columned_Store_Record
            )  return Tree_Model_Flags is
   begin
      if Model.Reference = Null_Gtk_Tree_Model then
         return Tree_Model_List_Only;
      else
         return Get_Flags (Model.Reference) or Tree_Model_List_Only;
      end if;
   end Get_Flags;

   function Get_Iter
            (  Model : not null access Gtk_Columned_Store_Record;
               Path  : Gtk_Tree_Path
            )  return Gtk_Tree_Iter is
   begin
      if (  Path = Null_Gtk_Tree_Path
         or else
            Model.Reference = Null_Gtk_Tree_Model
         )
      then
         return Null_Iter;
      else
         declare
            Indices : constant GInt_Array := Get_Indices (Path);
         begin
            if Indices'Length = 1 then
               return Compose_Columned (Model, Indices (Indices'First));
            else
               return Null_Iter;
            end if;
         end;
      end if;
   end Get_Iter;

   function Get_Major_Columns
            (  Model : not null access Gtk_Columned_Store_Record
            )  return Positive is
   begin
      return Positive (Model.Columns);
   end Get_Major_Columns;

   function Get_N_Columns
            (  Model : not null access Gtk_Columned_Store_Record
            )  return GInt is
   begin
      if Model.Reference = Null_Gtk_Tree_Model then
         return 0;
      else
         return Get_N_Columns (Model.Reference) * Model.Columns;
      end if;
   end Get_N_Columns;

   function Get_Path
            (  Model : not null access Gtk_Columned_Store_Record;
               Iter  : Gtk_Tree_Iter
            )  return Gtk_Tree_Path is
      Row : GInt;
   begin
      Split_Columned (Model, Iter, Row);
      if Row >= 0 then
         declare
            Result : Gtk_Tree_Path;
         begin
            Gtk_New (Result);
            Append_Index (Result, Row);
            return Result;
         end;
      else
         return Null_Gtk_Tree_Path;
      end if;
   end Get_Path;

   function Get_Reference
            (  Model : not null access Gtk_Columned_Store_Record
            )  return Gtk_Tree_Model is
   begin
      return Model.Reference;
   end Get_Reference;

   function Get_Reference_Iter
            (  Model  : not null access Gtk_Columned_Store_Record;
               Row    : Positive;
               Column : Positive
            )  return Gtk_Tree_Iter is
   begin
      if Model.Reference = Null_Gtk_Tree_Model then
         return Null_Iter;
      else
         return
            Compose_Reference
            (  Model,
               GInt (Row) - 1,
               GInt (Column) - 1
            );
      end if;
   end Get_Reference_Iter;

   function Get_Root
            (  Model : not null access Gtk_Columned_Store_Record
            )  return Gtk_Tree_Iter is
   begin
      if Model.Reference = Null_Gtk_Tree_Model then
         return Null_Iter;
      elsif Model.Path = Null_Gtk_Tree_Path then
         return Null_Iter;
      elsif Get_Depth (Model.Path) = 0 then -- Original root
         return Null_Iter;
      else
         return Get_Iter (Model.Reference, Model.Path);
      end if;
   end Get_Root;

   function Get_Root
            (  Model : not null access Gtk_Columned_Store_Record
            )  return Gtk_Tree_Path is
   begin
      if Model.Reference = Null_Gtk_Tree_Model then
         return Null_Gtk_Tree_Path;
      elsif Model.Path = Null_Gtk_Tree_Path then
         return Null_Gtk_Tree_Path;
      else
         return Copy (Model.Path);
      end if;
   end Get_Root;

   function Get_Rows
            (  Model  : not null access Gtk_Columned_Store_Record;
               Filled : Boolean
            )  return Natural is
   begin
      if Filled then
         return Get_Column_Height (Model, Positive (Model.Columns));
      else
         return Natural (Model.Rows);
      end if;
   end Get_Rows;

   function Get_Row_Width
            (  Model : not null access Gtk_Columned_Store_Record;
               Row   : Positive
            )  return Natural is
   begin
      if Row <= Get_Column_Height (Model, Positive (Model.Columns)) then
         return Natural (Model.Columns);
      elsif GInt (Row) <= Model.Rows then
         return Natural (Model.Columns - 1);
      else
         return 0;
      end if;
   end Get_Row_Width;

   procedure Get_Value
             (  Model  : not null access Gtk_Columned_Store_Record;
                Iter   : Gtk_Tree_Iter;
                Column : GInt;
                Value  : out GValue
             )  is
   begin
      if Model.Reference = Null_Gtk_Tree_Model then
         Log
         (  GtkAda_Contributions_Domain,
            Log_Level_Critical,
            "Null reference model" & Where ("Get_Value")
         );
      else
         declare
            Minors : constant GInt := Get_N_Columns (Model.Reference);
            Row    : constant Gtk_Tree_Iter :=
                        From_Columned
                        (  Model,
                           Iter,
                           Positive (Column / Minors + 1)
                        );
         begin
            if Row = Null_Iter then
               Init (Value, Get_Column_Type (Model, Column));
            else
               Get_Value
               (  Model.Reference,
                  Row,
                  Column mod Minors,
                  Value
               );
            end if;
         end;
      end if;
   end Get_Value;

   procedure Gtk_New
             (  Model     : out Gtk_Columned_Store;
                Reference : not null access
                            Gtk_Root_Tree_Model_Record'Class;
                Columns   : Positive;
                Root      : Gtk_Tree_Iter := Null_Iter
             )  is
   begin
      Model := new Gtk_Columned_Store_Record;
      Initialize (Model, Reference.all'Access, Columns, Root);
   exception
      when others =>
         Unref (Model);
         raise;
   end Gtk_New;

   procedure Gtk_New (Model : out Gtk_Columned_Store) is
   begin
      Model := new Gtk_Columned_Store_Record;
      Gtk.Tree_Model.Columned_Store.Initialize (Model);
   exception
      when others =>
         Unref (Model);
         raise;
   end Gtk_New;

   function Has_Child
            (  Model : not null access Gtk_Columned_Store_Record;
               Iter  : Gtk_Tree_Iter
            )  return Boolean is
   begin
      return False;
   end Has_Child;

   Signals : constant Chars_Ptr_Array :=
                      (0 => New_String ("root-changed"));

   procedure Initialize
             (  Model     : not null access
                            Gtk_Columned_Store_Record'Class;
                Reference : not null access
                            Gtk_Root_Tree_Model_Record'Class;
                Columns   : Positive;
                Root      : Gtk_Tree_Iter
             )  is
   begin
      if GTK_Type = GType_Invalid then
         GTK_Type := Register ("GtkColumnedStore", Signals);
      end if;
      Initialize (Model, GTK_Type);
      Set_Reference (Model, Reference, Columns, Root);
   end Initialize;

   procedure Initialize
             (  Model : not null access Gtk_Columned_Store_Record'Class
             )  is
   begin
      if GTK_Type = GType_Invalid then
         GTK_Type := Register ("GtkColumnedStore", Signals);
      end if;
      Initialize (Model, GTK_Type);
   end Initialize;

   function Is_Ancestor
            (  Model : not null access Gtk_Columned_Store_Record;
               Iter  : Gtk_Tree_Iter
            )  return Boolean is
   begin
      if Model.Reference = Null_Gtk_Tree_Model then
         return False;
      end if;
      declare
         Path   : constant Gtk_Tree_Path :=
                  Get_Path (Model.Reference, Iter);
         Result : constant Boolean := Is_Ancestor (Model.Path, Path);
      begin
         Path_Free (Path);
         return Result;
      end;
   end Is_Ancestor;

   function Is_Ancestor
            (  Model : not null access Gtk_Columned_Store_Record;
               Path  : Gtk_Tree_Path
            )  return Boolean is
   begin
      return Is_Ancestor (Model.Path, Path);
   end Is_Ancestor;

   function Is_Descendant
            (  Model : not null access Gtk_Columned_Store_Record;
               Iter  : Gtk_Tree_Iter
            )  return Boolean is
   begin
      if Model.Reference = Null_Gtk_Tree_Model then
         return False;
      end if;
      declare
         Path   : constant Gtk_Tree_Path :=
                  Get_Path (Model.Reference, Iter);
         Result : constant Boolean := Is_Descendant (Model.Path, Path);
      begin
         Path_Free (Path);
         return Result;
      end;
   end Is_Descendant;

   function Is_Descendant
            (  Model : not null access Gtk_Columned_Store_Record;
               Path  : Gtk_Tree_Path
            )  return Boolean is
   begin
      return Is_Descendant (Model.Path, Path);
   end Is_Descendant;

   procedure Next
             (  Model : not null access Gtk_Columned_Store_Record;
                Iter  : in out Gtk_Tree_Iter
             )  is
      Row : GInt;
   begin
      Split_Columned (Model, Iter, Row);
      if Row >= 0 then
         Iter := Compose_Columned (Model, Row + 1);
      else
         Iter := Null_Iter;
      end if;
   end Next;

   function Nth_Child
            (  Model  : not null access Gtk_Columned_Store_Record;
               Parent : Gtk_Tree_Iter;
               N      : GInt
            )  return Gtk_Tree_Iter is
   begin
      return Compose_Columned (Model, N);
   end Nth_Child;

   function N_Children
            (  Model : not null access Gtk_Columned_Store_Record;
               Iter  : Gtk_Tree_Iter := Null_Iter
            )  return GInt is
   begin
      if Iter = Null_Iter then
         return Model.Rows;
      else
         return 0;
      end if;
   end N_Children;

   procedure On_Changed_Row
             (  Reference  : access Gtk_Root_Tree_Model_Record'Class;
                Params     : GValues;
                Model      : Gtk_Columned_Store
             )  is
      Path : constant Gtk_Tree_Path :=
             Convert (Get_Address (Nth (Params, 1)));
      Row  : GInt := Get_Position (Model, Path);
   begin
      if Row >= 0 then
         if Model.Rows > 0 then
            Row := Row mod Model.Rows;
            declare
               Path : Gtk_Tree_Path;
            begin
               Gtk_New_First (Path);
               Set_Path (Path, Row);
               Row_Changed
               (  To_Interface (Model),
                  Path,
                  Compose_Columned (Model, Row)
               );
               Path_Free (Path);
            end;
         else
            --
            -- Changing an unknown item is treated as insertion
            --
            On_Row_Inserted (Reference, Params, Model);
         end if;
      end if;
   exception
      when Error : others =>
         Log
         (  GtkAda_Contributions_Domain,
            Log_Level_Critical,
            Exception_Message (Error) & Where ("On_Changed_Row")
         );
         Log
         (  GtkAda_Contributions_Domain,
            Log_Level_Critical,
            GNAT.Traceback.Symbolic.Symbolic_Traceback (Error)
         );
   end On_Changed_Row;

   procedure On_Deleted_Row
             (  Reference : access Gtk_Root_Tree_Model_Record'Class;
                Params    : GValues;
                Model     : Gtk_Columned_Store
             )  is
      Path : Gtk_Tree_Path := Convert (Get_Address (Nth (Params, 1)));
      Row  : GInt;
   begin
      if Path = Null_Gtk_Tree_Path or else Get_Depth (Path) = 0 then
         return;
      end if;
      case Compare (To_Interface (Model), Path, Model.Path) is
         when After =>
            --
            -- A node following the root has been deleted.  Iterator  to
            -- the root has to be requested again, anyway.
            --
            Row := Get_Position (Model, Path);
            if Row >= 0 then
               declare
                  Rows_Before : constant GInt := Model.Rows;
                  Column : GInt;
                  Size   : constant GInt :=
                              N_Children
                              (  Model.Reference,
                                 Model.Get_Root
                              );
               begin
                  Model.Rows := -- The new number of
                     (Size + Model.Columns - 1) / Model.Columns;
                  if Rows_Before /= 0 then
                     Column := Row / Rows_Before;
                     Row := Row mod Rows_Before;
                     --
                     -- Row,  Column  is where an item was deleted. This
                     -- changes  all following items. When Column is not
                     -- the last  column  then  it  is  all  rows  which
                     -- change. Otherwise it is the rows  starting  from
                     -- Row. The last row is deleted as requred.
                     --
                     if Column < Model.Columns - 1 then
                        Row := 0;
                     end if;
                     Gtk_New_First (Path);
                     for Changed in Row..Model.Rows - 1 loop
                        Set_Path (Path, Changed);
                        Row_Changed
                        (  To_Interface (Model),
                           Path,
                           Compose_Columned (Model, Changed)
                        );
                     end loop;
                     for Deleted in Model.Rows..Rows_Before - 1 loop
                        Set_Path (Path, Deleted);
                        Row_Deleted (To_Interface (Model), Path);
                     end loop;
                     Path_Free (Path);
                  end if;
               end;
            end if;
         when Equal =>
            --
            -- The root node itself has been deleted, move to its parent
            --
            Path := Copy (Model.Path);
            if Up (Path) and then Get_Depth (Path) > 0 then
               Set_Root (Model, Get_Iter (Model.Reference, Path));
            else
               Set_Root (Model, Null_Iter);
            end if;
            Path_Free (Path);
         when Before =>
            --
            -- A  row  was deleted before the root, only the iterator to
            -- the root is evaluated again as it might get lost.
            --
            if Is_Ancestor (Path, Model.Path) then
               --
               -- A  parent  node  of  the  root was deleted. Its parent
               -- becomes new root.
               --
               Path := Copy (Path);
               if Up (Path) and then Get_Depth (Path) > 0 then
                  Set_Root (Model, Get_Iter (Model.Reference, Path));
               else
                  Set_Root (Model, Null_Iter);
               end if;
               Path_Free (Path);
            elsif Is_Sibling (Model.Reference, Path, Model.Path) then
               --
               -- A sibling node of the root was deleted which itself is
               -- before the root. The  path  of  the  root  has  to  be
               -- decremented.
               --
               if not Prev (Model.Path) then
                  Log
                  (  GtkAda_Contributions_Domain,
                     Log_Level_Critical,
                     "Inconsitent path" & Where ("On_Deleted_Row")
                  );
               end if;
            end if;
      end case;
   exception
      when Error : others =>
         Log
         (  GtkAda_Contributions_Domain,
            Log_Level_Critical,
            Exception_Message (Error) & Where ("On_Deleted_Row")
         );
         Log
         (  GtkAda_Contributions_Domain,
            Log_Level_Critical,
            GNAT.Traceback.Symbolic.Symbolic_Traceback (Error)
         );
   end On_Deleted_Row;

   procedure On_Row_Inserted
             (  Reference  : access Gtk_Root_Tree_Model_Record'Class;
                Params     : GValues;
                Model      : Gtk_Columned_Store
             )  is
      Path : Gtk_Tree_Path := Convert (Get_Address (Nth (Params, 1)));
      Row  : GInt;
   begin
      case Compare (To_Interface (Model), Path, Model.Path) is
         when Before =>
            --
            -- A row was inserted before the root. The iterator to  root
            -- has to be evaluated again.
            --
            if Is_Sibling (Model.Reference, Path, Model.Path) then
               --
               -- A sibling node was insered before the root  node.  The
               -- path of has to be incremented.
               --
               Next (Model.Path);
            end if;
         when Equal =>
            --
            -- A sibling node was insered at the root node. The path  of
            -- has to be incremented.
            --
            Next (Model.Path);
         when After =>
            --
            -- Some  other node has been inserted. Iterator the the root
            -- has to be requested again, anyway.
            --
            Row := Get_Position (Model, Path);
            if Row >= 0 then
               declare
                  Rows_Before : constant GInt := Model.Rows;
                  Column : GInt;
                  Size   : constant GInt :=
                              N_Children
                              (  Model.Reference,
                                 Model.Get_Root
                              );
               begin
                  if Size = 0 then
                     return;
                  end if;
                  Model.Rows := -- The new number of rows
                     (Size + Model.Columns - 1) / Model.Columns;
                  Column := Row / Model.Rows;
                  Row := Row mod Model.Rows;
                  --
                  -- Row  and Column is where an item was inserted. This
                  -- changes it all following items. When it is the last
                  -- column  then  only  rows  starting  with  Row   and
                  -- involved. Otherwise it is  all  rows.  One  row  is
                  -- possibly inserted when necessary.
                  --
                  if Column < Model.Columns - 1 then
                     Row := 0;
                  end if;
                  Gtk_New_First (Path);
                  for Changed in Row..Rows_Before - 1 loop
                     Set_Path (Path, Changed);
                     Row_Changed
                     (  To_Interface (Model),
                        Path,
                        Compose_Columned (Model, Changed)
                     );
                  end loop;
                  for Inserted in Rows_Before..Model.Rows - 1 loop
                     Set_Path (Path, Inserted);
                     Row_Inserted
                     (  To_Interface (Model),
                        Path,
                        Compose_Columned (Model, Inserted)
                     );
                  end loop;
                  Path_Free (Path);
               end;
            end if;
      end case;
   exception
      when Error : others =>
         Log
         (  GtkAda_Contributions_Domain,
            Log_Level_Critical,
            Exception_Message (Error) & Where ("On_Row_Inserted")
         );
         Log
         (  GtkAda_Contributions_Domain,
            Log_Level_Critical,
            GNAT.Traceback.Symbolic.Symbolic_Traceback (Error)
         );
   end On_Row_Inserted;

   type Index_Array is array (Natural range <>) of aliased Gint;
   pragma Convention (C, Index_Array);

   package Index_Array_Pointers is
      new Interfaces.C.Pointers (Natural, GInt, Index_Array, 0);
   use Index_Array_Pointers;

   function To_Pointer is
      new Ada.Unchecked_Conversion
          (  Address,
             Index_Array_Pointers.Pointer
          );

   procedure On_Rows_Reordered
             (  Reference  : access Gtk_Root_Tree_Model_Record'Class;
                Params     : GValues;
                Model      : Gtk_Columned_Store
             )  is
   begin
      if (  Compare
            (  To_Interface (Model),
               Convert (Get_Address (Nth (Params, 1))),
               Model.Path
            )
         /= Equal
         )
      then
         return;
      end if;
      declare
         Order : Pointer := To_Pointer (Get_Address (Nth (Params, 2)));
         Rows  : constant GInt := N_Children (Model, Null_Iter) - 1;
         List  : array (0..Model.Rows - 1) of Boolean :=
                    (others => False);
         Path  : Gtk_Tree_Path;
      begin
         Gtk_New_First (Path);
         for Row in 0..Rows loop
            --
            -- Checking for permutations of  the  reference  rows.  Each
            -- moved  rows  causes  a change in the corresponding row of
            -- the columned model.
            --
            if Row /= Order.all then
               List (Row mod Model.Rows) := True;
            end if;
            Order := Order + 1;
        end loop;
        for Row in List'Range loop
           if List (Row) then
              Row_Changed
              (  To_Interface (Model),
                 Path,
                 Compose_Columned (Model, Row)
              );
           end if;
           Next (Path);
        end loop;
        Path_Free (Path);
      end;
   end On_Rows_Reordered;

   function Parent
            (  Model : not null access Gtk_Columned_Store_Record;
               Child : Gtk_Tree_Iter
            )  return Gtk_Tree_Iter is
   begin
      return Null_Iter;
   end Parent;

   procedure Previous
             (  Model : not null access Gtk_Columned_Store_Record;
                Iter  : in out Gtk_Tree_Iter
             )  is
      Row : GInt;
   begin
      Split_Columned (Model, Iter, Row);
      if Row >= 0 then
         Iter := Compose_Columned (Model, Row - 1);
      else
         Iter := Null_Iter;
      end if;
   end Previous;

   procedure Set_Null_Reference
             (  Model : not null access Gtk_Columned_Store_Record;
                Root_Changed : Boolean := True
             )  is
   begin
      if Model.Reference /= Null_Gtk_Tree_Model then
         -- Disconnect callbacks
         for Index in Model.Callbacks'Range loop
            Disconnect (-Model.Reference, Model.Callbacks (Index));
         end loop;
         Erase (Model);
         Unref (-Model.Reference);
         Model.Reference := Null_Gtk_Tree_Model;
         Model.Columns   := 1;
         if Root_Changed then
            Emit_Root_Changed (Model);
         end if;
      end if;
   end Set_Null_Reference;

   procedure Set_Reference
             (  Model     : not null access Gtk_Columned_Store_Record;
                Reference : not null access
                            Gtk_Root_Tree_Model_Record'Class;
                Columns   : Positive;
                Root      : Gtk_Tree_Iter
             )  is
   begin
      if (  To_Interface (Reference) = Model.Reference
         and then
            GInt (Columns) = Model.Columns
         )
      then
         --
         -- This is the same model and same columns number, so  we  just
         -- change the root
         --
         Set_Root (Model, Root);
      else
         --
         -- Everything  get changed. First we reference the new model so
         -- that it will not get occasionally collected. Then we set the
         -- reference model to null.
         --
         Ref (Reference);
         Set_Null_Reference (Model, False);
         Model.Columns   := GInt (Columns);
         Model.Reference := To_Interface (Reference);
         Do_Set_Root (Model, Root);
         Model.Callbacks (Changed) :=
            Tree_Handlers.Connect
            (  Reference,
               "row-changed",
               On_Changed_Row'Access,
               Model.all'Access
            );
         Model.Callbacks (Deleted) :=
            Tree_Handlers.Connect
            (  Reference,
               "row-deleted",
               On_Deleted_Row'Access,
               Model.all'Access
            );
         Model.Callbacks (Inserted) :=
            Tree_Handlers.Connect
            (  Reference,
               "row-inserted",
               On_Row_Inserted'Access,
               Model.all'Access
            );
         Model.Callbacks (Reordered) :=
            Tree_Handlers.Connect
            (  Reference,
               "rows-reordered",
               On_Rows_Reordered'Access,
               Model.all'Access
            );
      end if;
   end Set_Reference;

   procedure Set_Root
             (  Model : not null access Gtk_Columned_Store_Record;
                Root  : Gtk_Tree_Iter
             )  is
   begin
      if Model.Reference = Null_Gtk_Tree_Model then
         if Root /= Null_Iter then
            Log
            (  GtkAda_Contributions_Domain,
               Log_Level_Critical,
               "Wrong iterator" & Where ("Set_Root")
            );
         end if;
         return;
      end if;
      Erase (Model);
      Do_Set_Root (Model, Root);
   end Set_Root;

   procedure To_Columned
             (  Model  : not null access Gtk_Columned_Store_Record;
                Iter   : in out Gtk_Tree_Iter;
                Column : out Positive
             )  is
      Path : constant Gtk_Tree_Path := Get_Path (Model.Reference, Iter);
      Row  : GInt;
      Col  : GInt;
   begin
      Split_Reference (Model, Path, Row, Col);
      Iter := Compose_Columned (Model, Row);
      Column := Positive (Col + 1);
      if Path /= Null_Gtk_Tree_Path then
         Path_Free (Path);
      end if;
   end To_Columned;

   function To_Columned
            (  Model : not null access Gtk_Columned_Store_Record;
               Iter  : Gtk_Tree_Iter
            )  return Gtk_Tree_Iter is
      Row    : Gtk_Tree_Iter := Iter;
      Column : Positive;
   begin
      To_Columned (Model, Row, Column);
      return Row;
   end To_Columned;

   function To_Columned
            (  Model : not null access Gtk_Columned_Store_Record;
               Path  : Gtk_Tree_Path
            )  return Gtk_Tree_Path is
      Row    : GInt;
      Col    : GInt;
      Result : Gtk_Tree_Path;
   begin
      Split_Reference (Model, Path, Row, Col);
      if Row > 0 then
         Gtk_New (Result);
         Append_Index (Result, Row);
         return Result;
      else
         return Null_Gtk_Tree_Path;
      end if;
   end To_Columned;

end Gtk.Tree_Model.Columned_Store;
