diff --git a/lib/et/src/et_wx_contents_viewer.erl b/lib/et/src/et_wx_contents_viewer.erl index 580c921139..0bc70ce22a 100644 --- a/lib/et/src/et_wx_contents_viewer.erl +++ b/lib/et/src/et_wx_contents_viewer.erl @@ -47,9 +47,10 @@ filters, % List of possible filters win, % GUI: Frame object frame, % GUI: Frame object - panel, % GUI: Panel object - width, % GUI: Window width + width, height, + panel, % GUI: Panel object + font, editor, menu_data, % GUI: Window height wx_debug, % GUI: WX debug level @@ -107,7 +108,7 @@ default_state() -> filters = [?DEFAULT_FILTER], width = 600, height = 300, - wx_debug = 0, + wx_debug = 0, trap_exit = true}. parse_opt([], S) -> @@ -464,8 +465,10 @@ create_window(S) -> _ = wxTextCtrl:setDefaultStyle(Editor, TextAttr), Sizer = wxBoxSizer:new(?wxHORIZONTAL), _ = wxSizer:add(Sizer, Editor, [{flag, ?wxEXPAND}, {proportion, 1}]), - FilteredEvent = config_editor(Editor, S), - S2 = S#state{win = Frame, panel = Panel, filtered_event = FilteredEvent}, + S1 = S#state{win = Frame, panel = Panel, font = Font}, + FilteredEvent = config_editor(Editor, S1), + {NewW, NewH} = wxWindow:getSize(Frame), + S2 = S1#state{filtered_event = FilteredEvent, width = NewW, height = NewH}, HideData = create_hide_menu(Bar, S2), SearchData = create_search_menu(Bar, S2), FilterData = create_filter_menu(Bar, S#state.filters), @@ -586,19 +589,26 @@ config_editor(Editor, S) -> FilterFun = F#filter.function, case catch FilterFun(Event) of true -> - do_config_editor(Editor, Event, lightblue, S#state.event_order); + do_config_editor(Editor, Event, lightblue, S#state.event_order, S); {true, Event2} when is_record(Event2, event) -> - do_config_editor(Editor, Event2, lightblue, S#state.event_order); + do_config_editor(Editor, Event2, lightblue, S#state.event_order, S); false -> - do_config_editor(Editor, Event, red, S#state.event_order); + do_config_editor(Editor, Event, red, S#state.event_order, S); Bad -> Contents = {bad_filter, Name, Bad}, BadEvent = Event#event{contents = Contents}, - do_config_editor(Editor, BadEvent, red, S#state.event_order) + do_config_editor(Editor, BadEvent, red, S#state.event_order, S) end. -do_config_editor(Editor, Event, _Colour, TsKey) -> +do_config_editor(Editor, Event, _Colour, TsKey, S) -> String = event_to_string(Event, TsKey), + %% Auto-fit the window size to the content + Ext = wxWindow:getTextExtent(Editor, String, [{theFont, S#state.font}]), + {W, H, _, _} = Ext, + %% Add a little space to prevent the scrollbar. Could do this more smartly? + wxWindow:setSize(Editor, W + 20, H + 20), + wxWindow:fit(S#state.panel), + wxWindow:fit(S#state.win), wxTextCtrl:appendText(Editor, String), Event. diff --git a/lib/et/src/et_wx_viewer.erl b/lib/et/src/et_wx_viewer.erl index 4dd44e7a4c..a84dd7cf21 100644 --- a/lib/et/src/et_wx_viewer.erl +++ b/lib/et/src/et_wx_viewer.erl @@ -38,7 +38,6 @@ -define(unknown, "UNKNOWN"). -define(initial_x, 10). --define(incr_x, 60). -define(initial_y, 15). -define(incr_y, 15). @@ -59,6 +58,9 @@ n_events, % Number of events available in the collector max_actors, % Maximum number of shown actors actors, % List of known actors + vis_actors, % Current visible actors + actors_offset, % the first actor to draw + actors_per_page, % Maximum number of shown actors refresh_needed, % Refresh is needed in order to show all actors detail_level, % Show only events with lesser detail level hide_actions, % Hide/show events where to == from actor (bool) @@ -74,6 +76,7 @@ scale, % GUI: Scaling factor on canvas normal_font, % GUI: Font to be used on text labels bold_font, % GUI: Font to be used on text labels + actor_width, pen, brush, print_psdd, @@ -146,11 +149,15 @@ default_state() -> events = queue_new(), max_actors = 5, actors = [create_actor(?unknown)], + vis_actors = [create_actor(?unknown)], + actors_offset = 0, + actors_per_page = 100, pending_actor = ?unknown, hide_actions = false, hide_actors = false, display_all = true, context = display, + actor_width = 80, refresh_needed = false, scale = 2, canvas_height = 0, @@ -327,8 +334,9 @@ init([S]) when is_record(S, state) -> ?MODULE), S2 = create_main_window(S), EventsPerPage = events_per_page(S2, S2#state.height), - S3 = revert_main_window(S2#state{events_per_page = EventsPerPage}), - Timeout = timeout(S3), + ActorsPerPage = actors_per_page(S2, S2#state.width), + S3 = revert_main_window(S2#state{events_per_page = EventsPerPage, actors_per_page = ActorsPerPage}), + Timeout = timeout(S3), {ok, S3, Timeout}. %%---------------------------------------------------------------------- @@ -625,7 +633,7 @@ handle_info(#wx{event = #wxMouse{type = left_down, x = X, y = Y}}, S) -> S; Actors -> N = x_to_n(X, S), - A = lists:nth(N, Actors), + A = lists:nth(N + S#state.actors_offset, Actors), S#state{pending_actor = A} end; {event, N} -> @@ -646,7 +654,7 @@ handle_info(#wx{event = #wxMouse{type = left_up, x = X, y = Y}}, S) -> S; Actors -> N = x_to_n(X, S), - A = lists:nth(N, Actors), + A = lists:nth(N + S#state.actors_offset, Actors), Pending = S#state.pending_actor, if A#actor.name =:= Pending#actor.name -> @@ -678,7 +686,7 @@ handle_info(#wx{event = #wxMouse{type = right_up, x = X, y = Y}}, S) -> Actors -> %% Toggle exclude actor N = x_to_n(X, S), - A = lists:nth(N, Actors), + A = lists:nth(N + S#state.actors_offset, Actors), A2 = A#actor{exclude = not A#actor.exclude}, Actors2 = lists:keyreplace(A#actor.name, #actor.name, Actors, A2), S2 = S#state{actors = Actors2}, @@ -713,6 +721,22 @@ handle_info(#wx{event = #wxKey{keyCode = KeyCode, shiftDown = SD}}, S) -> ?WXK_PAGEDOWN -> S2 = scroll_next(S), noreply(S2); + $W -> + S2 = S#state{actor_width = S#state.actor_width + 80}, + S3 = S2#state{actors_per_page = actors_per_page(S2, S2#state.width)}, + noreply(refresh_main_window(S3)); + $S when S#state.actor_width > 80 -> + S2 = S#state{actor_width = S#state.actor_width - 80}, + S3 = S2#state{actors_per_page = actors_per_page(S2, S2#state.width)}, + noreply(refresh_main_window(S3)); + ?WXK_LEFT -> + NewOffset = lists:max([0, S#state.actors_offset - 1]), + S2 = S#state{actors_offset = NewOffset}, + noreply(refresh_main_window(S2)); + ?WXK_RIGHT -> + NewOffset = lists:min([length(S#state.actors) - 1, S#state.actors_offset + 1]), + S2 = S#state{actors_offset = NewOffset}, + noreply(refresh_main_window(S2)); $F when SD =:= true -> et_collector:multicast(S#state.collector_pid, first), noreply(S); @@ -825,27 +849,34 @@ handle_info(#wx{event = #wxSize{size = {OldW, OldH}}} = Wx, S) -> #wx{event = #wxSize{type = size, size = {W, H}}} = get_latest_resize(Wx), S2 = S#state{width = W, height = H, canvas_width = W, canvas_height = H}, EventsPerPage = events_per_page(S, H), - Diff = EventsPerPage - S#state.events_per_page, - S6 = + DiffE = EventsPerPage - S#state.events_per_page, + ActorsPerPage = actors_per_page(S, W), + DiffA = ActorsPerPage - S#state.actors_per_page, + {ToRefresh, S3} = if - OldW =:= W, OldH =:= H, S2#state.events_per_page =:= EventsPerPage -> - S2; - Diff =:= 0 -> - refresh_main_window(S2); - Diff > 0 -> - OldEvents = queue_to_list(S2#state.events), - {S3, NewEvents} = collect_more_events(S2, S2#state.last_event, Diff), - S4 = S3#state{events_per_page = EventsPerPage}, - S5 = replace_events(S4, OldEvents ++ NewEvents), - refresh_main_window(S5); - Diff < 0 -> - OldEvents = queue_to_list(S2#state.events), - RevEvents = delete_n(lists:reverse(OldEvents), abs(Diff)), - S3 = S2#state{events_per_page = EventsPerPage}, - S4 = replace_events(S3, lists:reverse(RevEvents)), - refresh_main_window(S4) + OldW =:= W, OldH =:= H, DiffE =:= 0, DiffA =:= 0 -> + {false, S2}; + DiffE =:= 0 -> + {true, S2#state{actors_per_page = ActorsPerPage}}; + DiffE > 0 -> + OldEvents = queue_to_list(S2#state.events), + {_S3, NewEvents} = collect_more_events(S2, S2#state.last_event, DiffE), + _S4 = _S3#state{events_per_page = EventsPerPage, actors_per_page = ActorsPerPage}, + _S5 = replace_events(_S4, OldEvents ++ NewEvents), + {true, _S5}; + DiffE < 0 -> + OldEvents = queue_to_list(S2#state.events), + RevEvents = delete_n(lists:reverse(OldEvents), abs(DiffE)), + _S3 = S2#state{events_per_page = EventsPerPage, actors_per_page = ActorsPerPage}, + _S4 = replace_events(_S3, lists:reverse(RevEvents)), + {true, _S4} end, - noreply(S6); + case ToRefresh of + true -> + noreply(refresh_main_window(S3)); + false -> + noreply(S3) + end; handle_info(#wx{event = #wxMouse{type = enter_window}}, S) -> wxWindow:setFocus(S#state.canvas), % Get keyboard focus noreply(S); @@ -1640,84 +1671,86 @@ draw_label(Label, FromName, ToName, FromPos, ToPos, S, DC) -> draw_all_actors(S, DC) -> Scale = S#state.scale, - Fun = fun(A, X) -> - case draw_actor(A, X, S, DC) of - true -> - X + (?incr_x * Scale); - false -> - X - end + NewVisActors = lists:filter(fun (A) -> to_draw_actor(A, S) end, S#state.actors), + NewOffset = lists:max([0, lists:min([length(NewVisActors) - S#state.actors_per_page, S#state.actors_offset])]), + Fun = fun(A, {C, X}) -> + if + %% also draw the first out-of-bound actor + NewOffset =< C, C =< NewOffset + S#state.actors_per_page -> + draw_actor(A, X, S, DC), + {C + 1, X + (S#state.actor_width * Scale)}; + true -> + {C + 1, X} + end end, - lists:foldl(Fun, ?initial_x * Scale, S#state.actors), - S. + lists:foldl(Fun, {0, ?initial_x * Scale}, NewVisActors), + S#state{actors_offset = NewOffset, vis_actors = NewVisActors, y_pos = S#state.y_pos}. -%% Returns: {NeedsRefreshBool, {ActorPos, NewsS, NewActors}} +%% Returns: {NeedsRefreshBool, {Name, Pos, NewState}} ensure_actor(Name, S, DC) -> do_ensure_actor(Name, S, S#state.actors, 0, DC). do_ensure_actor(Name, S, [H | _], N, _DC) when H#actor.name =:= Name -> - Pos = (?initial_x + (N * ?incr_x)) * S#state.scale, + Pos = (?initial_x + ((N - S#state.actors_offset) * S#state.actor_width)) * S#state.scale, {false, {Name, Pos, S}}; do_ensure_actor(Name, S, [H | T], N, DC) -> - if - S#state.hide_actors, H#actor.exclude -> - do_ensure_actor(Name, S, T, N, DC); - true -> - do_ensure_actor(Name, S, T, N + 1, DC) + case to_draw_actor(H, S) of + true -> + do_ensure_actor(Name, S, T, N + 1, DC); + false -> + do_ensure_actor(Name, S, T, N, DC) end; do_ensure_actor(Name, S, [], N, DC) -> %% A brand new actor, let's see if it does fit - Pos = (?initial_x + (N * ?incr_x)) * S#state.scale, + Pos = (?initial_x + ((N - S#state.actors_offset) * S#state.actor_width)) * S#state.scale, MaxActors = S#state.max_actors, if is_integer(MaxActors), N > MaxActors -> %% Failed on max_actors limit, put into unknown %% Assume that unknown always is in actor list ensure_actor(?unknown, S, DC); - Pos > (S#state.canvas_width - ((?initial_x - 15) * S#state.scale)) -> + Pos < 0; Pos > (S#state.canvas_width - ((?initial_x - 15) * S#state.scale)) -> %% New actor does not fit in canvas, refresh needed A = create_actor(Name), draw_actor(A, Pos, S, DC), - {true, {Name, Pos, S#state{actors = S#state.actors ++ [A]}}}; + {true, {Name, Pos, S#state{actors = S#state.actors ++ [A], vis_actors = S#state.vis_actors ++ [A]}}}; true -> %% New actor fits in canvas. Draw the new actor. A = create_actor(Name), draw_actor(A, Pos, S, DC), - {false, {Name, Pos, S#state{actors = S#state.actors ++ [A]}}} + {false, {Name, Pos, S#state{actors = S#state.actors ++ [A], vis_actors = S#state.vis_actors ++ [A]}}} end. +to_draw_actor(A, S) when S#state.hide_actors, A#actor.exclude -> false; +to_draw_actor(_, _) -> true. + draw_actor(A, LineX, S, DC) -> - if - S#state.hide_actors, A#actor.exclude -> - false; - true -> - Scale = S#state.scale, - TextX = LineX - (5 * Scale), - {TextY, LineTopY, LineBotY} = calc_y(S), - Color = - case A#actor.name of - ?unknown -> {255,126,0};% orange - _ -> {227,38,54} % red - end, - {String, Font} = - if - S#state.context =:= display, A#actor.exclude -> - {"(" ++ A#actor.string ++ ")", S#state.normal_font}; - S#state.context =:= display, A#actor.include -> - {"[" ++ A#actor.string ++ "]", S#state.bold_font}; - true -> - {A#actor.string, S#state.normal_font} - end, - write_text(String, TextX, TextY, Color, Font, S, DC), - wxPen:setColour(S#state.pen, Color), - wxDC:setPen(DC, S#state.pen), - wxDC:drawLines(DC, [{LineX, LineTopY}, {LineX, LineBotY}]), - true - end. + Scale = S#state.scale, + TextX = LineX - (5 * Scale), + {TextY, LineTopY, LineBotY} = calc_y(S), + Color = + case A#actor.name of + ?unknown -> {255,126,0};% orange + _ -> {227,38,54} % red + end, + {String, Font} = + if + S#state.context =:= display, A#actor.exclude -> + {"(" ++ A#actor.string ++ ")", S#state.normal_font}; + S#state.context =:= display, A#actor.include -> + {"[" ++ A#actor.string ++ "]", S#state.bold_font}; + true -> + {A#actor.string, S#state.normal_font} + end, + write_text(String, TextX, TextY, Color, Font, S, DC), + wxPen:setColour(S#state.pen, Color), + wxDC:setPen(DC, S#state.pen), + wxDC:drawLines(DC, [{LineX, LineTopY}, {LineX, LineBotY}]), + ok. calc_y(#state{canvas_height = Height, scale = Scale}) -> TextY = ?initial_y * Scale, - LineTopY = round(TextY + ((?incr_y / 2) * Scale)), + LineTopY = round(TextY + (?incr_y * 0.5) * Scale), LineBotY = Height, %% LineBotY = round(Height - ((?incr_y / 2) * Scale)), {TextY, LineTopY, LineBotY}. @@ -1821,7 +1854,7 @@ x_to_n(X, S) -> Scale = S#state.scale, Len = length(S#state.actors), X2 = X - (?initial_x * Scale), - N = X2 / (?incr_x * Scale), + N = X2 / (S#state.actor_width * Scale), N2 = trunc(N + 1.5), if N2 > Len -> Len; @@ -2031,7 +2064,10 @@ update_scroll_bar(#state{scroll_bar = ScrollBar, events_per_page(S, PageHeight) -> EventsPerPage = ((PageHeight - (?initial_y * S#state.scale)) div (?incr_y * S#state.scale)), - lists:max([1, EventsPerPage]). + lists:max([1, EventsPerPage]). + +actors_per_page(S, PageWidth) -> + lists:max([1, (PageWidth - (?initial_x * S#state.scale)) div (S#state.actor_width * S#state.scale)]). select_file(Frame, Message, DefaultFile, Style) -> Dialog = wxFileDialog:new(Frame,