1-----------------------------------------------------------------------
2--               GtkAda - Ada95 binding for Gtk+/Gnome               --
3--                                                                   --
4--                 Copyright (C) 2001-2013, AdaCore                  --
5--                                                                   --
6-- This library is free software; you can redistribute it and/or     --
7-- modify it under the terms of the GNU General Public               --
8-- License as published by the Free Software Foundation; either      --
9-- version 2 of the License, or (at your option) any later version.  --
10--                                                                   --
11-- This library is distributed in the hope that it will be useful,   --
12-- but WITHOUT ANY WARRANTY; without even the implied warranty of    --
13-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU --
14-- General Public License for more details.                          --
15--                                                                   --
16-- You should have received a copy of the GNU General Public         --
17-- License along with this library; if not, write to the             --
18-- Free Software Foundation, Inc., 59 Temple Place - Suite 330,      --
19-- Boston, MA 02111-1307, USA.                                       --
20--                                                                   --
21-- As a special exception, if other files instantiate generics from  --
22-- this unit, or you link this unit with other files to produce an   --
23-- executable, this  unit  does not  by itself cause  the resulting  --
24-- executable to be covered by the GNU General Public License. This  --
25-- exception does not however invalidate any other reasons why the   --
26-- executable file  might be covered by the  GNU Public License.     --
27-----------------------------------------------------------------------
28
29--  <description>
30--  This widget organizes its children into resizable panes. Within each
31--  pane, multiple children can be put, and they will be accessible through
32--  a notebook.
33--  </description>
34--  <group>Layout containers</group>
35
36with Ada.Tags;
37with GNAT.Strings;
38with Glib;        use Glib;
39with Glib.Main;
40with Glib.Xml_Int;
41with Gdk.Color;
42with Gdk.Cursor;
43with Gdk.Event;
44with Gdk.Pixbuf;
45with Gdk.Rectangle;
46with Gtk.Accel_Group;
47with Gtk.Box;
48with Gtk.Enums;
49with Gtk.Event_Box;
50with Gtk.Handlers;
51with Gtk.Label;
52with Gtk.Menu;
53with Gtk.Menu_Item;
54with Gtk.Notebook;
55with Gtk.Style;
56with Gtk.Check_Menu_Item;
57with Gtk.Radio_Menu_Item;
58with Gtk.Widget;
59with Gtk.Window;
60with Gtkada.Handlers;
61with Gtkada.Multi_Paned;
62with Pango.Font;
63with Pango.Layout;
64
65package Gtkada.MDI is
66
67   type MDI_Window_Record is new Gtk.Widget.Gtk_Widget_Record with private;
68   type MDI_Window is access all MDI_Window_Record'Class;
69   --  Although this widget is implemented as a gtk_layout, you shouldn't
70   --  use the standard Gtk_Layout functions like Put and Move yourself.
71
72   type MDI_Child_Record is new Gtk.Event_Box.Gtk_Event_Box_Record
73     with private;
74   type MDI_Child is access all MDI_Child_Record'Class;
75   pragma No_Strict_Aliasing (MDI_Child);
76   --  A child of the MDI, that encapsulates the widgets you have put in the
77   --  MDI window.
78   --  You can easily convert from this to the initial widget using the
79   --  functions Find_MDI_Child and Get_Widget.
80
81   type MDI_Child_Array is array (Natural range <>) of MDI_Child;
82   No_Children : constant MDI_Child_Array := (1 .. 0 => null);
83
84   type State_Type is (Normal, Floating, Invisible);
85   --  This type indicates the state of an item in the MDI:
86   --  - Normal: the item can be manipulated (moved and resized) by the user.
87   --      It is found either in the middle notebook (maximized items), or
88   --      in the layout.
89   --  - Floating: the item has its own toplevel window, and is thus managed
90   --      by the window manager.
91   --  - Invisible: the child was part of a previously displayed perspective,
92   --      but is no longer in the current perspective. We still keep it to
93   --      reuse it when switching back to the previous perspective.
94
95   procedure Gtk_New
96     (MDI   : out MDI_Window;
97      Group : access Gtk.Accel_Group.Gtk_Accel_Group_Record'Class;
98      Independent_Perspectives : Boolean := False);
99   --  Create a new MDI window.
100   --  Note that it is recommended that you modify the style (Set_Background
101   --  in State_Normal) to have a different color.
102   --  You should call Setup_Toplevel_Window once you have added the MDI to a
103   --  toplevel widget, so that focus is correctly handled when the toplevel
104   --  window gains the focus
105   --  When Independent_Perspectives is True, switching perspectives will not
106   --  preserve any window. Otherwise, the windows that are in the central
107   --  area will be preserved in the new perspective.
108
109   procedure Initialize
110     (MDI   : access MDI_Window_Record'Class;
111      Group : access Gtk.Accel_Group.Gtk_Accel_Group_Record'Class;
112      Independent_Perspectives : Boolean := False);
113   --  Internal initialization function.
114   --  See the section "Creating your own widgets" in the documentation.
115
116   procedure Setup_Toplevel_Window
117     (MDI    : access MDI_Window_Record;
118      Parent : access Gtk.Window.Gtk_Window_Record'Class);
119   --  Setup Parent to properly handle focus when the window manager changes
120   --  the window that currently has the focus.
121   --  Parent must be the toplevel window that contains the MDI.
122
123   type Show_Tabs_Policy_Enum is (Always, Never, Automatic);
124   type Title_Bars_Policy     is (Always, Never, Central_Only);
125
126   procedure Configure
127     (MDI                       : access MDI_Window_Record;
128      Opaque_Resize             : Boolean := False;
129      Close_Floating_Is_Unfloat : Boolean := True;
130      Title_Font         : Pango.Font.Pango_Font_Description := null;
131      Background_Color   : Gdk.Color.Gdk_Color := Gdk.Color.Null_Color;
132      Title_Bar_Color    : Gdk.Color.Gdk_Color := Gdk.Color.Null_Color;
133      Focus_Title_Color  : Gdk.Color.Gdk_Color := Gdk.Color.Null_Color;
134      Draw_Title_Bars    : Title_Bars_Policy   := Always;
135      Tabs_Position      : Gtk.Enums.Gtk_Position_Type := Gtk.Enums.Pos_Bottom;
136      Show_Tabs_Policy   : Show_Tabs_Policy_Enum := Automatic);
137   --  Change the setup of the MDI.
138   --  Close_Floating_Is_Unfloat, if True, means that closing a floating child
139   --  will put it back in the MDI instead of destroying it (unless its flag
140   --  Always_Destroy_Float is set).
141   --  Title_Font is the font used in the title bars (if null, "sans 8"
142   --  is used).
143   --  The colors, when Null_Color, will not change the current setup.
144   --  If Draw_Title_Bars is False, then no extra title bar will be displayed
145   --  for the MDI children when they are maximized. This saves space on the
146   --  screen. However, the notebook tabs will be highlighted with
147   --  Title_Bar_Color in exchange.
148   --  Tabs_Position indicates where the notebook tabs should be put.
149   --  Show_Tabs_Policy indicates when the notebook tabs should be displayed.
150
151   function Independent_Perspectives
152     (MDI : access MDI_Window_Record) return Boolean;
153   --  Whether the MDI is using independent perspectives
154
155   -------------
156   -- Windows --
157   -------------
158
159   type Child_Flags is mod 2 ** 5;
160   Destroy_Button       : constant Child_Flags := 2 ** 2;
161   Float_As_Transient   : constant Child_Flags := 2 ** 3;
162   Always_Destroy_Float : constant Child_Flags := 2 ** 4;
163   All_Buttons          : constant Child_Flags := Destroy_Button;
164   --  Special flags to set up the widgets:
165   --  The first is the buttons that should be displayed in the title
166   --  bar of the MDI children.
167   --  If Float_As_Transient is set, then the child will be set up as a
168   --  transient window when floating: on most window managers, it will stay on
169   --  top of the MDI, but the window will have less decorations in its title
170   --  bar, in particular no destroy button. In such a case, <Esc> will close
171   --  the window, or unfloat it depending on the MDI's setup, as is the case
172   --  for all dialogs in GtkAda. The MDI's setup will be ignored (and the
173   --  child always destroyed when Esc is pressed) if Always_Destroy_Float is
174   --  true.
175
176   type Child_Group is new Positive;
177   Group_Default : constant Child_Group := 1;
178   Group_Any     : constant Child_Group := Child_Group'Last;
179   --  This type can be used to help group windows by type within the MDI.
180   --  Group_Default as a special status when computing the initial position
181   --  for a window. But you can create your own groups as needed, so that for
182   --  instance editors tend to be grouped with other editors, graphs with
183   --  other graphs,... depending on your application.
184   --  The group has an impact when the last window from a notebook is closed:
185   --  If the window belongs to Group_Default, and it is the last of its group,
186   --  then the space currently occupied by that window is not reclaimed, and
187   --  therefore an empty area will exist in the MDI. The idea is that for
188   --  instance editors typically play a special role in an integrated
189   --  development environment, and the users like to have them in the center
190   --  of the window. When closing the last editor, they do not want the side
191   --  windows (browsers, consoles,...) to take up that space that should
192   --  really only be used for editors.
193   --  To get such a behavior, editors should belong to Group_Default, and all
194   --  other windows to custom groups.
195   --
196   --  Do not use Group_Any, it is used internally with special meanings.
197
198   procedure Gtk_New
199     (Child        : out MDI_Child;
200      Widget       : access Gtk.Widget.Gtk_Widget_Record'Class;
201      Flags        : Child_Flags := All_Buttons;
202      Group        : Child_Group := Group_Default;
203      Focus_Widget : Gtk.Widget.Gtk_Widget := null);
204   --  Create a new MDI child that contains widget.
205   --  Widget mustn't be of type Gtk_Window.
206   --
207   --  You shouldn't access Widget directly afterwards, but should manipulate
208   --  Child only. However, as a special exception, you can still pass Widget
209   --  as a parameter to the subprograms in this package to manipulate it (e.g.
210   --  in Raise_Child, ...)
211   --
212   --  Note: You might have to call Set_Size_Request on Widget to set its
213   --  initial size. This won't prevent it from being resized by the user.
214   --
215   --  If Focus_Widget is not null, this is the widget that gets the keyboard
216   --  focus when the child is selected.
217
218   procedure Initialize
219     (Child        : access MDI_Child_Record'Class;
220      Widget       : access Gtk.Widget.Gtk_Widget_Record'Class;
221      Flags        : Child_Flags := All_Buttons;
222      Group        : Child_Group := Group_Default;
223      Focus_Widget : Gtk.Widget.Gtk_Widget := null);
224   --  Internal initialization function.
225   --  See the section "Creating your own widgets" in the documentation.
226
227   type Child_Position is
228     (Position_Automatic,
229      Position_Bottom,
230      Position_Top,
231      Position_Left,
232      Position_Right);
233   subtype Side_Position is Child_Position
234      range Position_Bottom .. Position_Right;
235   --  The initial position of windows within the MDI.
236   --  In all cases, the initial location for a window is computed with the
237   --  following algorithm. This algorithm is designed with the notion of
238   --  groups of windows in mind, so that some windows (typically editors) have
239   --  a special status.
240   --     - If another window with the same Group is already in the MDI, the
241   --       new window is put on top of it.
242   --     - Otherwise, if Position_Automatic, if an empty area exists within
243   --       the MDI, the new window is put in that area.
244   --     - Else if the Position is Bottom .. Right, the new window is put
245   --       below all others (resp. to the top, left or right)
246   --     - Else the window is put on top of the currently selected window
247
248   procedure Put
249     (MDI              : access MDI_Window_Record;
250      Child            : access MDI_Child_Record'Class;
251      Initial_Position : Child_Position := Position_Automatic);
252   --  Add a new child to the MDI window, and return its embedding widget.
253   --  Calling Put does not give the focus to the newly inserted widget.
254   --  To do that, you should call Set_Focus_Child.
255
256   procedure Set_Size
257     (MDI        : access MDI_Window_Record;
258      Child      : access MDI_Child_Record'Class;
259      Width      : Glib.Gint;
260      Height     : Glib.Gint;
261      Fixed_Size : Boolean := False);
262   --  Forces a new size for a child. If Width or Height is left to -1, the
263   --  matching size will be computed from the child's requisition. If they are
264   --  left to 0, the corresponding length is left to its current value.
265   --  If Fixed_Size is True, then the widget will not be resized when the MDI
266   --  itself is resized (unless the user has first moved one of the handles to
267   --  manually resize it). Otherwise, it will grow proportionally with the
268   --  rest of the MDI.
269
270   procedure Close
271     (MDI   : access MDI_Window_Record;
272      Child : access Gtk.Widget.Gtk_Widget_Record'Class;
273      Force : Boolean := False);
274   --  Close the child that contains Child, and remove its window from the
275   --  MDI. See also Close_Child if you need to close a MDI_Child itself.
276   --  This first checks through a delete_event callback whether the child
277   --  accepts to be closed.
278   --  "delete_event" is not sent, and the child is automatically closed, if
279   --  Force is set to True.
280
281   procedure Set_Title
282     (Child       : access MDI_Child_Record;
283      Title       : UTF8_String;
284      Short_Title : UTF8_String := "");
285   --  Set the title for a child. Title is the title put in titlebar of
286   --  the children, whereas Short_Title is the name of the notebook tab when
287   --  children are maximized. By default, it is the same as Title.
288   --
289   --  The default title is the empty string.
290   --  This title will be the one used for the window when the child is set to
291   --  floating state.
292
293   function Get_MDI (Child : access MDI_Child_Record) return MDI_Window;
294   --  Return the MDI to which Child is associated. In Child is a floating
295   --  child, it might not be in the MDI window itself.
296
297   function Get_Title (Child : access MDI_Child_Record) return UTF8_String;
298   --  Return the title for a specific child
299
300   function Get_Short_Title
301     (Child : access MDI_Child_Record) return UTF8_String;
302   --  Return the name of the notebook tab used when children are maximized.
303
304   function Has_Title_Bar (Child : access MDI_Child_Record) return Boolean;
305   --  Whether a title bar is currently displayed for Child
306
307   function Get_State (Child : access MDI_Child_Record) return State_Type;
308   --  Return the current state of the child
309
310   procedure Set_Icon
311     (Child : access MDI_Child_Record;
312      Icon  : Gdk.Pixbuf.Gdk_Pixbuf);
313   --  Associate an icon with Child. This icon is visible in the title bar, the
314   --  notebook tabs, the Window menu and the interactive selection dialog.
315   --  The icon is updated dynamically on the screen.
316
317   function Get_Icon
318     (Child : access MDI_Child_Record) return Gdk.Pixbuf.Gdk_Pixbuf;
319   --  Returns the icon associated with Child
320
321   ---------------------------
322   -- Drag and Drop support --
323   ---------------------------
324
325   function Dnd_Data
326     (Child : access MDI_Child_Record; Copy : Boolean) return MDI_Child;
327   --  When a drag-and-drop operation took place to move a child from one
328   --  position to the next, this function is called to know what child should
329   --  be moved.
330   --  As a result, the implementor can choose whether a copy of the child
331   --  should be returned (creating a new view for an editor for instance), or
332   --  if the child itself should be moved (the default).
333   --  The returned MDI_Child must have been added to the MDI before it is
334   --  returned.
335   --  Copy is set to true if a copy operation was requested, to False if a
336   --  simple move operation was requested. It can be ignored if Child doesn't
337   --  know how to create a copy of itself for instance.
338
339   procedure Set_Dnd_Message
340     (MDI     : access MDI_Window_Record;
341      Message : String);
342   --  Override the message that is displayed in the popup window while
343   --  performing a drag. By default, this message mentions:
344   --     "... will be (preserved|hidden) when changing perspective"
345   --     "Use shift to create a new view for editors"
346   --  so might not be suitable for all applications.
347   --  Through this function you can override the message. If you insert the
348   --  special sequence "(#)" in the message, it will be replaced either with
349   --  "preserved" or "hidden" depending on where the drop occurs (the central
350   --  area or the perspectives). It could also be replaced by an empty string
351   --  if the MDI was configured with independent perspectives.
352   --  You can use markup like "<b>...</b>" to put keywords in bold.
353   --
354   --  Passing an empty string for Message will restore the default message.
355
356   procedure Child_Drag_Begin
357     (Child  : access MDI_Child_Record'Class;
358      Event  : Gdk.Event.Gdk_Event);
359   --  Starts a drag-and-drop operation for the child, so that it can be put in
360   --  some other place on the desktop. This should only be called when a
361   --  handler for the "button_press_event" signal, passing the event itself in
362   --  parameter.
363   --  The Child is immediately raised and gains the focus.
364
365   procedure Cancel_Child_Drag (Child : access MDI_Child_Record'Class);
366   --  Cancel a drag operation started by Child_Drag_Begin.
367   --  It doesn't call Child_Drag_Finished.
368
369   procedure Child_Drag_Finished (Child  : access MDI_Child_Record);
370   --  Called when a drag operation is either aborted or completed. It should
371   --  be overriden if special cleanup should be done.
372
373   -----------
374   -- Menus --
375   -----------
376
377   type Tab_Contextual_Menu_Factory is access procedure
378     (Child : access MDI_Child_Record'Class;
379      Menu  : access Gtk.Menu.Gtk_Menu_Record'Class);
380
381   procedure Set_Tab_Contextual_Menu_Factory
382     (MDI     : access MDI_Window_Record;
383      Factory : Tab_Contextual_Menu_Factory);
384   --  Set (or unset if Factory is null) the callback to create the contextual
385   --  menu entries when the user clicks on a notebook tab.
386   --  Factory should add entries to Menu (which already contains the default
387   --  entries, but you can remove them if needed).
388
389   ------------------------
390   -- Selecting children --
391   ------------------------
392
393   procedure Highlight_Child
394     (Child : access MDI_Child_Record; Highlight : Boolean := True);
395   --  Highlight the child until it is selected by the user.
396   --  The color of its menu label and of the text in the notebook tabs is
397   --  changed.
398   --  Nothing is done if the child is already fully visible (either in the
399   --  active page in one of the notebooks, or the child that has the selection
400   --  in the layout).
401   --  This is meant to be used as a graphical note to the user that the child
402   --  has been updated and the user should look at it.
403
404   function Get_Focus_Child
405     (MDI : access MDI_Window_Record) return MDI_Child;
406   --  Return the child that currently has the MDI focus.
407   --  null is returned if no child has the focus.
408
409   procedure Set_Focus_Child
410     (MDI        : access MDI_Window_Record;
411      Containing : access Gtk.Widget.Gtk_Widget_Record'Class);
412   --  Give the focus to the child containing Containing. This will not
413   --  Grab_Focus for the child in all cases, since you might want to give the
414   --  focus to some specific part of your widget (an entry field,...) in some
415   --  cases.
416
417   procedure Set_Focus_Child (Child : access MDI_Child_Record);
418   --  Make Child the active widget, and raise it at the top.
419
420   procedure Check_Interactive_Selection_Dialog
421     (MDI          : access MDI_Window_Record;
422      Event        : Gdk.Event.Gdk_Event;
423      Move_To_Next : Boolean;
424      Only_Group   : Child_Group := Group_Any);
425   --  Open the interactive dialog for selecting windows.
426   --  This dialog should be open as a result of a key press event.
427   --  Move_To_Next indicates whether we want to select the next child (True)
428   --  or the previous child (False).
429   --  This dialog will be closed only when the key that opened it is fully
430   --  released. For instance, if the dialog was opened as a result of
431   --  pressing Ctrl-Tab, the dialog will only be closed when Ctrl itself is
432   --  released.
433   --  You can call this procedure even if a dialog is currently open. This
434   --  simply forces a move to the next or previous child. In fact, it is
435   --  your responsability to call this procedure when the user presses
436   --  the keys to move between children.
437   --
438   --  If Event is null, then no dialog is displayed. Instead, the next or
439   --  previous visible child is immediately selected. In such a mode, windows
440   --  that are not on top of their respective notebook are ignored. This can
441   --  be used to emulate Emacs's behavior for goto-other-window.
442   --
443   --  If Only_Group is specified, then only the windows from that group will
444   --  be shown in the dialog.
445
446   --  This function is not internal to the MDI since connecting to the
447   --  key_press_event and key_release_event should be done in the gtk_window
448   --  that contains the MDI. Otherwise, some events are intercepted by gtk+,
449   --  for instance the key_release_events, and the key_press_events for some
450   --  specified keys.
451   --  It also gives the choice to the application of whether this feature is
452   --  wanted or not.
453
454   -----------------------------------------
455   -- MDI_Child and encapsulated children --
456   -----------------------------------------
457
458   function Get_Widget
459     (Child : access MDI_Child_Record) return Gtk.Widget.Gtk_Widget;
460   --  Return the widget that Child encapsulates. This is the widget you
461   --  initially Put() in MDI.
462
463   function Find_MDI_Child
464     (MDI    : access MDI_Window_Record;
465      Widget : access Gtk.Widget.Gtk_Widget_Record'Class) return MDI_Child;
466   --  Return the MDI_Child that encapsulates Widget.
467   --  Widget must be the exact same one you gave in argument to Put.
468   --  If the child is currently not visible in the perspective (for instance
469   --  it was created for another perspective, but is not present in the
470   --  current one), it is inserted automatically back in the MDI.
471
472   function Find_MDI_Child_From_Widget
473     (Widget : access Gtk.Widget.Gtk_Widget_Record'Class) return MDI_Child;
474   --  Return the MDI child that encapsulate the parent of Widget.
475   --  As opposed to Find_MDI_Child, Widget can be anywhere within the
476   --  widget tree. This function properly handles floating children
477   --  If the child is currently not visible in the perspective (for instance
478   --  it was created for another perspective, but is not present in the
479   --  current one), it is inserted automatically back in the MDI.
480
481   function Find_MDI_Child_By_Tag
482     (MDI : access MDI_Window_Record;
483      Tag : Ada.Tags.Tag;
484      Visible_Only : Boolean := False) return MDI_Child;
485   --  Return the first child matching Tag
486   --  If the child is currently not visible in the perspective (for instance
487   --  it was created for another perspective, but is not present in the
488   --  current one), it is inserted automatically back in the MDI.
489   --  If Visible_Only is True, an invisible child is not returned. This is
490   --  useful to check whether a child is currently visible.
491
492   function Find_MDI_Child_By_Name
493     (MDI  : access MDI_Window_Record;
494      Name : String) return MDI_Child;
495   --  Return the first child matching Name.
496   --  If the child is currently not visible in the perspective (for instance
497   --  it was created for another perspective, but is not present in the
498   --  current one), it is inserted automatically back in the MDI.
499
500   type Child_Iterator is private;
501
502   function First_Child
503     (MDI               : access MDI_Window_Record;
504      Group_By_Notebook : Boolean := False;
505      Visible_Only      : Boolean := True) return Child_Iterator;
506   --  Return an access to the first child of the MDI.
507   --
508   --  If Group_By_Notebook is True, then the children are reported one after
509   --  the other, but all the widget from the same notebook are reported in the
510   --  same order as the notebook pages. Floating children do not belong to a
511   --  notebook, and are also reported together. To find out to which notebook
512   --  a child belongs, use Get_Notebook below.
513   --
514   --  If Group_By_Notebook is False, it is garanteed that the first child is
515   --  the one that currently has the focus in the MDI. The children are
516   --  returned in the order in which they last had the focus.
517   --
518   --  If Visible_Only is true, then only those children currently visible in
519   --  the perspective are returned. The children that were part of a former
520   --  perspective are not returned.
521
522   procedure Next (Iterator : in out Child_Iterator);
523   --  Move to the next child in the MDI
524
525   function Get_Notebook
526     (Iterator : Child_Iterator) return Gtk.Notebook.Gtk_Notebook;
527   --  Return the notebook to which the current child belongs. null is returned
528   --  for floating children
529
530   function Get (Iterator : Child_Iterator) return MDI_Child;
531   --  Return the child pointed to by Iterator.
532   --  If Iterator is no longer valid, null is returned.
533
534   -----------------------------------
535   -- Floating and closing children --
536   -----------------------------------
537
538   procedure Float_Child
539     (Child : access MDI_Child_Record'Class; Float : Boolean);
540   --  Change the floating state of a child
541
542   function Is_Floating
543     (Child : access MDI_Child_Record'Class) return Boolean;
544   --  Return True if Child is currently in a separate window
545
546   procedure Close_Child
547     (Child : access MDI_Child_Record'Class;
548      Force : Boolean := False);
549   --  Same as Close, but applies directly to a MDI_Child.
550
551   procedure Set_All_Floating_Mode
552     (MDI : access MDI_Window_Record; All_Floating : Boolean);
553   --  If All_Floating is set to true, the MDI will have a size of 0x0, and all
554   --  children are set to floating. This can be used if you wish to let the
555   --  window manager handle the windows. If All_Floating is True, children
556   --  can no longer be maximized.
557
558   procedure Use_Short_Titles_For_Floats
559     (MDI : access MDI_Window_Record; Short_Titles : Boolean);
560   --  If Short_Titles is set to true, only short titles will ever be used
561   --  either in the title bars (in notebooks) or as the title for floating
562   --  windows.
563
564   ---------------------------
565   -- Reorganizing children --
566   ---------------------------
567
568   procedure Raise_Child
569     (Child : access MDI_Child_Record'Class; Give_Focus : Boolean := True);
570   --  Put Child in the foreground.
571   --  Note that this does not give the focus to this child, unless
572   --  Give_Focus is set to True. If Child and the current focus child are in
573   --  the same notebook, Child will always gain the focus, so that the focus
574   --  is not left on an invisible window.
575
576   function Is_Raised (Child : access MDI_Child_Record'Class) return Boolean;
577   --  Whether the child is currently raised, ie fully visible to the user
578
579   procedure Lower_Child (Child : access MDI_Child_Record'Class);
580   --  Put Child in the background.
581   --  If the children are maximized, this selected the next page from the
582   --  notebook.
583
584   type Split_Mode is
585     (Before, Before_Reuse,
586      After,  After_Reuse,
587      Any_Side_Reuse);
588   --  How a child should be split:
589   --  If "Before", the child is put above or to the left of its current
590   --  position. A new window is created to containing it. If the "_Reuse"
591   --  version is used, and a window already exist at that position, the child
592   --  will be put in it instead of creating a new one.
593   --  Any_Side_Reuse indicates that the child will be put on either side,
594   --  depending on where a window already exists. If there is no window on the
595   --  side, a new one is created.
596
597   procedure Split
598     (MDI           : access MDI_Window_Record;
599      Orientation   : Gtk.Enums.Gtk_Orientation;
600      Child         : MDI_Child := null;
601      Mode          : Split_Mode := Before;
602      Width, Height : Glib.Gint := 0);
603   --  Split the notebook containing Child (by default, the current focus
604   --  child).
605   --  Mode indicates in which direction the splitting should occur. If you
606   --  are splitting a child in the central area, splitting will never reuse
607   --  a window outside of the central area.
608   --  Width and Height indicate the desired geometry for the splitted area,
609   --  0 indicate a 50/50 split.
610
611   ----------------------
612   -- Desktop Handling --
613   ----------------------
614   --  The MDI provides a way to save desktops, i.e the list of children
615   --  currently open in the MDI and their location. It can then restore the
616   --  desktop at some later point.
617   --
618   --  Desktops require support from the widgets that are put in the MDI. They
619   --  need to register a function to save them and a function to recreate
620   --  them. Using Ada streams for this didn't prove workable since some
621   --  children might need extra parameters not available to them through
622   --  streams. This is why the following subprograms are in a generic package,
623   --  so that you can pass whatever parameter(s) is needed in your
624   --  application.
625   --
626   --  Desktops are saved and restored in XML trees.
627   --
628   --  If you need your application to load a "default desktop" when the user
629   --  hasn't defined one, it is recommended that you distribute an actual
630   --  file containing this desktop. You could also create the XML tree in
631   --  memory yourself, and thus hard-code the default desktop if need be.
632
633   generic
634      type User_Data is private;
635      --  Generic type of parameter that is passed to all the children's save
636      --  and restore functions.
637
638      --  This package needs to be instantiated at library level
639
640   package Desktop is
641
642      type Menu_Registration_Procedure is access procedure
643        (User       : User_Data;
644         Item_Name  : String;
645         Accel_Path : String);
646      --  Function used to register in the application a static menu
647      --  created by the MDI.
648
649      function Create_Menu
650        (MDI               : access MDI_Window_Record'Class;
651         Accel_Path_Prefix : String := "<gtkada>";
652         User              : User_Data;
653         Registration      : Menu_Registration_Procedure := null)
654         return Gtk.Menu.Gtk_Menu;
655      --  Create a dynamic menu that can then be inserted into a menu bar. This
656      --  menu is dynamic, ie its content will changed based on the focus
657      --  child.
658      --  If this function is called several times, the same menu is returned
659      --  every time. Accel_Path_Prefix must be the same for every call.
660      --  Accel_Path_Prefix is used so that the key shortcuts associated with
661      --  these menu items can be changed dynamically by the user (see
662      --  gtk-accel_map.ads). The prefix must start with "<" and end with ">".
663      --  User is used for the callbacks on perspective changes, and passed to
664      --  Load_Perspective
665      --  If Registration is specified, call it for
666
667      type Save_Desktop_Function is access function
668        (Widget : access Gtk.Widget.Gtk_Widget_Record'Class;
669         User   : User_Data) return Glib.Xml_Int.Node_Ptr;
670      --  A general function that dumps the parameters of a widget into an XML
671      --  tree.
672      --
673      --  Note: you should register one such function for all the widget types
674      --  you will put in the MDI and that need to be saved when a desktop is
675      --  saved. The MDI will call all the registered functions one after the
676      --  other. Therefore, your function should return null if Widget is not
677      --  of a type that is it can handle.
678
679      type Load_Desktop_Function is access function
680        (MDI : MDI_Window; Node : Glib.Xml_Int.Node_Ptr; User : User_Data)
681         return MDI_Child;
682      --  A general function that loads a widget from an XML tree.
683      --
684      --  As for Save_Desktop_Function, this function should return null if it
685      --  doesn't know how to handle Node or if Node doesn't describe a widget
686      --  type that it can handle.
687      --
688      --  This function returns an MDI_Widget that has been put in the MDI.
689
690      procedure Register_Desktop_Functions
691        (Save : Save_Desktop_Function;
692         Load : Load_Desktop_Function);
693      --  Register a set of functions to save and load desktops for some
694      --  specific widget types. This can be called multiple times.
695      --  Neither Save nor Load can be null.
696
697      function Restore_Desktop
698        (MDI          : access MDI_Window_Record'Class;
699         Perspectives : Glib.Xml_Int.Node_Ptr;
700         From_Tree    : Glib.Xml_Int.Node_Ptr;
701         User         : User_Data) return Boolean;
702      --  Restore the contents of the MDI from its saved XML tree.
703      --  Perspectives is the list of perspectives. It is cloned as needed, so
704      --  the caller is still responsible for freeing it. The first perspective
705      --  is loaded.
706      --  From_Tree is the part of the desktop that describes the editor area.
707      --  User is passed as a parameter to all of the Load_Desktop_Function
708      --  registered by the widgets.
709      --  Return False if the desktop couldn't be loaded
710      --  It also restores the size and position of the toplevel window that
711      --  contains the MDI
712
713      procedure Load_Perspective
714        (MDI          : access MDI_Window_Record'Class;
715         Name         : String;
716         User         : User_Data);
717      --  Replace the current perspective by another one. This preserves the
718      --  editor area.
719      --  If the perspective does not exist, nothing is done, unless no
720      --  perspective is currently loaded (in which case we load the first
721      --  on in the list).
722
723      procedure Create_Perspective
724        (MDI          : access MDI_Window_Record'Class;
725         Name         : String;
726         User         : User_Data);
727      --  Create a new perspective with the current desktop layout. If another
728      --  perspective with the same name exists, it is replaced.
729
730      procedure Define_Perspective
731        (MDI          : access MDI_Window_Record'Class;
732         XML          : Glib.Xml_Int.Node_Ptr;
733         User         : User_Data);
734      --  Define a new perspective (in the same format as returned by
735      --  Save_Desktop, the central area is under control of the user so you
736      --  cannot change it).
737      --  If such a perspective already exists, nothing is done (since the user
738      --  might have modified it already).
739      --  XML's root node is the <perspective> node, including its "name"
740      --  attribute.
741      --  XML must be freed by the caller.
742
743      procedure Save_Desktop
744        (MDI          : access MDI_Window_Record'Class;
745         User         : User_Data;
746         Perspectives : out Glib.Xml_Int.Node_Ptr;
747         Central      : out Glib.Xml_Int.Node_Ptr);
748      --  Return XML representations of the perspectives and central area. Both
749      --  nodes need to be freed by the caller, and can be saved in a file (to
750      --  be passed to Restore_Desktop later on).
751      --  This function calls each of the registered function for the children
752      --  of the MDI.
753      --  It also saves the size and position of the toplevel window that
754      --  contains the MDI
755
756      function Get_XML_Content
757        (MDI : access MDI_Window_Record'Class;
758         Tag : String) return Glib.Xml_Int.Node_Ptr;
759      --  Return the first XML subtree starting with 'Tag'. This allows a
760      --  module to retrieve its content after the 'Load_Desktop' call.
761
762      procedure Free_Registered_Desktop_Functions;
763      --  Free the memory allocated for the registered functions.
764
765   private
766      type Register_Node_Record;
767      type Register_Node is access Register_Node_Record;
768      type Register_Node_Record is record
769         Save : Save_Desktop_Function;
770         Load : Load_Desktop_Function;
771         Next : Register_Node;
772      end record;
773
774      type Perspective_Menu_Item_Record
775        is new Gtk.Radio_Menu_Item.Gtk_Radio_Menu_Item_Record
776      with record
777         MDI  : MDI_Window;
778         Name : Natural;
779         User : User_Data;
780      end record;
781      type Perspective_Menu_Item
782        is access all Perspective_Menu_Item_Record'Class;
783
784      procedure Change_Perspective
785        (Item : access Gtk.Widget.Gtk_Widget_Record'Class);
786      CP_Access : constant
787        Gtkada.Handlers.Widget_Callback.Marshallers.Marshaller :=
788        Gtkada.Handlers.Widget_Callback.To_Marshaller
789          (Change_Perspective'Access);
790      --  Internal, but needed so that we can have a 'Access on a callback
791
792      procedure Create_Perspective_CB
793        (Item : access Gtk.Widget.Gtk_Widget_Record'Class);
794      CreateP_Access : constant
795        Gtkada.Handlers.Widget_Callback.Marshallers.Marshaller :=
796        Gtkada.Handlers.Widget_Callback.To_Marshaller
797          (Create_Perspective_CB'Access);
798
799      Registers : Register_Node;
800      --  Global variable that contains the list of functions that have been
801      --  registered.
802   end Desktop;
803
804   function Desktop_Was_Loaded (MDI : access MDI_Window_Record) return Boolean;
805   --  Return True if a desktop was loaded, False if the MDI is only the result
806   --  of calls to Gtk_New and Put.
807
808   function List_Of_Perspectives
809     (MDI : access MDI_Window_Record)
810      return GNAT.Strings.String_List_Access;
811   --  Return the list of perspectives known to the MDI. The caller must not
812   --  free the list
813
814   function Current_Perspective
815     (MDI : access MDI_Window_Record'Class) return String;
816   --  Return the name of the currently displayed perspective
817
818   -------------
819   -- Signals --
820   -------------
821
822   --  <signals>
823   --  The following new signals are defined for this widget:
824   --
825   --  - "child_selected"
826   --    procedure Handler
827   --       (MDI : access MDI_Window_Record'Class; Child : System.Address);
828   --
829   --    This signal is emitted when a new child has gained the focus. Convert
830   --    Child to a MDI_Child by calling Gtk.Arguments.To_Object. This can be
831   --    used to change some global information at the MDI level. You should
832   --    connect to "selected" (see below) instead if you want to change some
833   --    information at the child level.
834   --    Child might be null if no child has the focus anymore
835   --
836   --  - "float_child"
837   --    procedure Handler
838   --       (MDI : access MDI_Window_Record'Class; Child : System.Address);
839   --
840   --    A child was set as floating in the MDI. A similar signal is emitted on
841   --    the child itself.
842   --
843   --  - "child_title_changed"
844   --    procedure Handler
845   --      (MDI : access MDI_Window_Record'Class; Child : System.Address);
846   --
847   --    Emitted when the title of a child is changed. This signal is not
848   --    emitted if Set_Title is called for a child that hasn't been put in the
849   --    MDI yet.
850   --
851   --  - "child_added"
852   --     procedure Handler
853   --       (MDI : access MDI_Window_Record'Class; Child : System.Address);
854   --     Emitted when a new child is added. You cannot use the "add" signal
855   --     since in fact the children are added to notebooks that are part of
856   --     the MDI, and thus "add" is only emitted when a new notebook is
857   --     created.
858   --
859   --  - "child_removed"
860   --     procedure Handler
861   --       (MDI : access MDI_Window_Record'Class; Child : System.Address);
862   --     Emitted when a new child is removed. You cannot use the "remove"
863   --     signal since in fact the children are removed from notebooks that are
864   --     part of the MDI, and thus "remove" is only emitted when a new
865   --     notebook is destroyed.
866   --     When this signal is emitted, Child no longer contains a widget, and
867   --     is no longer part of the children, although you can still access its
868   --     titles.
869   --
870   --  - "child_icon_changed"
871   --     procedure Handler
872   --       (MDI : access MDI_Window_Record'Class; Child : System.Address);
873   --     Emitted when the icon for Child has changed
874   --
875   --  - "children_reorganized"
876   --     procedure Handler (MDI : access MDI_Window_Record'Class);
877   --     Emitted when the children have been reorganized: either a split
878   --     occurred, or a window was dropped into another position
879   --
880   --  - "perspective_changed"
881   --     procedure Handler (MDI : access MDI_Window_Record'Class);
882   --     Called when the user has selected a new perspective. One use is to
883   --     save the new desktop to a file.
884   --
885   --  </signals>
886   --
887   --  <signals>
888   --  The following new signals are defined for the MDI_Child_Record object:
889   --
890   --  - "delete_event"
891   --    function Handler (Child : access Gtk_Widget_Record'Class)
892   --                     return Boolean;
893   --
894   --    This signal is emitted for each item in the MDI window before it is
895   --    actually deleted. The child is destroyed only if the handler returns
896   --    False.
897   --    Note that the Child passed in argument is exactly the one you passed
898   --    to Put to insert it in the MDI window.
899   --    Note that this is also the signal to use to prevent top level
900   --    Gtk_Window from being destroyed.
901   --
902   --  - "selected"
903   --    procedure Handler (Child : access MDI_Child_Record'Class);
904   --
905   --    This is emitted when the child is selected, ie gains the
906   --    MDI focus. You should probably also connect to the "grab_focus" signal
907   --    to be informed when the child gets the keyboard focus. This can be
908   --    used to transfer the focus to some specific part of the
909   --    widget. Connecting to "grab_focus" should be done with the After
910   --    parameter set to True.
911   --
912   --  - "float_child"
913   --    procedure Handler (Child : access MDI_Child_Record'Class);
914   --
915   --    Emitted when a child is set as floating
916   --
917   --  - "unfloat_child"
918   --    procedure Handler (Child : access MDI_Child_Record'Class);
919   --
920   --    Emitted when a child is put back in the main MDI window
921   --
922   --  - "child_state_changed"
923   --    procedure Handler (Child : access MDI_Child_Record'Class);
924   --
925   --    Emitted when the state of the child has changed. See the function
926   --    Get_State. In particular, this signal can be detected when a child is
927   --    removed from the current perspective (the new state is "invisible"),
928   --    and when it is put back (the new state is "normal" or "floating").
929   --
930   --  </signals>
931
932   Signal_Child_Selected       : constant Signal_Name := "child_selected";
933   Signal_Float_Child          : constant Signal_Name := "float_child";
934   Signal_Child_Title_Changed  : constant Signal_Name := "child_title_changed";
935   Signal_Child_Added          : constant Signal_Name := "child_added";
936   Signal_Child_Removed        : constant Signal_Name := "child_removed";
937   Signal_Child_Icon_Changed   : constant Signal_Name := "child_icon_changed";
938   Signal_Delete_Event         : constant Signal_Name := "delete_event";
939   Signal_Selected             : constant Signal_Name := "selected";
940   Signal_Unfloat_Child        : constant Signal_Name := "unfloat_child";
941   Signal_Perspective_Changed  : constant Signal_Name := "perspective_changed";
942   Signal_Children_Reorganized : constant Signal_Name :=
943                                   "children_reorganized";
944   Signal_Child_State_Changed  : constant Signal_Name := "child_state_changed";
945
946private
947   type String_Access is access all UTF8_String;
948
949   type MDI_Child_Record is new Gtk.Event_Box.Gtk_Event_Box_Record with record
950      Initial       : Gtk.Widget.Gtk_Widget;
951      --  The widget we use to build this child.
952
953      Main_Box      : Gtk.Box.Gtk_Box;
954      --  The main container.
955
956      State         : State_Type := Normal;
957
958      Group         : Child_Group := Group_Default;
959
960      Title         : String_Access;
961      Short_Title   : String_Access;
962      --  Title of the item, as it appears in the title bar.
963      --  These are UTF8-Encoded
964
965      XML_Node_Name : String_Access;
966      --  The name of the XML node when this child is saved in a desktop (if
967      --  we know it). This is used to reuse a child when switching
968      --  perspectives.
969
970      MDI           : MDI_Window;
971      --  The MDI to which the child belongs. We cannot get this information
972      --  directly from Get_Parent since some children are actually floating
973      --  and do not belong to the MDI anymore.
974
975      Menu_Item     : Gtk.Radio_Menu_Item.Gtk_Radio_Menu_Item;
976      --  The item in the dynamic menu that represents this child.
977
978      Flags         : Child_Flags;
979
980      Focus_Widget  : Gtk.Widget.Gtk_Widget;
981      --  The widget which should actually get the keyboard focus
982
983      Icon          : Gdk.Pixbuf.Gdk_Pixbuf;
984
985      Title_Box     : Gtk.Box.Gtk_Box;
986      --  Box that contains the title. It will be resized whenever the title
987      --  font changes.
988
989      Tab_Label     : Gtk.Label.Gtk_Label;
990      --  label used when child is in a notebook, null if not in a notebook
991   end record;
992
993   type Child_Iterator (Group_By_Notebook : Boolean := False) is record
994      Visible_Only : Boolean;
995
996      case Group_By_Notebook is
997         when False =>
998            Iter : Gtk.Widget.Widget_List.Glist;
999
1000         when True =>
1001            MDI                 : MDI_Window;
1002
1003            --  While iterating children
1004            Paned_Iter          : Gtkada.Multi_Paned.Child_Iterator;
1005
1006            --  Whether we have already visited the children of the central
1007            --  area. This is True while iterating them, False afterward
1008            In_Central          : Boolean;
1009
1010            --  While iterating the pages of a specific notebook (notebook is
1011            --  set to null when returning floating children)
1012            Notebook            : Gtk.Notebook.Gtk_Notebook;
1013            Notebook_Page       : Glib.Gint;
1014
1015            --  While iterating the floating children
1016            Floating_Iter       : Gtk.Widget.Widget_List.Glist;
1017      end case;
1018   end record;
1019
1020   type Drag_Status is (No_Drag, In_Pre_Drag, In_Drag);
1021
1022   type MDI_Window_Record is new Gtkada.Multi_Paned.Gtkada_Multi_Paned_Record
1023   with record
1024      Items : Gtk.Widget.Widget_List.Glist := Gtk.Widget.Widget_List.Null_List;
1025      --  The list of all MDI children. This includes children in the editor
1026      --  area, even though they are technically in a separate multi_paned.
1027      --  Warning: this list might contain items which are in fact invisible in
1028      --  the MDI (in fact that are not even children of the MDI), if they
1029      --  existed in a previous perspective but no longer in the current one.
1030
1031      Desktop_Was_Loaded : Boolean := False;
1032      --  True if a desktop was loaded
1033
1034      Loading_Desktop : Boolean := False;
1035      --  Whether we are currently loading the desktop. This impacts a number
1036      --  of focus and sizing parameters, so that the desktop can be restored
1037      --  as accurately as possible.
1038
1039      Delay_Before_Focus_Id : Glib.Main.G_Source_Id := Glib.Main.No_Source_Id;
1040      Delay_Before_Focus : Glib.Guint := 700;
1041      --  Delay in ms before a floating window gains the GPS focus, after the
1042      --  "focus_in" event. In all floating mode, this ensures that when the
1043      --  user is passing briefly over floating windows they do not gain the
1044      --  focus, thus potentially leaving the focus to the window that had it
1045      --  at the beginning of the move.
1046      --  Set to 0 to remove any delay.
1047
1048      Focus_Child : MDI_Child := null;
1049      --  The child that currently has the focus. Some default actions will
1050      --  apply to this child only.
1051
1052      Dnd_Message : String_Access;
1053      --  The message displayed during a dnd operation (see Set_Dnd_Message)
1054
1055      Accel_Path_Prefix  : String_Access;
1056      --  The Accel path used for the dynamic menu
1057
1058      Menu               : Gtk.Menu.Gtk_Menu;
1059      Float_Menu_Item    : Gtk.Check_Menu_Item.Gtk_Check_Menu_Item;
1060      Float_Menu_Item_Id : Gtk.Handlers.Handler_Id;
1061      Close_Menu_Item    : Gtk.Menu_Item.Gtk_Menu_Item;
1062      --  The dynamic menu used to provide access to the most common
1063      --  functions of MDI.
1064
1065      Tab_Factory : Tab_Contextual_Menu_Factory;
1066      --  Build the contextual menu when right-clicking on tabs
1067
1068      Title_Layout        : Pango.Layout.Pango_Layout;
1069      --  Layout used to draw titles in the MDI children
1070
1071      Title_Bar_Height    : Glib.Gint;
1072      --  Height of the title bar for all the children
1073
1074      Close_Floating_Is_Unfloat : Boolean;
1075      --  True if destroying a floating window will put the child back in the
1076      --  MDI instead of destroying it. False if the child should be destroyed
1077      --  (provided it accepts so in its delete_event handler).
1078
1079      Highlight_Style : Gtk.Style.Gtk_Style;
1080      --  Style to use to highlight the tabs and menus for the highlighted
1081      --  children.
1082
1083      Background_Color  : Gdk.Color.Gdk_Color := Gdk.Color.Null_Color;
1084      Title_Bar_Color   : Gdk.Color.Gdk_Color := Gdk.Color.Null_Color;
1085      Focus_Title_Color : Gdk.Color.Gdk_Color := Gdk.Color.Null_Color;
1086      Default_Title_Color : Gdk.Color.Gdk_Color := Gdk.Color.Null_Color;
1087
1088      Cursor_Cross      : Gdk.Cursor.Gdk_Cursor;
1089      Cursor_Fleur      : Gdk.Cursor.Gdk_Cursor;
1090      --  Cached cursors
1091
1092      Draw_Title_Bars   : Title_Bars_Policy := Always;
1093      Tabs_Position     : Gtk.Enums.Gtk_Position_Type := Gtk.Enums.Pos_Bottom;
1094      Show_Tabs_Policy  : Show_Tabs_Policy_Enum := Automatic;
1095
1096      Selection_Dialog : Gtk.Widget.Gtk_Widget;
1097      --  The interactive dialog for selecting new children.
1098
1099      Dnd_Window       : Gtk.Window.Gtk_Window;
1100      Dnd_Window_Label : Gtk.Label.Gtk_Label;
1101      --  The small window displayed while a drag-and-drop operation is
1102      --  taking place.
1103
1104      Group : Gtk.Accel_Group.Gtk_Accel_Group;
1105
1106      All_Floating_Mode : Boolean := False;
1107      --  Set to true if all windows should be set to floating
1108
1109      Independent_Perspectives : Boolean := False;
1110      --  See documentation for Configure.
1111
1112      Use_Short_Titles_For_Floats : Boolean := False;
1113      --  Set to true if all floating children should use their short titles
1114
1115      --  Handling of Dnd
1116      Drag_Start_X, Drag_Start_Y : Gint;
1117      In_Drag           : Drag_Status := No_Drag;
1118      Dnd_Rectangle     : Gdk.Rectangle.Gdk_Rectangle;  --  Highlighted area
1119      Dnd_Target        : Gdk.Gdk_Window;     --  The current target for DND
1120      Dnd_Target_Window : Gtk.Window.Gtk_Window;  --  The overlay window
1121
1122      --  Loaded perspectives
1123      Perspective_Menu_Item  : Gtk.Menu_Item.Gtk_Menu_Item;
1124      Perspectives           : Glib.Xml_Int.Node_Ptr;
1125      View_Contents          : Glib.Xml_Int.Node_Ptr;
1126      Perspective_Names      : GNAT.Strings.String_List_Access;
1127      Central                : Gtkada.Multi_Paned.Gtkada_Multi_Paned;
1128
1129      Current_Perspective    : Glib.Xml_Int.Node_Ptr;
1130      --  pointer into Perspectives
1131   end record;
1132
1133   pragma Inline (Get_Widget);
1134   pragma Inline (Get_Focus_Child);
1135   pragma Inline (Get);
1136   pragma Inline (Next);
1137   pragma Inline (First_Child);
1138
1139end Gtkada.MDI;
1140