aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/observer/src/Makefile2
-rw-r--r--lib/observer/src/observer_defs.hrl2
-rw-r--r--lib/observer/src/observer_lib.erl77
-rw-r--r--lib/observer/src/observer_pro_wx.erl428
-rw-r--r--lib/observer/src/observer_procinfo.erl9
-rw-r--r--lib/observer/src/observer_trace_wx.erl31
-rw-r--r--lib/observer/src/observer_tv_table.erl19
-rw-r--r--lib/observer/src/observer_tv_wx.erl2
-rw-r--r--lib/observer/src/observer_wx.erl180
9 files changed, 403 insertions, 347 deletions
diff --git a/lib/observer/src/Makefile b/lib/observer/src/Makefile
index 9ea4118809..95954d8587 100644
--- a/lib/observer/src/Makefile
+++ b/lib/observer/src/Makefile
@@ -65,7 +65,7 @@ EXAMPLE_FILES= multitrace.erl
TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(APP_TARGET) $(APPUP_TARGET)
PRIVDIR= ../priv
-WEBTOOLFILES= $(PRIVDIR)/crashdump_viewer.tool
+WEBTOOLFILES= $(PRIVDIR)/crashdump_viewer.tool $(PRIVDIR)/erlang_observer.png
BINDIR= $(PRIVDIR)/bin
ifeq ($(findstring win32,$(TARGET)),win32)
WIN32_EXECUTABLES= $(BINDIR)/etop.bat $(BINDIR)/getop.bat $(BINDIR)/cdv.bat
diff --git a/lib/observer/src/observer_defs.hrl b/lib/observer/src/observer_defs.hrl
index 0af15f6422..567eb26e3b 100644
--- a/lib/observer/src/observer_defs.hrl
+++ b/lib/observer/src/observer_defs.hrl
@@ -47,3 +47,5 @@
type = append,
check = false
}).
+
+-record(attrs, {even, odd, deleted, changed, searched}).
diff --git a/lib/observer/src/observer_lib.erl b/lib/observer/src/observer_lib.erl
index cab9d5ccf2..b36aaa7541 100644
--- a/lib/observer/src/observer_lib.erl
+++ b/lib/observer/src/observer_lib.erl
@@ -21,9 +21,13 @@
-export([get_wx_parent/1,
display_info_dialog/1,
interval_dialog/4, start_timer/1, stop_timer/1,
- display_info/2, update_info/2, to_str/1]).
+ display_info/2, update_info/2, to_str/1,
+ create_menus/3, create_menu_item/3,
+ create_attrs/0
+ ]).
-include_lib("wx/include/wx.hrl").
+-include("observer_defs.hrl").
get_wx_parent(Window) ->
Parent = wxWindow:getParent(Window),
@@ -171,6 +175,77 @@ to_str(No) when is_integer(No) ->
to_str(ShouldNotGetHere) ->
erlang:error({?MODULE, to_str, ShouldNotGetHere}).
+create_menus(Menus, MenuBar, Type) ->
+ Add = fun({Tag, Ms}, Index) ->
+ create_menu(Tag, Ms, Index, MenuBar, Type)
+ end,
+ [{First, _}|_] = Menus,
+ OnMac = os:type() =:= {unix, darwin},
+ Index = if Type =:= default -> 0;
+ First =:= "File" -> 0;
+ OnMac -> 0;
+ true -> 1
+ end,
+ wx:foldl(Add, Index, Menus),
+ ok.
+
+create_menu("File", MenuItems, Index, MenuBar, Type) ->
+ OnMac = os:type() =:= {unix, darwin},
+ if OnMac, Type =:= default ->
+ Index;
+ not OnMac, Type =:= plugin ->
+ MenuId = wxMenuBar:findMenu(MenuBar, "File"),
+ Menu = wxMenuBar:getMenu(MenuBar, MenuId),
+ lists:foldl(fun(Record, N) ->
+ create_menu_item(Record, Menu, N)
+ end, 0, MenuItems),
+ Index + 1;
+ true ->
+ Menu = wxMenu:new(),
+ lists:foldl(fun(Record, N) ->
+ create_menu_item(Record, Menu, N)
+ end, 0, MenuItems),
+ wxMenuBar:insert(MenuBar, Index, Menu, "File"),
+ Index+1
+ end;
+create_menu(Name, MenuItems, Index, MenuBar, _Type) ->
+ Menu = wxMenu:new(),
+ lists:foldl(fun(Record, N) ->
+ create_menu_item(Record, Menu, N)
+ end, 0, MenuItems),
+ wxMenuBar:insert(MenuBar, Index, Menu, Name),
+ Index+1.
+
+create_menu_item(#create_menu{id = ?wxID_HELP=Id}, Menu, Index) ->
+ wxMenu:insert(Menu, Index, Id),
+ Index+1;
+create_menu_item(#create_menu{id = Id, text = Text, type = Type, check = Check}, Menu, Index) ->
+ case Type of
+ append ->
+ wxMenu:insert(Menu, Index, Id, [{text, Text}]);
+ check ->
+ wxMenu:insertCheckItem(Menu, Index, Id, Text),
+ wxMenu:check(Menu, Id, Check);
+ radio ->
+ wxMenu:insertRadioItem(Menu, Index, Id, Text),
+ wxMenu:check(Menu, Id, Check);
+ separator ->
+ wxMenu:insertSeparator(Menu, Index)
+ end,
+ Index+1;
+create_menu_item(separator, Menu, Index) ->
+ wxMenu:insertSeparator(Menu, Index),
+ Index+1.
+
+create_attrs() ->
+ Font = wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT),
+ Text = wxSystemSettings:getColour(?wxSYS_COLOUR_LISTBOXTEXT),
+ #attrs{even = wxListItemAttr:new(Text, {255,255,255}, Font),
+ odd = wxListItemAttr:new(Text, {240,240,255}, Font),
+ deleted = wxListItemAttr:new({240,30,30}, {100,100,100}, Font),
+ changed = wxListItemAttr:new(Text, {255,215,0}, Font),
+ searched = wxListItemAttr:new(Text, {235,215,90}, Font)
+ }.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/observer/src/observer_pro_wx.erl b/lib/observer/src/observer_pro_wx.erl
index c99abd371a..8e3e28caec 100644
--- a/lib/observer/src/observer_pro_wx.erl
+++ b/lib/observer/src/observer_pro_wx.erl
@@ -50,7 +50,6 @@
-define(ID_ACCUMULATE, 209).
%% Records
--record(attrs, {even, odd, deleted, changed, searched}).
-record(sort,
{
@@ -60,44 +59,43 @@
-record(holder, {parent,
info,
- sort = #sort{},
- accum = [],
+ sort=#sort{},
+ accum=[],
attrs,
node,
backend_pid
}).
--record(pro_wx_state, {parent,
- grid,
- panel,
- popup_menu,
- parent_notebook,
- trace_options = #trace_options{},
- match_specs = [],
- timer,
- tracemenu_opened,
- procinfo_menu_pids = [],
- selected_pids = [],
- last_selected,
- holder}).
+-record(state, {parent,
+ grid,
+ panel,
+ popup_menu,
+ parent_notebook,
+ trace_options=#trace_options{},
+ match_specs=[],
+ timer,
+ tracemenu_opened,
+ procinfo_menu_pids=[],
+ sel={[], []},
+ holder}).
start_link(Notebook, Parent) ->
wx_object:start_link(?MODULE, [Notebook, Parent], []).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
init([Notebook, Parent]) ->
- Attrs = create_attrs(),
+ Attrs = observer_lib:create_attrs(),
Self = self(),
Holder = spawn_link(fun() -> init_table_holder(Self, Attrs) end),
{ProPanel, State} = setup(Notebook, Parent, Holder),
MatchSpecs = generate_matchspecs(),
- {ProPanel, State#pro_wx_state{holder = Holder, match_specs = MatchSpecs}}.
+ {ProPanel, State#state{holder=Holder, match_specs=MatchSpecs}}.
setup(Notebook, Parent, Holder) ->
ProPanel = wxPanel:new(Notebook, []),
- Grid = create_list_box(ProPanel, Holder),
+ Grid = create_list_box(ProPanel, Holder),
Sizer = wxBoxSizer:new(?wxVERTICAL),
wxSizer:add(Sizer, Grid, [{flag, ?wxEXPAND bor ?wxALL},
{proportion, 1},
@@ -107,15 +105,15 @@ setup(Notebook, Parent, Holder) ->
Popup = create_popup_menu(ProPanel),
- State = #pro_wx_state{parent = Parent,
- grid = Grid,
- panel = ProPanel,
- popup_menu = Popup,
- parent_notebook = Notebook,
- tracemenu_opened = false,
- holder = Holder,
- timer = {false, 10}
- },
+ State = #state{parent=Parent,
+ grid=Grid,
+ panel=ProPanel,
+ popup_menu=Popup,
+ parent_notebook=Notebook,
+ tracemenu_opened=false,
+ holder=Holder,
+ timer={false, 10}
+ },
{ProPanel, State}.
generate_matchspecs() ->
@@ -147,18 +145,18 @@ generate_matchspecs() ->
create_pro_menu(Parent, Holder) ->
MenuEntries = [{"File",
- [#create_menu{id = ?ID_DUMP_TO_FILE, text = "Dump to file"}]},
+ [#create_menu{id=?ID_DUMP_TO_FILE, text="Dump to file"}]},
{"View",
- [#create_menu{id = ?ID_ACCUMULATE, text = "Accumulate",
- type = check,
- check = call(Holder, {get_accum, self()})},
+ [#create_menu{id=?ID_ACCUMULATE, text="Accumulate",
+ type=check,
+ check=call(Holder, {get_accum, self()})},
separator,
- #create_menu{id = ?ID_REFRESH, text = "Refresh\tCtrl-R"},
- #create_menu{id = ?ID_REFRESH_INTERVAL, text = "Refresh Interval"}]},
+ #create_menu{id=?ID_REFRESH, text="Refresh\tCtrl-R"},
+ #create_menu{id=?ID_REFRESH_INTERVAL, text="Refresh Interval"}]},
{"Trace",
- [#create_menu{id = ?ID_TRACEMENU, text = "Trace selected processes"},
- #create_menu{id = ?ID_TRACE_NEW_MENU, text = "Trace new processes"},
- #create_menu{id = ?ID_TRACE_ALL_MENU, text = "Trace all processes"}]}
+ [#create_menu{id=?ID_TRACEMENU, text="Trace selected processes"},
+ #create_menu{id=?ID_TRACE_NEW_MENU, text="Trace new processes"},
+ #create_menu{id=?ID_TRACE_ALL_MENU, text="Trace all processes"}]}
],
observer_wx:create_menus(Parent, MenuEntries).
@@ -213,59 +211,16 @@ create_list_box(Panel, Holder) ->
lists:foldl(AddListEntry, 0, ListItems),
wxListItem:destroy(Li),
+ wxListCtrl:setItemCount(ListCtrl, 1),
wxListCtrl:connect(ListCtrl, size, [{skip, true}]),
wxListCtrl:connect(ListCtrl, command_list_item_activated),
wxListCtrl:connect(ListCtrl, command_list_item_right_click),
wxListCtrl:connect(ListCtrl, command_list_col_click),
- wxListCtrl:connect(ListCtrl, command_list_item_selected),
+ %% Use focused instead of selected, selected doesn't generate events
+ %% for all multiple selections on Linux
+ wxListCtrl:connect(ListCtrl, command_list_item_focused),
ListCtrl.
-clear_all(Grid) ->
- lists:foreach(fun(I) ->
- wxListCtrl:setItemState(Grid, I, 0, ?wxLIST_STATE_SELECTED),
- wxListCtrl:setItemState(Grid, I, 0, ?wxLIST_STATE_FOCUSED)
- end,
- lists:seq(0, wxListCtrl:getItemCount(Grid))).
-
-set_selected_items(Grid, Holder, Pids) ->
- Count = wxListCtrl:getItemCount(Grid),
- set_selected_items(Grid, Holder, 0, Pids, Count, []).
-
-set_selected_items(_, _, Index, Pids, Max, Acc) when Pids =:= []; Index =:= Max ->
- Acc;
-set_selected_items(Grid, Holder, Index, Pids, Max, Acc) ->
- {ok, Pid} = call(Holder, {get_row, self(), Index, pid}),
- case lists:member(Pid, Pids) of
- true ->
- wxListCtrl:setItemState(Grid, Index,
- ?wxLIST_STATE_SELECTED,
- ?wxLIST_STATE_SELECTED),
- set_selected_items(Grid, Holder, Index+1, lists:delete(Pid, Pids), Max, [Pid | Acc]);
- false ->
- set_selected_items(Grid, Holder, Index+1, Pids, Max, Acc)
- end.
-
-get_selected_items(Grid) ->
- get_selected_items(Grid, -1, []).
-
-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+1 | ItemAcc])
- end.
-
-create_attrs() ->
- Font = wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT),
- Text = wxSystemSettings:getColour(?wxSYS_COLOUR_LISTBOXTEXT),
- #attrs{even = wx:typeCast(wx:null(), wxListItemAttr),
- odd = wxListItemAttr:new(Text, {240,240,255}, Font),
- searched = wxListItemAttr:new(Text, {235,215,90}, Font)
- }.
-
dump_to_file(Parent, FileName, Holder) ->
case file:open(FileName, [write]) of
{ok, Fd} ->
@@ -278,6 +233,8 @@ dump_to_file(Parent, FileName, Holder) ->
wxDialog:destroy(MD)
end.
+start_procinfo(_Node, undefined, _Frame, Opened) ->
+ Opened;
start_procinfo(Node, Pid, Frame, Opened) ->
case lists:member(Pid, Opened) of
true ->
@@ -296,57 +253,53 @@ call(Holder, What) ->
erlang:demonitor(Ref),
Res
after 2000 ->
- io:format("Hanging call ~p~n",[What])
+ io:format("Hanging call ~p~n",[What]),
+ ""
end.
%%%%%%%%%%%%%%%%%%%%%%% Callbacks %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-handle_info({holder_updated, Count}, #pro_wx_state{grid = Grid,
- holder = Holder,
- selected_pids = Pids} = State) ->
- Pids2 = wx:batch(fun() ->
- clear_all(Grid),
- Pids2 = set_selected_items(Grid, Holder, Pids),
- wxListCtrl:setItemCount(Grid, Count),
- wxListCtrl:refreshItems(Grid, 0, Count),
- Pids2
- end),
- {noreply, State#pro_wx_state{selected_pids = Pids2}};
-
-handle_info(refresh_interval, #pro_wx_state{holder = Holder} = State) ->
+handle_info({holder_updated, Count}, State0=#state{grid=Grid}) ->
+ State = update_selection(State0),
+
+ wxListCtrl:setItemCount(Grid, Count),
+ wxListCtrl:refreshItems(Grid, 0, Count-1),
+
+ {noreply, State};
+
+handle_info(refresh_interval, #state{holder=Holder}=State) ->
Holder ! refresh,
{noreply, State};
handle_info({tracemenu_closed, TraceOpts, MatchSpecs}, State) ->
- {noreply, State#pro_wx_state{tracemenu_opened = false,
- trace_options = TraceOpts,
- match_specs = MatchSpecs}};
+ {noreply, State#state{tracemenu_opened=false,
+ trace_options=TraceOpts,
+ match_specs=MatchSpecs}};
handle_info({procinfo_menu_closed, Pid},
- #pro_wx_state{procinfo_menu_pids = Opened} = State) ->
+ #state{procinfo_menu_pids=Opened}=State) ->
NewPids = lists:delete(Pid, Opened),
- {noreply, State#pro_wx_state{procinfo_menu_pids = NewPids}};
+ {noreply, State#state{procinfo_menu_pids=NewPids}};
handle_info({active, Node},
- #pro_wx_state{holder = Holder, timer = Timer, parent = Parent} = State) ->
+ #state{holder=Holder, timer=Timer, parent=Parent}=State) ->
create_pro_menu(Parent, Holder),
Holder ! {change_node, Node},
- {noreply, State#pro_wx_state{timer = observer_lib:start_timer(Timer)}};
+ {noreply, State#state{timer=observer_lib:start_timer(Timer)}};
-handle_info(not_active, #pro_wx_state{timer = Timer0} = State) ->
+handle_info(not_active, #state{timer=Timer0}=State) ->
Timer = observer_lib:stop_timer(Timer0),
- {noreply, State#pro_wx_state{timer=Timer, selected_pids = [], last_selected = undefined}};
+ {noreply, State#state{timer=Timer}};
-handle_info({node, Node}, #pro_wx_state{holder = Holder} = State) ->
+handle_info({node, Node}, #state{holder=Holder}=State) ->
Holder ! {change_node, Node},
- {noreply, State#pro_wx_state{selected_pids = [],
- last_selected = undefined}};
+ {noreply, State};
handle_info(Info, State) ->
io:format("~p, ~p, Handled unexpected info: ~p~n", [?MODULE, ?LINE, Info]),
{noreply, State}.
-terminate(Reason, #pro_wx_state{holder = Holder}) ->
+terminate(Reason, #state{holder=Holder}) ->
io:format("~p terminating. Reason: ~p~n", [?MODULE, Reason]),
Holder ! stop,
etop:stop(),
@@ -367,9 +320,7 @@ handle_cast(Msg, State) ->
%%%%%%%%%%%%%%%%%%%%LOOP%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-handle_event(#wx{id = ?ID_DUMP_TO_FILE},
- #pro_wx_state{panel = Panel,
- holder = Holder} = State) ->
+handle_event(#wx{id=?ID_DUMP_TO_FILE}, #state{panel=Panel, holder=Holder}=State) ->
FD = wxFileDialog:new(Panel,
[{style,?wxFD_SAVE bor ?wxFD_OVERWRITE_PROMPT}]),
case wxFileDialog:showModal(FD) of
@@ -382,51 +333,48 @@ handle_event(#wx{id = ?ID_DUMP_TO_FILE},
end,
{noreply, State};
-handle_event(#wx{id = ?ID_ACCUMULATE,
- event = #wxCommand{type = command_menu_selected, commandInt = CmdInt}},
- #pro_wx_state{holder = Holder} = State) ->
+handle_event(#wx{id=?ID_ACCUMULATE,
+ event=#wxCommand{type=command_menu_selected, commandInt=CmdInt}},
+ #state{holder=Holder}=State) ->
Holder ! {accum, CmdInt =:= 1},
{noreply, State};
-handle_event(#wx{id = ?ID_REFRESH, event = #wxCommand{type = command_menu_selected}},
- #pro_wx_state{holder = Holder} = State) ->
+handle_event(#wx{id=?ID_REFRESH, event=#wxCommand{type=command_menu_selected}},
+ #state{holder=Holder}=State) ->
Holder ! refresh,
{noreply, State};
-handle_event(#wx{id = ?ID_REFRESH_INTERVAL},
- #pro_wx_state{panel = Panel, timer=Timer0} = State) ->
+handle_event(#wx{id=?ID_REFRESH_INTERVAL},
+ #state{panel=Panel, timer=Timer0}=State) ->
Timer = observer_lib:interval_dialog(Panel, Timer0, 1, 5*60),
- {noreply, State#pro_wx_state{timer=Timer}};
-
-handle_event(#wx{id = ?ID_KILL}, #pro_wx_state{popup_menu = Pop,
- selected_pids = Pids,
- last_selected = ToKill} = State) ->
+ {noreply, State#state{timer=Timer}};
+handle_event(#wx{id=?ID_KILL},
+ #state{popup_menu=Pop,sel={[_|Ids], [ToKill|Pids]}}=State) ->
wxWindow:show(Pop, [{show, false}]),
exit(ToKill, kill),
- Pids2 = lists:delete(ToKill, Pids),
- {noreply, State#pro_wx_state{selected_pids = Pids2, last_selected = undefined}};
+ {noreply, State#state{sel={Ids,Pids}}};
-handle_event(#wx{id = ?ID_PROC},
- #pro_wx_state{holder=Holder,
- panel = Panel,
- popup_menu = Pop,
- last_selected = Pid,
- procinfo_menu_pids = Opened} = State) ->
+handle_event(#wx{id=?ID_PROC},
+ #state{panel=Panel,
+ popup_menu=Pop,
+ holder=Holder,
+ sel={_, [Pid|_]},
+ procinfo_menu_pids=Opened}=State) ->
wxWindow:show(Pop, [{show, false}]),
Node = call(Holder, {get_node, self()}),
Opened2 = start_procinfo(Node, Pid, Panel, Opened),
- {noreply, State#pro_wx_state{procinfo_menu_pids = Opened2}};
-
-handle_event(#wx{id = ?ID_TRACEMENU},
- #pro_wx_state{holder=Holder,
- popup_menu = Pop,
- trace_options = Options,
- match_specs = MatchSpecs,
- selected_pids = Pids,
- tracemenu_opened = false,
- panel = Panel} = State) ->
+ {noreply, State#state{procinfo_menu_pids=Opened2}};
+
+handle_event(#wx{id=?ID_TRACEMENU},
+ #state{holder=Holder,
+ popup_menu=Pop,
+ trace_options=Options,
+ match_specs=MatchSpecs,
+ sel=Pids,
+ tracemenu_opened=false,
+ panel=Panel}=State) ->
wxWindow:show(Pop, [{show, false}]),
case Pids of
[] ->
@@ -440,15 +388,15 @@ handle_event(#wx{id = ?ID_TRACEMENU},
MatchSpecs,
Panel,
self()),
- {noreply, State#pro_wx_state{tracemenu_opened = true}}
+ {noreply, State#state{tracemenu_opened=true}}
end;
-handle_event(#wx{id = ?ID_TRACE_ALL_MENU, event = #wxCommand{type = command_menu_selected}},
- #pro_wx_state{holder=Holder,
- trace_options = Options,
- match_specs = MatchSpecs,
- tracemenu_opened = false,
- panel = Panel} = State) ->
+handle_event(#wx{id=?ID_TRACE_ALL_MENU, event=#wxCommand{type=command_menu_selected}},
+ #state{holder=Holder,
+ trace_options=Options,
+ match_specs=MatchSpecs,
+ tracemenu_opened=false,
+ panel=Panel}=State) ->
Node = call(Holder, {get_node, self()}),
observer_trace_wx:start(Node,
all,
@@ -456,15 +404,15 @@ handle_event(#wx{id = ?ID_TRACE_ALL_MENU, event = #wxCommand{type = command_menu
MatchSpecs,
Panel,
self()),
- {noreply, State#pro_wx_state{tracemenu_opened = true}};
+ {noreply, State#state{tracemenu_opened=true}};
-handle_event(#wx{id = ?ID_TRACE_NEW_MENU, event = #wxCommand{type = command_menu_selected}},
- #pro_wx_state{holder=Holder,
- trace_options = Options,
- match_specs = MatchSpecs,
- tracemenu_opened = false,
- panel = Panel} = State) ->
+handle_event(#wx{id=?ID_TRACE_NEW_MENU, event=#wxCommand{type=command_menu_selected}},
+ #state{holder=Holder,
+ trace_options=Options,
+ match_specs=MatchSpecs,
+ tracemenu_opened=false,
+ panel=Panel}=State) ->
Node = call(Holder, {get_node, self()}),
observer_trace_wx:start(Node,
new,
@@ -472,10 +420,10 @@ handle_event(#wx{id = ?ID_TRACE_NEW_MENU, event = #wxCommand{type = command_menu
MatchSpecs,
Panel,
self()),
- {noreply, State#pro_wx_state{tracemenu_opened = true}};
+ {noreply, State#state{tracemenu_opened=true}};
handle_event(#wx{event=#wxSize{size={W,_}}},
- #pro_wx_state{grid=Grid} = State) ->
+ #state{grid=Grid}=State) ->
wx:batch(fun() ->
Cols = wxListCtrl:getColumnCount(Grid),
Last = lists:foldl(fun(I, Last) ->
@@ -486,10 +434,10 @@ handle_event(#wx{event=#wxSize{size={W,_}}},
end),
{noreply, State};
-handle_event(#wx{event = #wxList{type = command_list_item_right_click,
- itemIndex = Row}},
- #pro_wx_state{popup_menu = Popup,
- holder = Holder} = State) ->
+handle_event(#wx{event=#wxList{type=command_list_item_right_click,
+ itemIndex=Row}},
+ #state{popup_menu=Popup,
+ holder=Holder}=State) ->
case call(Holder, {get_row, self(), Row, pid}) of
{error, undefined} ->
@@ -501,55 +449,96 @@ handle_event(#wx{event = #wxList{type = command_list_item_right_click,
end,
{noreply, State};
-handle_event(#wx{event = #wxList{type = command_list_item_selected,
- itemIndex = Row}},
- #pro_wx_state{grid = Grid,
- popup_menu = Pop,
- holder = Holder} = State) ->
-
- NewPid = case call(Holder, {get_row, self(), Row, pid}) of
- {error, undefined} ->
- undefined;
- {ok, P} when is_pid(P) ->
- P
- end,
- wxWindow:show(Pop, [{show, false}]),
- Pids = call(Holder, {get_pids, self(), get_selected_items(Grid)}),
- {noreply, State#pro_wx_state{selected_pids = Pids,
- last_selected = NewPid}};
+handle_event(#wx{event=#wxList{type=command_list_item_focused,
+ itemIndex=Row}},
+ #state{grid=Grid,popup_menu=Pop,holder=Holder} = State) ->
+ case Row >= 0 of
+ true ->
+ wxWindow:show(Pop, [{show, false}]),
+ SelIds = [Row|lists:delete(Row, get_selected_items(Grid))],
+ Pids = call(Holder, {get_pids, self(), SelIds}),
+ %% io:format("Focused ~p -> ~p~n",[State#state.sel, {SelIds, Pids}]),
+ {noreply, State#state{sel={SelIds, Pids}}};
+ false ->
+ %% io:format("Focused -1~n",[]),
+ {noreply, State}
+ end;
-handle_event(#wx{event = #wxList{type = command_list_col_click, col = Col}},
- #pro_wx_state{holder = Holder} = State) ->
+handle_event(#wx{event=#wxList{type=command_list_col_click, col=Col}},
+ #state{holder=Holder}=State) ->
Holder ! {change_sort, Col},
{noreply, State};
-handle_event(#wx{event = #wxList{type = command_list_item_activated}},
- #pro_wx_state{holder=Holder,
- panel = Panel,
- procinfo_menu_pids= Opened,
- last_selected = Pid} = State) when Pid =/= undefined ->
+handle_event(#wx{event=#wxList{type=command_list_item_activated}},
+ #state{panel=Panel, procinfo_menu_pids=Opened,
+ holder=Holder,
+ sel={_, [Pid|_]}}=State)
+ when Pid =/= undefined ->
Node = call(Holder, {get_node, self()}),
- Opened2 = start_procinfo(Node, Pid, Panel, Opened),
- {noreply, State#pro_wx_state{procinfo_menu_pids = Opened2}};
+ Opened2=start_procinfo(Node, Pid, Panel, Opened),
+ {noreply, State#state{procinfo_menu_pids=Opened2}};
handle_event(Event, State) ->
io:format("~p~p, handle event ~p\n", [?MODULE, ?LINE, Event]),
{noreply, State}.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+update_selection(State=#state{holder=Holder, grid=Grid,
+ sel={SelIds0, SelPids0}}) ->
+ Sel = {SelIds,_SelPids} = call(Holder, {get_rows_from_pids, self(), SelPids0}),
+ set_focus(SelIds0, SelIds, Grid),
+ case SelIds =:= SelIds0 of
+ true -> ok;
+ false ->
+ wx:batch(fun() ->
+ [wxListCtrl:setItemState(Grid, I, 0, ?wxLIST_STATE_SELECTED) ||
+ I <- SelIds0],
+ [wxListCtrl:setItemState(Grid, I, 16#FFFF, ?wxLIST_STATE_SELECTED) ||
+ I <- SelIds]
+ end)
+ end,
+ %%io:format("Update ~p -> ~p~n",[{SelIds0, SelPids0}, Sel]),
+ State#state{sel=Sel}.
+
+get_selected_items(Grid) ->
+ get_selected_items(Grid, -1, []).
+
+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.
+
+set_focus([], [], _Grid) -> ok;
+set_focus([Same|_], [Same|_], _Grid) -> ok;
+set_focus([], [New|_], Grid) ->
+ wxListCtrl:setItemState(Grid, New, 16#FFFF, ?wxLIST_STATE_FOCUSED);
+set_focus([Old|_], [], Grid) ->
+ wxListCtrl:setItemState(Grid, Old, 0, ?wxLIST_STATE_FOCUSED);
+set_focus([Old|_], [New|_], Grid) ->
+ wxListCtrl:setItemState(Grid, Old, 0, ?wxLIST_STATE_FOCUSED),
+ wxListCtrl:setItemState(Grid, New, 16#FFFF, ?wxLIST_STATE_FOCUSED).
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%TABLE HOLDER%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
init_table_holder(Parent, Attrs) ->
Backend = spawn_link(node(), observer_backend,etop_collect,[self()]),
- table_holder(#holder{parent = Parent,
- info = #etop_info{procinfo=[]},
- node = node(),
- backend_pid = Backend,
- attrs = Attrs
+ table_holder(#holder{parent=Parent,
+ info=#etop_info{procinfo=[]},
+ node=node(),
+ backend_pid=Backend,
+ attrs=Attrs
}).
table_holder(#holder{info=#etop_info{procinfo=Info}, attrs=Attrs,
- node=Node, backend_pid=Backend} = S0) ->
+ node=Node, backend_pid=Backend}=S0) ->
receive
{get_row, From, Row, Col} ->
get_row(From, Row, Col, Info),
@@ -557,7 +546,7 @@ table_holder(#holder{info=#etop_info{procinfo=Info}, attrs=Attrs,
{get_attr, From, Row} ->
get_attr(From, Row, Attrs),
table_holder(S0);
- {Backend, EtopInfo = #etop_info{}} ->
+ {Backend, EtopInfo=#etop_info{}} ->
State = handle_update(EtopInfo, S0),
table_holder(State#holder{backend_pid=undefined});
refresh when is_pid(Backend)->
@@ -571,6 +560,10 @@ table_holder(#holder{info=#etop_info{procinfo=Info}, attrs=Attrs,
{get_pids, From, Indices} ->
get_pids(From, Indices, Info),
table_holder(S0);
+ {get_rows_from_pids, From, Pids} ->
+ get_rows_from_pids(From, Pids, Info),
+ table_holder(S0);
+
{get_node, From} ->
From ! {self(), Node},
table_holder(S0);
@@ -598,50 +591,48 @@ table_holder(#holder{info=#etop_info{procinfo=Info}, attrs=Attrs,
table_holder(S0)
end.
-change_sort(Col, S0 = #holder{parent=Parent, info=EI=#etop_info{procinfo=Data}, sort=Sort0}) ->
- {Sort, ProcInfo} = sort(Col, Sort0, Data),
+change_sort(Col, S0=#holder{parent=Parent, info=EI=#etop_info{procinfo=Data}, sort=Sort0}) ->
+ {Sort, ProcInfo}=sort(Col, Sort0, Data),
Parent ! {holder_updated, length(Data)},
S0#holder{info=EI#etop_info{procinfo=ProcInfo}, sort=Sort}.
change_accum(true, S0) ->
S0#holder{accum=true};
-change_accum(false, S0 = #holder{info=#etop_info{procinfo=Info}}) ->
+change_accum(false, S0=#holder{info=#etop_info{procinfo=Info}}) ->
self() ! refresh,
S0#holder{accum=lists:sort(Info)}.
handle_update(EI=#etop_info{procinfo=ProcInfo0},
- S0 = #holder{parent=Parent, sort=Sort=#sort{sort_key=KeyField}}) ->
+ S0=#holder{parent=Parent, sort=Sort=#sort{sort_key=KeyField}}) ->
{ProcInfo1, S1} = accum(ProcInfo0, S0),
{_SO, ProcInfo} = sort(KeyField, Sort#sort{sort_key=undefined}, ProcInfo1),
Parent ! {holder_updated, length(ProcInfo)},
S1#holder{info=EI#etop_info{procinfo=ProcInfo}}.
-accum(ProcInfo, State = #holder{accum=true}) ->
+accum(ProcInfo, State=#holder{accum=true}) ->
{ProcInfo, State};
-accum(ProcInfo0, State = #holder{accum=Previous}) ->
+accum(ProcInfo0, State=#holder{accum=Previous}) ->
ProcInfo = lists:sort(ProcInfo0),
{accum2(ProcInfo,Previous,[]), State#holder{accum=ProcInfo}}.
-accum2([PI = #etop_proc_info{pid=Pid, reds=Reds, runtime=RT}|PIs],
+accum2([PI=#etop_proc_info{pid=Pid, reds=Reds, runtime=RT}|PIs],
[#etop_proc_info{pid=Pid, reds=OldReds, runtime=OldRT}|Old], Acc) ->
accum2(PIs, Old, [PI#etop_proc_info{reds=Reds-OldReds, runtime=RT-OldRT}|Acc]);
-accum2(PIs = [#etop_proc_info{pid=Pid}|_], [#etop_proc_info{pid=OldPid}|Old], Acc)
+accum2(PIs=[#etop_proc_info{pid=Pid}|_], [#etop_proc_info{pid=OldPid}|Old], Acc)
when Pid > OldPid ->
accum2(PIs, Old, Acc);
accum2([PI|PIs], Old, Acc) ->
accum2(PIs, Old, [PI|Acc]);
accum2([], _, Acc) -> Acc.
-sort(Col, Opt = #sort{sort_key=Col, sort_incr=Bool}, Table) ->
+sort(Col, Opt=#sort{sort_key=Col, sort_incr=Bool}, Table) ->
{Opt#sort{sort_incr=not Bool}, lists:reverse(Table)};
sort(Col, S=#sort{sort_incr=true}, Table) ->
{S#sort{sort_key=Col}, lists:keysort(col_to_element(Col), Table)};
sort(Col, S=#sort{sort_incr=false}, Table) ->
{S#sort{sort_key=Col}, lists:reverse(lists:keysort(col_to_element(Col), Table))}.
-
-
-
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
get_procinfo_data(Col, Info) ->
element(col_to_element(Col), Info).
@@ -654,8 +645,8 @@ col_to_element(?COL_FUN) -> #etop_proc_info.cf;
col_to_element(?COL_MSG) -> #etop_proc_info.mq.
get_pids(From, Indices, ProcInfo) ->
- Processes = [lists:nth(I, ProcInfo) || I <- Indices],
- From ! {self(), [X#etop_proc_info.pid || X <- Processes]}.
+ Processes = [(lists:nth(I+1, ProcInfo))#etop_proc_info.pid || I <- Indices],
+ From ! {self(), Processes}.
get_row(From, Row, pid, Info) ->
Pid = case Row =:= -1 of
@@ -666,12 +657,21 @@ get_row(From, Row, pid, Info) ->
get_row(From, Row, Col, Info) ->
Data = case Row+1 > length(Info) of
true ->
- null;
+ "";
false ->
ProcInfo = lists:nth(Row+1, Info),
get_procinfo_data(Col, ProcInfo)
end,
- From ! {self(), io_lib:format("~p", [Data])}.
+ From ! {self(), observer_lib:to_str(Data)}.
+
+get_rows_from_pids(From, Pids0, Info) ->
+ Res = lists:foldl(fun(Pid, Data = {Ids, Pids}) ->
+ case index(Pid, Info, 0) of
+ false -> Data;
+ Index -> {[Index|Ids], [Pid|Pids]}
+ end
+ end, {[],[]}, Pids0),
+ From ! {self(), Res}.
get_attr(From, Row, Attrs) ->
Attribute = case Row rem 2 =:= 0 of
@@ -679,3 +679,7 @@ get_attr(From, Row, Attrs) ->
false -> Attrs#attrs.odd
end,
From ! {self(), Attribute}.
+
+index(Pid, [#etop_proc_info{pid=Pid}|_], Index) -> Index;
+index(Pid, [_|PI], Index) -> index(Pid, PI, Index+1);
+index(_, _, _) -> false.
diff --git a/lib/observer/src/observer_procinfo.erl b/lib/observer/src/observer_procinfo.erl
index b01b91c0b2..6414a26ec9 100644
--- a/lib/observer/src/observer_procinfo.erl
+++ b/lib/observer/src/observer_procinfo.erl
@@ -192,12 +192,9 @@ create_page(Notebook, Node, Module, What) ->
{Panel, Stc}.
create_menus(MenuBar) ->
- observer_wx:create_menu(
- [
- {"File", [#create_menu{id = ?CLOSE, text = "Close"}]},
- {"View", [#create_menu{id = ?REFRESH, text = "Refresh"}]}
- ],
- MenuBar).
+ Menus = [{"File", [#create_menu{id = ?CLOSE, text = "Close"}]},
+ {"View", [#create_menu{id = ?REFRESH, text = "Refresh"}]}],
+ observer_lib:create_menus(Menus, MenuBar, new_window).
check_boxes(CheckListBox, Bool, all) ->
lists:foreach(fun(Index) ->
diff --git a/lib/observer/src/observer_trace_wx.erl b/lib/observer/src/observer_trace_wx.erl
index 8e08b57b92..b79358193e 100644
--- a/lib/observer/src/observer_trace_wx.erl
+++ b/lib/observer/src/observer_trace_wx.erl
@@ -110,24 +110,17 @@ create_window(ParentFrame, TraceOpts) ->
trace_options = TraceOpts#trace_options{main_window = false}}.
create_menues(MenuBar) ->
- observer_wx:create_menu(
- [
- {"File", [
- #create_menu{id = ?LOAD_TRACEOPTS, text = "Load settings"},
- #create_menu{id = ?SAVE_TRACEOPTS, text = "Save settings"},
- separator,
- #create_menu{id = ?SAVE_BUFFER, text = "Save buffer"},
- separator,
- #create_menu{id = ?CLOSE, text = "Close"}
- ]},
- {"View", [
- #create_menu{id = ?CLEAR, text = "Clear buffer"}
- ]},
- {"Options", [
- #create_menu{id = ?OPTIONS, text = "Trace options"}
- ]}
- ],
- MenuBar).
+ Menus = [{"File", [#create_menu{id = ?LOAD_TRACEOPTS, text = "Load settings"},
+ #create_menu{id = ?SAVE_TRACEOPTS, text = "Save settings"},
+ separator,
+ #create_menu{id = ?SAVE_BUFFER, text = "Save buffer"},
+ separator,
+ #create_menu{id = ?CLOSE, text = "Close"}
+ ]},
+ {"View", [#create_menu{id = ?CLEAR, text = "Clear buffer"}]},
+ {"Options", [#create_menu{id = ?OPTIONS, text = "Trace options"}]}
+ ],
+ observer_lib:create_menus(Menus, MenuBar, new_window).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -370,7 +363,7 @@ textformat(Other) ->
io_lib:format("~p~n",[Other]).
-tuple_space(X) when is_tuple(X) -> print(size(X), X, "}");
+tuple_space(X) when is_tuple(X) -> print(tuple_size(X), X, "}");
tuple_space(X) -> io_lib:format("~p",[X]).
diff --git a/lib/observer/src/observer_tv_table.erl b/lib/observer/src/observer_tv_table.erl
index a2797700d8..d4990ec0ff 100644
--- a/lib/observer/src/observer_tv_table.erl
+++ b/lib/observer/src/observer_tv_table.erl
@@ -26,6 +26,7 @@
-export([get_table/3]).
+-include("observer_defs.hrl").
-import(observer_lib, [to_str/1]).
-behaviour(wx_object).
@@ -68,8 +69,6 @@
sort_incr=true
}).
--record(attrs, {even, odd, deleted, changed, searched}).
-
-record(search,
{enable=true, % Subwindow is enabled
win, % Sash Sub window obj
@@ -116,7 +115,7 @@ init([Parent, Opts]) ->
ColumnNames = column_names(Node, Source, TabId),
KeyPos = key_pos(Node, Source, TabId),
- Attrs = create_attrs(),
+ Attrs = observer_lib:create_attrs(),
Self = self(),
Holder = spawn_link(fun() ->
@@ -587,7 +586,7 @@ parse_ets_data([], Cols, Tab) ->
sort(Col, S=#holder{n=N, parent=Parent, sort=Opt0, table=Table0}) ->
{Opt, Table} = sort(Col, Opt0, Table0),
- Parent ! {refresh, 0, N},
+ Parent ! {refresh, 0, N-1},
S#holder{sort=Opt, table=Table}.
sort(Col, Opt = #opt{sort_key=Col, sort_incr=Bool}, Table) ->
@@ -595,7 +594,7 @@ sort(Col, Opt = #opt{sort_key=Col, sort_incr=Bool}, Table) ->
sort(Col, S=#opt{sort_incr=true}, Table) ->
{S#opt{sort_key=Col}, keysort(Col, Table)};
sort(Col, S=#opt{sort_incr=false}, Table) ->
- {S=#opt{sort_key=Col}, lists:reverse(keysort(Col, Table))}.
+ {S#opt{sort_key=Col}, lists:reverse(keysort(Col, Table))}.
keysort(Col, Table) ->
Sort = fun([A0|_], [B0|_]) ->
@@ -800,13 +799,3 @@ key_pos(Node, ets, TabId) ->
KeyPos = rpc:call(Node, ets, info, [TabId, keypos]),
is_integer(KeyPos) orelse throw(node_or_table_down),
KeyPos.
-
-create_attrs() ->
- Font = wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT),
- Text = wxSystemSettings:getColour(?wxSYS_COLOUR_LISTBOXTEXT),
- #attrs{even = wx:typeCast(wx:null(), wxListItemAttr),
- odd = wxListItemAttr:new(Text, {240,240,255}, Font),
- deleted = wxListItemAttr:new({240,30,30}, {100,100,100}, Font),
- changed = wxListItemAttr:new(Text, {255,215,0}, Font),
- searched = wxListItemAttr:new(Text, {235,215,90}, Font)
- }.
diff --git a/lib/observer/src/observer_tv_wx.erl b/lib/observer/src/observer_tv_wx.erl
index 230195c9de..dbf573151f 100644
--- a/lib/observer/src/observer_tv_wx.erl
+++ b/lib/observer/src/observer_tv_wx.erl
@@ -339,7 +339,7 @@ display_table_info(Parent0, Node, Source, Table) ->
Parent = observer_lib:get_wx_parent(Parent0),
Title = "Table Info: " ++ atom_to_list(Table#tab.name),
Frame = wxMiniFrame:new(Parent, ?wxID_ANY, Title,
- [{style, ?wxCAPTION bor ?wxCLOSE_BOX bor ?wxRESIZE_BORDER}]),
+ [{style, ?wxCAPTION bor ?wxCLOSE_BOX}]),
IdInfo = {"Identification and Owner",
[{"Name", Table#tab.name},
diff --git a/lib/observer/src/observer_wx.erl b/lib/observer/src/observer_wx.erl
index ef3c6bb304..4688e271ee 100644
--- a/lib/observer/src/observer_wx.erl
+++ b/lib/observer/src/observer_wx.erl
@@ -20,7 +20,7 @@
-behaviour(wx_object).
-export([start/0]).
--export([create_menus/2, create_menu/2, create_txt_dialog/4, try_rpc/4,
+-export([create_menus/2, create_txt_dialog/4, try_rpc/4,
return_to_localnode/2]).
-export([init/1, handle_event/2, handle_cast/2, terminate/2, code_change/3,
@@ -44,6 +44,7 @@
-record(state,
{frame,
menubar,
+ menus = [],
status_bar,
notebook,
main_panel,
@@ -86,7 +87,7 @@ setup(#state{frame = Frame} = State) ->
{Nodes, NodeMenus} = get_nodes(),
DefMenus = default_menus(NodeMenus),
- create_menu(DefMenus, MenuBar),
+ observer_lib:create_menus(DefMenus, MenuBar, default),
wxFrame:setMenuBar(Frame, MenuBar),
StatusBar = wxFrame:createStatusBar(Frame, []),
@@ -115,9 +116,9 @@ setup(#state{frame = Frame} = State) ->
wxSizer:add(MainSizer, Notebook, [{proportion, 1}, {flag, ?wxEXPAND}]),
wxPanel:setSizer(Panel, MainSizer),
- wxNotebook:connect(Notebook, command_notebook_page_changed),
+ wxNotebook:connect(Notebook, command_notebook_page_changing),
wxFrame:connect(Frame, close_window, [{skip, true}]),
- wxMenu:connect(Frame, command_menu_selected, [{skip, true}]),
+ wxMenu:connect(Frame, command_menu_selected),
SysPid = wx_object:get_pid(SysPanel),
SysPid ! {active, node()},
@@ -139,9 +140,9 @@ setup(#state{frame = Frame} = State) ->
%%Callbacks
handle_event(#wx{obj = Notebook, id = ?ID_NOTEBOOK,
- event = #wxNotebook{type = command_notebook_page_changed}},
+ event = Ev = #wxNotebook{type = command_notebook_page_changing}},
#state{active_tab=Previous, node=Node, notebook = Notebook} = State) ->
- io:format("Command notebook changed ~n"),
+ io:format("Command notebook changed ~p ~n", [Ev]),
Pid = get_active_pid(State),
Previous ! not_active,
Pid ! {active, Node},
@@ -151,13 +152,22 @@ handle_event(#wx{event = #wxClose{}}, State) ->
{stop, normal, State};
handle_event(#wx{id = ?wxID_EXIT, event = #wxCommand{type = command_menu_selected}}, State) ->
- io:format("~p ~p, you clicked close", [?MODULE, ?LINE]),
{stop, normal, State};
handle_event(#wx{id = ?wxID_HELP, event = #wxCommand{type = command_menu_selected}}, State) ->
io:format("~p ~p, you clicked help", [?MODULE, ?LINE]),
{noreply, State};
+handle_event(#wx{id = ?wxID_ABOUT, event = #wxCommand{type = command_menu_selected}},
+ State = #state{frame=Frame}) ->
+ AboutString = "Observe an erlang system\n"
+ "Authors: Olle Mattson & Magnus Eriksson & Dan Gudmundsson",
+ Style = [{style, ?wxOK bor ?wxSTAY_ON_TOP},
+ {caption, "About"}],
+ wxMessageDialog:showModal(wxMessageDialog:new(Frame, AboutString, Style)),
+ {noreply, State};
+
+
handle_event(#wx{id = ?ID_CONNECT, event = #wxCommand{type = command_menu_selected}},
#state{frame = Frame} = State) ->
UpdState = case create_connect_dialog(connect, State) of
@@ -192,11 +202,9 @@ handle_event(#wx{id = ?ID_CONNECT, event = #wxCommand{type = command_menu_select
handle_event(#wx{id = ?ID_PING, event = #wxCommand{type = command_menu_selected}},
#state{frame = Frame} = State) ->
UpdState = case create_connect_dialog(ping, State) of
- cancel ->
- State;
+ cancel -> State;
{value, Value} when is_list(Value) ->
try
-
Node = list_to_atom(Value),
case net_adm:ping(Node) of
pang ->
@@ -205,7 +213,6 @@ handle_event(#wx{id = ?ID_PING, event = #wxCommand{type = command_menu_selected}
pong ->
change_node_view(Node, State)
end
-
catch _:_ ->
create_txt_dialog(Frame, "Connect failed", "Pang", ?wxICON_EXCLAMATION),
State
@@ -229,15 +236,13 @@ handle_cast(Cast, State) ->
io:format("~p:~p: Got cast ~p~n", [?MODULE, ?LINE, Cast]),
{noreply, State}.
-handle_call({create_menus, TabMenus}, _From, State = #state{menubar=MenuBar}) ->
+handle_call({create_menus, TabMenus}, _From,
+ State = #state{menubar=MenuBar, menus=PrevTabMenus}) ->
wx:batch(fun() ->
- {_Nodes, NodeMenus} = get_nodes(),
- DefMenus = default_menus(NodeMenus),
- Menus = merge_menus(DefMenus, TabMenus),
- clean_menus(MenuBar),
- create_menu(Menus, MenuBar)
+ clean_menus(PrevTabMenus, MenuBar),
+ observer_lib:create_menus(TabMenus, MenuBar, plugin)
end),
- {reply, ok, State};
+ {reply, ok, State#state{menus=TabMenus}};
handle_call(Msg, _From, State) ->
io:format("~p~p: Got Call ~p~n",[?MODULE, ?LINE, Msg]),
@@ -308,6 +313,13 @@ connect(NodeName, 1, Cookie) ->
connect2(NodeName, longnames, Cookie).
connect2(NodeName, Opts, Cookie) ->
+ case net_adm:names() of
+ {ok, _} -> %% Epmd is running
+ ok;
+ {error, address} ->
+ Epmd = os:find_executable("epmd"),
+ os:cmd(Epmd)
+ end,
case net_kernel:start([NodeName, Opts]) of
{ok, _} ->
case is_alive() of
@@ -367,10 +379,11 @@ create_connect_dialog(connect, #state{frame = Frame}) ->
{style, ?wxHORIZONTAL}]),
NameText = wxStaticText:new(Dialog, ?wxID_ANY, "Node name: "),
- NameCtrl = wxTextCtrl:new(Dialog, ?wxID_ANY, [{size, {200, 25}}, {style, ?wxDEFAULT}]),
+ NameCtrl = wxTextCtrl:new(Dialog, ?wxID_ANY, [{size, {200, 25}}]),
wxTextCtrl:setValue(NameCtrl, "observer"),
CookieText = wxStaticText:new(Dialog, ?wxID_ANY, "Secret cookie: "),
- CookieCtrl = wxTextCtrl:new(Dialog, ?wxID_ANY, [{size, {200, 25}}, {style, ?wxDEFAULT}]),
+ CookieCtrl = wxTextCtrl:new(Dialog, ?wxID_ANY,
+ [{size, {200, 25}}, {style, ?wxTE_PASSWORD}]),
BtnSizer = wxDialog:createStdDialogButtonSizer(Dialog, ?wxID_DEFAULT),
Flags = [{flag, ?wxEXPAND bor ?wxALL}, {border, 5}],
@@ -390,13 +403,8 @@ create_connect_dialog(connect, #state{frame = Frame}) ->
CookiePath = filename:join(os:getenv("HOME"), ".erlang.cookie"),
DefaultCookie = case filelib:is_file(CookiePath) of
true ->
- {ok, IoDevice} = file:open(CookiePath, read),
- case file:read_line(IoDevice) of
- {ok, Cookie} ->
- Cookie;
- _ ->
- ""
- end;
+ {ok, Bin} = file:read_file(CookiePath),
+ binary_to_list(Bin);
false ->
""
end,
@@ -414,66 +422,57 @@ create_connect_dialog(connect, #state{frame = Frame}) ->
end.
default_menus(NodesMenuItems) ->
- FileMenu = {"File", [#create_menu{id = ?wxID_EXIT, text = "Quit"}]},
- HelpMenu = {"Help", [#create_menu{id = ?wxID_HELP, text = "Help"}]},
+ Quit = #create_menu{id = ?wxID_EXIT, text = "Quit"},
+ About = #create_menu{id = ?wxID_ABOUT, text = "About"},
+ Help = #create_menu{id = ?wxID_HELP},
NodeMenu = case erlang:is_alive() of
- true ->
- {"Nodes", NodesMenuItems ++
- [#create_menu{id = ?ID_PING, text = "Connect Node"}]};
- false ->
- {"Nodes", NodesMenuItems ++
- [#create_menu{id = ?ID_CONNECT, text = "Enable distribution"}]}
+ true -> {"Nodes", NodesMenuItems ++
+ [#create_menu{id = ?ID_PING, text = "Connect Node"}]};
+ false -> {"Nodes", NodesMenuItems ++
+ [#create_menu{id = ?ID_CONNECT, text = "Enable distribution"}]}
end,
- [FileMenu, NodeMenu, HelpMenu].
+ case os:type() =:= {unix, darwin} of
+ false ->
+ FileMenu = {"File", [Quit]},
+ HelpMenu = {"Help", [About,Help]},
+ [FileMenu, NodeMenu, HelpMenu];
+ true ->
+ %% On Mac quit and about will be moved to the "default' place
+ %% automagicly, so just add them to a menu that always exist.
+ %% But not to the help menu for some reason
+ {Tag, Menus} = NodeMenu,
+ [{Tag, Menus ++ [Quit,About]}, {"&Help", [Help]}]
+ end.
-clean_menus(MenuBar) ->
- Count = wxMenuBar:getMenuCount(MenuBar),
- remove_menu_item(MenuBar, Count).
+clean_menus(Menus, MenuBar) ->
+ remove_menu_items(Menus, MenuBar).
-remove_menu_item(MenuBar, Item) when Item > -1 ->
- Menu = wxMenuBar:getMenu(MenuBar, Item),
- wxMenuBar:remove(MenuBar, Item),
+remove_menu_items([{MenuStr = "File", Menus}|Rest], MenuBar) ->
+ MenuId = wxMenuBar:findMenu(MenuBar, MenuStr),
+ Menu = wxMenuBar:getMenu(MenuBar, MenuId),
+ Items = [wxMenu:findItem(Menu, Tag) || #create_menu{text=Tag} <- Menus],
+ [wxMenu:delete(Menu, MItem) || MItem <- Items],
+ case os:type() =:= {unix, darwin} of
+ true ->
+ wxMenuBar:remove(MenuBar, MenuId),
+ wxMenu:destroy(Menu);
+ false ->
+ ignore
+ end,
+ remove_menu_items(Rest, MenuBar);
+remove_menu_items([{"Nodes", _}|_], _MB) ->
+ ok;
+remove_menu_items([{Tag, _Menus}|Rest], MenuBar) ->
+ MenuId = wxMenuBar:findMenu(MenuBar, Tag),
+ Menu = wxMenuBar:getMenu(MenuBar, MenuId),
+ wxMenuBar:remove(MenuBar, MenuId),
+ Items = wxMenu:getMenuItems(Menu),
+ [wxMenu:'Destroy'(Menu, Item) || Item <- Items],
wxMenu:destroy(Menu),
- remove_menu_item(MenuBar, Item-1);
-remove_menu_item(_, _) ->
- ok.
-
-merge_menus([{Label, Items}|Default], [{Label, TabItems}|TabMenus]) ->
- [{Label, TabItems ++ Items} | merge_menus(Default, TabMenus)];
-merge_menus([Menu = {"File", _}|Default], TabMenus) ->
- [Menu | merge_menus(Default, TabMenus)];
-merge_menus(Default = [{"Nodes", _}|_], TabMenus) ->
- TabMenus ++ Default.
-
-create_menu(Menus, MenuBar) ->
- Add = fun({Name, MenuItems}) ->
- Menu = wxMenu:new(),
- lists:foreach(fun(Record) ->
- create_menu_item(Record, Menu)
- end,
- MenuItems),
- wxMenuBar:append(MenuBar, Menu, Name)
- end,
- wx:foreach(Add, Menus),
+ remove_menu_items(Rest, MenuBar);
+remove_menu_items([], _MB) ->
ok.
-create_menu_item(#create_menu{id = Id, text = Text, type = Type, check = Check}, Menu) ->
- case Type of
- append ->
- wxMenu:append(Menu, Id, Text);
- check ->
- wxMenu:appendCheckItem(Menu, Id, Text),
- wxMenu:check(Menu, Id, Check);
- radio ->
- wxMenu:appendRadioItem(Menu, Id, Text),
- wxMenu:check(Menu, Id, Check);
- separator ->
- wxMenu:appendSeparator(Menu)
- end;
-create_menu_item(separator, Menu) ->
- wxMenu:appendSeparator(Menu).
-
-
get_nodes() ->
Nodes = [node()| nodes()],
{_, Menues} =
@@ -489,19 +488,16 @@ update_node_list(State = #state{menubar=MenuBar}) ->
{Nodes, NodesMenuItems} = get_nodes(),
NodeMenuId = wxMenuBar:findMenu(MenuBar, "Nodes"),
NodeMenu = wxMenuBar:getMenu(MenuBar, NodeMenuId),
- wx:foreach(fun(Item) ->
- wxMenu:'Destroy'(NodeMenu, Item)
- end,
+ wx:foreach(fun(Item) -> wxMenu:'Destroy'(NodeMenu, Item) end,
wxMenu:getMenuItems(NodeMenu)),
- wx:foreach(fun(Record) ->
- create_menu_item(Record, NodeMenu)
- end, NodesMenuItems),
+ Index = wx:foldl(fun(Record, Index) ->
+ observer_lib:create_menu_item(Record, NodeMenu, Index)
+ end, 0, NodesMenuItems),
- case erlang:is_alive() of
- true ->
- create_menu_item(#create_menu{id = ?ID_PING, text = "Connect node"}, NodeMenu);
- false ->
- create_menu_item(#create_menu{id = ?ID_CONNECT, text = "Enable distribution"}, NodeMenu)
- end,
+ Dist = case erlang:is_alive() of
+ true -> #create_menu{id = ?ID_PING, text = "Connect node"};
+ false -> #create_menu{id = ?ID_CONNECT, text = "Enable distribution"}
+ end,
+ observer_lib:create_menu_item(Dist, NodeMenu, Index),
State#state{nodes = Nodes}.