File: rect.adb

package info (click to toggle)
libgtkada 2.24.4dfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 12,208 kB
  • ctags: 1,676
  • sloc: ada: 119,686; ansic: 4,719; sh: 3,003; makefile: 690; xml: 338; perl: 70
file content (306 lines) | stat: -rw-r--r-- 11,761 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
-----------------------------------------------------------------------
--          GtkAda - Ada95 binding for the Gimp Toolkit              --
--                                                                   --
--                     Copyright (C) 2007 AdaCore                    --
--                                                                   --
-- This program is free software; you can redistribute it and/or     --
-- modify it under the terms of the GNU General Public               --
-- License as published by the Free Software Foundation; either      --
-- version 2 of the License, or (at your option) any later version.  --
--                                                                   --
-- This program is distributed in the hope that it will be useful,   --
-- but WITHOUT ANY WARRANTY; without even the implied warranty of    --
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU --
-- General Public License for more details.                          --
--                                                                   --
-- You should have received a copy of the GNU General Public         --
-- License along with this library; if not, write to the             --
-- Free Software Foundation, Inc., 59 Temple Place - Suite 330,      --
-- Boston, MA 02111-1307, USA.                                       --
--                                                                   --
-----------------------------------------------------------------------

--  This example displays a toplevel window with a PNG image loaded from
--  the disk. The user can then use the mouse to select a rectangular
--  area, as often done in drawing tools.
--  This example shows how to load and draw an image efficiently, redrawing
--  only the minimal amount every time, and how to connect to mouse events
--  to create an interactive rectangular selection.

with Glib;             use Glib;
with Glib.Error;       use Glib.Error;
with Gdk.Color;        use Gdk.Color;
with Gdk.Drawable;     use Gdk, Gdk.Drawable;
with Gdk.Event;        use Gdk.Event;
with Gdk.GC;           use Gdk.GC;
with Gdk.Pixbuf;       use Gdk.Pixbuf;
with Gdk.Pixmap;       use Gdk.Pixmap;
with Gdk.Rectangle;    use Gdk.Rectangle;
with Gtk.Drawing_Area; use Gtk.Drawing_Area;
with Gtk.Enums;        use Gtk.Enums;
with Gtk.Main;         use Gtk.Main;
with Gtk.Widget;       use Gtk.Widget;
with Gtk.Window;       use Gtk.Window;
with Gtkada.Handlers;  use Gtkada.Handlers;

with Cairo.Region;

