aboutsummaryrefslogtreecommitdiffstats
path: root/lib/observer/src/observer_lib.erl
diff options
context:
space:
mode:
authorSiri Hansen <[email protected]>2013-07-11 11:27:29 +0200
committerDan Gudmundsson <[email protected]>2014-01-27 15:52:52 +0100
commite2d565532d25024c1c0552d8eaaddf90eed88629 (patch)
treeca6f09ddf4bde35b10868746f4ee2a33e3edea7e /lib/observer/src/observer_lib.erl
parent7d4e5e2458e0627882f49078b9757dd6ae21a6fe (diff)
downloadotp-e2d565532d25024c1c0552d8eaaddf90eed88629.tar.gz
otp-e2d565532d25024c1c0552d8eaaddf90eed88629.tar.bz2
otp-e2d565532d25024c1c0552d8eaaddf90eed88629.zip
observer: add wx version of crashdump_viewer
The old web base crashdump_viewer is now removed.
Diffstat (limited to 'lib/observer/src/observer_lib.erl')
-rw-r--r--lib/observer/src/observer_lib.erl225
1 files changed, 202 insertions, 23 deletions
diff --git a/lib/observer/src/observer_lib.erl b/lib/observer/src/observer_lib.erl
index f7712cf3da..ce85fb4af6 100644
--- a/lib/observer/src/observer_lib.erl
+++ b/lib/observer/src/observer_lib.erl
@@ -19,17 +19,24 @@
-module(observer_lib).
-export([get_wx_parent/1,
- display_info_dialog/1, user_term/3, user_term_multiline/3,
+ display_info_dialog/1, display_yes_no_dialog/1,
+ user_term/3, user_term_multiline/3,
interval_dialog/4, start_timer/1, stop_timer/1,
display_info/2, fill_info/2, update_info/2, to_str/1,
create_menus/3, create_menu_item/3,
create_attrs/0,
- set_listctrl_col_size/2
+ set_listctrl_col_size/2,
+ create_status_bar/1,
+ html_window/2
]).
-include_lib("wx/include/wx.hrl").
-include("observer_defs.hrl").
+-define(SINGLE_LINE_STYLE, ?wxBORDER_NONE bor ?wxTE_READONLY bor ?wxTE_RICH2).
+-define(MULTI_LINE_STYLE, ?SINGLE_LINE_STYLE bor ?wxTE_MULTILINE).
+
+
get_wx_parent(Window) ->
Parent = wxWindow:getParent(Window),
case wx:is_null(Parent) of
@@ -101,6 +108,12 @@ display_info_dialog(Str) ->
wxMessageDialog:destroy(Dlg),
ok.
+display_yes_no_dialog(Str) ->
+ Dlg = wxMessageDialog:new(wx:null(), Str, [{style,?wxYES_NO}]),
+ R = wxMessageDialog:showModal(Dlg),
+ wxMessageDialog:destroy(Dlg),
+ R.
+
%% display_info(Parent, [{Title, [{Label, Info}]}]) -> {Panel, Sizer, InfoFieldsToUpdate}
display_info(Frame, Info) ->
Panel = wxPanel:new(Frame),
@@ -108,24 +121,50 @@ display_info(Frame, Info) ->
Sizer = wxBoxSizer:new(?wxVERTICAL),
wxSizer:addSpacer(Sizer, 5),
Add = fun(BoxInfo) ->
- {Box, InfoFs} = create_box(Panel, BoxInfo),
- wxSizer:add(Sizer, Box, [{flag, ?wxEXPAND bor ?wxALL},
- {border, 5}]),
- wxSizer:addSpacer(Sizer, 5),
- InfoFs
+ case create_box(Panel, BoxInfo) of
+ {Box, InfoFs} ->
+ wxSizer:add(Sizer, Box, [{flag, ?wxEXPAND bor ?wxALL},
+ {border, 5}]),
+ wxSizer:addSpacer(Sizer, 5),
+ InfoFs;
+ undefined ->
+ []
+ end
end,
InfoFs = [Add(I) || I <- Info],
wxWindow:setSizerAndFit(Panel, Sizer),
{Panel, Sizer, InfoFs}.
+fill_info([{dynamic, Key}|Rest], Data)
+ when is_atom(Key); is_function(Key) ->
+ %% Special case used by crashdump_viewer when the value decides
+ %% which header to use
+ case get_value(Key, Data) of
+ undefined -> fill_info(Rest, Data);
+ {Str,Value} -> [{Str, Value} | fill_info(Rest, Data)]
+ end;
fill_info([{Str, Key}|Rest], Data) when is_atom(Key); is_function(Key) ->
- [{Str, get_value(Key, Data)} | fill_info(Rest, Data)];
+ case get_value(Key, Data) of
+ undefined -> fill_info(Rest, Data);
+ Value -> [{Str, Value} | fill_info(Rest, Data)]
+ end;
+fill_info([{Str,Attrib,Key}|Rest], Data) when is_atom(Key); is_function(Key) ->
+ case get_value(Key, Data) of
+ undefined -> fill_info(Rest, Data);
+ Value -> [{Str,Attrib,Value} | fill_info(Rest, Data)]
+ end;
fill_info([{Str, {Format, Key}}|Rest], Data)
when is_atom(Key); is_function(Key), is_atom(Format) ->
case get_value(Key, Data) of
- undefined -> [{Str, undefined} | fill_info(Rest, Data)];
+ undefined -> fill_info(Rest, Data);
Value -> [{Str, {Format, Value}} | fill_info(Rest, Data)]
end;
+fill_info([{Str, Attrib, {Format, Key}}|Rest], Data)
+ when is_atom(Key); is_function(Key), is_atom(Format) ->
+ case get_value(Key, Data) of
+ undefined -> fill_info(Rest, Data);
+ Value -> [{Str, Attrib, {Format, Value}} | fill_info(Rest, Data)]
+ end;
fill_info([{Str,SubStructure}|Rest], Data) when is_list(SubStructure) ->
[{Str, fill_info(SubStructure, Data)}|fill_info(Rest,Data)];
fill_info([{Str,Attrib,SubStructure}|Rest], Data) ->
@@ -147,13 +186,18 @@ update_info([], []) ->
ok.
update_info2([Field|Fs], [{_Str, Value}|Rest]) ->
- wxStaticText:setLabel(Field, to_str(Value)),
+ wxTextCtrl:setValue(Field, to_str(Value)),
update_info2(Fs, Rest);
update_info2([], []) -> ok.
to_str(Value) when is_atom(Value) ->
atom_to_list(Value);
+to_str({Unit, X}) when (Unit==bytes orelse Unit==time_ms) andalso is_list(X) ->
+ try list_to_integer(X) of
+ B -> to_str({Unit,B})
+ catch error:badarg -> X
+ end;
to_str({bytes, B}) ->
KB = B div 1024,
MB = KB div 1024,
@@ -289,26 +333,142 @@ get_box_info({Title, List}) when is_list(List) -> {Title, ?wxALIGN_LEFT, List};
get_box_info({Title, left, List}) -> {Title, ?wxALIGN_LEFT, List};
get_box_info({Title, right, List}) -> {Title, ?wxALIGN_RIGHT, List}.
+create_box(_Panel, {scroll_boxes,[]}) ->
+ undefined;
+create_box(Panel, {scroll_boxes,Data}) ->
+ OuterBox = wxBoxSizer:new(?wxHORIZONTAL),
+ Cursor = wxCursor:new(?wxCURSOR_HAND),
+ AddBox = fun({Title,Proportion,{Format,List}}) ->
+ Box = wxStaticBoxSizer:new(?wxVERTICAL, Panel,
+ [{label, Title}]),
+ Scroll = wxScrolledWindow:new(Panel),
+ wxScrolledWindow:enableScrolling(Scroll,true,true),
+ wxScrolledWindow:setScrollbars(Scroll,1,1,0,0),
+ ScrollSizer = wxBoxSizer:new(?wxVERTICAL),
+ wxScrolledWindow:setSizer(Scroll, ScrollSizer),
+ BC = wxWindow:getBackgroundColour(Panel),
+ wxWindow:setBackgroundColour(Scroll,BC),
+ case Format of
+ click ->
+ [begin
+ TC = link_entry(Scroll, Link, Cursor),
+ wxWindow:setBackgroundColour(TC,BC),
+ wxSizer:add(ScrollSizer,TC,[{flag,?wxEXPAND}])
+ end || Link <- List];
+ plain ->
+ [begin
+ TC = wxTextCtrl:new(Scroll, ?wxID_ANY,
+ [{style,?SINGLE_LINE_STYLE},
+ {value,String}]),
+ wxSizer:add(ScrollSizer,TC,[{flag,?wxEXPAND}])
+ end || String <- List]
+ end,
+ wxSizer:add(Box,Scroll,[{flag,?wxEXPAND}]),
+ wxSizer:add(OuterBox,Box,
+ [{proportion,Proportion},{flag,?wxEXPAND}]),
+ {Scroll,length(List)}
+ end,
+ Boxes = [AddBox(Entry) || Entry <- Data],
+ wxCursor:destroy(Cursor),
+
+ MaxL = lists:foldl(fun({_,L},Max) when L>Max -> L;
+ (_,Max) -> Max
+ end,
+ 0,
+ Boxes),
+
+ Dummy = wxTextCtrl:new(Panel, ?wxID_ANY, [{style, ?SINGLE_LINE_STYLE}]),
+ {_,H} = wxWindow:getSize(Dummy),
+ wxTextCtrl:destroy(Dummy),
+
+ MaxH = if MaxL > 8 -> 8*H;
+ true -> MaxL*H
+ end,
+ [wxWindow:setMinSize(B,{0,MaxH}) || {B,_} <- Boxes],
+ wxSizer:layout(OuterBox),
+ {OuterBox, []};
+
create_box(Panel, Data) ->
{Title, Align, Info} = get_box_info(Data),
- Box = wxStaticBoxSizer:new(?wxHORIZONTAL, Panel, [{label, Title}]),
- Left = wxBoxSizer:new(?wxVERTICAL),
- Right = wxBoxSizer:new(?wxVERTICAL),
- Expand = [{flag, ?wxEXPAND}],
- ExpAlign = [{flag, Align}],
- AddRow = fun({Desc, Value}) ->
- wxSizer:add(Left, wxStaticText:new(Panel, ?wxID_ANY, Desc ++ ":"), Expand),
- Field = wxStaticText:new(Panel, ?wxID_ANY, to_str(Value)),
- wxSizer:add(Right, Field, ExpAlign),
+ Box = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label, Title}]),
+ LeftSize = get_max_size(Panel,Info),
+ LeftProportion = [{proportion,0}],
+ RightProportion = [{proportion,1}, {flag, Align bor ?wxEXPAND}],
+ AddRow = fun({Desc0, Value0}) ->
+ Desc = Desc0++":",
+ Line = wxBoxSizer:new(?wxHORIZONTAL),
+ wxSizer:add(Line,
+ wxTextCtrl:new(Panel, ?wxID_ANY,
+ [{style,?SINGLE_LINE_STYLE},
+ {size,LeftSize},
+ {value,Desc}]),
+ LeftProportion),
+ Field =
+ case Value0 of
+ {click,"unknown"} ->
+ wxTextCtrl:new(Panel, ?wxID_ANY,
+ [{style,?MULTI_LINE_STYLE},
+ {value,"unknown"}]);
+ {click,Value} ->
+ link_entry(Panel,{Value,Value});
+ _ ->
+ Value = to_str(Value0),
+ wxTextCtrl:new(Panel, ?wxID_ANY,
+ [{style,?MULTI_LINE_STYLE},
+ {value,Value}])
+ end,
+ wxSizer:add(Line, 10, 0), % space of size 10 horisontally
+ wxSizer:add(Line, Field, RightProportion),
+
+ {_,H,_,_} = wxTextCtrl:getTextExtent(Field,"W"),
+ wxTextCtrl:setMinSize(Field,{0,H}),
+
+ wxSizer:add(Box, Line, [{proportion,0},{flag,?wxEXPAND}]),
Field
end,
InfoFields = [AddRow(Entry) || Entry <- Info],
- wxSizer:add(Box, Left),
- wxSizer:addSpacer(Box, 10),
- wxSizer:add(Box, Right),
- wxSizer:addSpacer(Box, 30),
{Box, InfoFields}.
+link_entry(Panel, Link) ->
+ Cursor = wxCursor:new(?wxCURSOR_HAND),
+ TC = link_entry(Panel, Link, Cursor),
+ wxCursor:destroy(Cursor),
+ TC.
+link_entry(Panel,{Target,Str},Cursor) ->
+ TC = wxTextCtrl:new(Panel, ?wxID_ANY,
+ [{style, ?SINGLE_LINE_STYLE}]),
+ wxTextCtrl:setForegroundColour(TC,?wxBLUE),
+ wxTextCtrl:appendText(TC, Str),
+ wxWindow:setCursor(TC, Cursor),
+ wxTextCtrl:connect(TC, left_down, [{userData,Target}]),
+ wxTextCtrl:connect(TC, enter_window),
+ wxTextCtrl:connect(TC, leave_window),
+ ToolTip = wxToolTip:new("Click to see properties for " ++ Target),
+ wxWindow:setToolTip(TC, ToolTip),
+ TC.
+
+html_window(Panel,Html) ->
+ Win = wxHtmlWindow:new(Panel, [{style, ?wxHW_SCROLLBAR_AUTO}]),
+ wxHtmlWindow:setPage(Win,Html),
+ wxHtmlWindow:connect(Win,command_html_link_clicked),
+ Win.
+
+get_max_size(Panel,Info) ->
+ Txt = wxTextCtrl:new(Panel, ?wxID_ANY, []),
+ Size = get_max_size(Txt,Info,0,0),
+ wxTextCtrl:destroy(Txt),
+ Size.
+
+get_max_size(Txt,[{Desc,_}|Info],MaxX,MaxY) ->
+ {X,Y,_,_} = wxTextCtrl:getTextExtent(Txt,Desc++":"),
+ if X>MaxX ->
+ get_max_size(Txt,Info,X,Y);
+ true ->
+ get_max_size(Txt,Info,MaxX,MaxY)
+ end;
+get_max_size(_,[],X,Y) ->
+ {X+2,Y}.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
set_listctrl_col_size(LCtrl, Total) ->
wx:batch(fun() -> calc_last(LCtrl, Total) end).
@@ -430,3 +590,22 @@ ensure_last_is_dot(String) ->
false ->
String ++ "."
end.
+
+%%%-----------------------------------------------------------------
+%%% Status bar for warnings
+create_status_bar(Panel) ->
+ StatusStyle = ?wxTE_MULTILINE bor ?wxTE_READONLY bor ?wxTE_RICH2,
+ Red = wxTextAttr:new(?wxRED),
+
+ %% wxTextCtrl:setSize/3 does not work, so we must create a dummy
+ %% text ctrl first to get the size of the text, then set it when
+ %% creating the real text ctrl.
+ Dummy = wxTextCtrl:new(Panel, ?wxID_ANY,[{style,StatusStyle}]),
+ {X,Y,_,_} = wxTextCtrl:getTextExtent(Dummy,"WARNING"),
+ wxTextCtrl:destroy(Dummy),
+ StatusBar = wxTextCtrl:new(Panel, ?wxID_ANY,
+ [{style,StatusStyle},
+ {size,{X,Y+2}}]), % Y+2 to avoid scrollbar
+ wxTextCtrl:setDefaultStyle(StatusBar,Red),
+ wxTextAttr:destroy(Red),
+ StatusBar.