From e4c25c20f548c068057547ad2095a4c65afc0d86 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 29 Apr 2016 12:00:23 +0200 Subject: [observer] Allow multiple select in Ports tab This is helpful when selecting ports to be traced. --- lib/observer/src/observer_app_wx.erl | 8 +- lib/observer/src/observer_port_wx.erl | 193 +++++++++++++++++++++------------ lib/observer/src/observer_pro_wx.erl | 6 +- lib/observer/src/observer_trace_wx.erl | 10 +- 4 files changed, 134 insertions(+), 83 deletions(-) diff --git a/lib/observer/src/observer_app_wx.erl b/lib/observer/src/observer_app_wx.erl index cef83037d0..936b2783e2 100644 --- a/lib/observer/src/observer_app_wx.erl +++ b/lib/observer/src/observer_app_wx.erl @@ -221,21 +221,21 @@ handle_event(#wx{id=?ID_PROC_KILL, event=#wxCommand{type=command_menu_selected}} %%% Trace api handle_event(#wx{id=?ID_TRACE_PID, event=#wxCommand{type=command_menu_selected}}, State = #state{sel={Box,_}}) -> - observer_trace_wx:add_processes(observer_wx:get_tracer(), [box_to_pid(Box)]), + observer_trace_wx:add_processes([box_to_pid(Box)]), {noreply, State}; handle_event(#wx{id=?ID_TRACE_NAME, event=#wxCommand{type=command_menu_selected}}, State = #state{sel={Box,_}}) -> - observer_trace_wx:add_processes(observer_wx:get_tracer(), [box_to_reg(Box)]), + observer_trace_wx:add_processes([box_to_reg(Box)]), {noreply, State}; handle_event(#wx{id=?ID_TRACE_TREE_PIDS, event=#wxCommand{type=command_menu_selected}}, State = #state{sel=Sel}) -> Get = fun(Box) -> box_to_pid(Box) end, - observer_trace_wx:add_processes(observer_wx:get_tracer(), tree_map(Sel, Get)), + observer_trace_wx:add_processes(tree_map(Sel, Get)), {noreply, State}; handle_event(#wx{id=?ID_TRACE_TREE_NAMES, event=#wxCommand{type=command_menu_selected}}, State = #state{sel=Sel}) -> Get = fun(Box) -> box_to_reg(Box) end, - observer_trace_wx:add_processes(observer_wx:get_tracer(), tree_map(Sel, Get)), + observer_trace_wx:add_processes(tree_map(Sel, Get)), {noreply, State}; handle_event(Event, _State) -> diff --git a/lib/observer/src/observer_port_wx.erl b/lib/observer/src/observer_port_wx.erl index a09637161f..c8548e176e 100644 --- a/lib/observer/src/observer_port_wx.erl +++ b/lib/observer/src/observer_port_wx.erl @@ -32,11 +32,12 @@ -define(ID_REFRESH, 301). -define(ID_REFRESH_INTERVAL, 302). -define(ID_PORT_INFO, 303). --define(ID_TRACE_PORTS, 304). --define(ID_TRACE_NAMES, 305). --define(ID_TRACE_NEW, 306). --define(ID_TRACE_ALL, 307). --define(ID_CLOSE_PORT, 308). +-define(ID_PORT_INFO_SELECTED, 304). +-define(ID_TRACE_PORTS, 305). +-define(ID_TRACE_NAMES, 306). +-define(ID_TRACE_NEW, 307). +-define(ID_TRACE_ALL, 308). +-define(ID_CLOSE_PORT, 309). -define(TRACE_PORTS_STR, "Trace selected ports"). -define(TRACE_NAMES_STR, "Trace selected ports, " @@ -64,7 +65,7 @@ panel, node=node(), opt=#opt{}, - selected, + right_clicked_port, ports, timer, open_wins=[] @@ -76,7 +77,7 @@ start_link(Notebook, Parent) -> init([Notebook, Parent]) -> Panel = wxPanel:new(Notebook), Sizer = wxBoxSizer:new(?wxVERTICAL), - Style = ?wxLC_REPORT bor ?wxLC_SINGLE_SEL bor ?wxLC_HRULES, + Style = ?wxLC_REPORT bor ?wxLC_HRULES, Grid = wxListCtrl:new(Panel, [{winid, ?GRID}, {style, Style}]), wxSizer:add(Sizer, Grid, [{flag, ?wxEXPAND bor ?wxALL}, {proportion, 1}, {border, 5}]), @@ -99,7 +100,6 @@ init([Notebook, Parent]) -> wxListCtrl:connect(Grid, command_list_item_right_click), wxListCtrl:connect(Grid, command_list_item_activated), - wxListCtrl:connect(Grid, command_list_item_selected), wxListCtrl:connect(Grid, command_list_col_click), wxListCtrl:connect(Grid, size, [{skip, true}]), @@ -143,76 +143,92 @@ handle_event(#wx{event=#wxList{type=command_list_item_activated, NewOpened = display_port_info(Grid, Port, Opened), {noreply, State#state{open_wins=NewOpened}}; -handle_event(#wx{event=#wxList{type=command_list_item_right_click}}, - State=#state{panel=Panel}) -> - - Menu = wxMenu:new(), - wxMenu:append(Menu, ?ID_PORT_INFO, "Port info"), - wxMenu:append(Menu, ?ID_TRACE_PORTS, "Trace ports", - [{help, ?TRACE_PORTS_STR}]), - wxMenu:append(Menu, ?ID_TRACE_NAMES, "Trace named ports (all nodes)", - [{help, ?TRACE_NAMES_STR}]), - wxMenu:append(Menu, ?ID_CLOSE_PORT, "Close Port"), - wxWindow:popupMenu(Panel, Menu), - wxMenu:destroy(Menu), - {noreply, State}; - -handle_event(#wx{event=#wxList{type=command_list_item_selected, itemIndex=Index}}, - State) -> - {noreply, State#state{selected=Index}}; +handle_event(#wx{event=#wxList{type=command_list_item_right_click, + itemIndex=Index}}, + State=#state{panel=Panel, ports=Ports}) -> + case Index of + -1 -> + {noreply, State}; + _ -> + Port = lists:nth(Index+1, Ports), + Menu = wxMenu:new(), + wxMenu:append(Menu, ?ID_PORT_INFO, + "Port info for " ++ erlang:port_to_list(Port#port.id)), + wxMenu:append(Menu, ?ID_TRACE_PORTS, + "Trace selected ports", + [{help, ?TRACE_PORTS_STR}]), + wxMenu:append(Menu, ?ID_TRACE_NAMES, + "Trace selected ports by name (all nodes)", + [{help, ?TRACE_NAMES_STR}]), + wxMenu:append(Menu, ?ID_CLOSE_PORT, + "Close " ++ erlang:port_to_list(Port#port.id)), + wxWindow:popupMenu(Panel, Menu), + wxMenu:destroy(Menu), + {noreply, State#state{right_clicked_port=Port}} + end; handle_event(#wx{id=?ID_PORT_INFO}, - State = #state{grid=Grid, ports=Ports, - selected=Sel, open_wins=Opened}) -> - case Sel of + State = #state{grid=Grid, right_clicked_port=Port, + open_wins=Opened}) -> + case Port of undefined -> {noreply, State}; - R when is_integer(R) -> - Port = lists:nth(Sel+1, Ports), + _ -> NewOpened = display_port_info(Grid, Port, Opened), - {noreply, State#state{open_wins = NewOpened}} + {noreply, State#state{right_clicked_port=undefined, + open_wins=NewOpened}} end; -handle_event(#wx{id=?ID_CLOSE_PORT}, State = #state{selected=Sel, ports=Ports}) -> - case Sel of - undefined -> +handle_event(#wx{id=?ID_PORT_INFO_SELECTED}, + State = #state{grid=Grid, ports=Ports, open_wins=Opened}) -> + case get_selected_items(Grid,Ports) of + [] -> + observer_wx:create_txt_dialog(State#state.panel, "No selected ports", + "Port Info", ?wxICON_EXCLAMATION), {noreply, State}; - R when is_integer(R) -> - Port = lists:nth(Sel+1, Ports), - erlang:port_close(Port#port.id), - {noreply, State#state{selected=undefined}} + Selected -> + NewOpened = lists:foldl(fun(P,O) -> display_port_info(Grid, P, O) end, + Opened, Selected), + {noreply, State#state{open_wins = NewOpened}} end; -handle_event(#wx{id=?ID_TRACE_PORTS}, #state{selected=Sel, ports=Ports}=State) -> - case Sel of +handle_event(#wx{id=?ID_CLOSE_PORT}, State = #state{right_clicked_port=Port}) -> + case Port of undefined -> - observer_wx:create_txt_dialog(State#state.panel, "No selected port", - "Tracer", ?wxICON_EXCLAMATION), {noreply, State}; - R when is_integer(R) -> - Port = lists:nth(Sel+1, Ports), - observer_trace_wx:add_ports(observer_wx:get_tracer(), [Port#port.id]), - {noreply, State} - end; + _ -> + erlang:port_close(Port#port.id), + {noreply, State#state{right_clicked_port=undefined}} + end; + +handle_event(#wx{id=?ID_TRACE_PORTS}, #state{grid=Grid, ports=Ports}=State) -> + case get_selected_items(Grid, Ports) of + [] -> + observer_wx:create_txt_dialog(State#state.panel, "No selected ports", + "Tracer", ?wxICON_EXCLAMATION); + Selected -> + SelectedIds = [Port#port.id || Port <- Selected], + observer_trace_wx:add_ports(SelectedIds) + end, + {noreply, State}; -handle_event(#wx{id=?ID_TRACE_NAMES}, #state{selected=Sel, ports=Ports}=State) -> - case Sel of - undefined -> - observer_wx:create_txt_dialog(State#state.panel, "No selected port", - "Tracer", ?wxICON_EXCLAMATION), - {noreply, State}; - R when is_integer(R) -> - Port = lists:nth(Sel+1, Ports), - IdOrReg = case Port#port.name of - ignore -> Port#port.id; - Name -> Name - end, - observer_trace_wx:add_ports(observer_wx:get_tracer(), [IdOrReg]), - {noreply, State} - end; +handle_event(#wx{id=?ID_TRACE_NAMES}, #state{grid=Grid, ports=Ports}=State) -> + case get_selected_items(Grid, Ports) of + [] -> + observer_wx:create_txt_dialog(State#state.panel, "No selected ports", + "Tracer", ?wxICON_EXCLAMATION); + Selected -> + IdsOrRegs = + [case Port#port.name of + undefined -> Port#port.id; + Name -> Name + end || Port <- Selected], + observer_trace_wx:add_ports(IdsOrRegs) + end, + {noreply, State}; handle_event(#wx{id=?ID_TRACE_NEW, event=#wxCommand{type=command_menu_selected}}, State) -> - observer_trace_wx:add_ports(observer_wx:get_tracer(), [new_ports]), + observer_trace_wx:add_ports([new_ports]), {noreply, State}; handle_event(#wx{id=?ID_REFRESH_INTERVAL}, @@ -292,11 +308,18 @@ code_change(_, _, State) -> create_menus(Parent) -> MenuEntries = [{"View", - [#create_menu{id = ?ID_PORT_INFO, text = "Port information\tCtrl-I"}, + [#create_menu{id = ?ID_PORT_INFO_SELECTED, + text = "Port info for selected ports\tCtrl-I"}, separator, #create_menu{id = ?ID_REFRESH, text = "Refresh\tCtrl-R"}, #create_menu{id = ?ID_REFRESH_INTERVAL, text = "Refresh Interval..."} - ]}], + ]}, + {"Trace", + [#create_menu{id=?ID_TRACE_PORTS, text="Trace selected ports"}, + #create_menu{id=?ID_TRACE_NAMES, text="Trace selected ports by name (all nodes)"}, + #create_menu{id=?ID_TRACE_NEW, text="Trace new ports"} + ]} + ], observer_wx:create_menus(Parent, MenuEntries). get_ports(Node) -> @@ -332,9 +355,9 @@ list_to_portrec(PL) -> id_str = erlang:port_to_list(PortId), slot = proplists:get_value(id, PL), connected = proplists:get_value(connected, PL), - links = proplists:get_value(links, PL, ignore), - name = proplists:get_value(registered_name, PL, ignore), - monitors = proplists:get_value(monitors, PL, ignore), + links = proplists:get_value(links, PL, []), + name = proplists:get_value(registered_name, PL, []), + monitors = proplists:get_value(monitors, PL, []), controls = proplists:get_value(name, PL)}. portrec_to_list(#port{id = Id, @@ -388,9 +411,16 @@ port_info_fields(Port) -> {"Controls", controls}]}, {scroll_boxes, [{"Links",1,{click,links}}, - {"Monitors",1,{click,monitors}}]}], + {"Monitors",1,{click,filter_monitor_info()}}]}], observer_lib:fill_info(Struct, Port). +filter_monitor_info() -> + fun(Data) -> + Ms = proplists:get_value(monitors, Data), + [Pid || {process, Pid} <- Ms] + end. + + handle_error(Foo) -> Str = io_lib:format("ERROR: ~s~n",[Foo]), observer_lib:display_info_dialog(Str). @@ -412,8 +442,7 @@ update_grid2(Grid, #opt{sort_key=Sort,sort_incr=Dir}, Ports) -> true -> ignore end, - lists:foreach(fun({_, ignore}) -> ignore; - ({Col, Val}) -> + lists:foreach(fun({Col, Val}) -> wxListCtrl:setItem(Grid, Row, Col, observer_lib:to_str(Val)) end, @@ -426,3 +455,25 @@ update_grid2(Grid, #opt{sort_key=Sort,sort_incr=Dir}, Ports) -> end, lists:foldl(Update, 0, PortInfo), PortInfo. + + +get_selected_items(Grid, Data) -> + get_indecies(get_selected_items(Grid, -1, []), Data). +get_selected_items(Grid, Index, ItemAcc) -> + Item = wxListCtrl:getNextItem(Grid, Index, [{geometry, ?wxLIST_NEXT_ALL}, + {state, ?wxLIST_STATE_SELECTED}]), + case Item of + -1 -> + lists:reverse(ItemAcc); + _ -> + get_selected_items(Grid, Item, [Item | ItemAcc]) + end. + +get_indecies(Items, Data) -> + get_indecies(Items, 0, Data). +get_indecies([I|Rest], I, [H|T]) -> + [H|get_indecies(Rest, I+1, T)]; +get_indecies(Rest = [_|_], I, [_|T]) -> + get_indecies(Rest, I+1, T); +get_indecies(_, _, _) -> + []. diff --git a/lib/observer/src/observer_pro_wx.erl b/lib/observer/src/observer_pro_wx.erl index 6296f89e7f..ee6829b847 100644 --- a/lib/observer/src/observer_pro_wx.erl +++ b/lib/observer/src/observer_pro_wx.erl @@ -321,7 +321,7 @@ handle_event(#wx{id=?ID_TRACE_PIDS}, #state{sel={_, Pids}, panel=Panel}=State) observer_wx:create_txt_dialog(Panel, "No selected processes", "Tracer", ?wxICON_EXCLAMATION), {noreply, State}; Pids -> - observer_trace_wx:add_processes(observer_wx:get_tracer(), Pids), + observer_trace_wx:add_processes(Pids), {noreply, State} end; @@ -332,12 +332,12 @@ handle_event(#wx{id=?ID_TRACE_NAMES}, #state{sel={SelIds,_Pids}, holder=Holder, {noreply, State}; _ -> PidsOrReg = call(Holder, {get_name_or_pid, self(), SelIds}), - observer_trace_wx:add_processes(observer_wx:get_tracer(), PidsOrReg), + observer_trace_wx:add_processes(PidsOrReg), {noreply, State} end; handle_event(#wx{id=?ID_TRACE_NEW, event=#wxCommand{type=command_menu_selected}}, State) -> - observer_trace_wx:add_processes(observer_wx:get_tracer(), [new_processes]), + observer_trace_wx:add_processes([new_processes]), {noreply, State}; handle_event(#wx{event=#wxSize{size={W,_}}}, diff --git a/lib/observer/src/observer_trace_wx.erl b/lib/observer/src/observer_trace_wx.erl index ac8c6cdc4f..dfb4857c0b 100644 --- a/lib/observer/src/observer_trace_wx.erl +++ b/lib/observer/src/observer_trace_wx.erl @@ -19,7 +19,7 @@ -module(observer_trace_wx). --export([start_link/2, add_processes/2, add_ports/2]). +-export([start_link/2, add_processes/1, add_ports/1]). -export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3, handle_event/2, handle_cast/2]). @@ -83,11 +83,11 @@ start_link(Notebook, ParentPid) -> wx_object:start_link(?MODULE, [Notebook, ParentPid], []). -add_processes(Tracer, Pids) when is_list(Pids) -> - wx_object:cast(Tracer, {add_processes, Pids}). +add_processes(Pids) when is_list(Pids) -> + wx_object:cast(observer_wx:get_tracer(), {add_processes, Pids}). -add_ports(Tracer, Ports) when is_list(Ports) -> - wx_object:cast(Tracer, {add_ports, Ports}). +add_ports(Ports) when is_list(Ports) -> + wx_object:cast(observer_wx:get_tracer(), {add_ports, Ports}). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -- cgit v1.2.3