0

"MISSING A callback handler is also needed for the X button when you close the dialog otherwise it never closes and remains in the background." I tried the attached code to solve this, but without success. The code execution doesn't stop.. -In fact, there is probably 2 similar questions which are a) the life of the X button & the need to close it b) the life of the window & close it when necessary. -My proposal here is only for the window. i don't see how to solve correctly the a). Thanks Ps : thread following the previous one @ How to retrieve the name of the file from a GtkAda widget?

    -- window_callbacks.ads

with Gtkada.Builder; use Gtkada.Builder;

package Window_Callbacks is
  function On_Window1_Delete_Event (Builder : access Gtkada_Builder_Record'Class) return Boolean;
  procedure On_File1_File_Set (Builder : access Gtkada_Builder_Record'Class);
end Window_Callbacks;



-- window_callbacks.adb

-- units from GtkAda
with Gtk.Main;

-- units from GtkAda
with Gtk.File_Chooser;           use Gtk.File_Chooser;
with Gtk.File_Chooser_Button;    use Gtk.File_Chooser_Button;

with Gtkada.File_Selection;      use Gtkada.File_Selection;


-- units from Glib
with Glib;              use Glib;
with Glib.Object;       use Glib.Object;

-- Ada predefined units
with Ada.Text_IO;            use Ada.Text_IO;
with Ada.Characters.Latin_1; use Ada.Characters.Latin_1;
with Ada.Exceptions;

package body Window_Callbacks  is

  -----------------------------------------------
  -- On_Window1_Delete_Event
  -----------------------------------------------
  function On_Window1_Delete_Event 
                 (Builder : access Gtkada_Builder_Record'Class) return Boolean is
    pragma Unreferenced (Builder);
    begin
      Gtk.Main.Main_Quit;
      return False;
    end On_Window1_Delete_Event;

  ---------------------------------
  -- On_File1_File_Set  --
  ---------------------------------
  procedure On_File1_File_Set (Builder : access Gtkada_Builder_Record'Class) is
                                                      
    Button : access Gtk_File_Chooser_Button_Record;

    function Name_Strict (File_Name : string) return string is
      I : natural := File_Name'Last; 
      begin
        loop
          exit when File_Name (I) = Solidus or File_Name (I) = Reverse_Solidus or I = File_Name'First;
            I := @ - 1;
        end loop;
        if File_Name (I) = Solidus or File_Name (I) = Reverse_Solidus then 
            return File_Name (I+1..File_Name'Last); --folder is present
          else
            return File_Name;     -- folder is absent
        end if;
      end; 

    begin
      -- Get the file chooser button
      Button := Gtk_File_Chooser_Button (Get_Object(Builder, "file1"));

      -- Get the filename
      declare
        File_Name   : constant string := Get_FileName (Button);
        Folder_Name : constant string := Get_Current_Folder (Button);
        begin
          Ada.Text_IO.Put_Line ("File selected : "    & File_Name);
          Ada.Text_IO.Put_Line ("Current Folder : "   & Folder_Name);
          Ada.Text_IO.Put_Line ("Strict File Name : " & Name_Strict (File_Name));
          New_line;
        end;

    end On_File1_File_Set;

end Window_Callbacks;



-- Glade_8

-- units from Gtk
with Gtk.Main;
with Glib.Error;     use Glib.Error;
with Gtk.Widget;     use Gtk.Widget;
with Gtk.Builder;    use Gtk.Builder;
with Gtkada.Builder; use Gtkada.Builder;

-- Ada predefined units
with Ada.Text_IO;       use Ada.Text_IO;

-- Application specific units
with Window_Callbacks; use Window_Callbacks;

procedure Glade_8 is

  Mon_Interface   : Constant String :=
    "<?xml version=""1.0"" encoding=""UTF-8""?>"
  & "<!-- Generated with glade 3.40.0 -->"
  & "<interface>"
  & "  <requires lib=""gtk+"" version=""3.20""/>"
  & "  <object class=""GtkAdjustment"" id=""adjustment1"">"
  & "    <property name=""upper"">100</property>"
  & "    <property name=""step-increment"">1</property>"
  & "    <property name=""page-increment"">10</property>"
  & "  </object>"
  & "  <object class=""GtkListStore"" id=""liststore1"">"
  & "    <columns>"
  & "      <!-- column-name gchararray1 -->"
  & "      <column type=""gchararray""/>"
  & "    </columns>"
  & "    <data>"
  & "      <row>"
  & "        <col id=""0"" translatable=""yes"">test1</col>"
  & "      </row>"
  & "      <row>"
  & "        <col id=""0"" translatable=""yes"">test2</col>"
  & "      </row>"
  & "      <row>"
  & "        <col id=""0"" translatable=""yes"">test3</col>"
  & "      </row>"
  & "    </data>"
  & "  </object>"
  & "  <object class=""GtkWindow"" id=""window"">"
  & "    <property name=""can-focus"">False</property>"
  & "    <child>"
  & "      <object class=""GtkFixed"" id=""fixed1"">"
  & "        <property name=""visible"">True</property>"
  & "        <property name=""can-focus"">False</property>"
  & "        <child>"
  & "          <object class=""GtkFileChooserButton"" id=""file1"">"
  & "            <property name=""width-request"">196</property>"
  & "            <property name=""visible"">True</property>"
  & "            <property name=""can-focus"">False</property>"
  & "            <property name=""title"" translatable=""yes""/>"
  & "            <signal name=""file-set"" handler=""on_file1_file_set"" swapped=""no""/>"
  & "          </object>"
  & "          <packing>"
  & "            <property name=""x"">9</property>"
  & "            <property name=""y"">234</property>"
  & "          </packing>"
  & "        </child>"
  & "      </object>"
  & "    </child>"
  & "  </object>"
  & "</interface>";
  
  Builder       : Gtkada_Builder;
  Error         : aliased Glib.Error.GError;
  use type Glib.Guint;
     
  begin
    Gtk.Main.Init;

    -- Etape 1 : créer un Builder
    Gtk_New (Builder);
    
    if Add_From_String (Gtk_Builder(Builder), Mon_Interface, Error'Access) = 0 then
      Put_Line ("Error : " & Get_Message (Error));
      Error_Free (Error);
      return;
    end if;
   
    -- Etape 2 : créer les handlers des events
    Register_Handler (Builder, "on_window1_delete_event", On_Window1_Delete_Event'Access);
    Register_Handler (Builder, "on_file1_file_set", On_File1_File_Set'Access);

    -- Etape 3 : Do_Connect connecte tous les handlers enregistrés en une fois.
    Do_Connect (Builder);

    -- Etape 4 : Afficher la fenetre avec ses dépendances
    Show_All (Gtk_Widget (Get_Object (GTK_Builder (Builder), "window")));

    -- Etape 5 : Lancer la boucle infinie.
    Gtk.Main.Main;

    -- Etape 6 : Unref pour libérer la memoire associée au Builder.
    Unref (Builder);

  end Glade_8;

2 Answers 2

0

The way the X button works is

User presses X
      |
      | Delete event
      v
Delete Event Callback must return False.  If True
is returned, the window will not be destroyed.  Do
not kill the loop here.  There are some tidyups that
need to happen after False is returned.  Let the
window manager handle the destroy event
      |
      | Destroy event
      v
Destroy Event Callback terminates the main loop.

I don't know how this is done using builder so I'm just going to tell you the way I normally do it.

If you are going to do several programs, it is easier to put the callbacks into a package and just copy the package from one system to another.

Note: There are 2 handlers - one for procedures, one for functions

-- xbuttonpkg.ads
with Gtk.Window; use Gtk.Window;
package XButtonPkg is
    procedure XButtonHandler (window: Gtk_Window);
end XButtonPkg;

-- xbuttonpkg.adb
with Gdk.Event;    use Gdk.Event;
with Gtk.Widget;   use Gtk.Widget;
with Gtk.Handlers; use Gtk.Handlers;
with Ada.Text_IO;  use Ada.Text_IO;
with Gtk.Main;  -- for quit

package body XButtonPkg is

   -- For callback procedures.
   package Handlers is new Gtk.Handlers.Callback(
      Widget_Type => Gtk_Widget_Record
   );

   -- For callback functions
   package Return_Handlers is new Gtk.Handlers.Return_Callback(
      Widget_Type => Gtk_Widget_Record,
      Return_Type => Boolean);

   -- The delete event callback is a function
   function Delete_Event(
      Widget : access Gtk_Widget_Record'Class;
      Event  : Gdk_Event)
      return Boolean
   is
      pragma Unreferenced (Event);
      pragma Unreferenced (Widget);
   begin
      --  If you return False in the "delete_event" signal handler,
      --  GtkAda will emit the "destroy" signal. Returning True means
      --  you don't want the window to be destroyed.

      Put_Line ("delete event occurred");

      --  False = issue a destroy event

      return False;
   end Delete_Event;

   -- The destroy callback event is a procedure
   procedure Destroy (Widget : access Gtk_Widget_Record'Class) is
      pragma Unreferenced (Widget);
   begin
      Put_Line ("quitting");
      Gtk.Main.Main_Quit;
   end Destroy;

   procedure XButtonHandler(window:Gtk_Window) is
   begin
      --  Handle X button
      --  When the window is given the "delete_event" signal (this is given
      --  by the window manager, usually by the "close" option, or on the
      --  titlebar), the Delete_Event function gets invoked
      Return_Handlers.Connect (
         window,
         "delete_event",
         Return_Handlers.To_Marshaller (Delete_Event'Access));

      --  Connect the "destroy" event to a signal handler.
      --  This event occurs when Gtk.Widget.Destroy is called on the window,
      --  or if False is returned by the "delete_event" calback.
      Handlers.Connect (
         window,
         "destroy",
         Handlers.To_Marshaller (Destroy'Access));
   end XButtonInit;
end XButtonPkg;

In your main code, declare a window object

window: Gtk_Window;

Remove the delete event - this is handled by XButtonHandler.

Replace

-- Etape 4 : Afficher la fenetre avec ses dépendances
Show_All (Gtk_Widget (Get_Object (GTK_Builder (Builder), "window")));

with

window := Gtk_Window (Get_Object (GTK_Builder (Builder), "window"));
XButtonHandler (window);
Show_All (window);

That should just work - you will see some output for the delete event and again for the destroy event and you program should then terminate correctly.

I haven't used builder a lot. I'll experiment and see if there is a method using builder.

Sign up to request clarification or add additional context in comments.

Comments

0

If you wish to delete using builder

  1. Add callbacks to window_callbacks.ads. Note that delete must be a function, destroy must be a procedure

     function On_Delete_Event (Builder : access Gtkada_Builder_Record'Class) return Boolean;
     procedure On_Destroy (Builder : access Gtkada_Builder_Record'Class);
    
  2. Add the body to window_callbacks.adb

    function On_Delete_Event (
       Builder : access Gtkada_Builder_Record'Class)
    return Boolean is
       pragma Unreferenced (Builder);
    begin
       put_line("Delete event");
       return False;
    end On_Delete_Event;
    
    -- Callback to destroy a widget
    procedure On_Destroy (Builder : access Gtkada_Builder_Record'Class) is
       pragma Unreferenced (Builder);
    begin
       Put_Line ("quitting");
       Gtk.Main.Main_Quit;
    end On_Destroy;
    
  3. Add the signal names to the XML after can-focus. This XML can be generated by clicking on the Signals Tab in for the window in Glade. From there, expand GtkWidget. Add handlers to delete-event and destroy. destroy-event is for something else. There is no need to use that.

    & "    <property name=""can-focus"">False</property>"
    & "    <signal name=""delete-event"" handler=""on_delete_event"" swapped=""no""/>"
    & "    <signal name=""destroy"" handler=""on_destroy"" swapped=""no""/>"
    
  4. Register the signal handlers. Note that the handler name must be exactly the same as the one in the XML.

     Register_Handler (Builder, "on_delete_event", On_Delete_Event'Access);
     Register_Handler (Builder, "on_destroy", On_Destroy'Access);
    

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.