1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2008-2019. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21%%
22-module(dbg_wx_trace_win).
23
24%% External exports
25-export([init/0, stop/1]).
26-export([create_win/4,
27	 get_window/1,
28	 configure/2,
29	 enable/2, is_enabled/1, select/2,
30	 add_break/3, update_break/2, delete_break/2,
31	 clear_breaks/1, clear_breaks/2,
32	 display/2,                                   % Help messages
33	 is_shown/2,                                  % Code area
34	 show_code/3, show_no_code/1, remove_code/2,
35	 mark_line/3, unmark_line/1,
36	 select_line/2, selected_line/1,
37	 eval_output/3, eval_output/4, eval_output/5, % Evaluator area
38	 update_bindings/3,                           % Bindings area
39         update_strings/1,
40	 trace_output/2,                              % Trace area
41	 handle_event/2
42	]).
43-export([helpwin/2]).
44
45-include_lib("wx/include/wx.hrl").
46
47-record(breakInfo, {point, status, break}).
48-record(break, {mb, smi, emi, dimi, demi}).  %% BUGBUG defined in dbg_wx_win
49-record(winInfo, {window,          % wxobj()
50		  size,            % {W, H}
51		  find,            % #find{}
52		  m_szr,           % {Panel, Sizer},
53		  e_szr,           % {bool Shown, Sizer},
54
55		  code,            % code editor #sub{}
56		  sb,              % status bar
57		  sg,              % Search/Goto #sub{}
58		  bs,              % Buttons #sub{} subwindow info
59		  eval,            % Eval #sub{} subwindow info
60		  bind,            % Bindings #sub{} subwindow info
61		  trace,           % Trace #sub{} subwindow info
62
63		  marked_line=0,   % integer() Current line
64		  selected_line=0, % integer() Selected line
65
66		  breaks=[],       % [#breakInfo{}] Known breakpoints
67
68		  editor,          % {Mod, Editor}  Visible code editor
69		  editors=[]       % [{Mod,Editor}] Code editors
70		 }).
71
72-record(sub, {enable=true,         %  Subwindow is enabled
73	      win,                 %  Sash Sub window obj
74	      in,                  %  undefined or input window obj
75	      out,                 %  undefined or output window obj
76	      name                 %  name
77	     }).
78
79-record(sa, {search,               %  Search input ctrl
80	     goto,                 %  Goto  input ctrl
81	     radio}).              %  Radio buttons
82
83-record(find, {start,              % start pos
84	       strlen,             % search string len
85	       found               % status
86	      }).
87
88
89-define(StepButton,     401).
90-define(NextButton,     402).
91-define(ContinueButton, 403).
92-define(FinishButton,   404).
93-define(WhereButton,    405).
94-define(UpButton,       406).
95-define(DownButton,     407).
96
97-define(EVAL_ENTRY,     410).
98-define(EVAL_LOG,       411).
99-define(BIND_PANEL,     412).
100-define(SEARCH_ENTRY,   413).
101-define(GOTO_ENTRY,     414).
102
103
104-define(SASH_CODE,      425).
105-define(SASH_EVAL,      426).
106-define(SASH_TRACE,     427).
107
108-define(WIN_W,          700).
109-define(WIN_H,          650).
110
111-define(CODE_H,         400).
112-define(BUTT_H,          50).  % Maximum
113-define(EVAL_H,         200).
114-define(BIND_H,         200).
115-define(TRACE_H,        100).
116
117%%====================================================================
118%% External exports
119%%====================================================================
120
121%%--------------------------------------------------------------------
122%% init() -> GS
123%%   GS = term()
124%%--------------------------------------------------------------------
125init() ->
126    _ = dbg_wx_win:init(),
127    ok.
128
129stop(#winInfo{window=Win}) ->
130    (catch wxFrame:destroy(Win)),
131    ok.
132
133%%--------------------------------------------------------------------
134%% create_win(GS, Title, TraceWin, Menus) -> #winInfo{}
135%%  GS = gsobj()
136%%  Title = string()
137%%  TraceWin = [WinArea]
138%%    WinArea = 'Button|Evaluator|Bindings|Trace Area'
139%%  Menus = [menu()]  See dbg_wx_win.erl
140%%--------------------------------------------------------------------
141create_win(Parent, Title, Windows, Menus) ->
142    Do =
143	fun() ->
144		Win = wxFrame:new(Parent, ?wxID_ANY, dbg_wx_win:to_string(Title),
145				  [{size, {?WIN_W,?WIN_H}}]),
146		Panel = wxPanel:new(Win, [{size, {?WIN_W,?WIN_H}}]),
147		MenuBar = wxMenuBar:new(),
148		dbg_wx_win:create_menus(MenuBar, Menus, Win, 1),
149		wxFrame:setMenuBar(Win, MenuBar),
150
151		Sizer = wxBoxSizer:new(?wxVERTICAL),
152		Code = code_area(Panel),
153		_ = wxSizer:add(Sizer, Code#sub.win,
154			    [{proportion,1}, {border, 2},
155			     {flag, ?wxEXPAND bor ?wxDOWN}]),
156		wxSizer:setVirtualSizeHints(Sizer, Code#sub.win),
157
158		ExpandWithBorder = [{border, 3},{flag,?wxEXPAND bor ?wxALL}],
159		Search = search_area(Panel),
160		_ = wxSizer:add(Sizer, Search#sub.win, ExpandWithBorder),
161		Bs     = button_area(Panel),
162		_ = wxSizer:add(Sizer, Bs#sub.win, ExpandWithBorder),
163
164		InfoArea = wxBoxSizer:new(?wxHORIZONTAL),
165		wxSizer:setMinSize(InfoArea, {100, ?EVAL_H}),
166		Eval  = eval_area(Panel),
167		_ = wxSizer:add(InfoArea, Eval#sub.win, [{proportion,1},{flag,?wxEXPAND}]),
168		Bind  = bind_area(Panel),
169		_ = wxSizer:add(InfoArea, Bind#sub.win,
170			    [{proportion,1},{border, 2},
171			     {flag,?wxEXPAND bor ?wxLEFT}]),
172		_ = wxSizer:add(Sizer, InfoArea, ExpandWithBorder),
173
174		Trace = trace_area(Panel),
175		_ = wxSizer:add(Sizer, Trace#sub.win, ExpandWithBorder),
176		SB    = wxFrame:createStatusBar(Win,[]),
177
178		%% Note id and lastId to get the event when it dragged is complete
179		wxFrame:connect(Win, sash_dragged, [{id,?SASH_CODE},
180						    {lastId,?SASH_TRACE}]),
181		wxFrame:connect(Win, close_window, [{skip, true}]),
182		wxFrame:connect(Win, size, [{skip, true}]),
183		wxWindow:connect(Win, key_up, [{skip,true}]),
184		wxWindow:setFocus(Code#sub.out),
185
186		Wi0 = #winInfo{window=Win,
187			       m_szr={Panel, Sizer},
188			       e_szr={true, InfoArea},
189			       code=Code, sb=SB, sg=Search, bs=Bs,
190			       eval=Eval, trace=Trace, bind=Bind,
191			       editor={'$top', Code#sub.out},
192			       editors=[{'$top', Code#sub.out}]},
193
194		Wi = show_windows(enable_windows(Wi0,Windows)),
195		wxWindow:setSizer(Panel, Sizer),
196		_ = wxSizer:fit(Sizer, Win),
197		wxSizer:setSizeHints(Sizer,Win),
198
199		IconFile = dbg_wx_win:find_icon("erlang_bug.png"),
200		Icon = wxIcon:new(IconFile, [{type,?wxBITMAP_TYPE_PNG}]),
201		wxFrame:setIcon(Win, Icon),
202		wxIcon:destroy(Icon),
203
204		wxFrame:show(Win),
205		put(window, Win),
206                put(strings, [str_on]),
207		Wi
208        end,
209
210    try wx:batch(Do)
211    catch E:R ->
212	    io:format("Crashed ~p ~p",[E,R]),
213	    erlang:error(E)
214    end.
215
216
217%%--------------------------------------------------------------------
218%% get_window(WinInfo) -> Window
219%%   WinInfo = #winInfo{}
220%%   Window = gsobj()
221%%--------------------------------------------------------------------
222get_window(WinInfo) ->
223    WinInfo#winInfo.window.
224
225%%--------------------------------------------------------------------
226%% configure(WinInfo, Windows) -> WinInfo
227%%   WinInfo = #winInfo{}
228%%  Windows = [WinArea]
229%%    WinArea = 'Button|Evaluator|Bindings|Trace Area'
230%% Window areas should be opened or closed.
231%%--------------------------------------------------------------------
232configure(Wi=#winInfo{window=Win,m_szr={Panel,Sizer}}) ->
233    wx:batch(fun() ->
234		     _ = show_windows(Wi),
235		     wxSizer:layout(Sizer),
236		     %%wxWindow:setSizerAndFit(Panel,Sizer),
237		     wxWindow:setSizer(Panel, Sizer),
238		     _ = wxSizer:fit(Sizer, Win),
239		     wxSizer:setSizeHints(Sizer,Win),
240		     Wi
241	     end).
242
243configure(Wi0=#winInfo{window=Win,m_szr={Panel,Sizer}}, Windows) ->
244    wx:batch(fun() ->
245		     Wi = enable_windows(Wi0, Windows),
246		     _ = show_windows(Wi),
247		     wxSizer:layout(Sizer),
248		     wxWindow:setSizer(Panel, Sizer),
249		     _ = wxSizer:fit(Sizer, Win),
250		     wxSizer:setSizeHints(Sizer,Win),
251		     Wi
252	     end).
253
254enable_windows(Wi=#winInfo{e_szr={_,InfoArea},bs=Bs0,sg=SG0,
255			   eval=Eval0,trace=Trace0,bind=Bind0},Windows) ->
256    Subs  = [Window#sub{enable=lists:member(Window#sub.name,Windows)}
257	     || Window <- [SG0,Bs0,Eval0,Trace0,Bind0]],
258    [SG, Bs,Eval,Trace,Bind] = Subs,
259    ESzr = Eval#sub.enable orelse Bind#sub.enable,
260    Wi#winInfo{e_szr={ESzr, InfoArea},sg=SG,bs=Bs,
261	       eval=Eval,trace=Trace,bind=Bind}.
262
263
264show_windows(Wi=#winInfo{m_szr={_,Sizer}, e_szr={_,InfoArea},bs=Bs,sg=SG,
265		      eval=Eval,trace=Trace,bind=Bind}) ->
266    case SG#sub.enable of
267	false -> wxSizer:hide(Sizer, SG#sub.win);
268	_ ->     wxSizer:show(Sizer, SG#sub.win)
269    end,
270    case Bs#sub.enable of
271	false -> wxSizer:hide(Sizer, Bs#sub.win);
272	_ ->     wxSizer:show(Sizer, Bs#sub.win)
273    end,
274    if (not Eval#sub.enable) andalso (not Bind#sub.enable) ->
275	    wxSizer:hide(Sizer, InfoArea);
276       not Eval#sub.enable  ->
277	    wxSizer:show(Sizer, InfoArea),
278	    wxSizer:hide(InfoArea, Eval#sub.win),
279	    wxSizer:show(InfoArea, Bind#sub.win);
280       not Bind#sub.enable ->
281	    [EvalSI|_] = wxSizer:getChildren(InfoArea),
282	    wxSizerItem:setProportion(EvalSI, 1),
283	    wxSizer:show(Sizer, InfoArea),
284	    wxSizer:hide(InfoArea, Bind#sub.win),
285	    wxSizer:show(InfoArea, Eval#sub.win),
286	    true;
287       true ->
288	    wxSizer:show(Sizer, InfoArea),
289	    wxSizer:show(InfoArea, Eval#sub.win),
290	    wxSizer:show(InfoArea, Bind#sub.win)
291    end,
292    case Trace#sub.enable of
293	false -> wxSizer:hide(Sizer, Trace#sub.win);
294	_ ->     wxSizer:show(Sizer, Trace#sub.win)
295    end,
296    Wi.
297
298%%--------------------------------------------------------------------
299%% enable([MenuItem], Bool)
300%% is_enabled(MenuItem) -> Bool
301%%   MenuItem = atom()
302%%   Bool = boolean()
303%%--------------------------------------------------------------------
304enable(MenuItems, Bool) ->
305    wx:foreach(fun(MenuItem) ->
306		       MI = get(MenuItem),
307		       wxMenuItem:enable(MI, [{enable, Bool}]),
308		       case is_button(MenuItem) of
309			   {true, ButtonId} ->
310			       Parent = get(window),
311			       Butt = wxWindow:findWindowById(ButtonId,
312							      [{parent, Parent}]),
313			       case wx:is_null(Butt) of
314				   true -> ignore;
315				   false ->
316				       wxButton:enable(Butt, [{enable, Bool}])
317			       end;
318			   _ ->
319			       ignore
320		       end
321	       end,
322	       MenuItems).
323
324is_enabled(MenuItem) ->
325    MI = get(MenuItem),
326    wxMenuItem:isEnabled(MI).
327
328%%--------------------------------------------------------------------
329%% select(MenuItem, Bool)
330%%   MenuItem = atom()
331%%   Bool = boolean()
332%%--------------------------------------------------------------------
333select(MenuItem, Bool) ->
334    MI = get(MenuItem),
335    wxMenuItem:check(MI, [{check, Bool}]).
336
337%%--------------------------------------------------------------------
338%% add_break(WinInfo, Name, {Point, Options}) -> WinInfo
339%%   WinInfo = #winInfo{}
340%%   Name = atom() Menu name
341%%   Point = {Mod, Line}
342%%   Options = [Status, Action, Mods, Cond]
343%%     Status = active | inactive
344%%     Action = enable | disable | delete
345%%     Mods = null (not used)
346%%     Cond = null | {Mod, Func}
347%%--------------------------------------------------------------------
348add_break(WinInfo, Menu, {{Mod,Line},[Status|_Options]}=Break) ->
349    case WinInfo#winInfo.editor of
350	{Mod, Editor} ->
351	    dbg_wx_code:add_break_to_code(Editor, Line, Status);
352	_ -> ok
353    end,
354    add_break_to_menu(WinInfo, Menu, Break).
355
356add_break_to_menu(WinInfo, Menu, {Point, [Status|_Options]=Options}) ->
357    Break = dbg_wx_win:add_break(WinInfo#winInfo.window, Menu, Point),
358    dbg_wx_win:update_break(Break, Options),
359    BreakInfo = #breakInfo{point=Point, status=Status, break=Break},
360    WinInfo#winInfo{breaks=[BreakInfo|WinInfo#winInfo.breaks]}.
361
362%%--------------------------------------------------------------------
363%% update_break(WinInfo, {Point, Options}) -> WinInfo
364%%   WinInfo = #winInfo{}
365%%   Point = {Mod, Line}
366%%   Options = [Status, Action, Mods, Cond]
367%%     Status = active | inactive
368%%     Action = enable | disable | delete
369%%     Mods = null (not used)
370%%     Cond = null | {Mod, Func}
371%%--------------------------------------------------------------------
372update_break(WinInfo, {{Mod,Line},[Status|_Options]}=Break) ->
373    case WinInfo#winInfo.editor of
374	{Mod, Editor} ->
375	    dbg_wx_code:add_break_to_code(Editor, Line, Status);
376	_ -> ok
377    end,
378    update_break_in_menu(WinInfo, Break).
379
380update_break_in_menu(WinInfo, {Point, [Status|_Options]=Options}) ->
381    {value, BreakInfo} = lists:keysearch(Point, #breakInfo.point,
382					 WinInfo#winInfo.breaks),
383    dbg_wx_win:update_break(BreakInfo#breakInfo.break, Options),
384    BreakInfo2 = BreakInfo#breakInfo{status=Status},
385    WinInfo#winInfo{breaks=lists:keyreplace(Point, #breakInfo.point,
386					    WinInfo#winInfo.breaks,
387					    BreakInfo2)}.
388
389%%--------------------------------------------------------------------
390%% delete_break(WinInfo, Point) -> WinInfo
391%%   WinInfo = #winInfo{}
392%%   Point = {Mod, Line}
393%%--------------------------------------------------------------------
394delete_break(WinInfo, {Mod,Line}=Point) ->
395    case WinInfo#winInfo.editor of
396	{Mod, Editor} -> dbg_wx_code:del_break_from_code(Editor, Line);
397	_ -> ignore
398    end,
399    delete_break_from_menu(WinInfo, Point).
400
401delete_break_from_menu(WinInfo, Point) ->
402    {value, BreakInfo} = lists:keysearch(Point, #breakInfo.point,
403					 WinInfo#winInfo.breaks),
404    dbg_wx_win:delete_break(BreakInfo#breakInfo.break),
405    WinInfo#winInfo{breaks=lists:keydelete(Point, #breakInfo.point,
406					   WinInfo#winInfo.breaks)}.
407
408%%--------------------------------------------------------------------
409%% clear_breaks(WinInfo) -> WinInfo
410%% clear_breaks(WinInfo, Mod) -> WinInfo
411%%   WinInfo = #winInfo{}
412%%--------------------------------------------------------------------
413clear_breaks(WinInfo) ->
414    clear_breaks(WinInfo, all).
415clear_breaks(WinInfo, Mod) ->
416    Remove = if
417		 Mod =:= all -> WinInfo#winInfo.breaks;
418		 true ->
419		     lists:filter(fun(#breakInfo{point={Mod2,_L}}) ->
420					  if
421					      Mod2 =:= Mod -> true;
422					      true -> false
423					  end
424				  end,
425				  WinInfo#winInfo.breaks)
426	     end,
427    lists:foreach(fun(#breakInfo{point=Point}) ->
428			  delete_break(WinInfo, Point)
429		  end,
430		  Remove),
431    Remain = WinInfo#winInfo.breaks -- Remove,
432    WinInfo#winInfo{breaks=Remain}.
433
434%%--------------------------------------------------------------------
435%% display(Arg)
436%%   Arg = idle | {Status,Mod,Line} | {running,Mod}
437%%       | {exit,Where,Reason} | {text,Text}
438%%     Status = break | wait | Level
439%%       Level = int()
440%%     Mod = atom()
441%%     Line = integer()
442%%     Where = {Mod,Line} | null
443%%     Reason = term()
444%%     Text = string()
445%%--------------------------------------------------------------------
446display(#winInfo{window=Win, sb=Sb},Arg) ->
447    Str = case Arg of
448	      idle -> "State: uninterpreted";
449	      {exit, {Mod,Line}, Reason} ->
450		  wxWindow:raise(Win),
451		  dbg_wx_win:to_string("State: EXITED [~w.erl/~w], Reason:~w",
452				       [Mod, Line, Reason]);
453	      {exit, null, Reason} ->
454		  wxWindow:raise(Win),
455		  dbg_wx_win:to_string("State: EXITED [uninterpreted], "
456				       "Reason:~w", [Reason]);
457	      {Level, null, _Line} when is_integer(Level) ->
458		  dbg_wx_win:to_string("*** Call level #~w "
459				       "(in non-interpreted code)",
460				       [Level]);
461	      {Level, Mod, Line} when is_integer(Level) ->
462		  dbg_wx_win:to_string("*** Call level #~w [~w.erl/~w]",
463				       [Level, Mod, Line]);
464	      {Status, Mod, Line} ->
465		  What = case Status of
466			     wait -> 'receive';
467			     _ -> Status
468			 end,
469		  dbg_wx_win:to_string("State: ~w [~w.erl/~w]",
470				       [What, Mod, Line]);
471	      {running, Mod} ->
472		  dbg_wx_win:to_string("State: running [~w.erl]", [Mod]);
473	      {text, Text}  -> dbg_wx_win:to_string(Text)
474	  end,
475    wxStatusBar:setStatusText(Sb, Str).
476
477%%--------------------------------------------------------------------
478%% is_shown(WinInfo, Mod) -> {true, WinInfo} | false
479%% show_code(WinInfo, Mod, Contents) -> WinInfo
480%% show_no_code(WinInfo) -> WinInfo
481%% remove_code(WinInfo, Mod) -> WinInfo
482%%   WinInfo = #winInfo{}
483%%   Mod = atom()
484%%   Contents = string()
485%% Note: remove_code/2 should not be used for currently shown module.
486%%--------------------------------------------------------------------
487is_shown(_WinInfo, _Mod) ->
488    %% Previously cached modules here, nyi so return false
489    false.
490
491show_code(WinInfo = #winInfo{editor={_, Ed}}, Mod, Contents) ->
492    %% Insert code and update breakpoints, if any
493    dbg_wx_code:load_code(Ed, Contents),
494
495    lists:foreach(fun(BreakInfo) ->
496			  case BreakInfo#breakInfo.point of
497			      {Mod2, Line} when Mod2 =:= Mod ->
498				  Status = BreakInfo#breakInfo.status,
499				  dbg_wx_code:add_break_to_code(Ed, Line,Status);
500			      _Point -> ignore
501			  end
502		  end,
503		  WinInfo#winInfo.breaks),
504
505    WinInfo#winInfo{editor={Mod,Ed},find=undefined}.
506
507show_no_code(WinInfo = #winInfo{editor={_, Ed}}) ->
508    dbg_wx_code:unload_code(Ed),
509    WinInfo#winInfo{editor={'$top', Ed}}.
510
511remove_code(WinInfo, _Mod) ->
512    WinInfo.
513
514%%--------------------------------------------------------------------
515%% mark_line(WinInfo, Line, How) -> WinInfo
516%%   WinInfo = #winInfo{}
517%%   Line = integer()
518%%   How = break | where
519%% Mark the code line where the process is executing.
520%%--------------------------------------------------------------------
521mark_line(WinInfo = #winInfo{editor={_,Ed}}, Line, _How) ->
522    dbg_wx_code:mark_line(Ed, WinInfo#winInfo.marked_line, Line),
523    WinInfo#winInfo{marked_line=Line}.
524
525unmark_line(WinInfo) ->
526    mark_line(WinInfo, 0, false).
527
528
529%%--------------------------------------------------------------------
530%% select_line(WinInfo, Line) -> WinInfo
531%% selected_line(WinInfo) -> undefined | Line
532%%   WinInfo = #winInfo{}
533%%   Line = integer()
534%% Select/unselect a line (unselect if Line=0).
535%%--------------------------------------------------------------------
536select_line(WinInfo, Line) ->
537    {_Mod, Ed} = WinInfo#winInfo.editor,
538
539    %% Since 'Line' may be specified by the user in the 'Go To Line'
540    %% help window, it must be checked that it is correct
541    Size = dbg_wx_code:get_no_lines(Ed),
542    if
543	Line =:= 0 ->
544	    dbg_wx_code:goto_line(Ed,1),
545	    WinInfo#winInfo{selected_line=0};
546	Line < Size ->
547	    dbg_wx_code:goto_line(Ed,Line),
548	    WinInfo#winInfo{selected_line=Line};
549	true ->
550	    WinInfo
551    end.
552
553selected_line(#winInfo{editor={_,Ed}}) ->
554    wxStyledTextCtrl:getCurrentLine(Ed)+1.
555
556%%--------------------------------------------------------------------
557%% eval_output(winInfo{}, Str, Face)
558%%   Str = string()
559%%   Face = normal | bold
560%%--------------------------------------------------------------------
561eval_output(#winInfo{eval=#sub{out=Log}}, Text, bold) ->
562    Style = wxTextCtrl:getDefaultStyle(Log),
563    Font = wxTextAttr:getFont(Style),
564    wxFont:setWeight(Font, ?wxFONTWEIGHT_BOLD),
565    wxTextAttr:setFont(Style, Font),
566    wxTextCtrl:setDefaultStyle(Log, Style),
567    wxTextCtrl:appendText(Log, dbg_wx_win:to_string(Text)),
568    wxFont:setWeight(Font, ?wxFONTWEIGHT_NORMAL),
569    wxTextAttr:setFont(Style, Font),
570    wxTextCtrl:setDefaultStyle(Log, Style),
571    ok;
572eval_output(#winInfo{eval=#sub{out=Log}}, Text, _Face) ->
573    wxTextCtrl:appendText(Log, dbg_wx_win:to_string(Text)),
574    ok.
575
576%%--------------------------------------------------------------------
577%% eval_output(winInfo{}, Prefix, Term, [Mod,] Face)
578%%   Prefix = string()
579%%   Term = term   to be formatted
580%%   Mod = module() | undefined  for record formatting
581%%   Face = normal | bold
582%%--------------------------------------------------------------------
583eval_output(Wi, Prefix, Term, Face) ->
584    {Mod,_Bs} = get(bindings),
585    eval_output(Wi, Prefix, Term, Mod, Face).
586
587eval_output(#winInfo{eval=#sub{out=Log}}=Wi, Prefix, Term, Mod, Face) ->
588    {CW, _, _, _ } = wxTextCtrl:getTextExtent(Log,"w"),
589    {W, _} = wxTextCtrl:getClientSize(Log),
590    LineLength = max(40, W div CW),
591    Column = string:length(Prefix),
592    ValStr = format_term(Term, Mod, LineLength-Column, Column, -1, -1),
593    eval_output(Wi, Prefix, bold),
594    eval_output(Wi, [ValStr, "\n"], Face),
595    ok.
596
597%%--------------------------------------------------------------------
598%% update_bindings(Bs)
599%%   Bs = [{Var,Val}]
600%%--------------------------------------------------------------------
601update_bindings(#winInfo{bind=#sub{out=BA}}, Mod, Bs) ->
602    wxListCtrl:deleteAllItems(BA),
603    wx:foldl(fun({Var,Val},Row) ->
604		     wxListCtrl:insertItem(BA, Row, ""),
605		     wxListCtrl:setItem(BA, Row, 0, dbg_wx_win:to_string(Var)),
606                     Str = format_term_line(Val, Mod),
607                     wxListCtrl:setItem(BA, Row, 1, Str),
608		     Row+1
609	     end, 0, Bs),
610    put(bindings,{Mod,Bs}),
611    ok.
612
613format_term_line(Val, Mod) ->
614    format_term(Val, Mod, 0, 1, 20, 300).
615
616format_term(Val, Mod, LineLenght, Column, Depth, Limit) ->
617    RecFun = fun(Tag,NoFields) -> record_fields(Tag, NoFields, Mod) end,
618    UseStrings = case get(strings) of
619                     [] -> false;
620                     [str_on] -> true
621                 end,
622    Opts = [{line_length,LineLenght}, {depth, Depth}, {chars_limit, Limit}, {column, Column},
623            {strings, UseStrings}, {encoding, unicode}, {record_print_fun, RecFun}],
624    io_lib_pretty:print(Val, Opts).
625
626record_fields(Tag, NoFields, Mod) ->
627    case dbg_iserver:call({get_module_db, Mod}) of
628        not_found -> no;
629        ModDb ->
630            case dbg_idb:lookup(ModDb, {record, Mod, Tag, NoFields}) of
631                {ok, Value} ->
632                    Value;
633                not_found ->
634                    no
635            end
636    end.
637
638update_strings(Strings) ->
639    _ = put(strings, Strings),
640    ok.
641
642%%--------------------------------------------------------------------
643%% trace_output(Str)
644%%   Str = string()
645%%--------------------------------------------------------------------
646trace_output(#winInfo{trace=#sub{out=Log}}, Text) ->
647    wxTextCtrl:appendText(Log, dbg_wx_win:to_string(Text)),
648    ok.
649
650%%--------------------------------------------------------------------
651%% handle_event(GSEvent, WinInfo) -> Command
652%%   GSEvent = {gs, Id, Event, Data, Arg}
653%%   WinInfo = #winInfo{}
654%%   Command = ignore
655%%           | {win, WinInfo}
656%%           | stopped
657%%           | {coords, {X,Y}}
658%%
659%%           | {shortcut, Key}
660%%           | MenuItem | {Menu, [MenuItem]}
661%%               MenuItem = Menu = atom()
662%%           | {break, Point, What}
663%%               What = add | delete | {status,Status} |{trigger,Trigger}
664%%           | {module, Mod, view}
665%%
666%%           | {user_command, Cmd}
667%%
668%%           | {edit, {Var, Val}}
669%%--------------------------------------------------------------------
670%% Window events
671handle_event(_Ev=#wx{event=#wxClose{}}, _WinInfo) ->
672    stopped;
673
674handle_event(#wx{event=#wxSize{size=Size}}, Wi0) ->
675    Wi = Wi0#winInfo{size=Size},
676    resize(Wi),
677    {win, Wi};
678
679handle_event(#wx{event=#wxSash{dragStatus=?wxSASH_STATUS_OUT_OF_RANGE}},_Wi) ->
680    ignore;
681handle_event(#wx{id=?SASH_CODE, event=#wxSash{dragRect={_X,_Y,_W,H}}}, Wi) ->
682    #winInfo{code=Code,m_szr={_,Sizer},e_szr={Enable,InfoSzr},trace=Trace} = Wi,
683
684    case Enable orelse Trace#sub.enable of
685	false ->
686	    ignore;
687	true ->
688	    {_, CMH} = wxWindow:getMinSize(Code#sub.win),
689	    case CMH > H of
690		true ->  wxSashWindow:setMinSize(Code#sub.win, {500, H});
691		_ -> ignore
692	    end,
693	    {_, CH} = wxWindow:getSize(Code#sub.win),
694	    Change = CH - H,
695	    ChangeH = fun(Item) ->
696			      {ItemW, ItemH} = wxSizerItem:getMinSize(Item),
697			      wxSizerItem:setInitSize(Item, ItemW, erlang:max(ItemH+Change,-1))
698		      end,
699	    if Enable ->
700		    {IW, IH} = wxSizer:getMinSize(InfoSzr),
701		    [ChangeH(Child) || Child <- wxSizer:getChildren(InfoSzr)],
702		    wxSizer:setMinSize(InfoSzr, {IW, IH+Change}),
703		    ok;
704	       Trace#sub.enable ->
705		    {TW, TH} = wxWindow:getMinSize(Trace#sub.win),
706		    wxWindow:setMinSize(Trace#sub.win, {TW, TH+Change}),
707		    ok
708	    end,
709	    wxSizer:layout(Sizer),
710	    ignore
711    end;
712
713handle_event(#wx{id=?SASH_EVAL, event=#wxSash{dragRect={_X,_Y,W,_H}}}, Wi) ->
714    #winInfo{m_szr={_,Sizer},e_szr={Enable,InfoSzr},
715	     eval=#sub{enable=Enable, win=EvalSzr}} = Wi,
716    case Enable of
717	false ->
718	    ignore;
719	true ->
720	    [Eval,Bind] = wxSizer:getChildren(InfoSzr),
721	    {Tot,_} = wxSizer:getSize(InfoSzr),
722	    EvalWidth = Tot-W,
723
724	    Change = fun(Szr, Width) ->
725			     {_EW,EH} = wxSizerItem:getMinSize(Szr),
726			     wxSizerItem:setInitSize(Szr, Width, EH)
727		     end,
728
729	    Change(Eval, EvalWidth),
730	    [Change(Kid, EvalWidth) || Kid <- wxSizer:getChildren(EvalSzr)],
731	    Change(Bind, W),
732
733	    wxSizerItem:setProportion(Eval, 0),
734	    wxSizer:layout(InfoSzr),
735	    wxSizer:layout(Sizer),
736
737	    resize(Wi),
738	    ignore
739    end;
740
741handle_event(#wx{id=?SASH_TRACE, event=#wxSash{dragRect={_X,_Y,_W,H}}}, Wi) ->
742    #winInfo{code=Code,m_szr={_,Sizer},e_szr={Enable,InfoSzr},trace=Trace} = Wi,
743    {TW, TH} = wxWindow:getSize(Trace#sub.win),
744    Change = TH - H,
745    case Enable of
746	false ->  %% Eval Area or Bindings
747	    {_, CH}  = wxWindow:getSize(Code#sub.win),
748	    {_, CMH} = wxWindow:getMinSize(Code#sub.win),
749	    case CMH > CH+Change of
750		true ->  wxSashWindow:setMinSize(Code#sub.win, {500, CH+Change});
751		_ -> ignore
752	    end,
753	    wxWindow:setMinSize(Trace#sub.win, {TW, H}),
754	    wxSizer:layout(Sizer),
755	    ignore;
756	true ->  %% Change the Eval and Bindings area
757	    ChangeH = fun(Item) ->
758			      {ItemW, ItemH} = wxSizerItem:getMinSize(Item),
759			      wxSizerItem:setInitSize(Item, ItemW, erlang:max(ItemH+Change,-1))
760		      end,
761	    {IW, IH} = wxSizer:getMinSize(InfoSzr),
762	    [ChangeH(Child) || Child <- wxSizer:getChildren(InfoSzr)],
763	    Wanted = IH+Change,
764	    wxSizer:setMinSize(InfoSzr, {IW, Wanted}),
765	    {_,RH} = wxSizer:getMinSize(InfoSzr),
766	    case RH > Wanted of
767		true ->  %% Couldn't get the size we wanted try adjusting the code area
768		    {_, CH}  = wxWindow:getSize(Code#sub.win),
769		    {_, CMH} = wxWindow:getMinSize(Code#sub.win),
770		    CC = CH - (RH-Wanted),
771		    case CMH > CC of
772			true when CC > 50 ->
773			    wxWindow:setMinSize(Trace#sub.win, {TW, H}),
774			    wxSashWindow:setMinSize(Code#sub.win, {500, CC});
775			_ when CC < 50 ->
776			    ignore;
777			_ ->
778			    wxWindow:setMinSize(Trace#sub.win, {TW, H})
779		    end,
780		    ok;
781		false ->
782		    wxWindow:setMinSize(Trace#sub.win, {TW, H})
783	    end,
784	    wxSizer:layout(Sizer),
785	    ignore
786    end;
787
788%% Menus, buttons and keyboard shortcuts
789handle_event(_Ev = #wx{event=#wxKey{keyCode=Key, controlDown=true}}, _WinInfo) ->
790    %% io:format("Key ~p ~n",[_Ev]),
791    if
792	Key/=?WXK_UP, Key/=?WXK_DOWN, Key /=? WXK_RETURN ->
793	    try
794		{shortcut, list_to_atom([Key+($a-$A)])}
795	    catch _:_ -> ignore
796	    end;
797	true ->
798	    ignore
799    end;
800handle_event(#wx{userData={dbg_ui_winman, Win},
801		 event=#wxCommand{type=command_menu_selected}}, _WinInfo) ->
802    dbg_wx_winman:raise(Win),
803    ignore;
804
805handle_event(#wx{userData={break, Point, status},
806		 event=#wxCommand{type=command_menu_selected}},
807	     WinInfo) ->
808    {value, BreakInfo} = lists:keysearch(Point, #breakInfo.point,
809					 WinInfo#winInfo.breaks),
810    %% This is a temporary hack !!
811    #breakInfo{break=#break{smi=Smi}} = BreakInfo,
812
813    case wxMenuItem:getText(Smi) of
814	"Enable" -> {break, Point, {status, active}};
815	"Disable" -> {break, Point, {status, inactive}}
816    end;
817
818handle_event(#wx{userData=Data,
819		 event=_Cmd=#wxCommand{type=command_menu_selected}},
820	     _WinInfo) ->
821    %%io:format("Command  ~p ~p~n",[Data,_Cmd]),
822    Data;
823
824%% Code area
825handle_event(#wx{event=#wxStyledText{type=stc_doubleclick}},
826	     WinInfo = #winInfo{editor={Mod,Ed}}) ->
827    Line = wxStyledTextCtrl:getCurrentLine(Ed),
828    Point = {Mod, Line+1},
829    case lists:keymember(Point, #breakInfo.point, WinInfo#winInfo.breaks) of
830	true -> {break, Point, delete};
831	false -> {break, Point, add}
832    end;
833
834%% Search Area
835handle_event(#wx{id=?GOTO_ENTRY, event=#wxCommand{cmdString=Str}}, WinInfo) ->
836    try
837	Line = list_to_integer(Str),
838	{gotoline, Line}
839    catch
840	_:_ ->
841	    display(WinInfo, {text,"Not a line number"}),
842	    ignore
843    end;
844handle_event(#wx{id=?SEARCH_ENTRY, event=#wxFocus{}}, Wi) ->
845    {win, Wi#winInfo{find=undefined}};
846handle_event(#wx{id=?SEARCH_ENTRY, event=#wxCommand{type=command_text_enter,cmdString=Str}},
847	     Wi = #winInfo{code=Code,find=Find, sg=#sub{in=#sa{radio={NextO,_,CaseO}}}})
848  when Find =/= undefined ->
849    Dir  = wxRadioButton:getValue(NextO) xor wx_misc:getKeyState(?WXK_SHIFT),
850    Case = wxCheckBox:getValue(CaseO),
851    Pos = if Find#find.found, Dir ->  %% Forward Continuation
852		  wxStyledTextCtrl:getAnchor(Code#sub.out);
853	     Find#find.found ->  %% Backward Continuation
854		  wxStyledTextCtrl:getCurrentPos(Code#sub.out);
855	     Dir ->   %% Forward wrap
856		  0;
857	     true ->  %% Backward wrap
858		  wxStyledTextCtrl:getLength(Code#sub.out)
859	  end,
860    dbg_wx_code:goto_pos(Code#sub.out,Pos),
861    case dbg_wx_code:find(Code#sub.out, Str, Case, Dir) of
862	true ->
863	    display(Wi, {text,""}),
864	    {win, Wi#winInfo{find=Find#find{found=true}}};
865	false ->
866	    display(Wi, {text,"Not found (Hit Enter to wrap search)"}),
867	    {win, Wi#winInfo{find=Find#find{found=false}}}
868    end;
869handle_event(#wx{id=?SEARCH_ENTRY, event=#wxCommand{cmdString=""}},
870	     Wi=#winInfo{code=Code}) ->
871    %% Reset search (and selection pos)
872    Pos = dbg_wx_code:current_pos(Code#sub.out),
873    dbg_wx_code:goto_pos(Code#sub.out,Pos),
874    {win, Wi#winInfo{find=undefined}};
875handle_event(#wx{id=?SEARCH_ENTRY, event=#wxCommand{cmdString=Str}},
876	     Wi = #winInfo{code=Code,find=Find,
877			   sg=#sub{in=#sa{radio={NextO,_,CaseO}}}}) ->
878    Dir  = wxRadioButton:getValue(NextO),
879    Case = wxCheckBox:getValue(CaseO),
880
881    Cont = case Find of
882	       undefined ->
883		   Pos = dbg_wx_code:current_pos(Code#sub.out),
884		   #find{start=Pos, strlen=length(Str)};
885	       #find{strlen=Old} when Old < length(Str) ->
886		   Find#find{strlen=length(Str)};
887	       _ ->
888		   dbg_wx_code:goto_pos(Code#sub.out,Find#find.start),
889		   Find#find{strlen=length(Str)}
890	   end,
891    case dbg_wx_code:find(Code#sub.out, Str, Case, Dir) of
892	true ->
893	    display(Wi, {text,""}),
894	    {win, Wi#winInfo{find=Cont#find{found=true}}};
895	false ->
896	    display(Wi, {text,"Not found (Hit Enter to wrap search)"}),
897	    {win, Wi#winInfo{find=Cont#find{found=false}}}
898    end;
899
900%% Button area
901handle_event(#wx{id=ID, event=#wxCommand{type=command_button_clicked}},_Wi) ->
902    {Button, _} = lists:keyfind(ID, 2, buttons()),
903    Button;
904
905%% Evaluator area
906handle_event(#wx{id=?EVAL_ENTRY, event=#wxCommand{type=command_text_enter}},
907	     Wi = #winInfo{eval=#sub{in=TC}}) ->
908    case wxTextCtrl:getValue(TC) of
909	[10] ->
910	    eval_output(Wi, "\n", normal),
911	    ignore;
912	Cmd ->
913	    eval_output(Wi, [$>, Cmd, 10], bold),
914	    wxTextCtrl:setValue(TC,""),
915	    {user_command, Cmd}
916    end;
917
918%% Bindings area
919handle_event(#wx{event=#wxList{type=command_list_item_selected, itemIndex=Row}},Wi) ->
920    {Mod,Bs} = get(bindings),
921    {Var,Val} = lists:nth(Row+1, Bs),
922    Header = io_lib:format("< ~s = ", [Var]),
923    eval_output(Wi, Header, Val, Mod, normal),
924    ignore;
925handle_event(#wx{event=#wxList{type=command_list_item_activated, itemIndex=Row}},_Wi) ->
926    {_Mod,Bs} = get(bindings),
927    Binding = lists:nth(Row+1, Bs),
928    {edit, Binding};
929
930handle_event(_GSEvent, _WinInfo) ->
931    %%io:format("~p: unhandled ~p~n",[?MODULE, _GSEvent]),
932    ignore.
933
934
935%%====================================================================
936%% resize(WinInfo) -> WinInfo
937
938resize(#winInfo{bind=Bind}) ->
939    %% Tweak the Binding settings text size
940    if
941	Bind#sub.enable =:= false ->
942	    ok;
943	Bind#sub.enable ->
944	    {EW, _} = wxWindow:getClientSize(Bind#sub.out),
945	    B0W = wxListCtrl:getColumnWidth(Bind#sub.out, 0),
946	    wxListCtrl:setColumnWidth(Bind#sub.out, 1, EW - B0W),
947	    ok
948    end.
949
950%%====================================================================
951%% Internal functions
952%%====================================================================
953
954%%--Code Area-------------------------------------------------------
955code_area(Win) ->
956    CodeWin = wxSashWindow:new(Win, [{id,?SASH_CODE},
957				     {size, {?WIN_W,?CODE_H}},
958				     {style, ?wxSW_3D}]),
959    Code  = dbg_wx_code:code_area(CodeWin),
960    wxSashWindow:setSashVisible(CodeWin, ?wxSASH_BOTTOM, true),
961    wxWindow:setMinSize(CodeWin, {600, ?CODE_H}),
962    #sub{name='Code Area',enable=true, win=CodeWin, out=Code}.
963
964
965%%--Button Area-------------------------------------------------------
966
967buttons() ->
968    [{'Step',?StepButton}, {'Next',?NextButton},
969     {'Continue',?ContinueButton}, {'Finish',?FinishButton},
970     {'Where',?WhereButton}, {'Up',?UpButton}, {'Down',?DownButton}].
971
972is_button(Name) ->
973    case lists:keyfind(Name, 1, buttons()) of
974	{Name, Button} -> {true, Button};
975	false -> false
976    end.
977
978button_area(Parent) ->
979    Sz = wxBoxSizer:new(?wxHORIZONTAL),
980    wx:foreach(fun({Name0, Button}) ->
981                       Name = [$&|atom_to_list(Name0)],
982		       B=wxButton:new(Parent, Button,
983				      [{label,dbg_wx_win:to_string(Name)}]),
984		       Id = wxWindow:getId(B),
985		       _ = wxSizer:add(Sz,B, []),
986		       wxButton:connect(B, command_button_clicked, [{id,Id}])
987	       end, buttons()),
988    #sub{name='Button Area', win=Sz}.
989
990%%--Search/Goto Area-------------------------------------------------
991
992search_area(Parent) ->
993    HSz = wxBoxSizer:new(?wxHORIZONTAL),
994    _ = wxSizer:add(HSz, wxStaticText:new(Parent, ?wxID_ANY, "Find: "),
995		[{flag,?wxALIGN_CENTER_VERTICAL}]),
996    TC1 = wxTextCtrl:new(Parent, ?SEARCH_ENTRY, [{style, ?wxTE_PROCESS_ENTER}]),
997    _ = wxSizer:add(HSz, TC1,  [{proportion,3}, {flag, ?wxEXPAND}]),
998    Nbtn = wxRadioButton:new(Parent, ?wxID_ANY, "Next"),
999    wxRadioButton:setValue(Nbtn, true),
1000    _ = wxSizer:add(HSz,Nbtn,[{flag,?wxALIGN_CENTER_VERTICAL}]),
1001    Pbtn = wxRadioButton:new(Parent, ?wxID_ANY, "Previous"),
1002    _ = wxSizer:add(HSz,Pbtn,[{flag,?wxALIGN_CENTER_VERTICAL}]),
1003    Cbtn = wxCheckBox:new(Parent, ?wxID_ANY, "Match Case"),
1004    _ = wxSizer:add(HSz,Cbtn,[{flag,?wxALIGN_CENTER_VERTICAL}]),
1005    _ = wxSizer:add(HSz, 15,15, [{proportion,1}, {flag, ?wxEXPAND}]),
1006    _ = wxSizer:add(HSz, wxStaticText:new(Parent, ?wxID_ANY, "Goto Line: "),
1007		[{flag,?wxALIGN_CENTER_VERTICAL}]),
1008    TC2 = wxTextCtrl:new(Parent, ?GOTO_ENTRY, [{style, ?wxTE_PROCESS_ENTER}]),
1009    _ = wxSizer:add(HSz, TC2,  [{proportion,0}, {flag, ?wxEXPAND}]),
1010    wxTextCtrl:connect(TC1, command_text_updated),
1011    wxTextCtrl:connect(TC1, command_text_enter),
1012    wxTextCtrl:connect(TC1, kill_focus),
1013    wxTextCtrl:connect(TC2, command_text_enter),
1014    wxWindow:connect(Parent, command_button_clicked),
1015
1016    #sub{name='Search Area', win=HSz,
1017	 in=#sa{search=TC1,goto=TC2,radio={Nbtn,Pbtn,Cbtn}}}.
1018
1019%%--Evaluator Area----------------------------------------------------
1020
1021eval_area(Parent) ->
1022    VSz = wxBoxSizer:new(?wxVERTICAL),
1023    HSz = wxBoxSizer:new(?wxHORIZONTAL),
1024
1025    _ = wxSizer:add(HSz, wxStaticText:new(Parent, ?wxID_ANY, "Evaluator: "),
1026		[{flag,?wxALIGN_CENTER_VERTICAL}]),
1027    TC = wxTextCtrl:new(Parent, ?EVAL_ENTRY, [{style, ?wxTE_PROCESS_ENTER}]),
1028    _ = wxSizer:add(HSz, TC,  [{proportion,1}, {flag, ?wxEXPAND}]),
1029    _ = wxSizer:add(VSz, HSz, [{flag, ?wxEXPAND}]),
1030    TL = wxTextCtrl:new(Parent, ?EVAL_LOG, [{style,
1031                                             ?wxTE_DONTWRAP
1032                                                 bor ?wxTE_MULTILINE
1033                                                 bor ?wxTE_READONLY
1034                                                 bor ?wxTE_RICH2
1035                                            }]),
1036    FixedFont = wxFont:new(10, ?wxFONTFAMILY_TELETYPE, ?wxNORMAL, ?wxNORMAL,[]),
1037    wxWindow:setFont(TL, FixedFont),
1038    _ = wxSizer:add(VSz, TL, [{proportion,5}, {flag, ?wxEXPAND}]),
1039
1040    wxTextCtrl:connect(TC, command_text_enter),
1041    #sub{name='Evaluator Area', win=VSz, in=TC, out=TL}.
1042
1043%%--Bindings Area-----------------------------------------------------
1044
1045bind_area(Parent) ->
1046    Style = {style, ?wxSW_3D bor  ?wxCLIP_CHILDREN},
1047    Win = wxSashWindow:new(Parent, [{id, ?SASH_EVAL},Style]),
1048    wxSashWindow:setSashVisible(Win, ?wxSASH_LEFT, true),
1049
1050    BA = wxListCtrl:new(Win, [{style, ?wxLC_REPORT bor ?wxLC_SINGLE_SEL}]),
1051    LI = wxListItem:new(),
1052
1053    wxListItem:setText(LI, "Name"),
1054    wxListItem:setAlign(LI, ?wxLIST_FORMAT_LEFT),
1055    wxListCtrl:insertColumn(BA, 0, LI),
1056
1057    wxListItem:setText(LI, "Value"),
1058    wxListCtrl:insertColumn(BA, 1, LI),
1059    wxListItem:destroy(LI),
1060
1061    wxListCtrl:setColumnWidth(BA, 0, 100),
1062    wxListCtrl:setColumnWidth(BA, 1, 150),
1063    wxListCtrl:connect(BA, command_list_item_selected),
1064    wxListCtrl:connect(BA, command_list_item_activated),
1065
1066    #sub{name='Bindings Area', win=Win, out=BA}.
1067
1068%%--Trace Area--------------------------------------------------------
1069
1070trace_area(Parent) ->
1071    Style = {style, ?wxSW_3D bor  ?wxCLIP_CHILDREN},
1072    Win = wxSashWindow:new(Parent, [{id, ?SASH_TRACE},
1073				    {size, {?WIN_W,?TRACE_H}}, Style]),
1074    wxSashWindow:setSashVisible(Win, ?wxSASH_TOP, true),
1075    wxWindow:setMinSize(Win, {500, ?TRACE_H}),
1076    TC = wxTextCtrl:new(Win, ?wxID_ANY, [{style, ?wxTE_MULTILINE bor ?wxTE_READONLY}]),
1077    #sub{name='Trace Area', win=Win, out=TC}.
1078
1079%%====================================================================
1080%% 'Go To Line' and 'Search' help windows
1081%%====================================================================
1082
1083helpwin(Type, WinInfo = #winInfo{sg=Sg =#sub{in=Sa}}) ->
1084    Wi = case Sg#sub.enable of
1085	     false -> configure(WinInfo#winInfo{sg=Sg#sub{enable=true}});
1086	     true ->  WinInfo
1087	 end,
1088    case Type of
1089	gotoline ->  wxWindow:setFocus(Sa#sa.goto);
1090	search   ->  wxWindow:setFocus(Sa#sa.search)
1091    end,
1092    Wi.
1093