diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/observer/src/Makefile | 2 | ||||
-rw-r--r-- | lib/observer/src/observer_defs.hrl | 2 | ||||
-rw-r--r-- | lib/observer/src/observer_lib.erl | 77 | ||||
-rw-r--r-- | lib/observer/src/observer_pro_wx.erl | 428 | ||||
-rw-r--r-- | lib/observer/src/observer_procinfo.erl | 9 | ||||
-rw-r--r-- | lib/observer/src/observer_trace_wx.erl | 31 | ||||
-rw-r--r-- | lib/observer/src/observer_tv_table.erl | 19 | ||||
-rw-r--r-- | lib/observer/src/observer_tv_wx.erl | 2 | ||||
-rw-r--r-- | lib/observer/src/observer_wx.erl | 180 |
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}. |