-module(swEntry). -export([make/5]). -include("sw.hrl"). -import(ex11_lib,[eCopyArea/9, eFreeGC/1, eMapWindow/1, ePolyFillRectangle/3, ePolyText8/5, mkRectangle/4, rpc/2, xClearArea/1, xColor/2, xCreateGC/2, xCreatePixmap/4, xCreateSimpleWindow/10, xDo/2, xFlush/1, xGC/2, xGetVar/2, xSendMeAllEvents/2, xSetInputFocus/1, xSetVar/3, xVar/2]). -import(dict, [store/3]). -import(lists, [reverse/2, seq/2]). -define(CursorWidth, 2). -define(CursorHt, 14). -define(EntryColor, ?white). -define(BlinkTime, 800). -define(TextColor, ?DarkBlue). -record(e,{max,str,n,width,ht}). %% entries %% There is only ONE place per window where input can be %% accepted %% There is ONE blinking inpout area per window per window %% Variables %% {blinker,Win} => Pid %% {entry, E} => {Txt,Width,N} make(Parent, X, Y, Width, Str) -> spawn_link(fun() -> init(Parent, X, Y, Width, Str) end). init(Parent, X, Y, Width, Str) -> Display = rpc(Parent, display), Attach = rpc(Parent, mountPoint), ensure_blinker(Display), Wargs = #win{x=X, y=Y, parent=Attach,border=0,width=Width,ht=28, color=?white, type=button, mask = ?EVENT_EXPOSURE bor ?EVENT_BUTTON_PRESS bor ?EVENT_KEY_PRESS}, Wargs1 = sw:mkWindow(Display, Parent, Wargs), #win{width=Width,ht=Ht, win=Entry} = Wargs1, Max = (trunc((Width-4) div 9)), Data = #e{max=Max,str=Str,n=length(Str),width=Width,ht=Ht}, B = [ePolyText8(Entry, xGC(Display, "text"), 4, 15, Str)| sw:sunken_frame(Display, Entry, Width, Ht)], Draw = fun() -> xDo(Display, B), xFlush(Display) end, Draw(), %% setup some handlers loop(Display, Wargs1, Draw, Data, fun(_) -> void end). loop(Display, Wargs, Draw, Data, Fun) -> receive {event,_,buttonPress, {_,X,Y,_,_}} -> %% If there was a blinker running in another window then kill it #win{win=Entry} = Wargs, %% io:format("We have entered entry:~p at coords ~p ~p ~p ~n", %% [Entry, X, Y, Data]), #e{str=Str} = Data, xDo(Display, xSetInputFocus(Entry)), I = nearest_character(X, length(Str)), XX = 9*I+5, YY = 4, blink(Display, Entry, XX, YY), Data1 = Data#e{n=I}, loop(Display, Wargs, Draw, Data1, Fun); {event,_,expose, _} -> Draw(), loop(Display, Wargs, Draw, Data, Fun); {event, _, keyPress, X} -> Cmd = ex11_lib_keyboard_driver:analyse(X), #win{win=Entry} = Wargs, #e{str=Str,n=N,max=Max} = Data, case do_cmd(Cmd, Str, N, Max, Fun) of {N1, Str1} -> %% io:format("Here {N1,Str1}=~p~n",[{N1,Str1}]), Data1 = Data#e{n=N1,str=Str1}, Draw1 = fix_entry(Display, Entry, Data1, Fun), loop(Display, Wargs, Draw1, Data1, Fun); nothing -> loop(Display, Wargs, Draw, Data, Fun) end; {onReturn, Fun1} -> loop(Display, Wargs, Draw, Data, Fun1); {set, Str} -> #win{win=Entry} = Wargs, Data1 = Data#e{str=Str,n=length(Str)}, Draw1 = fix_entry(Display, Entry, Data1, Fun), loop(Display, Wargs, Draw1, Data1, Fun); {From, read} -> Str = Data#e.str, From ! {self(), Str}, loop(Display, Wargs, Draw, Data, Fun); {'EXIT', _, _} -> true; Other -> Wargs1 = sw:generic(Other, Display, Wargs), loop(Display, Wargs1, Draw, Data, Fun) end. fix_entry(Display, Entry, Data, Fun) -> #e{width=Width, n=N, ht=Ht, str=Str} = Data, B = [ePolyText8(Entry, xGC(Display, "text"), 4, 15, Str)| sw:sunken_frame(Display, Entry, Width, Ht)], Draw = fun() -> xDo(Display, B) end, xDo(Display, xClearArea(Entry)), Draw(), XX = 9*N+5, YY = 4, blink(Display, Entry, XX, YY), Draw. %%---------------------------------------------------------------------- %% keypress handler do_cmd({_,_,_,{char,13}}, Str, N, Max, Fun) -> spawn(fun() -> Fun(Str) end), nothing; do_cmd({_,_,_,{char,8}}, Str, 0, Max,_) -> % backspace nothing; do_cmd({_,_,_,{char,8}}, Str, N, Max,_) -> % backspace {[_|Before], After} = split(Str, N, []), {N-1, reverse(Before, After)}; do_cmd({_,_,Tag,{char,C}}, Str, N, Max, _) when Tag==none; Tag == shift-> insert_char(C, Str, N, Max); do_cmd({_,_,_, {cmd,left}}, Str, N, Max, _) when N > 0 -> {N-1, Str}; do_cmd({_,_,_, {keypad,left}}, Str, N, Max, _) when N > 0 -> {N-1, Str}; do_cmd({_,_,_, {cmd,right}}, Str, N, Max, _) when N < length(Str), N < Max -> {N+1, Str}; do_cmd({_,_,_, {keypad,right}}, Str, N, Max, _) when N < length(Str), N < Max -> {N+1, Str}; do_cmd(C, _, _, _, _) -> %% io:format("dropping:~p~n",[C]), nothing. insert_char(C, Str, N, Max) when length(Str) < Max -> {Before,After} = split(Str, N, []), Str1 = reverse(Before, [C|After]), {N+1, Str1}; insert_char(C, Str, N, Max) -> {N, Str}. split(Str, 0, L) -> {L, Str}; split([H|T], N, L) -> split(T, N-1, [H|L]). nearest_character(X, Max) when X < 0 -> 0; nearest_character(X, Max) -> N = (X-4) div 9, if N > Max -> Max; true -> N end. %%------------------------------------------------------------------------ %% The blinking cursor ... :-) %% This is incoorect Test or startr elseswere %% we might get two of them going ... ensure_blinker(Display) -> case xGetVar(Display, blinker) of {ok, Pid} -> Pid; error -> Pid = spawn(fun() -> blinker_start(Display) end), xSetVar(Display, blinker, Pid) end. blink(Display, Entry, X, Y) -> case xGetVar(Display, blinker) of {ok, Pid} -> Pid ! {blink, Entry, X, Y}; error -> Pid = spawn(fun() -> blinker_start(Display) end), xSetVar(Display, blinker, Pid), Pid ! {blink, Entry, X, Y} end. blinker_start(Display) -> %% create a Pixmap to draw the cursor %% this is filled rectangle containing the text color Win = xVar(Display, gcParent), DrawCursor = xCreatePixmap(Display, Win, ?CursorWidth, ?CursorHt), GC0 = xCreateGC(Display, [{function,copy}, {line_width,2}, {line_style,solid}, {graphics_exposures, false}, {foreground, xColor(Display, ?TextColor)}]), xDo(Display, ePolyFillRectangle(DrawCursor, GC0, [mkRectangle(0,0,?CursorWidth, ?CursorHt)])), %% create a PixMap to erase the cursor %% this is a filled rectangle containing the background color of %% the entry ClearCursor = xCreatePixmap(Display, Win, ?CursorWidth, ?CursorHt), GC1 = xCreateGC(Display, [{function,copy}, {line_width,2}, {line_style,solid}, {graphics_exposures, false}, {foreground, xColor(Display, ?EntryColor)}]), xDo(Display, ePolyFillRectangle(ClearCursor, GC1, [mkRectangle(0,0,?CursorWidth, ?CursorHt)])), %% free the GC xDo(Display, eFreeGC(GC1)), xFlush(Display), blinker(Display, DrawCursor, ClearCursor, GC0). blinker(Display, On, Off, GC0) -> receive {blink, Entry, X, Y} -> setCursor(Display, Entry, On, X, Y, GC0), blinking(Display, Entry, On, Off, true, X, Y, GC0) end. blinking(Display, Entry, On, Off, State, X, Y, GC0) -> receive {blink, Entry1, X1, Y1} -> case State of true -> setCursor(Display, Entry, Off, X, Y, GC0); false -> void end, setCursor(Display, Entry1, On, X1, Y1, GC0), blinking(Display, Entry1, On, Off, true, X1, Y1, GC0) after ?BlinkTime -> case State of true -> setCursor(Display, Entry, Off, X, Y, GC0); false -> setCursor(Display, Entry, On, X, Y, GC0) end, blinking(Display, Entry, On, Off, not State, X, Y, GC0) end. %% setCursor(Entry, N, PixMap, W, H) -> %% set the cursor after the N'th character %%% X = 9*N+4, Y = 2, %% xDo(eCopyArea(PixMap,Entry,xGC("text"),0,0,X, Y,W, H)). %% Note the GC used in setCursor has graphics_exposurs set to false %% this is to turn of update events caused then the Cursor pixmap writes %% onto the drawable - I *know* the underlying text is OK :-) setCursor(Display, Entry, PixMap, X, Y, GC) -> xDo(Display, eCopyArea(PixMap,Entry,GC,0,0,X,Y,?CursorWidth, ?CursorHt)), xFlush(Display).