1 ------------------------------------------------------------------------------
3 -- GNAT ncurses Binding Samples --
9 ------------------------------------------------------------------------------
10 -- Copyright (c) 2000,2004 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 -- $Date: 2004/08/21 21:37:00 $
40 -- Binding Version 01.00
41 ------------------------------------------------------------------------------
42 with ncurses2.util; use ncurses2.util;
44 with Terminal_Interface.Curses; use Terminal_Interface.Curses;
47 with System.Storage_Elements;
48 with System.Address_To_Access_Conversions;
51 -- with Ada.Real_Time; use Ada.Real_Time;
52 -- TODO is there a way to use Real_Time or Ada.Calendar in place of
56 procedure ncurses2.demo_pad is
58 type timestruct is record
60 microseconds : Integer;
63 type myfunc is access function (w : Window) return Key_Code;
65 function gettime return timestruct;
66 procedure do_h_line (y : Line_Position;
68 c : Attributed_Character;
69 to : Column_Position);
70 procedure do_v_line (y : Line_Position;
72 c : Attributed_Character;
74 function padgetch (win : Window) return Key_Code;
75 function panner_legend (line : Line_Position) return Boolean;
76 procedure panner_legend (line : Line_Position);
77 procedure panner_h_cleanup (from_y : Line_Position;
78 from_x : Column_Position;
79 to_x : Column_Position);
80 procedure panner_v_cleanup (from_y : Line_Position;
81 from_x : Column_Position;
82 to_y : Line_Position);
83 procedure panner (pad : Window;
84 top_xp : Column_Position;
85 top_yp : Line_Position;
86 portyp : Line_Position;
87 portxp : Column_Position;
90 function gettime return timestruct is
95 type timeval is record
99 pragma Convention (C, timeval);
101 -- TODO function from_timeval is new Ada.Unchecked_Conversion(
102 -- timeval_a, System.Storage_Elements.Integer_Address);
103 -- should Interfaces.C.Pointers be used here?
105 package myP is new System.Address_To_Access_Conversions (timeval);
108 t : constant Object_Pointer := new timeval;
110 function gettimeofday
111 (TP : System.Storage_Elements.Integer_Address;
112 TZP : System.Storage_Elements.Integer_Address) return int;
113 pragma Import (C, gettimeofday, "gettimeofday");
116 tmp := gettimeofday (System.Storage_Elements.To_Integer
117 (myP.To_Address (t)),
118 System.Storage_Elements.To_Integer
119 (myP.To_Address (null)));
122 retval.microseconds := 0;
124 retval.seconds := Integer (t.tv_sec);
125 retval.microseconds := Integer (t.tv_usec);
131 -- in C, The behavior of mvhline, mvvline for negative/zero length is
132 -- unspecified, though we can rely on negative x/y values to stop the
133 -- macro. Except Ada makes Line_Position(-1) = Natural - 1 so forget it.
134 procedure do_h_line (y : Line_Position;
136 c : Attributed_Character;
137 to : Column_Position) is
140 Move_Cursor (Line => y, Column => x);
141 Horizontal_Line (Line_Size => Natural (to - x), Line_Symbol => c);
145 procedure do_v_line (y : Line_Position;
147 c : Attributed_Character;
148 to : Line_Position) is
151 Move_Cursor (Line => y, Column => x);
152 Vertical_Line (Line_Size => Natural (to - y), Line_Symbol => c);
159 function padgetch (win : Window) return Key_Code is
164 c2 := Code_To_Char (c);
170 when Character'Val (Character'Pos ('r') mod 16#20#) => -- CTRL('r')
174 when Character'Val (Character'Pos ('l') mod 16#20#) => -- CTRL('l')
177 return Key_Cursor_Up;
179 return Key_Cursor_Down;
181 return Key_Cursor_Right;
183 return Key_Cursor_Left;
185 return Key_Insert_Line;
187 return Key_Delete_Line;
189 return Key_Insert_Char;
191 return Key_Delete_Char;
192 -- when ERR=> /* FALLTHRU */
200 show_panner_legend : Boolean := True;
202 function panner_legend (line : Line_Position) return Boolean is
203 legend : constant array (0 .. 3) of String (1 .. 61) :=
205 "Use arrow keys (or U,D,L,R) to pan, q to quit (?,t,s flags) ",
206 "Use ! to shell-out. Toggle legend:?, timer:t, scroll mark:s.",
207 "Use +,- (or j,k) to grow/shrink the panner vertically. ",
208 "Use <,> (or h,l) to grow/shrink the panner horizontally. ");
209 legendsize : constant := 4;
211 n : constant Integer := legendsize - Integer (Lines - line);
213 if line < Lines and n >= 0 then
214 Move_Cursor (Line => line, Column => 0);
215 if show_panner_legend then
216 Add (Str => legend (n));
218 Clear_To_End_Of_Line;
219 return show_panner_legend;
224 procedure panner_legend (line : Line_Position) is
226 if not panner_legend (line) then
231 procedure panner_h_cleanup (from_y : Line_Position;
232 from_x : Column_Position;
233 to_x : Column_Position) is
235 if not panner_legend (from_y) then
236 do_h_line (from_y, from_x, Blank2, to_x);
238 end panner_h_cleanup;
240 procedure panner_v_cleanup (from_y : Line_Position;
241 from_x : Column_Position;
242 to_y : Line_Position) is
244 if not panner_legend (from_y) then
245 do_v_line (from_y, from_x, Blank2, to_y);
247 end panner_v_cleanup;
250 procedure panner (pad : Window;
251 top_xp : Column_Position;
252 top_yp : Line_Position;
253 portyp : Line_Position;
254 portxp : Column_Position;
257 function f (y : Line_Position) return Line_Position;
258 function f (x : Column_Position) return Column_Position;
259 function greater (y1, y2 : Line_Position) return Integer;
260 function greater (x1, x2 : Column_Position) return Integer;
262 top_x : Column_Position := top_xp;
263 top_y : Line_Position := top_yp;
264 porty : Line_Position := portyp;
265 portx : Column_Position := portxp;
267 -- f[x] returns max[x - 1, 0]
268 function f (y : Line_Position) return Line_Position is
277 function f (x : Column_Position) return Column_Position is
286 function greater (y1, y2 : Line_Position) return Integer is
295 function greater (x1, x2 : Column_Position) return Integer is
305 pymax : Line_Position;
306 basey : Line_Position := 0;
307 pxmax : Column_Position;
308 basex : Column_Position := 0;
310 scrollers : Boolean := True;
311 before, after : timestruct;
312 timing : Boolean := True;
314 package floatio is new Ada.Text_IO.Float_IO (Long_Float);
316 Get_Size (pad, pymax, pxmax);
317 Allow_Scrolling (Mode => False); -- we don't want stdscr to scroll!
321 -- During shell-out, the user may have resized the window. Adjust
322 -- the port size of the pad to accommodate this. Ncurses
323 -- automatically resizes all of the normal windows to fit on the
325 if top_x > Columns then
328 if portx > Columns then
331 if top_y > Lines then
334 if porty > Lines then
339 when Key_Refresh | Character'Pos ('?') =>
340 if c = Key_Refresh then
343 show_panner_legend := not show_panner_legend;
345 panner_legend (Lines - 4);
346 panner_legend (Lines - 3);
347 panner_legend (Lines - 2);
348 panner_legend (Lines - 1);
349 when Character'Pos ('t') =>
350 timing := not timing;
352 panner_legend (Lines - 1);
354 when Character'Pos ('s') =>
355 scrollers := not scrollers;
357 -- Move the top-left corner of the pad, keeping the
358 -- bottom-right corner fixed.
359 when Character'Pos ('h') =>
360 -- increase-columns: move left edge to left
364 panner_v_cleanup (top_y, top_x, porty);
368 when Character'Pos ('j') =>
369 -- decrease-lines: move top-edge down
370 if top_y >= porty then
374 panner_h_cleanup (top_y - 1, f (top_x), portx);
378 when Character'Pos ('k') =>
379 -- increase-lines: move top-edge up
384 panner_h_cleanup (top_y, top_x, portx);
387 when Character'Pos ('l') =>
388 -- decrease-columns: move left-edge to right
389 if top_x >= portx then
393 panner_v_cleanup (f (top_y), top_x - 1, porty);
398 -- Move the bottom-right corner of the pad, keeping the
399 -- top-left corner fixed.
400 when Key_Insert_Char =>
401 -- increase-columns: move right-edge to right
402 if portx >= pxmax or portx >= Columns then
405 panner_v_cleanup (f (top_y), portx - 1, porty);
407 -- C had ++portx instead of portx++, weird.
409 when Key_Insert_Line =>
410 -- increase-lines: move bottom-edge down
411 if porty >= pymax or porty >= Lines then
414 panner_h_cleanup (porty - 1, f (top_x), portx);
418 when Key_Delete_Char =>
419 -- decrease-columns: move bottom edge up
420 if portx <= top_x then
424 panner_v_cleanup (f (top_y), portx, porty);
427 when Key_Delete_Line =>
429 if porty <= top_y then
433 panner_h_cleanup (porty, f (top_x), portx);
435 when Key_Cursor_Left =>
442 when Key_Cursor_Right =>
444 -- if (basex + portx - (pymax > porty) < pxmax)
446 Column_Position (greater (pymax, porty)) < pxmax then
447 -- if basex + portx < pxmax or
448 -- (pymax > porty and basex + portx - 1 < pxmax) then
454 when Key_Cursor_Up =>
462 when Key_Cursor_Down =>
464 -- same as if (basey + porty - (pxmax > portx) < pymax)
466 Line_Position (greater (pxmax, portx)) < pymax then
467 -- if (basey + porty < pymax) or
468 -- (pxmax > portx and basey + porty - 1 < pymax) then
474 when Character'Pos ('H') |
479 when Character'Pos ('E') |
482 if pymax < porty then
485 basey := pymax - porty;
492 -- more writing off the screen.
493 -- Interestingly, the exception is not handled if
494 -- we put a block around this.
496 if top_y /= 0 and top_x /= 0 then
497 Add (Line => top_y - 1, Column => top_x - 1,
498 Ch => ACS_Map (ACS_Upper_Left_Corner));
501 do_v_line (top_y, top_x - 1, ACS_Map (ACS_Vertical_Line), porty);
504 do_h_line (top_y - 1, top_x, ACS_Map (ACS_Horizontal_Line), portx);
506 -- exception when Curses_Exception => null; end;
508 -- in C was ... pxmax > portx - 1
509 if scrollers and pxmax >= portx then
511 length : constant Column_Position := portx - top_x - 1;
512 lowend, highend : Column_Position;
514 -- Instead of using floats, I'll use integers only.
515 lowend := top_x + (basex * length) / pxmax;
516 highend := top_x + ((basex + length) * length) / pxmax;
518 do_h_line (porty - 1, top_x, ACS_Map (ACS_Horizontal_Line),
520 if highend < portx then
521 Switch_Character_Attribute
522 (Attr => (Reverse_Video => True, others => False),
524 do_h_line (porty - 1, lowend, Blank2, highend + 1);
525 Switch_Character_Attribute
526 (Attr => (Reverse_Video => True, others => False),
528 do_h_line (porty - 1, highend + 1,
529 ACS_Map (ACS_Horizontal_Line), portx);
533 do_h_line (porty - 1, top_x, ACS_Map (ACS_Horizontal_Line), portx);
536 if scrollers and pymax >= porty then
538 length : constant Line_Position := porty - top_y - 1;
539 lowend, highend : Line_Position;
541 lowend := top_y + (basey * length) / pymax;
542 highend := top_y + ((basey + length) * length) / pymax;
544 do_v_line (top_y, portx - 1, ACS_Map (ACS_Vertical_Line),
546 if highend < porty then
547 Switch_Character_Attribute
548 (Attr => (Reverse_Video => True, others => False),
550 do_v_line (lowend, portx - 1, Blank2, highend + 1);
551 Switch_Character_Attribute
552 (Attr => (Reverse_Video => True, others => False),
554 do_v_line (highend + 1, portx - 1,
555 ACS_Map (ACS_Vertical_Line), porty);
559 do_v_line (top_y, portx - 1, ACS_Map (ACS_Vertical_Line), porty);
563 Add (Line => top_y - 1, Column => portx - 1,
564 Ch => ACS_Map (ACS_Upper_Right_Corner));
567 Add (Line => porty - 1, Column => top_x - 1,
568 Ch => ACS_Map (ACS_Lower_Left_Corner));
572 -- Here is another place where it is possible
573 -- to write to the corner of the screen.
574 Add (Line => porty - 1, Column => portx - 1,
575 Ch => ACS_Map (ACS_Lower_Right_Corner));
577 when Curses_Exception => null;
582 Refresh_Without_Update;
585 -- the C version allows the panel to have a zero height
586 -- wich raise the exception
588 Refresh_Without_Update
593 porty - Line_Position (greater (pxmax, portx)) - 1,
594 portx - Column_Position (greater (pymax, porty)) - 1);
596 when Curses_Exception => null;
601 if timing then declare
603 elapsed : Long_Float;
606 elapsed := (Long_Float (after.seconds - before.seconds) +
607 Long_Float (after.microseconds - before.microseconds)
609 Move_Cursor (Line => Lines - 1, Column => Columns - 20);
610 floatio.Put (s, elapsed, Aft => 3, Exp => 0);
617 exit when c = Key_Exit;
621 Allow_Scrolling (Mode => True);
625 Gridsize : constant := 3;
626 Gridcount : Integer := 0;
628 Pad_High : constant Line_Count := 200;
629 Pad_Wide : constant Column_Count := 200;
630 panpad : Window := New_Pad (Pad_High, Pad_Wide);
632 if panpad = Null_Window then
633 Cannot ("cannot create requested pad");
637 for i in 0 .. Pad_High - 1 loop
638 for j in 0 .. Pad_Wide - 1 loop
639 if i mod Gridsize = 0 and j mod Gridsize = 0 then
640 if i = 0 or j = 0 then
645 Ch => Character'Val (Character'Pos ('A') +
647 Gridcount := Gridcount + 1;
649 elsif i mod Gridsize = 0 then
651 elsif j mod Gridsize = 0 then
655 -- handle the write to the lower right corner error
659 when Curses_Exception => null;
664 panner_legend (Lines - 4);
665 panner_legend (Lines - 3);
666 panner_legend (Lines - 2);
667 panner_legend (Lines - 1);
669 Set_KeyPad_Mode (panpad, True);
670 -- Make the pad (initially) narrow enough that a trace file won't wrap.
671 -- We'll still be able to widen it during a test, since that's required
672 -- for testing boundaries.
674 panner (panpad, 2, 2, Lines - 5, Columns - 15, padgetch'Access);
677 End_Windows; -- Hmm, Erase after End_Windows
679 end ncurses2.demo_pad;