How to connect a callback function to its signal - ada

i created two buttons a that display a Gtk_Radio_Button and a button that display a GtK_label but when i click on buttons nothing happens. I don't understand why it doesn't work yet the code compiles and runs without finding any error.
main_Program.adb
With Gtk.Window; Use Gtk.Window;
With Gtk.Button; Use Gtk.Button;
With Gtk.Grid; Use Gtk.Grid;
With file; Use file;
Procedure main_program is
Bouton : Gtk_Button;
Bouton2 : Gtk_Button;
Win : Gtk_Window;
Test : Test_Record;
begin
Gtk.Main.Init;
Init_Grid (Container => Test);
Gtk_New (Win);
Win.Add (Test.Grid);
Win.Set_Default_Size (Width => 600,
Height => 400);
Gtk_New (Bouton,Label => "Bouton");
Test.Grid.Attach (Bouton,0,0);
P.Connect (Widget => Bouton,
Name => Signal_Clicked,
Marsh => Test2.P.To_Marshaller (Init_Button'Access),
After => False);
Gtk_New (Bouton2,Label => "Bouton2");
Test.Grid.Attach (Bouton2,50,4);
P.Connect (Widget => Bouton2,
Name => Signal_Clicked,
Marsh => Test2.P.To_Marshaller (Init_Text'Access),
After => False);
Win.Show_All;
Main;
end Main_Program;
file.ads
With Gtk.Label; Use Gtk.Label;
With Gtk.Radio_Button; Use Gtk.Radio_Button;
With Gtk.Grid; Use Gtk.Grid;
With Gtk.Handlers;
Package file is
type Test_Record is record
Text : Gtk_Label;
Grid : Gtk_Grid;
Button_Radio : Gtk_Radio_Button;
end record;
Procedure Init_Text ( Self : access Gtk_Widget_Record'Class );
-- Callback for create the text
Procedure Init_Button (Self : access Gtk_Widget_Record'Class );
-- Callback for Initialize the Radio Button;
Procedure Init_Grid (Container : out Test_Record);
-- Initialize the Gtk.Grid.Gtk_Grid
Package P is new Gtk.Handlers.Callback (Gtk_Widget_Record);
Use P;
end file;
file.adb
With file; Use file;
Package body file is
Procedure Init_Grid ( Container : out Test_Record ) is
begin
Gtk_New (Container.Grid);
end Init_Grid;
Procedure Init_Button ( Self : access Gtk_Widget_Record'Class ) is
V : Test_Record;
begin
Init_Grid (Container => V);
Gtk_New (V.Button_Radio,
Group => null,
Label => "Button Radio");
V.Grid.Attach (V.Button_Radio,0,50);
V.Grid.Show;
end Init_Button;
Procedure Init_Text (Self : access Gtk_Widget_Record'Class) is
V : Test_Record;
begin
Init_Grid (Container => V);
Gtk_New (V.Text);
V.Text.Set_Label ("Hello,World");
V.Grid.Attach (V.Text,150,0);
V.Grid.Show;
end Init_Text;
end file;
I want that when I click on the first button it shows me a radio button and when I click on the second button it shows me a label.

I would consider to not create/destroy the label and radio button, but just toggle their visibility instead. Furthermore, I would also recommend to derive the main window from the GTK window type and always properly terminate the application when requested (see Destroy_Event_Callback in the example below).
app.ads
package App is
end App;
app-main_window.ads
with Gtk.Window; use Gtk.Window;
with Gtk.Grid; use Gtk.Grid;
with Gtk.Button; use Gtk.Button;
with Gtk.Check_Button; use Gtk.Check_Button;
with Gtk.Label; use Gtk.Label;
package App.Main_Window is
type App_Main_Window_Record is new Gtk_Window_Record with private;
type App_Main_Window is access all App_Main_Window_Record'Class;
------------------
-- Constructors --
------------------
procedure Gtk_New
(Main_Window : out App_Main_Window);
procedure Initialize
(Main_Window : not null access App_Main_Window_Record'Class);
private
Window_Width : constant := 300;
Window_Height : constant := 100;
type App_Main_Window_Record is
new Gtk.Window.Gtk_Window_Record with
record
Grid : Gtk_Grid;
Button_1 : Gtk_Button;
Button_2 : Gtk_Button;
Check_Button : Gtk_Check_Button;
Label : Gtk_Label;
end record;
end App.Main_Window;
app-main_window.adb
with Gtk.Main;
with Gtk.Widget;
with Gtk.Window;
with Gdk.Event;
package body App.Main_Window is
procedure Destroy_Event_Callback
(Widget : access Gtk.Widget.Gtk_Widget_Record'Class);
function On_Button_1_Pressed_Callback
(Self : access Gtk.Widget.Gtk_Widget_Record'Class;
Event : Gdk.Event.Gdk_Event_Button) return Boolean;
function On_Button_2_Pressed_Callback
(Self : access Gtk.Widget.Gtk_Widget_Record'Class;
Event : Gdk.Event.Gdk_Event_Button) return Boolean;
-------------
-- Gtk_New --
-------------
procedure Gtk_New (Main_Window : out App_Main_Window) is
begin
Main_Window := new App_Main_Window_Record;
App.Main_Window.Initialize (Main_Window);
end Gtk_New;
----------------
-- Initialize --
----------------
procedure Initialize
(Main_Window : not null access App_Main_Window_Record'Class)
is
begin
-- Initialize and setup the window.
Gtk.Window.Initialize (Main_Window);
Main_Window.Set_Title ("Demo Window");
Main_Window.Set_Size_Request (Window_Width, Window_Height);
Main_Window.Set_Resizable (False);
-- Attach callback: properly end the GTK application when requested.
Main_Window.On_Destroy
(Call => Destroy_Event_Callback'Access);
-- Add a grid.
Gtk_New (Main_Window.Grid);
Main_Window.Grid.Set_Hexpand (True);
Main_Window.Grid.Set_Vexpand (True);
Main_Window.Grid.Set_Column_Homogeneous (True);
Main_Window.Grid.Set_Row_Homogeneous (True);
Main_Window.Add (Main_Window.Grid);
-- Create the two buttons.
Gtk_New (Main_Window.Button_1, Label => "Button 1");
Gtk_New (Main_Window.Button_2, Label => "Button 2");
-- Insert both buttons into the grid.
--
-- +------+------+
-- | | |
-- +------+------+
-- (0,1) --> | XXXX | XXXX | <-- (1,1)
-- +------+------+
Main_Window.Grid.Attach
(Child => Main_Window.Button_1,
Left => 0,
Top => 1);
Main_Window.Grid.Attach
(Child => Main_Window.Button_2,
Left => 1,
Top => 1);
-- Attach "button pressed" callbacks.
Main_Window.Button_1.On_Button_Press_Event
(Call => On_Button_1_Pressed_Callback'Access);
Main_Window.Button_2.On_Button_Press_Event
(Call => On_Button_2_Pressed_Callback'Access);
-- Create new label and check button.
Gtk_New (Main_Window.Label, Str => "Hello World!");
Gtk_New (Main_Window.Check_Button, Label => "A Check Button");
-- Insert both into the grid.
--
-- +------+------+
-- (0,0) --> | XXXXXXXXXXX | Width: 2 columns
-- +------+------+
-- | | |
-- +------+------+
Main_Window.Grid.Attach
(Child => Main_Window.Label,
Left => 0,
Top => 0,
Width => 2);
Main_Window.Grid.Attach
(Child => Main_Window.Check_Button,
Left => 0,
Top => 0,
Width => 2);
-- Show everything except the check button.
Main_Window.Show;
Main_Window.Grid.Show;
Main_Window.Button_1.Show;
Main_Window.Button_2.Show;
Main_Window.Label.Show;
end Initialize;
----------------------------
-- Destroy_Event_Callback --
----------------------------
procedure Destroy_Event_Callback
(Widget : access Gtk.Widget.Gtk_Widget_Record'Class)
is
begin
Gtk.Main.Main_Quit;
end Destroy_Event_Callback;
----------------------------------
-- On_Button_1_Pressed_Callback --
----------------------------------
function On_Button_1_Pressed_Callback
(Self : access Gtk.Widget.Gtk_Widget_Record'Class;
Event : Gdk.Event.Gdk_Event_Button) return Boolean
is
-- (parent) (parent)
-- Button (Self) -------> Grid -------> Window
Grid : Gtk_Grid := Gtk_Grid (Self.Get_Parent);
Window : App_Main_Window := App_Main_Window (Grid.Get_Parent);
begin
-- Just toggle visibility.
Window.Label.Set_Visible (False);
Window.Check_Button.Set_Visible (True);
return True; -- GDK_EVENT_STOP, do not propagate event to parent.
end On_Button_1_Pressed_Callback;
----------------------------------
-- On_Button_2_Pressed_Callback --
----------------------------------
function On_Button_2_Pressed_Callback
(Self : access Gtk.Widget.Gtk_Widget_Record'Class;
Event : Gdk.Event.Gdk_Event_Button) return Boolean
is
Grid : Gtk_Grid := Gtk_Grid (Self.Get_Parent);
Window : App_Main_Window := App_Main_Window (Grid.Get_Parent);
begin
Window.Label.Set_Visible (True);
Window.Check_Button.Set_Visible (False);
return True; -- GDK_EVENT_STOP, do not propagate event to parent.
end On_Button_2_Pressed_Callback;
end App.Main_Window;
main.adb
with Gtk.Main;
with App.Main_Window;
procedure Main is
use App.Main_Window;
Main_Window : App_Main_Window;
begin
Gtk.Main.Init;
Gtk_New (Main_Window);
Gtk.Main.Main;
end Main;

Related

signals with GtkAda

My concern is that I created a callback function, which should display a Gtk_Entry when we click on the Gtk_Button but is that when I click on the button nothing happens. I don't understand.
File.ads
Package Test is
Type T_Test is record
Conteneur : Gtk_Fixe;
L_Entree : Gtk_Entry;
end Record;
Procedure Le_Callback (Emetteur : access Gtk_Button_Record'Class);
Package P is new Gtk.Handlers.Callback (Gtk_Button_Record);
Use P;
end Test;
File.adb
Package body Test is
Procedure Initialise_Conteneur (Object : T_Test) is
begin
Gtk_New (Object.Conteneur);
end Initialise_Conteneur;
Procedure Le_Callback (Emetteur : access Gtk_Button_Record'Classs) is
V : T_Test;
begin
Initialise_Conteneur (Object => V);
Gtk_New (V.L_Entree);
V.Conteneur.Add (V.L_Entree);
V.L_Entree.Show;
end Le_Callback;
end Test;
Main.adb
Procedure Main is
Win : Gtk_Window;
Button : Gtk_Button;
Posix : T_Test;
begin
Init;
Initialize (object => Posix);
1
Gtk_New (Win);
Win.Set_Default_Size (600,400);
Gtk_New (Button,"Bouton");
Test.P.Connect (Widget => Button,
Name => Signal_Clicked,
Marsh => P.To_Marshaller (Le_Test'Access),
After => true);
Posix.Conteneur.Add (Button);
Win.Add (Posix.Conteneur);
Win.Show_All;
Main;
end Main;
Revised answer.
Slightly hacked package ... to export the Initialize method called in Main. (I'm also adding a Button instead of an Entry to make my life simpler)
with Gtk; use Gtk;
with Gtk.Button; use Gtk.Button;
with Gtk.Handlers; use Gtk.Handlers;
with Gtk.Fixed; use Gtk.Fixed;
Package Test is
Type T_Test is record
Conteneur : Gtk_Fixed;
Bouton : Gtk_Button;
end Record;
procedure Initialize (Object : out T_Test);
Procedure Le_Callback (Emetteur : access Gtk_Button_Record'Class);
Package P is new Gtk.Handlers.Callback (Gtk_Button_Record);
Use P;
end Test;
Several issues in the package body...
The parameter passing mode in the Initialize functions.
Make sure the new visible object is in a different place than the old one ... (noting that GTK_Fixed is a harder container to use than the others, in terms of manual layout
The callback creates a new container (now with a button in it) ... but until the container belongs to something, it cannot be displayed. The main window isn't directly visible in this package, so I added it to the parent container of the button that emitted the signal. (There are ways of passing user data to the handler; you could use that to pass in teh top level window or some other container)
And of course we must display the modifications, so let's just redraw the top level window.
(junk text to fix markup issue)
Package body Test is
Procedure Initialise_Conteneur (Object : out T_Test) is
begin
Gtk_New (Object.Conteneur);
end Initialise_Conteneur;
procedure Initialize (Object : out T_Test) renames Initialise_Conteneur;
Procedure Le_Callback (Emetteur : access Gtk_Button_Record'Class) is
V : T_Test;
begin
Initialise_Conteneur (Object => V);
Gtk_New (V.Bouton,"Autre_Bouton");
V.Conteneur.Add (V.Bouton);
-- make sure it doesn't sit on the other button...
-- Using gtk.fixed is hard work compared to newer containers
V.Conteneur.Move(V.Bouton,0,35);
-- Add our new GTK_Fixed container to the outer one
-- note Get_Parent returns a GTK_Widget'Class so we must
-- view convert to a GTK_Container or GTK_Fixed to see the Add method
Gtk_Fixed(Emetteur.Get_Parent).Add(V.Conteneur);
-- And re-display the top level window
Emetteur.Get_Toplevel.Show_All;
end Le_Callback;
end Test;
And the main program (connecting Le_Callback, not the nonexistent Le_Test)...
with Gtk.Button; use Gtk.Button;
with Gtk.Window; use Gtk.Window;
with Gtk.Main;
with test; use test;
Procedure Main is
Win : Gtk_Window;
Button : Gtk_Button;
Posix : T_Test;
begin
Gtk.Main.Init;
Initialize (object => Posix);
Gtk_New (Win);
Win.Set_Default_Size (600,400);
Gtk_New (Button,"Bouton");
Test.P.Connect (Widget => Button,
Name => Signal_Clicked,
Marsh => P.To_Marshaller (Le_Callback'Access),
After => true);
Posix.Conteneur.Add (Button);
Win.Add (Posix.Conteneur);
Win.Show_All;
GTK.Main.Main;
end Main;
and my GPR file for it.
with "gtkada";
-- with "gtkada_gl";
project Test is
for Main use ("main.adb");
for Source_Dirs use (".");
for Object_Dir use "obj";
for Exec_Dir use ".";
package Compiler is
for Default_Switches ("Ada") use ("-g", "-O1", "-gnatafo");
end Compiler;
package Binder is
for Default_Switches ("Ada") use ("-E");
end Binder;
package Linker is
-- for Default_Switches ("Ada") use ("-lgtkglada");
end Linker;
end Test;
Now it builds (in future, PLEASE make the example code buildable! would have saved a good chunk of time) and I get to see a button...
Press the button and the second button appears below it, so we know the handler is connected to the button, and receiving button press messages.
This is more some sort of comment, but this is how would I do it in vala (Compile with valac --pkg=gtk+-3.0 $FILENAME.
int main (string[] args) {
Gtk.init (ref args);
var window = new Gtk.Window ();
window.title = "MyWindow";
window.destroy.connect (Gtk.main_quit);
// Create a box, where we later add the button and then the entry
var box = new Gtk.Box (Gtk.Orientation.VERTICAL, /*spacing*/ 2);
var button = new Gtk.Button.with_label ("Bouton!");
// Connect to the signal "clicked", that is executed, as soon
// as the button is clicked by the user
button.clicked.connect (() => { // () => {...} is your callback.
// Add the entry to the box
box.pack_start (new Gtk.Entry());
// Redraw the window
window.show_all ();
});
// Add the button to the box
box.pack_start (button);
// Add the box to the window
window.add (box);
// Show the main window
window.show_all ();
// GTK-Mainloop
Gtk.main ();
return 0;
}

Ada: How to get Access to Vector element?

I have a collection of things, which I deliberately want to allocate on the heap and access them 'by reference':
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Indefinite_Hashed_Maps;
with Ada.Containers; use Ada.Containers;
procedure Main is
type Thing_Key is new Integer;
type Thing is record
Key : Thing_Key;
Data : Integer;
end record;
type Thing_Access is access all Thing;
function Image (T : Thing) return String is
(T.Key'Image & '(' & T.Data'Image & ')');
function "=" (A, B : Thing) return Boolean is
(A.Key = B.Key);
function Thing_Hash (K : Thing_Key) return Hash_Type is
(Hash_Type (K));
package Thing_Map is new
Ada.Containers.Indefinite_Hashed_Maps
(Key_Type => Thing_Key,
Element_Type => Thing,
Hash => Thing_Hash,
Equivalent_Keys => "=");
use Thing_Map;
Map : Thing_Map.Map;
C : Cursor;
P : Thing_Access;
begin
P := new Thing '(Key => 1, Data => 2); -- on the heap
Map.Insert (P.Key, P.all);
Put_Line (Image (P.all)); -- '1( 2)', as expected
P.Data := 99;
Put_Line (Image (P.all)); -- '1( 99)', as expected
C := Map.Find (1); -- Get cursor to thing
-- Set P to point at the thing at the cursor?
-- Following lines don't compile
P := Map (C)'Access; -- access-to-variable designates constant
P := Map (C).Reference; -- undefined selector "Reference" for overloaded prefix
P := Map (C).Get_Element_Access; -- undefined selector "Get_Element_Access" for overloaded prefix
P := Map.Reference (C); -- no visible interpretation of "Reference" matches expected type "Thing_Access"
end Main;
What is the syntax to get a pointer from a cursor?
I assume that you only want to store elements on the heap in order to access them by reference for manipulation. However, you don't need to do that when using Ada containers. All containers have some way of accessing the elements by reference readily available (via some Constant_Reference or Reference function that can typically be omitted because of the Variable_Indexing aspect defined on the container type; see, for example, section 6.3 in the Ada 2012 rationale, and/or the answer of #Timur Samkharadze).
If you want to store the key as part of the element, then I think it might be more appropriate to use a hashed set (see RM A.18.7, RM A.18.8 and on learn.adacore.com). An element in a hashed set can be accessed by reference via the function Reference_Preserving_Key (see also RM 96.10 (3)).
Below are two examples: the first example shows how to update an element in a Hashed_Map and the second example shows how to update an element in a Hashed_Set, both using a key:
main.adb (Hashed_Map)
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers; use Ada.Containers;
with Ada.Containers.Hashed_Maps;
procedure Main is
type Thing_Key is new Integer;
type Thing is record
Key : Thing_Key;
Data : Integer;
end record;
function Image (T : Thing) return String is
("Key = " & T.Key'Image & ", Value = " & T.Data'Image);
function Hash (K : Thing_Key) return Hash_Type is (Hash_Type (K));
package Things is new Ada.Containers.Hashed_Maps
(Key_Type => Thing_Key,
Element_Type => Thing,
Hash => Hash,
Equivalent_Keys => "=");
Map : Things.Map;
begin
-- Inserting 4 elements. Note that the key is now stored twice: once in
-- the map's key index (its hash, to be more precise), and once in the item
-- itself (unhashed). You must now ensure that the key value in the
-- element does not accidentally get out-of-sync with the hashed key in the
-- map's key index (e.g. when you update the stored element). Of course,
-- you could also you just omit the key in the element itself if possible
-- given your use-case.
Map.Insert (Key => 1, New_Item => (Key => 1, Data => 10));
Map.Insert (Key => 2, New_Item => (Key => 2, Data => 20));
Map.Insert (Key => 3, New_Item => (Key => 3, Data => 30));
Map.Insert (Key => 4, New_Item => (Key => 4, Data => 40));
for T of Map loop
Put_Line (Image (T));
end loop;
New_Line;
-- Update element with key 3.
--
-- Note that the following expressions are all equivalent:
--
-- Map.Reference (3).Element.all.Data := 300; -- Original expression
-- Map.Reference (3).Element.Data := 300; -- Omit "all" due to implicit dereferencing of access types in Ada.
-- Map.Reference (3).Data := 300; -- Omit "Element" due to the "Implicit_Dereferencing" aspect on the "Hashed_Maps.Reference_Type".
-- Map (3).Data := 300; -- Omit "Reference" due to the "Variable_Indexing" aspect on the "Hashed_Maps.Map" type.
--
Map (3).Data := 300;
-- Example if you really need a pointer to element with key 3.
declare
type Thing_Access is not null access all Thing;
type Thing_Constant_Access is not null access constant Thing;
-- Element is mutable via P , i.e. P.Data := 301 (OK)
-- Element is not mutable via CP, i.e. CP.Data := 302 (Error)
P : Thing_Access := Map.Reference (3).Element;
CP : Thing_Constant_Access := Map.Constant_Reference (3).Element;
begin
null;
end;
for T of Map loop
Put_Line (Image (T));
end loop;
New_Line;
end Main;
main.adb (Hashed_Set)
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers; use Ada.Containers;
with Ada.Containers.Hashed_Sets;
procedure Main is
type Thing_Key is new Integer;
type Thing is record
Key : Thing_Key;
Data : Integer;
end record;
function Image (T : Thing) return String is
("Key = " & T.Key'Image & ", Value = " & T.Data'Image);
function Key (T : Thing) return Thing_Key is (T.Key);
function Hash (T : Thing) return Hash_Type is (Hash_Type (T.Key));
function Hash (K : Thing_Key) return Hash_Type is (Hash_Type (K));
package Things is new Ada.Containers.Hashed_Sets
(Element_Type => Thing,
Hash => Hash,
Equivalent_Elements => "=");
package Things_Keys is new Things.Generic_Keys
(Key_Type => Thing_Key,
Key => Key,
Hash => Hash,
Equivalent_Keys => "=");
Set : Things.Set;
begin
-- Inserting 4 elements. Note that the key is stored only in the element.
Set.Insert ((Key => 1, Data => 10));
Set.Insert ((Key => 2, Data => 20));
Set.Insert ((Key => 3, Data => 30));
Set.Insert ((Key => 4, Data => 40));
for T of Set loop
Put_Line (Image (T));
end loop;
New_Line;
-- Update the element. See also RM 96.10 (3). Opposed to most other
-- containers, you cannot omit "Reference_Preserving_Key" as the "Set" type
-- does not have a "Variable_Indexing" aspect specifying "Reference_Preserving_Key".
-- Hence, you need write it out explicitly.
Things_Keys.Reference_Preserving_Key (Set, 3).Data := 300;
-- Example if you really need a pointer to element with key 3.
declare
type Thing_Access is not null access all Thing;
type Thing_Constant_Access is not null access constant Thing;
-- Element is mutable via P , i.e. P.Data := 301 (OK)
-- Element is not mutable via CP, i.e. CP.Data := 302 (Error)
P : Thing_Access := Things_Keys.Reference_Preserving_Key (Set, 3).Element;
CP : Thing_Constant_Access := Things_Keys.Constant_Reference (Set, 3).Element;
begin
null;
end;
for T of Set loop
Put_Line (Image (T));
end loop;
New_Line;
end Main;
output (same for both)
Key = 1, Value = 10
Key = 2, Value = 20
Key = 3, Value = 30
Key = 4, Value = 40
Key = 1, Value = 10
Key = 2, Value = 20
Key = 3, Value = 300
Key = 4, Value = 40
You might want to use P := Map.Reference(C).Element;
Function Reference returns a value of Reference_Type that has aspect Implicit_Dereference whose value is Element and whose type is not null access Element_Type.

Passing map to common procedure

The Java programming languages frequently uses interfaces like java.util.Map.
In the following example two custom map packages are created by using the generic packages Ada.Containers.Hashed_Maps and Ada.Containers.Ordered_Maps. Both generic packages are offering the functions/procedures Clear and Length. The procedures Do_Something are using this functions/procedures to clear the passed map and to print the container length (stupid example ...).
I am right then it is not possible to create a procedure Do_Something_Special that would accept maps of both types Map_One.Map and Map_Two.Map? In Java it would be possible to define a parameter with the type Map<Natural, Unbounded_String>.
with Ada.Containers.Hashed_Maps;
with Ada.Containers.Ordered_Maps;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Ada.Text_IO;
procedure Main is
function Hash (Value : Natural) return Ada.Containers.Hash_Type is
begin
return Ada.Containers.Hash_Type (Value);
end Hash;
package Map_One is new Ada.Containers.Hashed_Maps
(Key_Type => Natural,
Element_Type => Unbounded_String,
Hash => Hash,
Equivalent_Keys => "=");
package Map_Two is new Ada.Containers.Ordered_Maps
(Key_Type => Natural,
Element_Type => Unbounded_String);
procedure Do_Something (Input : in out Map_One.Map) is
begin
Input.Clear;
Ada.Text_IO.Put_Line ("Length: " & Input.Length'Image);
end Do_Something;
procedure Do_Something (Input : in out Map_Two.Map) is
begin
Input.Clear;
Ada.Text_IO.Put_Line ("Length: " & Input.Length'Image);
end Do_Something;
procedure Do_Something_Special (Input : in out ???) is
begin
Input.Clear;
Ada.Text_IO.Put_Line ("Length: " & Input.Length'Image);
end Do_Something_Special;
begin
null;
end Main;
Just like in Java you can use generics or interfaces.
A generic only solution:
generic
type Map is private;
with procedure Clear(Self : in out Map);
with function Length(Self : Map) return Ada.Containers.Count_Type;
procedure Do_Something_Special(Input : in out Map);
procedure Do_Something_Special(Input : in out Map) is
begin
Clear(Input);
Ada.Text_IO.Put_Line("Length: " & Length(Input)'Image);
end Do_Something_Special;
procedure Do_Something_Map_One is new Do_Something_Special
(Map => Map_One.Map,
Clear => Map_One.Clear,
Length => Map_One.Length);
procedure Do_Something_Map_Two is new Do_Something_Special
(Map => Map_Two.Map,
Clear => Map_Two.Clear,
Length => Map_Two.Length);
If instead you want to go the route of using a Map interface you can do the following:
Create a generic interface for any key/value types
use Ada.Containers;
generic
type Key_Type is private;
type Element_Type is private;
package Map_Interfaces is
type Map_Interface is interface;
procedure Clear(Self : in out Map_Interface) is abstract;
function Length(Self : Map_Interface) return Count_Type is abstract;
-- other operations
end Map_Interfaces;
Next implement it for the key/value types you want:
package My_Map_Interfaces is new Map_Interfaces
(Key_Type => Natural,
Element_Type => Unbounded_String);
use My_Map_Interfaces;
Now you are able to use the class type of the interface to operate on any map that implements that interface:
procedure Do_Something_Special_1(Input : in out Map_Interface'Class) is
begin
Input.Clear;
Ada.Text_IO.Put_Line ("Length: " & Input.Length'Image);
end Do_Something_Special_1;
Then you just need to extend the Ada map types and implement the interface:
type Map_1 is new Map_One.Map and Map_Interface with null record;
type Map_2 is new Map_Two.Map and Map_Interface with null record;
M1 : Map_1;
M2 : Map_2;
and you can call it this way:
Do_Something_Special_1(M1);
Do_Something_Special_1(M2);
OR you an create another generic function if you want static dispatch instead of dynamic:
generic
type Map is new Map_Interface with private;
procedure Do_Something_Special_2(Input : in out Map);
procedure Do_Something_Special_2(Input : in out Map) is
begin
Input.Clear;
Ada.Text_IO.Put_Line ("Length: " & Input.Length'Image);
end Do_Something_Special_2;
procedure Do_Something_Map_1 is new Do_Something_Special_2(Map_1);
procedure Do_Something_Map_2 is new Do_Something_Special_2(Map_2);
and call it like this:
Do_Something_Map_1(M1);
Do_Something_Map_2(M2);

How can I find the `'First` and `'Last` expressions for a string field in a record using ASIS

I'm using ASIS to analyse a big Ada project. One of the things I need to do is to find the 'First and 'Last expressions for a string field in a record variable.
My problem occurs when I have a Discrete_Range, which is not A_Discrete_Simple_Expression_Range (for which one can use the functions Lower_Bound and Upper_Bound directly), but instead A_Discrete_Range_Attribute_Reference.
The source example which I'm analysing basically looks like this:
with Ada.Text_IO;
procedure Minimal_Example is
type R is
record
F : String (1 .. 5);
end record;
V : R;
subtype S is String (V.F'Range); -- It would have been nice if they didn't do like this.
function F return S is ("12345");
begin
Ada.Text_IO.Put_Line (F);
end Minimal_Example;
Here is a minimised version of the program I use to perform the analysis:
-- Standard library packages:
with Ada.Wide_Text_IO;
-- ASIS packages:
with Asis;
with Asis.Ada_Environments;
with Asis.Compilation_Units;
with Asis.Declarations;
with Asis.Definitions;
with Asis.Elements;
with Asis.Expressions;
with Asis.Implementation;
with Asis.Iterator;
with Asis.Statements;
with Asis.Text;
procedure Minimal_Analyzer is
procedure Pre_Operation (Element : in Asis.Element;
Control : in out Asis.Traverse_Control;
State : in out Boolean) is
pragma Unreferenced (Control, State);
use all type Asis.Element_Kinds;
use all type Asis.Statement_Kinds;
begin
if Asis.Elements.Element_Kind (Element) = A_Statement and then
Asis.Elements.Statement_Kind (Element) = A_Procedure_Call_Statement
then
for Parameter_Association of Asis.Statements.Call_Statement_Parameters (Statement => Element,
Normalized => True) loop
declare
Actual_Parameter : Asis.Element;
Type_Of_Expression : Asis.Element;
Type_Definition : Asis.Definition;
Constraint : Asis.Constraint;
begin
Actual_Parameter := Asis.Expressions.Actual_Parameter (Parameter_Association);
Type_Of_Expression := Asis.Expressions.Corresponding_Expression_Type (Actual_Parameter);
Type_Definition := Asis.Declarations.Type_Declaration_View (Declaration => Type_Of_Expression);
Constraint := Asis.Definitions.Subtype_Constraint (Type_Definition);
for Index_Range of Asis.Definitions.Discrete_Ranges (Constraint) loop
declare
Range_Attribute : Asis.Definition;
Range_Prefix : Asis.Element;
begin
Range_Attribute := Asis.Definitions.Range_Attribute (Index_Range);
Range_Prefix := Asis.Expressions.Prefix (Range_Attribute);
Ada.Wide_Text_IO.Put_Line (Asis.Elements.Debug_Image (Range_Prefix));
Ada.Wide_Text_IO.Put_Line (Asis.Text.Element_Image (Range_Prefix));
end;
end loop;
end;
end loop;
end if;
end Pre_Operation;
procedure Post_Operation (Element : in Asis.Element;
Control : in out Asis.Traverse_Control;
State : in out Boolean) is null;
procedure Traverse_Declaration is
new Asis.Iterator.Traverse_Element (State_Information => Boolean,
Pre_Operation => Pre_Operation,
Post_Operation => Post_Operation);
Context : Asis.Context;
begin
Asis.Implementation.Initialize ("");
Asis.Ada_Environments.Associate (The_Context => Context,
Name => "CLPG",
Parameters => "-CA -FM");
Asis.Ada_Environments.Open (The_Context => Context);
Analyze :
declare
Complation_Unit_Body : Asis.Compilation_Unit;
Complation_Unit_Body_Declaration : Asis.Declaration;
Process_Control : Asis.Traverse_Control := Asis.Continue;
State : Boolean := False;
begin
Complation_Unit_Body := Asis.Compilation_Units.Compilation_Unit_Body (Name => "Minimal_Example",
The_Context => Context);
Complation_Unit_Body_Declaration := Asis.Elements.Unit_Declaration (Compilation_Unit => Complation_Unit_Body);
Traverse_Declaration (Element => Complation_Unit_Body_Declaration,
Control => Process_Control,
State => State);
end Analyze;
Asis.Ada_Environments.Close (The_Context => Context);
Asis.Ada_Environments.Dissociate (The_Context => Context);
Asis.Implementation.Finalize (Parameters => "");
end Minimal_Analyzer;
Project file:
with "asis";
project Build is
for Main use ("minimal_analyzer.adb",
"minimal_example.adb");
for Source_Dirs use (".");
for Object_Dir use "obj";
for Exec_Dir use "bin";
package Builder is
for Default_Switches ("Ada") use ("-m", -- Do not recompile if only comments have changed
"-s", -- Recompile if switches change
"-j0"); -- Build concurrently
end Builder;
package Compiler is
for Default_Switches ("Ada") use ("-gnatoU",
"-gnat2012",
"-funwind-tables",
"-fstack-check",
"-gnata");
end Compiler;
end Build;
Build command:
gprbuild -j0 -p -P build.gpr
You need to have ASIS installed to build the tool. If you run minimal_analyzer from the directory where minimal_example.adb is located, you get the output:
Element Debug_Image:
A_SELECTED_COMPONENT
located in Minimal_Example (body, Unit_Id = 2, Context_Id = 1)
text position : minimal_example.adb:8:40
Nodes:
Node : 2332 - N_SELECTED_COMPONENT
R_Node : 2332 - N_SELECTED_COMPONENT
Node_Field_1 : 0 - N_EMPTY
Node_Field_2 : 0 - N_EMPTY
Rel_Sloc : 157
obtained from the tree /tmp/minimal_example.adt (Tree_Id = 1)
V.F
... but how can I get to the definition of V.F, so I can extract the Discrete_Simple_Expression_Range 1 .. 5?
I found a solution:
The trick is to know when to use ASIS.Expressions.Corresponding_Name_Declaration...
-- Standard library packages:
with Ada.Wide_Text_IO;
-- ASIS packages:
with ASIS;
with ASIS.Ada_Environments;
with ASIS.Compilation_Units;
with ASIS.Declarations;
with ASIS.Definitions;
with ASIS.Elements;
with ASIS.Expressions;
with ASIS.Implementation;
with ASIS.Iterator;
with ASIS.Statements;
with ASIS.Text;
procedure Minimal_Analyzer is
procedure Pre_Operation (Element : in ASIS.Element;
Control : in out ASIS.Traverse_Control;
State : in out Boolean) is
pragma Unreferenced (Control, State);
use all type ASIS.Element_Kinds;
use all type ASIS.Statement_Kinds;
begin
if ASIS.Elements.Element_Kind (Element) = A_Statement and then
ASIS.Elements.Statement_Kind (Element) = A_Procedure_Call_Statement
then
for Parameter_Association of ASIS.Statements.Call_Statement_Parameters (Statement => Element,
Normalized => True) loop
declare
Actual_Parameter : ASIS.Element;
Type_Of_Expression : ASIS.Element;
Type_Definition : ASIS.Definition;
Constraint : ASIS.Constraint;
begin
Actual_Parameter := ASIS.Expressions.Actual_Parameter (Parameter_Association);
Type_Of_Expression := ASIS.Expressions.Corresponding_Expression_Type (Actual_Parameter);
Type_Definition := ASIS.Declarations.Type_Declaration_View (Declaration => Type_Of_Expression);
Constraint := ASIS.Definitions.Subtype_Constraint (Type_Definition);
for Index_Range of ASIS.Definitions.Discrete_Ranges (Constraint) loop
declare
Range_Attribute : ASIS.Definition;
Range_Prefix : ASIS.Element;
Field_Name : ASIS.Defining_Name;
Field_Declaration : ASIS.Element;
Field_Definition : ASIS.Definition;
Field_Type_Definition : ASIS.Definition;
Constraint : ASIS.Constraint;
begin
Range_Attribute := ASIS.Definitions.Range_Attribute (Index_Range);
Range_Prefix := ASIS.Expressions.Prefix (Range_Attribute);
Field_Name := ASIS.Expressions.Selector (Range_Prefix);
Field_Declaration := ASIS.Expressions.Corresponding_Name_Declaration (Field_Name);
Field_Definition := ASIS.Declarations.Object_Declaration_View (Field_Declaration);
Field_Type_Definition := ASIS.Definitions.Component_Definition_View (Component_Definition => Field_Definition);
Constraint := ASIS.Definitions.Subtype_Constraint (Field_Type_Definition);
for Index_Range of ASIS.Definitions.Discrete_Ranges (Constraint) loop
declare
First, Last : ASIS.Expression;
begin
First := ASIS.Definitions.Lower_Bound (Index_Range);
Last := ASIS.Definitions.Upper_Bound (Index_Range);
Ada.Wide_Text_IO.Put_Line (ASIS.Elements.Debug_Image (First));
Ada.Wide_Text_IO.Put_Line (ASIS.Text.Element_Image (First));
Ada.Wide_Text_IO.Put_Line (ASIS.Elements.Debug_Image (Last));
Ada.Wide_Text_IO.Put_Line (ASIS.Text.Element_Image (Last));
end;
end loop;
end;
end loop;
end;
end loop;
end if;
end Pre_Operation;
procedure Post_Operation (Element : in ASIS.Element;
Control : in out ASIS.Traverse_Control;
State : in out Boolean) is null;
procedure Traverse_Declaration is
new ASIS.Iterator.Traverse_Element (State_Information => Boolean,
Pre_Operation => Pre_Operation,
Post_Operation => Post_Operation);
Context : ASIS.Context;
begin
ASIS.Implementation.Initialize ("");
ASIS.Ada_Environments.Associate (The_Context => Context,
Name => "CLPG",
Parameters => "-CA -FM");
ASIS.Ada_Environments.Open (The_Context => Context);
Analyze :
declare
Complation_Unit_Body : ASIS.Compilation_Unit;
Complation_Unit_Body_Declaration : ASIS.Declaration;
Process_Control : ASIS.Traverse_Control := ASIS.Continue;
State : Boolean := False;
begin
Complation_Unit_Body := ASIS.Compilation_Units.Compilation_Unit_Body (Name => "Minimal_Example",
The_Context => Context);
Complation_Unit_Body_Declaration := ASIS.Elements.Unit_Declaration (Compilation_Unit => Complation_Unit_Body);
Traverse_Declaration (Element => Complation_Unit_Body_Declaration,
Control => Process_Control,
State => State);
end Analyze;
ASIS.Ada_Environments.Close (The_Context => Context);
ASIS.Ada_Environments.Dissociate (The_Context => Context);
ASIS.Implementation.Finalize (Parameters => "");
end Minimal_Analyzer;

QtAda - Drag and drop images from one table view to another

I'm new to programming and just started with Qtada 3.2, so please be indulgent :D
My goal is to drag a picture (png) from one table view model to another.
While the image is already shown during drag in the minimal example below, I guess overriding mouse press and mouse release events should be the solution?
Is there anything else I need to do, like create drop action, mime data or item delegate?
Although I had a look at the QtAda tutorials, I still don't know how to correctly setup overriding events for my table views.
Really tried a lot, but nothing worked. Right now I'm stuck and confused.
It would be nice if somebody could point me to the right direction, or even better, show me a working example!
Bonus question #1: Is there a better / more correct way to load an image into a item model?
Bonus question #2: How to get rid of the line edits in the table? I just need the image to be shown in a cell, nothing else.
Thanks in advance!
Michael
My example:
main.adb
with Qt4.Core_Applications;
with Qt4.Objects;
with Qt4.Push_Buttons.Constructors;
with Qt4.Splitters.Constructors;
with Qt4.Splitters;
with Qt4.Strings;
with Qt_Ada.Application;
with Table_View_Big;
with Table_View_Small;
procedure Main is
begin
Qt_Ada.Application.Initialize;
declare
Quit : constant not null access Qt4.Push_Buttons.Q_Push_Button'Class
:= Qt4.Push_Buttons.Constructors.Create
(Qt4.Strings.From_Utf_16 ("Quit"));
Splitter : constant not null Qt4.Splitters.Q_Splitter_Access
:= Qt4.Splitters.Constructors.Create(The_Orientation => Qt4.Vertical);
Big_Table : constant not null Table_View_Big.Big_Table_Access
:= Table_View_Big.Constructors.Create;
Small_Table : constant not null Table_View_Small.Small_Table_Access
:= Table_View_Small.Constructors.Create;
begin
Splitter.Add_Widget(Big_Table);
Splitter.Add_Widget(Small_Table);
Splitter.Add_Widget(Quit);
Qt4.Objects.Connect (Quit,
Qt4.Signal ("clicked()"),
Qt4.Core_Applications.Instance,
Qt4.Slot ("quit()"));
Splitter.Set_Fixed_Size(640,480);
Splitter.Show;
Qt_Ada.Application.Execute;
end;
end Main;
table_view_small.ads :
with Qt4.Table_Views.Constructors;
with Qt4.Table_Views.Directors;
with Qt4.Abstract_Item_Models;
--with Qt4.Mouse_Events;
package Table_View_Small is
type Small_Table is limited new Qt4.Table_Views.Q_Table_View with private;
type Small_Table_Access is access all Small_Table'Class;
package Constructors is
function Create return not null Small_Table_Access;
end Constructors;
private
type Small_Table is new Qt4.Table_Views.Directors.Q_Table_View_Director with record
Small_Table_Item_Model : Qt4.Abstract_Item_Models.Q_Abstract_Item_Model_Access;
end record;
-- overriding procedure Mouse_Press_Event
-- (Self : not null access Table_Small;
-- Event : not null access Qt4.Mouse_Events.Q_Mouse_Event'Class);
-- overriding procedure Mouse_Move_Event
-- (Self : not null access Table_Small;
-- Event : not null access Qt4.Mouse_Events.Q_Mouse_Event'Class);
-- overriding procedure Mouse_Release_Event
-- (Self : not null access Table_Small;
-- Event : not null access Qt4.Mouse_Events.Q_Mouse_Event'Class);
end Table_View_Small;
table_view_small.adb
with Qt4.Icons;
with Qt4.Model_Indices;
with Qt4.Standard_Item_Models.Constructors;
with Qt4.Strings;
with Qt4.Variants;
with Table_View_Small.MOC;
pragma Warnings (Off, Table_View_Small.MOC);
package body Table_View_Small is
use Qt4;
package body Constructors is
function Create return not null Small_Table_Access is
Self : constant Table_View_Small.Small_Table_Access := new Table_View_Small.Small_Table;
begin
Qt4.Table_Views.Directors.Constructors.Initialize (Self);
declare
Icon : Qt4.Icons.Q_Icon;
Data_Role : Qt4.Item_Data_Role := qt4.Decoration_Role;
Index : Qt4.Model_Indices.Q_Model_Index;
begin
Self.Small_Table_Item_Model := Qt4.Abstract_Item_Models.Q_Abstract_Item_Model_Access
(Qt4.Standard_Item_Models.Constructors.Create (1, 6, Self));
Icon := Qt4.Icons.Create(Qt4.Strings.From_Ucs_4("anypng.png"));
Index := Self.Small_Table_Item_Model.Index(0,0);
Qt4.Abstract_Item_Models.Set_Data(Self.Small_Table_Item_Model,index,icon.To_Q_Variant,Data_Role);
Self.Set_Model(Self.Small_Table_Item_Model);
-- Drag & Drop
Self.Set_Accept_Drops(true);
Self.Set_Drag_Enabled(true);
Self.Set_Drop_Indicator_Shown(true);
-- Self.Set_Drag_Drop_Mode(Qt4.Abstract_Item_Views.Internal_Move);
return Self;
end;
end Create;
end Constructors;
-- overriding procedure Mouse_Press_Event
-- (Self : not null access Small_Table;
-- Event : not null access Qt4.Mouse_Events.Q_Mouse_Event'Class)
-- is
-- null;
-- end Mouse_Press_Event;
end Table_View_Small;
table_view_big.ads
with Qt4.Table_Views.Constructors;
with Qt4.Table_Views.Directors;
with Qt4.Abstract_Item_Models;
package Table_View_Big is
type Big_Table is limited new Qt4.Table_Views.Q_Table_View with private;
type Big_Table_Access is access all Big_Table'Class;
package Constructors is
function Create return not null Big_Table_Access;
end Constructors;
private
type Big_Table is new Qt4.Table_Views.Directors.Q_Table_View_Director with record
Big_Table_Item_Model : Qt4.Abstract_Item_Models.Q_Abstract_Item_Model_Access;
end record;
-- overriding procedure Mouse_Press_Event
-- (Self : not null access Big_Table;
-- Event : not null access Qt4.Mouse_Events.Q_Mouse_Event'Class);
--
-- overriding procedure Mouse_Move_Event
-- (Self : not null access Big_Table;
-- Event : not null access Qt4.Mouse_Events.Q_Mouse_Event'Class);
-- overriding procedure Mouse_Release_Event
-- (Self : not null access Big_Table;
-- Event : not null access Qt4.Mouse_Events.Q_Mouse_Event'Class);
end Table_View_Big;
table_view_big.adb
with Qt4.Abstract_Item_Views;
with Qt4.Sizes;
with Qt4.Standard_Item_Models.Constructors;
with Table_View_Big.MOC;
pragma Warnings (Off, Table_View_Big.MOC);
package body Table_View_Big is
package body Constructors is
function Create return not null Big_Table_Access is
Self : constant Table_View_Big.Big_Table_Access := new Table_View_Big.Big_Table;
begin
Qt4.Table_Views.Directors.Constructors.Initialize (Self);
declare
begin
Self.Big_Table_Item_Model := Qt4.Abstract_Item_Models.Q_Abstract_Item_Model_Access
(Qt4.Standard_Item_Models.Constructors.Create (25, 25, Self));
Self.Set_Model(Self.Big_Table_Item_Model);
Self.Set_Selection_Mode(Qt4.Abstract_Item_Views.No_Selection);
-- Drag & Drop
Self.Set_Accept_Drops(true);
Self.Set_Drag_Enabled(false);
Self.Set_Drop_Indicator_Shown(true);
-- Self.Set_Drag_Drop_Mode (Qt4.Abstract_Item_Views.Drop_Only);
return Self;
end;
end Create;
end Constructors;
end Table_View_Big;
table_view_view_test_dd.gpr
with "qt_gui";
project Table_View_Test_DD is
type Build_Modes is ("Application", "Metadata");
Build_Mode : Build_Modes := external ("BUILD_MODE");
for Source_Dirs use (".", ".amoc");
case Build_Mode is
when "Application" =>
for Main use ("main.adb");
for Object_Dir use ".objs";
for Exec_Dir use ".";
when "Metadata" =>
for Languages use ("Amoc");
for Object_Dir use ".amoc";
for Source_Files use ("table_view_small.ads",
"table_view_big.ads");
end case;
package Compiler is
for Default_Switches ("Ada") use ("-g", "-gnat05");
end Compiler;
package IDE is
for QtAda_Amoc_Invocation_Switch use "-XBUILD_MODE=Metadata";
end IDE;
-- for Languages use ("Ada");
package Naming is
for Casing use "lowercase";
end Naming;
end Table_View_Test_DD;

Resources