1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2009-2017. 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%% This is example of the widgets and usage of wxErlang 21%% Hopefully it will contain all implemented widgets, it's event handling 22%% and some tutorials of how to use sizers and other stuff. 23 24-module(demo). 25 26-include_lib("wx/include/wx.hrl"). 27 28-behaviour(wx_object). 29-export([start/0, start/1, start_link/0, start_link/1, format/3, 30 init/1, terminate/2, code_change/3, 31 handle_info/2, handle_call/3, handle_cast/2, handle_event/2]). 32 33 34-record(state, {win, demo, example, selector, log, code}). 35 36start() -> 37 start([]). 38 39start(Debug) -> 40 wx_object:start(?MODULE, Debug, []). 41 42start_link() -> 43 start_link([]). 44 45start_link(Debug) -> 46 wx_object:start_link(?MODULE, Debug, []). 47 48format(#state{log=Log}, Str, Args) -> 49 wxTextCtrl:appendText(Log, io_lib:format(Str, Args)), 50 ok; 51format(Config,Str,Args) -> 52 Log = proplists:get_value(log, Config), 53 wxTextCtrl:appendText(Log, io_lib:format(Str, Args)), 54 ok. 55 56 57-define(DEBUG_NONE, 101). 58-define(DEBUG_VERBOSE, 102). 59-define(DEBUG_TRACE, 103). 60-define(DEBUG_DRIVER, 104). 61 62init(Options) -> 63 wx:new(Options), 64 process_flag(trap_exit, true), 65 66 Frame = wxFrame:new(wx:null(), ?wxID_ANY, "wxErlang widgets", [{size,{1400,800}}]), 67 MB = wxMenuBar:new(), 68 File = wxMenu:new([]), 69 wxMenu:append(File, ?wxID_PRINT, "&Print code"), 70 wxMenu:appendSeparator(File), 71 wxMenu:append(File, ?wxID_EXIT, "&Quit"), 72 Debug = wxMenu:new([]), 73 wxMenu:appendRadioItem(Debug, ?DEBUG_NONE, "None"), 74 wxMenu:appendRadioItem(Debug, ?DEBUG_VERBOSE, "Verbose"), 75 wxMenu:appendRadioItem(Debug, ?DEBUG_TRACE, "Trace"), 76 wxMenu:appendRadioItem(Debug, ?DEBUG_DRIVER, "Driver"), 77 Help = wxMenu:new([]), 78 wxMenu:append(Help, ?wxID_HELP, "Help"), 79 wxMenu:append(Help, ?wxID_ABOUT, "About"), 80 wxMenuBar:append(MB, File, "&File"), 81 wxMenuBar:append(MB, Debug, "&Debug"), 82 wxMenuBar:append(MB, Help, "&Help"), 83 wxFrame:setMenuBar(Frame,MB), 84 85 wxFrame:connect(Frame, command_menu_selected), 86 wxFrame:connect(Frame, close_window), 87 88 _SB = wxFrame:createStatusBar(Frame,[]), 89 90 %% Setup on toplevel because stc seems to steal this on linux 91 wxFrame:dragAcceptFiles(Frame, true), 92 wxFrame:connect(Frame, drop_files), 93 94 %% T Uppersplitter 95 %% O Left | Right 96 %% P Widgets|Code | Demo 97 %% S ------------------------------- 98 %% P Log Window 99 TopSplitter = wxSplitterWindow:new(Frame, [{style, ?wxSP_NOBORDER}]), 100 UpperSplitter = wxSplitterWindow:new(TopSplitter, [{style, ?wxSP_NOBORDER}]), 101 LeftSplitter = wxSplitterWindow:new(UpperSplitter, [{style, ?wxSP_NOBORDER}]), 102 %% Setup so that sizers and initial sizes, resizes the windows correct 103 wxSplitterWindow:setSashGravity(TopSplitter, 0.5), 104 wxSplitterWindow:setSashGravity(UpperSplitter, 0.60), 105 wxSplitterWindow:setSashGravity(LeftSplitter, 0.20), 106 107 %% LeftSplitter: 108 Example = fun(Beam) -> 109 "ex_" ++ F = filename:rootname(Beam), 110 F 111 end, 112 Mods = [Example(F) || F <- filelib:wildcard("ex_*.beam")], 113 114 CreateLB = fun(Parent) -> 115 wxListBox:new(Parent, ?wxID_ANY, 116 [{style, ?wxLB_SINGLE}, 117 {choices, Mods}]) 118 end, 119 {LBPanel, [LB],_} = create_subwindow(LeftSplitter, "Example", [CreateLB]), 120 wxListBox:setSelection(LB, 0), 121 wxListBox:connect(LB, command_listbox_selected), 122 123 CreateCode = fun(Parent) -> 124 code_area(Parent) 125 end, 126 {CodePanel, [Code],_} = create_subwindow(LeftSplitter, "Code", [CreateCode]), 127 128 wxSplitterWindow:splitVertically(LeftSplitter, LBPanel, CodePanel, 129 [{sashPosition,150}]), 130 131 %% Demo: 132 {DemoPanel, [], DemoSz} = create_subwindow(UpperSplitter, "Demo", []), 133 134 %% UpperSplitter: 135 wxSplitterWindow:splitVertically(UpperSplitter, LeftSplitter, DemoPanel, 136 [{sashPosition,600}]), 137 138 %% TopSplitter: 139 AddEvent = fun(Parent) -> 140 EventText = wxTextCtrl:new(Parent, 141 ?wxID_ANY, 142 [{style, ?wxTE_DONTWRAP bor 143 ?wxTE_MULTILINE bor ?wxTE_READONLY} 144 ]), 145 wxTextCtrl:appendText(EventText, "Welcome\n"), 146 EventText 147 end, 148 149 {EvPanel, [EvCtrl],_} = create_subwindow(TopSplitter, "Events", [AddEvent]), 150 151 wxSplitterWindow:splitHorizontally(TopSplitter, UpperSplitter, EvPanel, 152 [{sashPosition,-100}]), 153 154 wxFrame:show(Frame), 155 156 State = #state{win=Frame, demo={DemoPanel,DemoSz}, selector=LB, log=EvCtrl, code=Code}, 157 %% Load the first example: 158 Ex = wxListBox:getStringSelection(LB), 159 process_flag(trap_exit, true), 160 ExampleObj = load_example(Ex, State), 161 wxSizer:add(DemoSz, ExampleObj, [{proportion,1}, {flag, ?wxEXPAND}]), 162 wxSizer:layout(DemoSz), 163 164 %% The windows should be set up now, Reset Gravity so we get what we want 165 wxSplitterWindow:setSashGravity(TopSplitter, 1.0), 166 wxSplitterWindow:setSashGravity(UpperSplitter, 0.0), 167 wxSplitterWindow:setSashGravity(LeftSplitter, 0.0), 168 wxSplitterWindow:setMinimumPaneSize(TopSplitter, 1), 169 wxSplitterWindow:setMinimumPaneSize(UpperSplitter, 1), 170 wxSplitterWindow:setMinimumPaneSize(LeftSplitter, 1), 171 172 wxToolTip:enable(true), 173 wxToolTip:setDelay(500), 174 175 {Frame, State#state{example=ExampleObj}}. 176 177create_subwindow(Parent, BoxLabel, Funs) -> 178 Panel = wxPanel:new(Parent), 179 Sz = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label, BoxLabel}]), 180 wxPanel:setSizer(Panel, Sz), 181 Ctrls = [Fun(Panel) || Fun <- Funs], 182 [wxSizer:add(Sz, Ctrl, [{proportion, 1}, {flag, ?wxEXPAND}]) 183 || Ctrl <- Ctrls], 184 {Panel, Ctrls, Sz}. 185 186%%%%%%%%%%%% 187%% Callbacks 188 189%% Handled as in normal gen_server callbacks 190handle_info({'EXIT',_, wx_deleted}, State) -> 191 {noreply,State}; 192handle_info({'EXIT',_, shutdown}, State) -> 193 {noreply,State}; 194handle_info({'EXIT',_, normal}, State) -> 195 {noreply,State}; 196handle_info(Msg, State) -> 197 format(State, "Got Info ~p~n",[Msg]), 198 {noreply,State}. 199 200handle_call(Msg, _From, State) -> 201 format(State, "Got Call ~p~n",[Msg]), 202 {reply,ok,State}. 203 204handle_cast(Msg, State) -> 205 format(State, "Got cast ~p~n",[Msg]), 206 {noreply,State}. 207 208%% Async Events are handled in handle_event as in handle_info 209handle_event(#wx{event=#wxCommand{type=command_listbox_selected, cmdString=Ex}}, 210 State = #state{demo={_,DemoSz}, example=Example, code=Code}) -> 211 case Ex of 212 [] -> 213 {noreply, State}; 214 _ -> 215 wxSizer:detach(DemoSz, Example), 216 wx_object:call(Example, shutdown), 217 unload_code(Code), 218 NewExample = load_example(Ex, State), 219 wxSizer:add(DemoSz, NewExample, [{proportion,1}, {flag, ?wxEXPAND}]), 220 wxSizer:layout(DemoSz), 221 {noreply, State#state{example=NewExample}} 222 end; 223handle_event(#wx{id = Id, 224 event = #wxCommand{type = command_menu_selected}}, 225 State = #state{}) -> 226 case Id of 227 ?wxID_PRINT -> 228 %% If you are going to printout mainly text it is easier if 229 %% you generate HTML code and use a wxHtmlEasyPrint 230 %% instead of using DCs 231 232 %% Printpreview doesn't work in >2.9 without this 233 wxIdleEvent:setMode(?wxIDLE_PROCESS_ALL), 234 Module = "ex_" ++ wxListBox:getStringSelection(State#state.selector) ++ ".erl", 235 HEP = wxHtmlEasyPrinting:new([{name, "Print"}, 236 {parentWindow, State#state.win}]), 237 Html = demo_html_tagger:erl2htmltext(Module), 238 wxHtmlEasyPrinting:previewText(HEP, Html), 239 {noreply, State}; 240 ?DEBUG_TRACE -> 241 wx:debug(trace), 242 {noreply, State}; 243 ?DEBUG_DRIVER -> 244 wx:debug(driver), 245 {noreply, State}; 246 ?DEBUG_VERBOSE -> 247 wx:debug(verbose), 248 {noreply, State}; 249 ?DEBUG_NONE -> 250 wx:debug(none), 251 {noreply, State}; 252 ?wxID_HELP -> 253 wx_misc:launchDefaultBrowser("http://www.erlang.org/doc/apps/wx/part_frame.html"), 254 {noreply, State}; 255 ?wxID_ABOUT -> 256 WxWVer = io_lib:format("~p.~p.~p.~p", 257 [?wxMAJOR_VERSION, ?wxMINOR_VERSION, 258 ?wxRELEASE_NUMBER, ?wxSUBRELEASE_NUMBER]), 259 application:load(wx), 260 {ok, WxVsn} = application:get_key(wx, vsn), 261 AboutString = 262 "Demo of various widgets\n" 263 "Authors: Olle & Dan\n\n" ++ 264 "Frontend: wx-" ++ WxVsn ++ 265 "\nBackend: wxWidgets-" ++ lists:flatten(WxWVer), 266 267 wxMessageDialog:showModal(wxMessageDialog:new(State#state.win, AboutString, 268 [{style, 269 ?wxOK bor 270 ?wxICON_INFORMATION bor 271 ?wxSTAY_ON_TOP}, 272 {caption, "About"}])), 273 {noreply, State}; 274 ?wxID_EXIT -> 275 wx_object:call(State#state.example, shutdown), 276 {stop, normal, State}; 277 _ -> 278 {noreply, State} 279 end; 280handle_event(#wx{event=#wxClose{}}, State = #state{win=Frame}) -> 281 io:format("~p Closing window ~n",[self()]), 282 ok = wxFrame:setStatusText(Frame, "Closing...",[]), 283 {stop, normal, State}; 284handle_event(Ev,State) -> 285 format(State, "~p Got event ~p ~n",[?MODULE, Ev]), 286 {noreply, State}. 287 288code_change(_, _, State) -> 289 {stop, not_yet_implemented, State}. 290 291terminate(_Reason, State = #state{win=Frame}) -> 292 catch wx_object:call(State#state.example, shutdown), 293 wxFrame:destroy(Frame), 294 wx:destroy(). 295 296%%%%%%%%%%%%%%%%% Internals %%%%%%%%%% 297 298load_example(Ex, #state{demo={DemoPanel,DemoSz}, log=EvCtrl, code=Code}) -> 299 ModStr = "ex_" ++ Ex, 300 Mod = list_to_atom(ModStr), 301 ModFile = ModStr ++ ".erl", 302 load_code(Code, file:read_file(ModFile)), 303 find(Code), 304 Mod:start([{parent, DemoPanel}, {demo_sz, DemoSz}, {log, EvCtrl}]). 305 306-define(stc, wxStyledTextCtrl). 307 308code_area(Parent) -> 309 FixedFont = wxFont:new(10, ?wxFONTFAMILY_TELETYPE, ?wxNORMAL, ?wxNORMAL,[]), 310 Ed = wxStyledTextCtrl:new(Parent), 311 312 ?stc:styleClearAll(Ed), 313 ?stc:styleSetFont(Ed, ?wxSTC_STYLE_DEFAULT, FixedFont), 314 ?stc:setLexer(Ed, ?wxSTC_LEX_ERLANG), 315 ?stc:setMarginType(Ed, 0, ?wxSTC_MARGIN_NUMBER), 316 LW = ?stc:textWidth(Ed, ?wxSTC_STYLE_LINENUMBER, "9"), 317 ?stc:setMarginWidth(Ed, 0, LW), 318 ?stc:setMarginWidth(Ed, 1, 0), 319 320 ?stc:setSelectionMode(Ed, ?wxSTC_SEL_LINES), 321 %%?stc:hideSelection(Ed, true), 322 323 Styles = [{?wxSTC_ERLANG_DEFAULT, {0,0,0}}, 324 {?wxSTC_ERLANG_COMMENT, {160,53,35}}, 325 {?wxSTC_ERLANG_VARIABLE, {150,100,40}}, 326 {?wxSTC_ERLANG_NUMBER, {5,5,100}}, 327 {?wxSTC_ERLANG_KEYWORD, {130,40,172}}, 328 {?wxSTC_ERLANG_STRING, {170,45,132}}, 329 {?wxSTC_ERLANG_OPERATOR, {30,0,0}}, 330 {?wxSTC_ERLANG_ATOM, {0,0,0}}, 331 {?wxSTC_ERLANG_FUNCTION_NAME, {64,102,244}}, 332 {?wxSTC_ERLANG_CHARACTER,{236,155,172}}, 333 {?wxSTC_ERLANG_MACRO, {40,144,170}}, 334 {?wxSTC_ERLANG_RECORD, {40,100,20}}, 335 {?wxSTC_ERLANG_NODE_NAME,{0,0,0}}, 336 %% Optional 2.9 stuff 337 {?wxSTC_ERLANG_COMMENT_FUNCTION, {160,53,35}}, 338 {?wxSTC_ERLANG_COMMENT_MODULE, {160,53,35}}, 339 {?wxSTC_ERLANG_COMMENT_DOC, {160,53,35}}, 340 {?wxSTC_ERLANG_COMMENT_DOC_MACRO, {160,53,35}}, 341 {?wxSTC_ERLANG_ATOM_QUOTED, {0,0,0}}, 342 {?wxSTC_ERLANG_MACRO_QUOTED, {40,144,170}}, 343 {?wxSTC_ERLANG_RECORD_QUOTED, {40,100,20}}, 344 {?wxSTC_ERLANG_NODE_NAME_QUOTED, {0,0,0}}, 345 {?wxSTC_ERLANG_BIFS, {130,40,172}}, 346 {?wxSTC_ERLANG_MODULES, {64,102,244}}, 347 {?wxSTC_ERLANG_MODULES_ATT, {64,102,244}} 348 ], 349 SetStyle = fun({Style, Color}) -> 350 ?stc:styleSetFont(Ed, Style, FixedFont), 351 ?stc:styleSetForeground(Ed, Style, Color) 352 end, 353 [SetStyle(Style) || Style <- Styles], 354 ?stc:setKeyWords(Ed, 0, keyWords()), 355 356 %% Scrolling 357 Policy = ?wxSTC_CARET_SLOP bor ?wxSTC_CARET_JUMPS bor ?wxSTC_CARET_EVEN, 358 ?stc:setYCaretPolicy(Ed, Policy, 3), 359 ?stc:setVisiblePolicy(Ed, Policy, 3), 360 361 %% ?stc:connect(Ed, stc_doubleclick), 362 %% ?stc:connect(Ed, std_do_drop, fun(Ev, Obj) -> io:format("Ev ~p ~p~n",[Ev,Obj]) end), 363 ?stc:setReadOnly(Ed, true), 364 Ed. 365 366load_code(Ed, {ok, Code}) -> 367 ?stc:setReadOnly(Ed, false), 368 ?stc:setTextRaw(Ed, <<Code/binary, 0:8>>), 369 Lines = ?stc:getLineCount(Ed), 370 Sz = trunc(math:log10(Lines))+1, 371 LW = ?stc:textWidth(Ed, ?wxSTC_STYLE_LINENUMBER, lists:duplicate(Sz, $9)), 372 %%io:format("~p ~p ~p~n", [Lines, Sz, LW]), 373 ?stc:setMarginWidth(Ed, 0, LW+5), 374 ?stc:setReadOnly(Ed, true), 375 Ed. 376 377unload_code(Ed) -> 378 ?stc:setReadOnly(Ed, false), 379 ?stc:setTextRaw(Ed, <<0:8>>), 380 ?stc:setReadOnly(Ed, true), 381 Ed. 382 383find(Ed) -> 384 ?stc:searchAnchor(Ed), 385 Res = ?stc:searchNext(Ed, ?wxSTC_FIND_REGEXP, "^init"), 386 case Res >= 0 of 387 true -> 388 %% io:format("Found ~p ~n",[Res]), 389 ?stc:scrollToLine(Ed,?stc:lineFromPosition(Ed,Res) - 1), 390 true; 391 false -> 392 io:format("Not Found ~s ~n",["^init"]), 393 false 394 end. 395 396keyWords() -> 397 L = ["after","begin","case","try","cond","catch","andalso","orelse", 398 "end","fun","if","let","of","receive","when","bnot","not", 399 "div","rem","band","and","bor","bxor","bsl","bsr","or","xor"], 400 lists:flatten([K ++ " " || K <- L] ++ [0]). 401 402