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