procedure Rect is
   ----------------------
   -- Global variables --
   ----------------------
   --  In general, real applications should not use global variables.
   --  Among other things, this makes it much harder to have several
   --  toplevel windows displaying different things for instance.
   --  Since this example concentrates more on the handling of signals,
   --  we kept the code simpler through the use of global variables.
   --  The better implementation would be to use your own window type
   --  as in:
   --     type My_Window_Image_Record is new Gtk_Window_Record with record
   --       Pix              : Gdk_Pixbuf;
   --       Start_X, Start_Y : Gint;
   --       Prev_X,  Prev_Y  : Gint;
   --       GC               : Gdk_GC;
   --     end record;

   Win              : Gtk_Window;
   Pix              : Gdk_Pixbuf;
   Error            : GError;
   GC               : Gdk.Gdk_GC;

   Draw             : Gtk_Drawing_Area;
   --  A drawing area is a widget that users can use to draw their own
   --  contents. It doesn't memorize its contents, though, and it is the
   --  responsability of the user to redraw it every time there is a need
   --  for this (the "expose" event).
   --  See also the example doublebuffer which shows another possible
   --  implementation.

   Start_X, Start_Y : Gint;
   Prev_X, Prev_Y   : Gint;
   --  The variables memorize the initial position of the mouse when the
   --  user clicked on the button, and the last position when we drew the
   --  rectangle, so that we can delete it later on.

   --------------
   -- Draw_Pix --
   --------------
   --  This function draws whole or part of the image onto the drawing
   --  area. It includes a number of tests to make sure we only draw
   --  a valid area of the image (in case the image is shorter than the
   --  drawing area, or we are trying to draw outside of the drawing area
   --  for instance).

   procedure Draw_Pix
      (D : access Gtk_Widget_Record'Class;
       Area : Gdk_Rectangle := Full_Area)
   is
      A : Gdk_Rectangle := Area;
      use type Cairo.Region.Cairo_Rectangle_Int;
   begin
      --  If the caller wants to redraw the full image...

      if A = Full_Area then
        A := (0, 0, Get_Width (Pix), Get_Height (Pix));
      end if;

      --  Create the graphic context required to draw both the image and
      --  the rectange. This cannot be created before starting the main
      --  gtk+ loop, since we need to have access to the physical properties
      --  of the window (its color depth among others), which are only
      --  available when windows have been mapped -- we could also do that
      --  in a "mapped" signal handler

      if GC = null then
         Gdk_New (GC, Get_Window (D));
         Set_Foreground (GC, White (Get_Default_Colormap));
      end if;

      --  Make sure we draw a valid part of the image. Otherwise, we get
      --  gtk+ warnings

      if A.X < 0 then
         A.Width := Gint'Min (Get_Width (Pix), A.Width + A.X);
         A.X := 0;
      end if;

      if A.Y < 0 then
         A.Height := Gint'Min (Get_Height (Pix), A.Height + A.Y);
         A.Y := 0;
      end if;

      --  In case the image is smaller than the drawing area, make sure
      --  we pass the proper width and height

      if A.Width > Get_Width (Pix) - A.X then
         A.Width := Gint'Min (A.Width, Get_Width (Pix) - A.X);
         A.Width := Gint'Max (0, A.Width);
      end if;

      if A.Height > Get_Height (Pix) - A.Y then
         A.Height := Gint'Min (A.Height, Get_Height (Pix) - A.Y);
         A.Height := Gint'Max (0, A.Height);
      end if;

      if A.Width > 0 and then A.Height > 0 then
         Render_To_Drawable
           (Pix, Drawable => Get_Window (D),
            Gc     => GC,
            Src_X  => A.X,
            Src_Y  => A.Y,
            Dest_X => A.X,
            Dest_Y => A.Y,
            Width  => A.Width,
            Height => A.Height);
      end if;
   end Draw_Pix;

   ----------------
   -- On_Release --
   ----------------
   --  This callback is called when the user releases the mouse button.
   --  A drawing software would likely keep the rectangle visible to
   --  show the current selection. In this example, we simply hide the
   --  rectangle by redrawing the whole picture.

   function On_Release
      (D : access Gtk_Widget_Record'Class;
       Event : Gdk_Event) return Boolean is
   begin
      Draw_Pix (D);
      return True;
   end On_Release;

   --------------
   -- On_Press --
   --------------
   --  This callback reacts to the initial click by the user. It stores
   --  the current mouse location, so that we can draw the interactive
   --  selection rectangle later on.

   function On_Press
      (D : access Gtk_Widget_Record'Class;
       Event : Gdk_Event) return Boolean is
   begin
      Start_X := Gint (Get_X (Event));
      Start_Y := Gint (Get_Y (Event));
      Prev_X  := Start_X;
      Prev_Y  := Prev_Y;
      return True;
   end On_Press;

   -------------
   -- On_Move --
   -------------
   --  This callback is called when the user moves the mouse while
   --  pressing the button. It is used to update the selection
   --  rectangle. For this, we need to first delete the old one, then
   --  draw the new one. For efficiency reasons, we also need to do the
   --  minimal amount of redrawing, since this callback might be called
   --  often when the mouse is moved.

   function On_Move
      (D : access Gtk_Widget_Record'Class;
       Event : Gdk_Event) return Boolean
   is
      A : Gdk_Rectangle;
   begin
      --  Delete previous rectangle, redrawing as little as possible,
      --  ie just the borders of the rectangle. One limitation in this
      --  small example is when the image is smaller than the drawing
      --  area, the part of the rectangle that is off the image is not
      --  properly deleted. We could either scale the image to fit the
      --  window, or simply clear the area of the drawing area that is
      --  outside of the window.

      A := (Gint'Min (Prev_X, Start_X),
            Gint'Min (Prev_Y, Start_Y),
            abs (Prev_X - Start_X),
            abs (Prev_Y - Start_Y));
      if A.Width > 0 and then A.Height > 0 then
         Draw_Pix (D, (A.X, A.Y, A.Width, 1));
         Draw_Pix (D, (A.X, A.Y, 1, A.Height));
         Draw_Pix (D, (A.X, A.Y + A.Height - 1, A.Width, 1));
         Draw_Pix (D, (A.X + A.Width - 1, A.Y, 1, A.Height));
      end if;

      --  Draw the new rectangle, and memorize its position so that
      --  we can delete it later on.

      Prev_X := Gint (Get_X (Event));
      Prev_Y := Gint (Get_Y (Event));
      A := (Gint'Min (Prev_X, Start_X),
            Gint'Min (Prev_Y, Start_Y),
            abs (Prev_X - Start_X) - 1,
            abs (Prev_Y - Start_Y) - 1);
      if A.Width > 0 and then A.Height > 0 then
         Draw_Rectangle (Get_Window (D), GC, Filled => False,
                         X => A.X, Y => A.Y,
                         Width  => A.Width, Height => A.Height);
      end if;
      return True;
   end On_Move;

   ---------------
   -- On_Expose --
   ---------------
   --  This callback is called every time a part of the window has been
   --  obscured (for instance when a new window was temporarily moved on
   --  top of it), and the system things it needs redrawing.
   --  Some systems will provide caching for you, and will transparently
   --  redraw the window without calling On_Expose. Others will call it
   --  every time any part of the window needs refreshing. In all cases,
   --  your application needs to react to this signal, or the window will
   --  simply appear gray.

   function On_Expose
     (D : access Gtk_Widget_Record'Class;
      Event : Gdk_Event) return Boolean is
   begin
      --  For efficiency, only redraw relevant area that was damaged. The
      --  rest of the window is still up-to-date.

      Draw_Pix (D, Get_Area (Event));
      return True;
   end On_Expose;

begin
   Init;
   Gtk_New (Win, Window_Toplevel);
   Gtk_New (Draw);
   Add (Win, Draw);

   --  We need to indicate that the drawing area needs to react to mouse
   --  events. Without this call, these signals would not result in calls
   --  to On_Press, On_Release,...

   Add_Events (Draw, Button_Press_Mask or Button_Release_Mask
                     or Button_Motion_Mask);

   --  Load the image from the disk. A real application should test the
   --  error code of course, and provide fallbacks.
   --  Note that at this point the image has not been drawn to the screen

   Gdk_New_From_File (Pix, "Ada_Lovelace1.jpg", Error);

   --  Connect to the relevant signals

   Return_Callback.Connect
     (Draw, "expose_event",
      Return_Callback.To_Marshaller (On_Expose'Unrestricted_Access));
   Return_Callback.Connect
     (Draw, "button_press_event",
      Return_Callback.To_Marshaller (On_Press'Unrestricted_Access));
   Return_Callback.Connect
     (Draw, "button_release_event",
      Return_Callback.To_Marshaller (On_Release'Unrestricted_Access));
   Return_Callback.Connect
     (Draw, "motion_notify_event",
      Return_Callback.To_Marshaller (On_Move'Unrestricted_Access));

   Show_All (Win);

   --  Start the main loop

   Main;
end Rect;