1 ------------------------------------------------------------------------------
3 -- GNAT ncurses Binding Samples --
9 ------------------------------------------------------------------------------
10 -- Copyright (c) 2000 Free Software Foundation, Inc. --
12 -- Permission is hereby granted, free of charge, to any person obtaining a --
13 -- copy of this software and associated documentation files (the --
14 -- "Software"), to deal in the Software without restriction, including --
15 -- without limitation the rights to use, copy, modify, merge, publish, --
16 -- distribute, distribute with modifications, sublicense, and/or sell --
17 -- copies of the Software, and to permit persons to whom the Software is --
18 -- furnished to do so, subject to the following conditions: --
20 -- The above copyright notice and this permission notice shall be included --
21 -- in all copies or substantial portions of the Software. --
23 -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS --
24 -- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF --
25 -- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. --
26 -- IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, --
27 -- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR --
28 -- OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR --
29 -- THE USE OR OTHER DEALINGS IN THE SOFTWARE. --
31 -- Except as contained in this notice, the name(s) of the above copyright --
32 -- holders shall not be used in advertising or otherwise to promote the --
33 -- sale, use or other dealings in this Software without prior written --
35 ------------------------------------------------------------------------------
36 -- Author: Eugene V. Melaragno <aldomel@ix.netcom.com> 2000
39 -- Binding Version 01.00
40 ------------------------------------------------------------------------------
41 with ncurses2.util; use ncurses2.util;
43 with Terminal_Interface.Curses; use Terminal_Interface.Curses;
46 with System.Storage_Elements;
47 with System.Address_To_Access_Conversions;
50 -- with Ada.Real_Time; use Ada.Real_Time;
51 -- TODO is there a way to use Real_Time or Ada.Calendar in place of
55 procedure ncurses2.demo_pad is
57 type timestruct is record
59 microseconds : Integer;
62 type myfunc is access function (w : Window) return Key_Code;
64 function gettime return timestruct;
65 procedure do_h_line (y : Line_Position;
67 c : Attributed_Character;
68 to : Column_Position);
69 procedure do_v_line (y : Line_Position;
71 c : Attributed_Character;
73 function padgetch (win : Window) return Key_Code;
74 function panner_legend (line : Line_Position) return Boolean;
75 procedure panner_legend (line : Line_Position);
76 procedure panner_h_cleanup (from_y : Line_Position;
77 from_x : Column_Position;
78 to_x : Column_Position);
79 procedure panner_v_cleanup (from_y : Line_Position;
80 from_x : Column_Position;
81 to_y : Line_Position);
82 procedure panner (pad : Window;
83 top_xp : Column_Position;
84 top_yp : Line_Position;
85 portyp : Line_Position;
86 portxp : Column_Position;
89 function gettime return timestruct is
94 type timeval is record
98 pragma Convention (C, timeval);
100 -- TODO function from_timeval is new Ada.Unchecked_Conversion(
101 -- timeval_a, System.Storage_Elements.Integer_Address);
102 -- should Interfaces.C.Pointers be used here?
104 package myP is new System.Address_To_Access_Conversions (timeval);
107 t : Object_Pointer := new timeval;
109 function gettimeofday
110 (TP : System.Storage_Elements.Integer_Address;
111 TZP : System.Storage_Elements.Integer_Address) return int;
112 pragma Import (C, gettimeofday, "gettimeofday");
115 tmp := gettimeofday (System.Storage_Elements.To_Integer
116 (myP.To_Address (t)),
117 System.Storage_Elements.To_Integer
118 (myP.To_Address (null)));
119 retval.seconds := Integer (t.tv_sec);
120 retval.microseconds := Integer (t.tv_usec);
125 -- in C, The behavior of mvhline, mvvline for negative/zero length is
126 -- unspecified, though we can rely on negative x/y values to stop the
127 -- macro. Except Ada makes Line_Position(-1) = Natural - 1 so forget it.
128 procedure do_h_line (y : Line_Position;
130 c : Attributed_Character;
131 to : Column_Position) is
134 Move_Cursor (Line => y, Column => x);
135 Horizontal_Line (Line_Size => Natural (to - x), Line_Symbol => c);
139 procedure do_v_line (y : Line_Position;
141 c : Attributed_Character;
142 to : Line_Position) is
145 Move_Cursor (Line => y, Column => x);
146 Vertical_Line (Line_Size => Natural (to - y), Line_Symbol => c);
153 function padgetch (win : Window) return Key_Code is
158 c2 := Code_To_Char (c);
164 when Character'Val (Character'Pos ('r') mod 16#20#) => -- CTRL('r')
168 when Character'Val (Character'Pos ('l') mod 16#20#) => -- CTRL('l')
171 return Key_Cursor_Up;
173 return Key_Cursor_Down;
175 return Key_Cursor_Right;
177 return Key_Cursor_Left;
179 return Key_Insert_Line;
181 return Key_Delete_Line;
183 return Key_Insert_Char;
185 return Key_Delete_Char;
186 -- when ERR=> /* FALLTHRU */
194 show_panner_legend : Boolean := True;
196 function panner_legend (line : Line_Position) return Boolean is
197 legend : constant array (0 .. 3) of String (1 .. 61) :=
199 "Use arrow keys (or U,D,L,R) to pan, q to quit (?,t,s flags) ",
200 "Use ! to shell-out. Toggle legend:?, timer:t, scroll mark:s.",
201 "Use +,- (or j,k) to grow/shrink the panner vertically. ",
202 "Use <,> (or h,l) to grow/shrink the panner horizontally. ");
203 legendsize : constant := 4;
205 n : Integer := legendsize - Integer (Lines - line);
207 if line < Lines and n >= 0 then
208 Move_Cursor (Line => line, Column => 0);
209 if show_panner_legend then
210 Add (Str => legend (n));
212 Clear_To_End_Of_Line;
213 return show_panner_legend;
218 procedure panner_legend (line : Line_Position) is
221 tmp := panner_legend (line);
224 procedure panner_h_cleanup (from_y : Line_Position;
225 from_x : Column_Position;
226 to_x : Column_Position) is
228 if not panner_legend (from_y) then
229 do_h_line (from_y, from_x, Blank2, to_x);
231 end panner_h_cleanup;
233 procedure panner_v_cleanup (from_y : Line_Position;
234 from_x : Column_Position;
235 to_y : Line_Position) is
237 if not panner_legend (from_y) then
238 do_v_line (from_y, from_x, Blank2, to_y);
240 end panner_v_cleanup;
243 procedure panner (pad : Window;
244 top_xp : Column_Position;
245 top_yp : Line_Position;
246 portyp : Line_Position;
247 portxp : Column_Position;
250 function f (y : Line_Position) return Line_Position;
251 function f (x : Column_Position) return Column_Position;
252 function greater (y1, y2 : Line_Position) return Integer;
253 function greater (x1, x2 : Column_Position) return Integer;
255 top_x : Column_Position := top_xp;
256 top_y : Line_Position := top_yp;
257 porty : Line_Position := portyp;
258 portx : Column_Position := portxp;
260 -- f[x] returns max[x - 1, 0]
261 function f (y : Line_Position) return Line_Position is
270 function f (x : Column_Position) return Column_Position is
279 function greater (y1, y2 : Line_Position) return Integer is
288 function greater (x1, x2 : Column_Position) return Integer is
298 pymax : Line_Position;
299 basey : Line_Position := 0;
300 pxmax : Column_Position;
301 basex : Column_Position := 0;
303 scrollers : Boolean := True;
304 before, after : timestruct;
305 timing : Boolean := True;
307 package floatio is new Ada.Text_IO.Float_IO (Long_Float);
309 Get_Size (pad, pymax, pxmax);
310 Allow_Scrolling (Mode => False); -- we don't want stdscr to scroll!
314 -- During shell-out, the user may have resized the window. Adjust
315 -- the port size of the pad to accommodate this. Ncurses
316 -- automatically resizes all of the normal windows to fit on the
318 if top_x > Columns then
321 if portx > Columns then
324 if top_y > Lines then
327 if porty > Lines then
332 when Key_Refresh | Character'Pos ('?') =>
333 if c = Key_Refresh then
336 show_panner_legend := not show_panner_legend;
338 panner_legend (Lines - 4);
339 panner_legend (Lines - 3);
340 panner_legend (Lines - 2);
341 panner_legend (Lines - 1);
342 when Character'Pos ('t') =>
343 timing := not timing;
345 panner_legend (Lines - 1);
347 when Character'Pos ('s') =>
348 scrollers := not scrollers;
350 -- Move the top-left corner of the pad, keeping the
351 -- bottom-right corner fixed.
352 when Character'Pos ('h') =>
353 -- increase-columns: move left edge to left
357 panner_v_cleanup (top_y, top_x, porty);
361 when Character'Pos ('j') =>
362 -- decrease-lines: move top-edge down
363 if top_y >= porty then
367 panner_h_cleanup (top_y - 1, f (top_x), portx);
371 when Character'Pos ('k') =>
372 -- increase-lines: move top-edge up
377 panner_h_cleanup (top_y, top_x, portx);
380 when Character'Pos ('l') =>
381 -- decrease-columns: move left-edge to right
382 if top_x >= portx then
386 panner_v_cleanup (f (top_y), top_x - 1, porty);
391 -- Move the bottom-right corner of the pad, keeping the
392 -- top-left corner fixed.
393 when Key_Insert_Char =>
394 -- increase-columns: move right-edge to right
395 if portx >= pxmax or portx >= Columns then
398 panner_v_cleanup (f (top_y), portx - 1, porty);
400 -- C had ++portx instead of portx++, weird.
402 when Key_Insert_Line =>
403 -- increase-lines: move bottom-edge down
404 if porty >= pymax or porty >= Lines then
407 panner_h_cleanup (porty - 1, f (top_x), portx);
411 when Key_Delete_Char =>
412 -- decrease-columns: move bottom edge up
413 if portx <= top_x then
417 panner_v_cleanup (f (top_y), portx, porty);
420 when Key_Delete_Line =>
422 if porty <= top_y then
426 panner_h_cleanup (porty, f (top_x), portx);
428 when Key_Cursor_Left =>
435 when Key_Cursor_Right =>
437 -- if (basex + portx - (pymax > porty) < pxmax)
439 Column_Position (greater (pymax, porty)) < pxmax) then
440 -- if basex + portx < pxmax or
441 -- (pymax > porty and basex + portx - 1 < pxmax) then
447 when Key_Cursor_Up =>
455 when Key_Cursor_Down =>
457 -- same as if (basey + porty - (pxmax > portx) < pymax)
459 Line_Position (greater (pxmax, portx)) < pymax) then
460 -- if (basey + porty < pymax) or
461 -- (pxmax > portx and basey + porty - 1 < pymax) then
467 when Character'Pos ('H') |
472 when Character'Pos ('E') |
475 basey := pymax - porty;
476 if basey < 0 then -- basey := max(basey, 0);
484 -- more writing off the screen.
485 -- Interestingly, the exception is not handled if
486 -- we put a block around this.
488 if top_y /= 0 and top_x /= 0 then
489 Add (Line => top_y - 1, Column => top_x - 1,
490 Ch => ACS_Map (ACS_Upper_Left_Corner));
493 do_v_line (top_y, top_x - 1, ACS_Map (ACS_Vertical_Line), porty);
496 do_h_line (top_y - 1, top_x, ACS_Map (ACS_Horizontal_Line), portx);
498 -- exception when Curses_Exception => null; end;
500 -- in C was ... pxmax > portx - 1
501 if scrollers and pxmax >= portx then
503 length : Column_Position := portx - top_x - 1;
504 lowend, highend : Column_Position;
506 -- Instead of using floats, I'll use integers only.
507 lowend := top_x + (basex * length) / pxmax;
508 highend := top_x + ((basex + length) * length) / pxmax;
510 do_h_line (porty - 1, top_x, ACS_Map (ACS_Horizontal_Line),
512 if highend < portx then
513 Switch_Character_Attribute
514 (Attr => (Reverse_Video => True, others => False),
516 do_h_line (porty - 1, lowend, Blank2, highend + 1);
517 Switch_Character_Attribute
518 (Attr => (Reverse_Video => True, others => False),
520 do_h_line (porty - 1, highend + 1,
521 ACS_Map (ACS_Horizontal_Line), portx);
525 do_h_line (porty - 1, top_x, ACS_Map (ACS_Horizontal_Line), portx);
528 if scrollers and pymax >= porty then
530 length : Line_Position := porty - top_y - 1;
531 lowend, highend : Line_Position;
533 lowend := top_y + (basey * length) / pymax;
534 highend := top_y + ((basey + length) * length) / pymax;
536 do_v_line (top_y, portx - 1, ACS_Map (ACS_Vertical_Line),
538 if highend < porty then
539 Switch_Character_Attribute
540 (Attr => (Reverse_Video => True, others => False),
542 do_v_line (lowend, portx - 1, Blank2, highend + 1);
543 Switch_Character_Attribute
544 (Attr => (Reverse_Video => True, others => False),
546 do_v_line (highend + 1, portx - 1,
547 ACS_Map (ACS_Vertical_Line), porty);
551 do_v_line (top_y, portx - 1, ACS_Map (ACS_Vertical_Line), porty);
555 Add (Line => top_y - 1, Column => portx - 1,
556 Ch => ACS_Map (ACS_Upper_Right_Corner));
559 Add (Line => porty - 1, Column => top_x - 1,
560 Ch => ACS_Map (ACS_Lower_Left_Corner));
564 -- Here is another place where it is possible
565 -- to write to the corner of the screen.
566 Add (Line => porty - 1, Column => portx - 1,
567 Ch => ACS_Map (ACS_Lower_Right_Corner));
569 when Curses_Exception => null;
574 Refresh_Without_Update;
577 -- the C version allows the panel to have a zero height
578 -- wich raise the exception
580 Refresh_Without_Update
585 porty - Line_Position (greater (pxmax, portx)) - 1,
586 portx - Column_Position (greater (pymax, porty)) - 1);
588 when Curses_Exception => null;
593 if timing then declare
595 elapsed : Long_Float;
598 elapsed := (Long_Float (after.seconds - before.seconds) +
599 Long_Float (after.microseconds - before.microseconds)
601 Move_Cursor (Line => Lines - 1, Column => Columns - 20);
602 floatio.Put (s, elapsed, Aft => 3, Exp => 0);
609 exit when c = Key_Exit;
613 Allow_Scrolling (Mode => True);
617 Gridsize : constant := 3;
618 Gridcount : Integer := 0;
620 Pad_High : constant Line_Count := 200;
621 Pad_Wide : constant Column_Count := 200;
622 panpad : Window := New_Pad (Pad_High, Pad_Wide);
624 if panpad = Null_Window then
625 Cannot ("cannot create requested pad");
629 for i in 0 .. Pad_High - 1 loop
630 for j in 0 .. Pad_Wide - 1 loop
631 if i mod Gridsize = 0 and j mod Gridsize = 0 then
632 if i = 0 or j = 0 then
637 Ch => Character'Val (Character'Pos ('A') +
639 Gridcount := Gridcount + 1;
641 elsif i mod Gridsize = 0 then
643 elsif j mod Gridsize = 0 then
647 -- handle the write to the lower right corner error
651 when Curses_Exception => null;
656 panner_legend (Lines - 4);
657 panner_legend (Lines - 3);
658 panner_legend (Lines - 2);
659 panner_legend (Lines - 1);
661 Set_KeyPad_Mode (panpad, True);
662 -- Make the pad (initially) narrow enough that a trace file won't wrap.
663 -- We'll still be able to widen it during a test, since that's required
664 -- for testing boundaries.
666 panner (panpad, 2, 2, Lines - 5, Columns - 15, padgetch'Access);
669 End_Windows; -- Hmm, Erase after End_Windows
671 end ncurses2.demo_pad;