% ----------------------------------------------------------------- % ListWindow: % ============ % This Widget displays multi-line texts. It can be used in ONE % of 3 modes: as an 'outfile' to display files (initial state) % or as a menu (CHOICE) or as a Text-edit window (INPUT) where % the user can type in or delete data. The MODE defines which % events the window is sensitive to: % % DISPLAY CHOICE INPUT % ------- ------ ----- % pointer_motion_sensitize; X % enter_and_leave_sensitize; X X % key_sensitize; X % % - We also use a TextArray structure to store the displayed text. % This structure uses a text array object which can be replaced % if the text grows too large for the initial array % ----------------------------------------------------------------- % repair : Selection does not follow sacrolling. Corrected in InvertSelection. % Charles De Lean May 29 1995. % repair : Scrolling does not work if there is fewer lines to draw (info_top) % than the list window is able to draw at once (max_lines_in_window). % Thus wnd_bot becomes negative. % Corrected this problem in forcing wnd_bot to be at least 1. % corrected : replaced obselete xsetxtermcursor and xsetarrowcursor by % SetCursor. Charles De Lean, June 19, 1995. % changed : replaced Passivewait by new function Sleep which works in seconds. % Charles De Lean, June 26, 1995. % added function initputline to fill the structure faster while initialisation. % Charles De Lean June 28 1995. % eliminated Menu_size as a variable. It is now a constant. Charles De Lean, July 6, 1995; % selected text and cursor do not follow scrolling. Corrected. % elimination of selected text at end causes problems in refresh. Corrected. % problems with selection elimination if selection has been made with % mouse going outside the window. Corrected. % Charles De Lean, July 13 1995 % added function scrolllinetop for scrolling line at top of window instead of % center. % InputWindow class ListWindow(Menu_size); integer Menu_size; !CDL July 6 1995; InputWindow class ListWindow; begin ref(TextArray) Lines; integer Menu_size=500; Boolean array grayLines (1 : Menu_size); Boolean procedure gray(n); integer n; if n >=1 and n <= Menu_size then Gray := grayLines(N); text buffer; ! works as the IMAGE text for a file; integer invert_line_nr; integer max_lines_in_window, wnd_bot, wnd_top; integer procedure info_top; info_top := Lines.size; integer char_width; integer procedure chars_in_line; chars_in_line := (width - 2* text_left) // char_width; integer procedure text_border; text_border := depth_border+1 ; integer procedure text_left; text_left := depth_border+1; integer procedure text_width; text_width := width - text_left ; % text_width := width - 2*depth_border -1 ; ! ----------------------------------------------------------- The following functions give the Y coordinates for writing text (base_line) and for bounding rectangles (rect_y) Note: TL = line number in TEXT (LINES array) WL = line number in WINDOW ----------------------------------------------------------- ; integer procedure base_line(tL); integer tL; base_line := head_height + (tL-wnd_bot+1) * font_height; integer procedure rect_y (tL); integer tL; rect_y := head_height + (tL-wnd_bot+1)*font_height - font_ascent; integer procedure Wbase_line(wL); integer wL; Wbase_line := head_height + wL*font_height; integer procedure Wrect_y (wL); integer wL; Wrect_y := head_height + wL*font_height - font_ascent; % ----------------------------------------------------------------- % T E X T S E L E C T I O N % % Selected text is indicated by reverse video on the screen. % It can extend over several lines. It is expressed as 4 integers, % Col1-Line1 to Col2-Line2, where: % % - LINE numbers start at 1 and refer to lines in the TEXT array; % not the lines as seen in the window % - COLUMN numbers start at 0 and refer to position 'between' % characters (0 means left of first char on a line and 1 means % just after the first character). % - We ensure that comes before in the % text: line1 ; ref(XWindow) procedure AllowChoice; if not choice_allowed then begin choice_allowed := true; if input_allowed then OffInput else pointer_motion_sensitize; AllowChoice :- this ListWindow; end; ref(XWindow) procedure DisallowChoice; if choice_allowed then begin choice_allowed := false; pointer_motion_desensitize; DisallowChoice :- this ListWindow; end; ref(XWindow) procedure AllowInput; if not input_allowed then begin input_allowed := true; choice_allowed := false; InvertSelection; key_sensitize; pointer_motion_sensitize; % XSetXtermCursor(WindowID); !CDL June 19,1995; SetCursor("xterm"); AllowInput :- this InputWindow; end; ref(XWindow) procedure DisallowInput; if input_allowed then begin OffInput; pointer_motion_desensitize; DisallowInput :- this InputWindow; end; procedure OffInput; begin input_allowed := false; InvertSelection; key_desensitize; % XSetArrowCursor(WindowID); !CDL June 19,1995; SetCursor("arrow"); end; % ----------------------------------------------------------------- % Event Handling % ----------------------------------------------------------------- procedure handle_enter_window; if input_allowed then begin ThickBorder end; procedure handle_leave_window; begin if input_allowed then WhiteBorder else if choice_allowed then begin InvertLine(invert_line_nr); invert_line_nr := 0; end; end; procedure handle_button_down(button); integer button; if input_allowed then begin Button_Pressed := true; InvertSelection; ! to remove Cursor ; SelLine1 := SelLine2 := Line_last := min(info_top, Line_of(y_of_event)); SelCol1 := SelCol2 := Col_last:= min(Col_of (x_of_event), Lines.get(SelLine1).length ) ; end else if choice_allowed then begin integer ind; ind := invert_line_nr + wnd_bot - 1; if 1 <= ind and ind <= info_top and then not gray(ind) then begin parent.LineChoice (Lines.get(ind)); parent.LineChoiceIn(Lines.get(ind), ind, this ListWindow); end; end; procedure handle_pointer_motion(x, y); integer x, y; begin integer line_nr, Col, Line; Line := min(info_top, Line_of(y)); line_nr := Line - wnd_bot + 1; % Col := min(Col_of (x), % Lines.get(Line).length ) ; Col:=bounded(0,Col_of (x),Lines.get(Line).length); !CDL July 13 1995; if input_allowed and then Button_Pressed and (Col<>Col_Last or Line <> Line_last) then begin if Line>Line_Last or (Line=Line_Last and Col>=Col_Last) then InvertZone(Col_last, Line_last, Col, Line) else InvertZone(Col, Line, Col_last, Line_last); Col_last := Col; Line_Last := Line; end; if choice_allowed then begin if invert_line_nr <> line_nr then begin InvertLine(invert_line_nr); invert_line_nr := line_nr; InvertLine(invert_line_nr); end; end; end; procedure handle_button_up(button); integer button; if input_allowed and Button_Pressed then begin Button_Pressed := false; if SelLine1>Line_Last or (SelLine1=Line_Last and SelCol1>=Col_Last) then begin SelCol2 := SelCol1; SelLine2:= SelLine1; SelCol1 := Col_last; SelLine1:= Line_last; end else begin SelCol2 := Col_last; SelLine2:= Line_last; end; if SelLine1=SelLine2 and SelCol1=SelCol2 then invertSelection; end; integer procedure Col_of (X); integer X; Col_of := (x-text_left+2)//char_width; integer procedure Line_of(Y); integer Y; Line_of := (y - head_height-font_descent)//font_height + wnd_bot; procedure InvertSelection; if input_allowed then InvertZone(SelCol1, SelLine1!-wnd_bot+1;, SelCol2, SelLine2!-wnd_bot+1;) !CDL July 13; else if choice_allowed then % InvertLine(invert_line_nr); !CDL May 29 1995; InvertLine(invert_line_nr-wnd_bot+1); procedure InvertZone(C1,L1,C2,L2); integer C1,L1,C2,L2; if wnd_bot <= L2 and L1 <= wnd_top then begin ! NOTE: this procedure assumes that Point 1 is before Point2 ; integer i, X0, Xmax, X1,X2; X0 := text_left; X1 := X0 + C1 * char_width; X2 := X0 + C2 * char_width; Xmax:=X0 + text_width; if L1 = L2 then InvertRectangle( X1, rect_y(L1), max(1, X2-X1), font_height) else begin if wnd_bot <= L1 then InvertRectangle( X1, rect_y(L1), Xmax-X1, font_height); for i:= max(L1+1, wnd_bot) step 1 until min(L2-1, wnd_top) do InvertRectangle( X0, rect_y(i), Xmax-X0, font_height); if L2 <= wnd_top then InvertRectangle( X0, rect_y(L2), X2-X0, font_height); end; end === InvertZone === ; % ----------------------------------------------------------------- % H A N D L I N G I N P U T % ----------------------------------------------------------------- procedure handle_key_down(key); character key; if input_allowed then begin parent.InputInSubwindow(this InputWindow); IF isDelete( key ) then del_char else if char_ok(key) then insert_char(Key) else if key = char(return_code) and then parent in PromptWindow then parent qua PromptWindow.ReturnKey else if key = char(return_code) then handle_return; end; procedure DEL_CHAR; begin integer Code, i; text T; RemoveCursor; T :- Lines.get(SelLine2); if SelLine1 <> SelLine2 then ! ==> Big changes ; begin Lines.set(SelLine1, Lines.get(SelLine1).sub(1,SelCol1) & T.sub(SelCol2+1,T.length-SelCol2) ); Lines.delete(SelLine1+1, SelLine2-SelLine1); RedrawLines(SelLine1); end else if SelCol1 <> SelCol2 then ! ==> Shorten current line ; begin Lines.set(SelLine1, Lines.get(SelLine1).sub(1,SelCol1) & T.sub(SelCol2+1,T.length-SelCol2) ); redrawLine(SelLine1); end else if SelCol1 > 0 then ! ==> Delete 1 character ; begin Lines.set(SelLine1, Lines.get(SelLine1).sub(1,SelCol1-1) & T.sub(SelCol2+1,T.length-SelCol2) ); SelCol1 := SelCol1-1; redrawLine(SelLine1); end else if SelLine1 > 1 then begin ! ==> Remove line Break ; SelCol1 := Lines.get(SelLine1-1).length; Lines.set(SelLine1-1, Lines.get(SelLine1-1) & Lines.get(SelLine1) ); Lines.delete(SelLine1,1); SelLine1 := SelLine1 - 1; if SelLine1 < wnd_bot then begin wnd_top := wnd_top - 1; wnd_bot := wnd_bot - 1; end; redrawLines(SelLine1); end; Selcol2 := SelCol1; SelLine2 := SelLine1; MarkScrollbar; ShowCursor; end --- del_Char --- ; procedure INSERT_CHAR(C); character C; begin integer Code, i; text T, CT; RemoveCursor; CT :- blanks(1); CT.putchar(C); T :- Lines.get(SelLine2); if SelLine1 <> SelLine2 then ! ==> Big changes ; begin Lines.set(SelLine1, Lines.get(SelLine1).sub(1,SelCol1) & CT & T.sub(SelCol2+1,T.length-SelCol2) ); Lines.delete(SelLine1+1, SelLine2-SelLine1); RedrawLines(SelLine1); end else if SelCol1 <> SelCol2 then begin ! ==> delete selection then insert C ; Lines.set(SelLine1, Lines.get(SelLine1).sub(1,SelCol1) & CT & T.sub(SelCol2+1,T.length-SelCol2) ); RedrawLine(SelLine1); end else begin ! ==> add 1 character ; Lines.set(SelLine1, Lines.get(SelLine1).sub(1,SelCol1) & CT & T.sub(SelCol1+1,T.length-SelCol1) ); RedrawLine(SelLine1); end; SelCol1 := Selcol2 := SelCol1+1; SelLine2 := SelLine1; MarkScrollbar; ShowCursor; end --- INSERT_CHAR --- ; procedure HANDLE_RETURN; begin Text T, T2; Integer I, Delta; RemoveCursor; T :- Lines.get(SelLine2); T2 :- T.sub(SelCol2+1,T.length-SelCol2); Lines.set(SelLine1, T.sub(1,SelCol1)); delta := SelLine2 - SelLine1 - 1; if Delta < 0 then Lines.insert(SelLine1+1,1) else if Delta > 0 then Lines.delete(SelLine1+1,Delta); Lines.set(SelLine1+1, T2); redrawLines(SelLine1); SelCol2 := SelCol1 := 0;; SelLine2 := SelLine1:= SelLine1+1; ShowCursor; MarkScrollbar; if SelLine1 > wnd_top then ScrollLineDown; end --- handle_return --- ; % ===================================================================== % A R R O W S % --------------------------------------------------------------------- procedure handle_down_arrow; if input_allowed and Cursor_y < info_top then begin RemoveCursor; if Cursor_y = wnd_top then ScrollLineDown; PlaceCursor(Cursor_x, Cursor_y + 1); end; procedure handle_up_arrow; if input_allowed and current_line_nr > 1 then begin RemoveCursor; if Cursor_y = wnd_bot then ScrollLineUp; PlaceCursor(Cursor_x, Cursor_y - 1); end; procedure handle_right_arrow; begin RemoveCursor; PlaceCursor(Cursor_x+1, Cursor_y); end; procedure handle_left_arrow; begin RemoveCursor; PlaceCursor(Cursor_x-1, Cursor_y); end; procedure PlaceCursor(col, lnr); integer col, lnr; begin SelLine1:= SelLine2:= Bounded(1, lnr, info_top); SelCol1 := SelCol2 := Bounded(0, col, Lines.get(SelLine1).length ); ShowCursor; end; Text Procedure CurrentText; CurrentText :- Lines.get( SelLine1 ); Integer procedure Current_Line_nr; Current_Line_nr := SelLine1; procedure RemoveCursor; InvertSelection; procedure ShowCursor; InvertSelection; integer procedure Cursor_x; Cursor_x := SelCol1; integer procedure Cursor_y; Cursor_y := SelLine1; integer procedure Bounded( Minn, X, Maxx); integer X, Minn, Maxx; Bounded := min( max (Minn,X),Maxx ); % ----------------------------------------------------------------- % D R A W I N G R O U T I N E S % ----------------------------------------------------------------- procedure Refresh; if Lines =/= none then begin integer line_nr; SClear; for line_nr := wnd_bot step 1 until min(wnd_top, info_top) do DrawTextLine(line_nr, base_line(line_nr)); MarkScrollbar; InvertSelection; end; procedure SClear; begin external C procedure xclearwindow is procedure XClearWindow(WindowID); integer WindowID;; XClearWindow(windowID); end; procedure DrawTextLine(tLnr, y); integer tLnr, y; begin if gray(tLnr) then SetDrawStipple("gray50"); DrawText(text_left, y, Lines.get(tLnr)); if gray(tLnr) then SetDrawStipple("black"); end; procedure RedrawLines(Tn); integer Tn; ! Text line number; begin integer L; for L := Tn step 1 until min(wnd_top, info_top) do RedrawLine(L); if info_top 1 then begin InvertSelection; % InvertLine(invert_line_nr); CopyRectangle(text_left, Wrect_y(1), text_width,(max_lines_in_window-1)*font_height , text_left, Wrect_y(2)); ClearLine(1); wnd_top := wnd_top - 1; wnd_bot := wnd_bot - 1; MarkScrollbar; DrawTextLine(wnd_bot, Wbase_line(1)); InvertSelection; % InvertLine(invert_line_nr); end; ScrollLineUp :- this ListWindow; end; ref(ListWindow) procedure ScrollLineDown; begin if wnd_top <= info_top then begin InvertSelection; CopyRectangle(text_left, Wrect_y(2), text_width, (max_lines_in_window - 1)*font_height , text_left, Wrect_y(1)); ClearLine(max_lines_in_window); wnd_top := wnd_top + 1; wnd_bot := wnd_bot + 1; if wnd_top <= info_top then DrawTextLine(wnd_top, base_line(wnd_top)); MarkScrollbar; InvertSelection; end; ScrollLineDown :- this ListWindow; end; ref(ListWindow) procedure Scroll(n); integer n; begin integer i; for i := 1 step 1 until abs(n) do if n >= 0 then ScrollLineDown else ScrollLineUp; Scroll :- this ListWindow; end; ref(ListWindow) procedure ScrollPage(forward); Boolean forward; begin wnd_bot := Bounded(1, wnd_bot + (if forward then +1 else-1) *(max_lines_in_window-1), info_top); wnd_top := wnd_bot + max_lines_in_window-1; Refresh; ScrollPage :- this ListWindow; end; ref(ListWindow) procedure ScrollToBottom; begin wnd_bot := 1; wnd_top := wnd_bot + max_lines_in_window - 1; Refresh; ScrollToBottom :- this ListWindow; end; ref(ListWindow) procedure ScrollToTop; ScrollToTop :- ScrollTo(1); ref(ListWindow) procedure ScrollToLine(line_number); integer line_number; begin !CDL June 7 1995; wnd_bot := Bounded(1, line_number - max_lines_in_window//2, % info_top - max_lines_in_window + 2); max(1, info_top - max_lines_in_window + 2)); wnd_top := wnd_bot + max_lines_in_window - 1; Refresh; ScrollToLine :- this ListWindow; end; ref(ListWindow) procedure ScrollLineTop(line_number); !CDL July 26 1995; integer line_number; begin wnd_bot := Bounded(1, line_number,max(1, info_top - max_lines_in_window + 2)); wnd_top := wnd_bot + max_lines_in_window - 1; Refresh; ScrollLineTop :- this ListWindow; end; ref(ListWindow) procedure ScrollTo(fraction); real fraction; begin wnd_bot := bounded(1, fraction*info_top, info_top - max_lines_in_window + 2 ); wnd_top := wnd_bot + max_lines_in_window - 1; Refresh; ScrollTo :- this ListWindow; end; % ========================================================================= % O U T F I L E behaviour % ========================================================================= ref(ListWindow) procedure outtext(t); text t; outtext :- PutText(t); ref(ListWindow) procedure outint(i,w); integer i,w; outint :- PutText(int_as_text_fix(i,w)); ref(ListWindow) procedure outchar(C); character C; begin text t; t :- blanks(1); t.putchar(C); outchar :- PutText(t); end; ref(ListWindow) procedure outfix(r, dig, w); real R; integer dig, w; outfix :- PutText(real_as_text_fix(r, dig, w)); ref(ListWindow) procedure PutText(t); text t; begin text Buf; Buf :- buffer & t; if buffer =/= notext and then Buf.length > chars_in_line then PutImage; buffer :- buffer & t; PutText :- this ListWindow; end; ref(ListWindow) procedure setpos(pos); integer pos; begin buffer :- if pos <= 1 then notext else if buffer == notext then blanks(pos - 1) else if buffer.length + 1 < pos then buffer & blanks(pos-buffer.length-1) else if buffer.length >= pos then buffer.sub(1, pos - 1) else buffer; setpos :- this ListWindow; end; ref(ListWindow) procedure outimage; outimage :- PutImage; ref(ListWindow) procedure PutImage; begin PutLine(buffer); buffer :- notext; PutImage :- this ListWindow; end; ref(ListWindow) procedure PutLine(line); value line; text line; begin if info_top > wnd_top then Scroll(info_top - wnd_top + 1) else if info_top = wnd_top then ScrollLineDown; Lines.put(Line); RedrawLine(info_top); PutLine :- this ListWindow; end; ref(ListWindow) procedure InitPutLine(line); value line; text line; !CDL June 28 1995; begin lines.put(line); InitPutLine:-this ListWindow; end; ref(ListWindow) procedure PutLineNr(lnr, line); value line; integer lnr; text line; if 1 <= lnr and lnr <= info_top+1 then begin if lnr > wnd_top then Scroll(lnr - wnd_top); if lnr <= info_top then Lines.set(lnr,line) else Lines.put(line); RedrawLine(lnr); PutLineNr :- this ListWindow; end; text procedure GetLine(lnr); integer lnr; GetLIne :- Lines.get(lnr); ref(ListWindow) procedure PutVisibleLineNr(lnr, line); integer lnr; text line; PutVisibleLineNr :- PutLineNr(lnr+wnd_bot-1, line); text procedure GetVisibleLine(lnr); integer lnr; GetVisibleLine :- GetLine(lnr+wnd_bot-1); ref(ListWindow) procedure GrayLine(lnr); integer lnr; begin if 1 <= lnr and lnr <= Menu_size then begin grayLines(lnr) := true; RedrawLine(lnr); end; GrayLine :- this ListWindow; end; ref(ListWindow) procedure BlackLine(lnr); integer lnr; begin if 1 <= lnr and lnr <= Menu_size then begin grayLines(lnr) := false; RedrawLine(lnr); end; BlackLine :- this ListWindow; end; % ========================================================================= % I N I T I A L I S A T I O N % ========================================================================= ref(XWindow) procedure MakeEmpty; begin integer lnr; wnd_bot := 1; wnd_top := wnd_bot + max_lines_in_window -1; Lines.Clear; ! info_top := 0; buffer :- notext; current_text :- notext; for lnr := 1 step 1 until Menu_size do begin % Lines(lnr) :- notext; grayLines(lnr) := false; end; cursor_pos_set := false; invert_line_nr := 0; cursor_pos := 0; SClear; MarkScrollBar; MakeEmpty :- this ListWindow; end; Boolean procedure IsEmpty; IsEmpty := info_top = 0; ref(ListWindow) procedure SetTextSize(ncolumns, nrows); integer ncolumns, nrows; SetTextSize :- SetColumns(ncolumns).SetLines(nrows); ref(XWindow) procedure SetColumns(ncolumns); integer ncolumns; SetColumns :- SetWidth(ncolumns*max_char_width + 2*text_border); procedure SetSizeSub; begin max_lines_in_window := min(Menu_size, (height - head_height - 2*text_left)//font_height); max_item_length := min(max_item_length, max(1, (width - 2*text_border)//max_char_width)); wnd_top := wnd_bot + max_lines_in_window -1; end; ref(XWindow) procedure SetFont(font_name); text font_name; begin SetXFont(new XFont(font_name)); char_width := width_of_text("x"); SetSizeSub; Refresh; SetFont :- this XWindow; end; Lines :- new TextArray; MakeEmpty; button_sensitize; pointer_motion_sensitize; SetBackingStore; SetTypeFace("courier", default_font_points, false, false); char_width := width_of_text("x"); end ListWindow; %include TextTable.H