diff options
Diffstat (limited to 'ipl/gprocs/vtext.icn')
-rw-r--r-- | ipl/gprocs/vtext.icn | 479 |
1 files changed, 479 insertions, 0 deletions
diff --git a/ipl/gprocs/vtext.icn b/ipl/gprocs/vtext.icn new file mode 100644 index 0000000..abcd173 --- /dev/null +++ b/ipl/gprocs/vtext.icn @@ -0,0 +1,479 @@ +############################################################################ +# +# File: vtext.icn +# +# Subject: Procedures for textual vidgets +# +# Authors: Jon Lipp and Gregg M. Townsend +# +# Date: November 4, 2002 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Vidgets defined in this file: +# Vtext +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Includes: keysyms +# +############################################################################ +# +# Links: vidgets +# +############################################################################ + +link vidgets + +$include "keysyms.icn" + +$ifndef _X_WINDOW_SYSTEM + $define Key_KP_Up Key_Up + $define Key_KP_Down Key_Down + $define Key_KP_Left Key_Left + $define Key_KP_Right Key_Right +$endif + + +############################################################################ +# Vtext +############################################################################ + +record Vstd_text(draw_cursor, erase_cursor, draw_data, unblock, block, + DataPixelSize, MaxPixelSize, NumericData, CursorPos, DataLength, + OldCursorPos, CursorOn, ta, tb, dx, dy) + +record Vtext_rec (win, s, callback, id, MaxChars, mask, data, uid, + ax, ay, aw, ah, T, P, V) + +procedure Vtext(params[]) + local frame, x, y, ins, self + static procs, type + + initial { + procs := Vstd(event_Vtext, draw_Vtext, + outline_Vtext, resize_Vtext, inrange_Vpane, init_Vtext, + couplerset_Vtext,,,,, set_value_Vtext) + type := proc("type", 0) # protect attractive name + } + + if ins := Vinsert_check(params) then { + frame := pop(params); x := pop(params); y:= pop(params) + } + self := Vtext_rec ! params[1:7|0] + Vwin_check(self.win, "Vtext()") + if (\self.MaxChars, not numeric(self.MaxChars) ) then + _Vbomb("invalid size parameter to Vtext()") + if type(\self.mask) ~== "cset" then + _Vbomb("invalid mask parameter to Vtext()") + if type(\self.s) ~== "string" & not numeric(self.s) then + _Vbomb("invalid prompt passed to Vtext()") + + self.uid := Vget_uid() + self.V := procs + self.P := Vstd_pos() + self.T := Vstd_text(draw_cursor_Vtext, erase_cursor_Vtext, + draw_data_Vtext, unblock_Vtext, block_Vtext) + init_Vtext(self) + + if \ins then VInsert(frame, self, x, y) + return self +end + +# +# Initialization +# +procedure init_Vtext(self) + local p + + /self.s := "" + /self.MaxChars := 18 + self.s ? if self.s := tab(find("\\=")) then ="\\=" & self.data := tab(0) + /self.data := "" + if *self.data > self.MaxChars then self.data := self.data[1:self.MaxChars] + self.T.DataLength := *self.data + self.T.MaxPixelSize := WAttrib(self.win, "fwidth")*self.MaxChars +# /self.T.MaxPixelSize := 250 + +## check max length by pixel size. +# if TextWidth(self.win, self.data) > self.T.MaxPixelSize then { +# t := get_pos_Vtext(self, self.T.MaxPixelSize) +# self.data := self.data[1:t] +# } +# self.T.DataLength := *self.data + self.T.DataPixelSize := TextWidth(self.win, self.data) + +## size by characters - taken out. + /self.mask := &cset + +## initialize with cursor at end + self.T.ta := self.T.tb := self.T.CursorPos := self.T.DataLength + 1 + +## initialize with all data blocked out (selected) +# self.T.ta := 1 +# self.T.tb := self.T.CursorPos := self.T.DataLength + 1 + + self.T.dx := TextWidth (self.win, self.s) + 6 + self.aw := self.T.dx + self.T.MaxPixelSize + 4 + self.ah := WAttrib(self.win, "fheight") + 6 # 4 for bevel, 2 for I-bar + self.T.dy := self.ah - 3 - WAttrib(self.win, "descent") + + p := \self.callback + self.callback := Vcoupler() + add_clients_Vinit(self.callback, p, self) +end + +# +# Reconfigure the text vidget. +# +procedure resize_Vtext(s, x, y, w, h) + s.T.dx := TextWidth (s.win, s.s) + 6 + s.T.DataLength := *s.data + s.T.MaxPixelSize := WAttrib(s.win, "fwidth") * s.MaxChars + w := s.aw := s.T.dx + s.T.MaxPixelSize + 4 + h := s.ah := WAttrib(s.win, "fheight") + 6 + resize_Vidget(s, x, y, w, h) +end + +# +# Draw the prompt, the data, outline the data area, then draw +# the cursor if it was already on previous to calling this +# procedure (happens with dialog boxes and resize events). +# +procedure draw_Vtext(self) + local t + + t := self.T.CursorOn + self.T.CursorOn := &null + draw_prompt_Vtext(self) + draw_data_Vtext(self) + outline_Vtext(self) + if \t then draw_cursor_Vtext(self) +end + +# +# Outline the data field. +# +procedure outline_Vtext(self) + + BevelRectangle(self.win, self.ax+self.T.dx-4, self.ay, + self.aw-(self.T.dx-4), self.ah, -2) +end + +# +# Draw the prompt. +# +procedure draw_prompt_Vtext(self) + GotoXY(self.win, self.ax, self.ay+self.T.dy) + writes(self.win, self.s) + return +end + +# +# Since the cursor is drawn in "reverse" mode, erase it only if it +# is "on" upon entering this procedure. +# +procedure erase_cursor_Vtext(self) + local ocx, cy + + if /self.T.CursorOn then fail + ocx := self.T.OldCursorPos + +## bracket cursor + WAttrib(self.win, "drawop=reverse", "linewidth=1") + DrawSegment(self.win, \ocx-2, self.ay+2, ocx+2, self.ay+2, + ocx, self.ay+3, ocx, self.ay+self.ah-4, + ocx-2, self.ay+self.ah-3, ocx+2, self.ay+self.ah-3) + WAttrib(self.win, "drawop=copy") + self.T.CursorOn := &null +end + +# +# Draw the cursor only if it was previously "off" at this location. +# +procedure draw_cursor_Vtext(self) + local ocx, cx, cy + + if \self.T.CursorOn then fail + cx := self.ax+self.T.dx + get_pixel_pos_Vtext(self, self.T.CursorPos) - 1 +## bracket cursor + WAttrib(self.win, "drawop=reverse", "linewidth=1") + DrawSegment(self.win, cx-2, self.ay+2, cx+2, self.ay+2, + cx, self.ay+3, cx, self.ay+self.ah-4, + cx-2, self.ay+self.ah-3, cx+2, self.ay+self.ah-3) + WAttrib(self.win, "drawop=copy") + self.T.OldCursorPos := cx + self.T.CursorOn := 1 +end + +# +# De-block the data (reset ta and tb to CursorPos). +# +procedure unblock_Vtext(self) + self.T.ta := self.T.CursorPos := self.T.tb + draw_data_Vtext(self) +end + +# +# Block (select) all the data +# +procedure block_Vtext(self) + self.T.ta := 1 + self.T.tb := self.T.CursorPos := self.T.DataLength + 1 + draw_data_Vtext(self) + if self.T.DataLength = 0 then + draw_cursor_Vtext(self) +end + +# +# Draw the data, reversing that text that lies between ta and tb +# fields. +# +procedure draw_data_Vtext(self) + +# if self.T.ta = self.T.tb then return + erase_cursor_Vtext(self) + GotoXY(self.win, self.ax+self.T.dx, self.ay+self.T.dy) + if self.T.ta <= self.T.tb then { + writes(self.win, self.data[1:self.T.ta]) + WAttrib(self.win, "reverse=on") + writes(self.win, self.data[self.T.ta:self.T.tb]) + WAttrib(self.win, "reverse=off") + writes(self.win, self.data[self.T.tb:0]) + } + else { + writes(self.win, self.data[1:self.T.tb]) + WAttrib(self.win, "reverse=on") + writes(self.win, self.data[self.T.tb:self.T.ta]) + WAttrib(self.win, "reverse=off") + writes(self.win, self.data[self.T.ta:0]) + } + EraseArea(self.win, self.ax+self.T.dx+self.T.DataPixelSize, self.ay+2, + self.aw-(self.T.dx +self.T.DataPixelSize+1), self.ah-4) + return +end + +# +# Wow. Mouse events, block out text, key presses, enter, delete +# etcetera stuff. Call callback if linefeed key or return key +# is pressed. +# +procedure event_Vtext(self, e, x, y) + static ota + local otb, rv + + if \self.callback.locked then fail + /x := &x; /y := &y + self.T.DataLength := *self.data + if e === (&lpress|&mpress|&rpress) then { + WAttrib(self.win, "pointer=xterm") + otb := self.T.ta := self.T.tb := self.T.CursorPos := + get_pos_Vtext(self, &x-(self.ax+self.T.dx)) + if otb = self.T.DataLength+1 & otb = \ota then + self.T.ta := 1 + draw_data_Vtext(self) + draw_cursor_Vtext(self) + until e === (&lrelease|&mrelease|&rrelease) do { + self.T.tb := get_pos_Vtext(self, &x-(self.ax+self.T.dx)) + if otb ~= self.T.tb then { + draw_data_Vtext(self) + self.T.CursorPos := self.T.tb + draw_cursor_Vtext(self) + otb := self.T.tb + } + e := Event(self.win) + } + rv := &null + WAttrib(self.win, "pointer=top left arrow") + } ## end mouse event loop + else if (not &meta) & (not (integer(e) < 0)) then { + ## it's a keypress + if rv := case e of { + "\^b" | Key_Left | Key_KP_Left: move_cursor_Vtext(self, -1) + "\^f" | Key_Right | Key_KP_Right: move_cursor_Vtext(self, 1) + "\b" | "\d": delete_left_Vtext(self) + "\^k" | "\^u" | "\^x": delete_line_Vtext(self) + (&shift & "\t") | Key_Up | Key_KP_Up: return V_PREVIOUS + "\t" | Key_Down | Key_KP_Down: return V_NEXT + "\r" | "\l": { + self.callback.V.set(self.callback, self, self.data) + V_NEXT + } + default: insert_char_Vtext(self, e) + } + then { + draw_data_Vtext(self) + draw_cursor_Vtext(self) + self.T.ta := self.T.tb := self.T.CursorPos + } + } + else + fail # not our event + + ota := self.T.ta + return rv +end + +# Move the cursor one way or another, determine if at bounds. +# +procedure move_cursor_Vtext(self, increment) + local t + + t := self.T.CursorPos + increment + if t < 1 | t > self.T.DataLength+1 then fail + self.T.ta := self.T.tb := self.T.CursorPos := t + return +end + +# +# Blank out the whole data field. +# +procedure delete_line_Vtext(self) + + self.data := "" + self.T.DataLength := *self.data + self.T.DataPixelSize := 0 + self.T.ta := self.T.tb := self.T.CursorPos := 1 + return +end + +# +# Get the character position based on mouse x coordinate. +# +procedure get_pos_Vtext(self, x) + local tp, c, i, j + + c := 1 + i := j := 0 + while i < x do { + j := i + i +:= TextWidth(self.win, self.data[c]) + if (c +:= 1) > self.T.DataLength then break + } + if x <= ((i + j) / 2) then + c -:= 1 # less than halfway into the char + if i < x then tp := self.T.DataLength+1 + else tp := (1 <= c) | 1 + return tp +end + +# +# Get pixel position in data field based on character position. +# +procedure get_pixel_pos_Vtext(self, CursorPos) + local sum, i + + sum := 1 + every i := 1 to CursorPos-1 do sum +:= TextWidth(self.win, self.data[i]) + return sum +end + +# +# Insert a character; could replace blocked out text. Check if +# insertion will go over bounds. +# +procedure insert_char_Vtext(self, c) + + if *c > 1 then + fail # this isn't a character + + if TextWidth(self.win, c) == 0 then + fail # not displayable + + if (self.T.DataLength - abs(self.T.ta-self.T.tb) + 1) > self.MaxChars | + not (c ? any(self.mask)) then fail + + if self.T.ta ~= self.T.tb then + change_data_Vtext(self, c) + else + self.data := self.data[1:self.T.CursorPos] || c || + self.data[self.T.CursorPos:0] + self.T.DataLength := *self.data + self.T.DataPixelSize := TextWidth(self.win, self.data) + self.T.CursorPos +:= 1 + return +end + +# +# Replace a character at current position. +# +procedure change_data_Vtext(self, c) + if self.T.tb < self.T.ta then { + self.data := self.data[1:self.T.tb] || (\c | "") || + self.data[self.T.ta:0] + self.T.ta := self.T.CursorPos := self.T.tb + } + else { + self.data := self.data[1:self.T.ta] || (\c | "") || + self.data[self.T.tb:0] + self.T.tb := self.T.CursorPos := self.T.ta + } +end + +# +# Delete the character to the left of the cursor. +# +procedure delete_left_Vtext(self) + if self.T.ta ~= self.T.tb then { + change_data_Vtext(self) + self.T.DataPixelSize := TextWidth(self.win, self.data) + return + } + else + if self.T.CursorPos > 1 then { + self.data := self.data[1:self.T.CursorPos-1] || + self.data[self.T.CursorPos:0] + self.T.DataPixelSize := TextWidth(self.win, self.data) + self.T.CursorPos -:= 1 + return + } +end + +# +# Set the data field to value passed in. +# NOTE: doesn't pass it through mask right now. +# Call callback if value if different from internal coupler's +# value. +# +procedure couplerset_Vtext(self, caller, value) + local data + + data := string(\value) | "" + self.data := data + if *self.data > self.MaxChars then self.data := self.data[1:self.MaxChars] + self.T.DataLength := *self.data + self.T.DataPixelSize := TextWidth(self.win, self.data) + +## initialize with cursor at end + self.T.ta := self.T.tb := self.T.CursorPos := self.T.DataLength + 1 + +## initialize with all data blocked out (selected) +# self.T.ta := 1 +# self.T.tb := self.T.CursorPos := self.T.DataLength + 1 + + draw_data_Vtext(self) + + if numeric(value) then { + if value = \self.T.NumericData then fail + self.T.NumericData := value + } + else if data === self.data then fail + self.callback.V.set(self.callback, caller, value) +# draw_cursor_Vtext(self) +end + +# +# Call couplerset to set value. +# +procedure set_value_Vtext(self, value) + couplerset_Vtext(self, , value) + return +end |