%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
-module(tv_main).
-export([start/0,
init/0
]).
-export([get_ets_tables/1,
get_mnesia_tables/1
]).
-include("tv_main.hrl").
-include("tv_int_msg.hrl").
-include("tv_pd_int_msg.hrl").
-include("tv_pd_int_def.hrl").
%%%*********************************************************************
%%% EXTERNAL FUNCTIONS
%%%*********************************************************************
start() ->
spawn(?MODULE, init, []).
init() ->
process_flag(trap_exit,true),
%% OK, so it's *BAD* to use the process dictionary...
%% So why have I used it? Because it is simple to remove the haiku-functionality,
%% if that is desired. Otherwise a lot of functions (the parameters) would have
%% to be changed.
put(error_msg_mode, ?ERROR_MSG_MODE),
KindOfTable = ets,
SysTabHidden = true,
UnreadHidden = true,
SortKey = ?NAME_COL,
CurrNode = node(),
Children = start_tv_nodewin(CurrNode),
{MarkedCell, TempGridLines, WinSize, ShortcutList} = create_window([]),
Tables = get_tables(CurrNode, KindOfTable, UnreadHidden, SysTabHidden,SortKey),
gs:config(grid, [{rows, {1, get_nof_rows(length(Tables),
gs:read(grid, height))}}]),
GridLines = update_gridlines(Tables, TempGridLines, 1),
gs:config(win, [{map, true}, {cursor,arrow}]),
%% To avoid unpleasant error/exit messages, we surround the loop with a catch.
catch loop(KindOfTable, CurrNode, MarkedCell, GridLines, WinSize, Tables, ShortcutList,
UnreadHidden, SysTabHidden, SortKey, Children).
start_tv_nodewin(CurrNode) ->
NodewinPid = tv_nodewin:start(CurrNode, get(error_msg_mode)),
[{NodewinPid, tv_nodewin, CurrNode}].
get_ets_tables(SysTabHidden) ->
Tables = ets:all(),
get_ets_table_info(Tables,
hidden_tables(ets, SysTabHidden) ++
current_mnesia_tables(SysTabHidden),
owners_to_hide(ets, SysTabHidden),
[]).
get_mnesia_tables(SysTabHidden) ->
Tables = mnesia:system_info(tables),
get_mnesia_table_info(Tables -- hidden_tables(mnesia, SysTabHidden),
owners_to_hide(mnesia, SysTabHidden),
[]).
owners_to_hide(ets, true) ->
?SYSTEM_OWNERS;
owners_to_hide(ets, false) ->
[];
owners_to_hide(mnesia, true) ->
[];
owners_to_hide(mnesia, false) ->
[].
get_mnesia_table_info([], _OwnersToHide, Acc) ->
lists:keysort(?NAME_ELEM, Acc);
get_mnesia_table_info([TabId | Tail], OwnersToHide, Acc) ->
case catch get_mnesia_owner_size(TabId) of
{'EXIT', _Reason} ->
%% Ignore tables ceasing to exist.
%% Nodedown errors caught above!
get_mnesia_table_info(Tail, OwnersToHide, Acc);
{OwnerPid, OwnerName, Size} ->
case lists:member(OwnerName, OwnersToHide) of
true ->
get_mnesia_table_info(Tail, OwnersToHide, Acc);
false ->
Readable = not(lists:member(TabId, ?UNREADABLE_MNESIA_TABLES)),
get_mnesia_table_info(Tail,
OwnersToHide,
[{TabId, {notext}, {notext}, Readable,
OwnerPid, OwnerName, Size} | Acc])
end
end.
get_mnesia_owner_size(TabId) ->
{OwnerPid, OwnerName} =
case catch mnesia:table_info(TabId, owner) of
Pid when is_pid(Pid) ->
case lists:keysearch(registered_name, 1, process_info(Pid)) of
false ->
{Pid, {notext}};
{value, {registered_name, ProcName}} ->
{Pid, ProcName}
end;
_Other ->
{{notext}, {notext}}
end,
Size = mnesia:table_info(TabId, size),
{OwnerPid, OwnerName, Size}.
hidden_tables(_Any, true) ->
?SYSTEM_TABLES ++ ?MNESIA_TABLES;
hidden_tables(ets, _SysTabHidden) ->
?MNESIA_TABLES;
hidden_tables(mnesia, _SysTabHidden) ->
[].
get_tables(Node, KindOfTable, UnreadHidden, SysTabHidden,SortKey) ->
LocalNode = (Node =:= node()),
Tables =
case catch get_table_list(Node,LocalNode,KindOfTable,SysTabHidden) of
Result when is_list(Result) ->
case UnreadHidden of
true ->
lists:filter(fun(H) ->
element(?READABLE_ELEM, H)
end,
Result);
_Other ->
Result
end;
Error ->
analyze_error(Error, Node, undefined),
[]
end,
case SortKey of
?PROCNAME_ELEM ->
lists:keysort(SortKey,
lists:keysort(?PID_ELEM, Tables));
_OtherCol ->
lists:keysort(SortKey,
lists:keysort(?NAME_ELEM, Tables))
end.
get_ets_table_info([], _TablesToHide, _OwnersToHide, Acc) ->
lists:keysort(?ID_ELEM, Acc);
get_ets_table_info([TabId | Tail], TablesToHide, OwnersToHide, Acc) ->
case catch get_ets_name_owner_protection(TabId) of
{'EXIT', _Reason} ->
%% Ignore tables ceasing to exist.
%% Nodedown errors caught above!
get_ets_table_info(Tail, TablesToHide, OwnersToHide, Acc);
{Name, NamedTable, Id, Readable, OwnerPid, OwnerName, Size} ->
case lists:member(Name, TablesToHide) of
true ->
get_ets_table_info(Tail, TablesToHide, OwnersToHide, Acc);
false ->
case lists:member(OwnerName, OwnersToHide) of
true ->
get_ets_table_info(Tail, TablesToHide, OwnersToHide, Acc);
false ->
get_ets_table_info(Tail, TablesToHide, OwnersToHide,
[{Name,NamedTable,Id,Readable,
OwnerPid,OwnerName,Size} | Acc])
end
end
end.
get_ets_name_owner_protection(TabId) ->
Name = ets:info(TabId, name),
OwnerPid = ets:info(TabId, owner),
Readable = case ets:info(TabId, protection) of
private ->
false;
_Other ->
true
end,
Size = ets:info(TabId, size),
{NamedTable,Id} = case ets:info(TabId, named_table) of
true ->
{true,{notext}};
false ->
{false, TabId}
end,
PName = case lists:keysearch(registered_name, 1, process_info(OwnerPid)) of
false ->
{notext};
{value, {registered_name, ProcName}} ->
ProcName
end,
{Name, NamedTable, Id, Readable, OwnerPid, PName, Size}.
current_mnesia_tables(SysTabHidden) ->
case catch get_table_list(node(), true, mnesia, SysTabHidden) of
Result when is_list(Result) ->
lists:map(fun(H) ->
element(?NAME_ELEM, H)
end,
Result);
nodedown ->
handle_error(nodedown, node(), undefined),
[];
_Other ->
[]
end.
get_table_list(_Node, true, ets, SysTabHidden) ->
get_ets_tables(SysTabHidden);
get_table_list(Node, false, ets, SysTabHidden) ->
case rpc:block_call(Node, ?MODULE, get_ets_tables, [SysTabHidden]) of
{badrpc, Reason} ->
throw({badrpc,Reason});
Result ->
Result
end;
get_table_list(_Node, true, mnesia, SysTabHidden) ->
get_mnesia_tables(SysTabHidden);
get_table_list(Node, false, mnesia, SysTabHidden) ->
case rpc:block_call(Node, ?MODULE, get_mnesia_tables, [SysTabHidden]) of
{badrpc,Reason} ->
throw({badrpc,Reason});
Result ->
Result
end.
analyze_error(Cause, Node, Table) ->
case Cause of
{badrpc, {'EXIT', {badarg,_Reason}}} ->
done; %% Table has ceased to exist.
{'EXIT', {badarg, {ets,local_info,_Args}}} ->
done;
{badrpc, nodedown} ->
handle_error(nodedown, Node, Table);
{'EXIT', nodedown} ->
handle_error(nodedown, Node, Table);
{'EXIT', {aborted, {node_not_running,_ErrNode}}} ->
handle_error(mnesia_not_started, Node, Table);
{'EXIT', {'EXIT', {aborted, {node_not_running,_ErrNode}}}} ->
handle_error(mnesia_not_started, Node, Table);
{badrpc, {'EXIT', {aborted, {node_not_running,_ErrNode}}}} ->
handle_error(mnesia_not_started, Node, Table);
{'EXIT', {undef, {mnesia,_Fcn,_Args,_}}} ->
handle_error(mnesia_not_started, Node, Table);
{'EXIT', Reason} ->
handle_error({unexpected_error,Reason}, Node, Table);
Error when is_tuple(Error) ->
handle_error({unexpected_error,Error}, Node, Table)
end.
handle_error(mnesia_not_started, _Node, _Table) ->
gs:config(win, [beep]),
case get(error_msg_mode) of
normal ->
tv_utils:notify(win, "TV Notification", ["Mnesia not started!"]);
haiku ->
tv_utils:notify(win, "TV Notification", ["Mnesia is stopped.",
"We wish to reach all data",
"But we never will."])
end;
handle_error(nodedown, _Node, _Table) ->
gs:config(win, [beep]),
case get(error_msg_mode) of
normal ->
tv_utils:notify(win, "TV Notification", ["The selected node is down!"]);
haiku ->
Msg = ["With searching comes loss",
"And the presence of absence:",
"Node is down."],
tv_utils:notify(win, "TV Notification", Msg)
end,
self() ! nodedown;
handle_error({unexpected_error,Cause}, _Node, _Table) ->
io:format("Unexpected error: ~p~n", [Cause]),
gs:config(win, [beep]).
loop(KindOfTable,CurrNode,MarkedCell,GridLines,
WinSize,Tables,Shortcuts,UnreadHidden,SysTabHidden,SortKey,Children) ->
receive
{gs, Gridline, click, {grid,Readable}, [Col,Row,Text | _]} when Text =/= "" ->
unmark_cell(MarkedCell, Tables),
NewMarkedCell = mark_cell({Gridline, Col, Row}, MarkedCell, Readable),
loop(KindOfTable,CurrNode,NewMarkedCell,GridLines,WinSize,Tables,Shortcuts,
UnreadHidden,SysTabHidden,SortKey,Children);
{gs, _Gridline, click, {grid,_Readable}, [_Col,_Row,"" | _]} ->
NewMarkedCell = unmark_cell(MarkedCell, Tables),
loop(KindOfTable,CurrNode,NewMarkedCell,GridLines,WinSize,Tables,Shortcuts,
UnreadHidden,SysTabHidden,SortKey,Children);
{gs, Gridline, doubleclick, {grid,Data}, [?NAME_COL,Row,Text | _]} when Text =/= "" ->
unmark_cell(MarkedCell, Tables),
NewMarkedCell = mark_cell({Gridline, ?NAME_COL, Row}, undefined, Data),
{Table, Name, Readable} = get_table_id(KindOfTable, Row, Tables),
case start_tv_browser(Table,CurrNode,Name,KindOfTable,Readable,Children) of
Children ->
{FinalMarkedCell, NewTables, NewGridLines} =
refresh_window(NewMarkedCell,Tables,KindOfTable,CurrNode,GridLines,
UnreadHidden,SysTabHidden,SortKey, Children),
loop(KindOfTable,CurrNode,FinalMarkedCell,NewGridLines,WinSize,NewTables,
Shortcuts,UnreadHidden,SysTabHidden,SortKey,Children);
NewChildren ->
loop(KindOfTable,CurrNode,NewMarkedCell,GridLines,WinSize,Tables,Shortcuts,
UnreadHidden,SysTabHidden,SortKey,NewChildren)
end;
{gs, Gridline, doubleclick, {grid,Data}, [?ID_COL,Row,Text | _]} when Text =/= "" ->
unmark_cell(MarkedCell, Tables),
NewMarkedCell = mark_cell({Gridline, ?ID_COL, Row}, undefined, Data),
{Table, Name, Readable} = get_table_id(KindOfTable, Row, Tables),
case start_tv_browser(Table,CurrNode,Name,KindOfTable,Readable,Children) of
Children ->
{FinalMarkedCell, NewTables, NewGridLines} =
refresh_window(NewMarkedCell,Tables,KindOfTable,CurrNode,GridLines,
UnreadHidden,SysTabHidden,SortKey, Children),
loop(KindOfTable,CurrNode,FinalMarkedCell,NewGridLines,WinSize,NewTables,
Shortcuts,UnreadHidden,SysTabHidden,SortKey,Children);
NewChildren ->
loop(KindOfTable,CurrNode,NewMarkedCell,GridLines,WinSize,Tables,Shortcuts,
UnreadHidden,SysTabHidden,SortKey,NewChildren)
end;
{gs, Gridline, doubleclick, {grid,Data}, [?INFO_COL,Row,Text | _]} when Text =/= "" ->
unmark_cell(MarkedCell, Tables),
NewMarkedCell = mark_cell({Gridline, ?INFO_COL, Row}, undefined, Data),
{Table, _Name, _Readable} = get_table_id(KindOfTable, Row, Tables),
case start_tv_info(Table, CurrNode, CurrNode =:= node(), KindOfTable, Children) of
Children ->
{FinalMarkedCell, NewTables, NewGridLines} =
refresh_window(NewMarkedCell,Tables,KindOfTable,CurrNode,GridLines,
UnreadHidden,SysTabHidden,SortKey, Children),
loop(KindOfTable,CurrNode,FinalMarkedCell,NewGridLines,WinSize,NewTables,
Shortcuts,UnreadHidden,SysTabHidden,SortKey,Children);
NewChildren ->
loop(KindOfTable,CurrNode,NewMarkedCell,GridLines,WinSize,Tables,Shortcuts,
UnreadHidden,SysTabHidden,SortKey,NewChildren)
end;
{gs, Gridline, doubleclick, {grid,Data}, [?PID_COL,Row,Text | _]} when Text =/= "" ->
unmark_cell(MarkedCell, Tables),
NewMarkedCell = mark_cell({Gridline, ?PID_COL, Row}, undefined, Data),
OwnerPid = element(?PID_ELEM, lists:nth(Row, Tables)),
NewChildren = start_pman(OwnerPid, Children),
loop(KindOfTable,CurrNode,NewMarkedCell,GridLines,WinSize,Tables,Shortcuts,
UnreadHidden,SysTabHidden,SortKey, NewChildren);
{gs, Gridline, doubleclick, {grid,Data}, [?PROCNAME_COL,Row,Text | _]} when Text =/= "" ->
unmark_cell(MarkedCell, Tables),
NewMarkedCell = mark_cell({Gridline, ?PROCNAME_COL, Row}, undefined, Data),
OwnerPid = element(?PID_ELEM, lists:nth(Row, Tables)),
NewChildren = start_pman(OwnerPid, Children),
loop(KindOfTable,CurrNode,NewMarkedCell,GridLines,WinSize,Tables,Shortcuts,
UnreadHidden,SysTabHidden,SortKey, NewChildren);
%% {gs, win, configure, _Data, [Width, Height | _]} when {Width,Height} /= WinSize ->
Msg0 = {gs, win, configure, _Data, [Width0, Height0 | _]}
when {Width0,Height0} =/= WinSize ->
{gs, win, configure, _, [Width,Height|_]} = flush_msgs(Msg0),
NewSize = resize_window(Width, Height, length(Tables)),
loop(KindOfTable,CurrNode,MarkedCell,GridLines,NewSize,Tables,Shortcuts,
UnreadHidden,SysTabHidden,SortKey,Children);
{gs, _Id, click, update, _Args} ->
gs:config(win, [{cursor,busy}]),
NewMarkedCell = unmark_cell(MarkedCell, Tables),
{NewTables, NewGridLines} =
update_grid(KindOfTable,CurrNode,GridLines,UnreadHidden,SysTabHidden,SortKey),
update_tv_info(Children),
gs:config(win, [{cursor,arrow}]),
loop(KindOfTable,CurrNode,NewMarkedCell,NewGridLines,WinSize,NewTables,Shortcuts,
UnreadHidden,SysTabHidden,SortKey,Children);
{gs, _Id, click, open_table, _Args} ->
{Table, Name, Readable} = get_table_id(KindOfTable, element(3, MarkedCell),
Tables),
case start_tv_browser(Table,CurrNode,Name,KindOfTable,Readable,Children) of
Children ->
{NewMarkedCell, NewTables, NewGridLines} =
refresh_window(MarkedCell,Tables,KindOfTable,CurrNode,GridLines,
UnreadHidden,SysTabHidden,SortKey, Children),
loop(KindOfTable,CurrNode,NewMarkedCell,NewGridLines,WinSize,NewTables,
Shortcuts,UnreadHidden,SysTabHidden,SortKey,Children);
NewChildren ->
loop(KindOfTable,CurrNode,MarkedCell,GridLines,WinSize,Tables,Shortcuts,
UnreadHidden,SysTabHidden,SortKey,NewChildren)
end;
{gs, _Id, click, new_table, _Args} ->
NewChildren = start_tv_new_table(CurrNode, Children),
loop(KindOfTable,CurrNode,MarkedCell,GridLines,WinSize,Tables,Shortcuts,
UnreadHidden,SysTabHidden,SortKey,NewChildren);
{gs, _Id, click, select_node, _Args} ->
show_tv_nodewin(Children),
loop(KindOfTable,CurrNode,MarkedCell,GridLines,WinSize,Tables,Shortcuts,
UnreadHidden,SysTabHidden,SortKey,Children);
{gs, _Id, click, show_mnesia, _Args} when KindOfTable =:= ets ->
gs:config(win, [{cursor,busy}]),
NewMarkedCell = unmark_cell(MarkedCell, Tables),
gs:config(label2, [{fg, ?DISABLED_COLOR}]),
gs:config(sort_table_id, [{enable, false}]),
NewSortKey =
case SortKey of
?ID_ELEM ->
gs:config(sort_table_name, [{select,true}]),
?NAME_ELEM;
_Other ->
SortKey
end,
{NewTables, NewGridLines} =
update_grid(mnesia, CurrNode, GridLines, UnreadHidden, SysTabHidden, NewSortKey),
gs:config(win, [{cursor,arrow}]),
loop(mnesia,CurrNode,NewMarkedCell,NewGridLines,WinSize,NewTables,Shortcuts,
UnreadHidden,SysTabHidden,NewSortKey,Children);
{gs, _Id, click, show_ets, _Args} when KindOfTable =:= mnesia ->
gs:config(win, [{cursor,busy}]),
NewMarkedCell = unmark_cell(MarkedCell, Tables),
gs:config(label2, [{fg, ?NORMAL_FG_COLOR}]),
gs:config(label3, [{fg, ?NORMAL_FG_COLOR}]),
gs:config(label4, [{fg, ?NORMAL_FG_COLOR}]),
{NewTables, NewGridLines} =
update_grid(ets, CurrNode, GridLines, UnreadHidden, SysTabHidden,SortKey),
%% gs:config(show_unreadable, [{enable, true},
%% {select, not(UnreadHidden)}]),
gs:config(sort_table_id, [{enable, true}]),
gs:config(win, [{cursor,arrow}]),
loop(ets,CurrNode,NewMarkedCell,NewGridLines,WinSize,NewTables,Shortcuts,
UnreadHidden,SysTabHidden,SortKey,Children);
{gs, _Id, click, show_system, _Args} when SysTabHidden ->
gs:config(win, [{cursor,busy}]),
NewMarkedCell = unmark_cell(MarkedCell, Tables),
{NewTables, NewGridLines}
= update_grid(KindOfTable, CurrNode, GridLines, UnreadHidden, false, SortKey),
gs:config(show_system, [{data, hide_system}]),
gs:config(win, [{cursor,arrow}]),
loop(KindOfTable,CurrNode,NewMarkedCell,NewGridLines,WinSize,NewTables,Shortcuts,
UnreadHidden,false,SortKey,Children);
{gs, _Id, click, hide_system, _Args} when not SysTabHidden ->
gs:config(win, [{cursor,busy}]),
NewMarkedCell = unmark_cell(MarkedCell, Tables),
{NewTables, NewGridLines} =
update_grid(KindOfTable, CurrNode, GridLines, UnreadHidden, true, SortKey),
gs:config(show_system, [{label, {text, " System Tables "}},
{data, show_system}]),
gs:config(win, [{cursor,arrow}]),
loop(KindOfTable,CurrNode,NewMarkedCell,NewGridLines,WinSize,NewTables,Shortcuts,
UnreadHidden,true,SortKey,Children);
{gs, _Id, click, show_unreadable, _Args} when UnreadHidden ->
gs:config(win, [{cursor,busy}]),
NewMarkedCell = unmark_cell(MarkedCell, Tables),
{NewTables, NewGridLines}
= update_grid(KindOfTable, CurrNode, GridLines, false, SysTabHidden, SortKey),
gs:config(show_unreadable, [{data, hide_unreadable}]),
gs:config(win, [{cursor,arrow}]),
loop(KindOfTable,CurrNode,NewMarkedCell,NewGridLines,WinSize,NewTables,Shortcuts,
false,SysTabHidden,SortKey,Children);
{gs, _Id, click, hide_unreadable, _Args} when not UnreadHidden ->
gs:config(win, [{cursor,busy}]),
NewMarkedCell = unmark_cell(MarkedCell, Tables),
{NewTables, NewGridLines} =
update_grid(KindOfTable, CurrNode, GridLines, true, SysTabHidden, SortKey),
gs:config(show_unreadable, [{label, {text, " Unreadable Tables "}},
{data, show_unreadable}]),
gs:config(win, [{cursor,arrow}]),
loop(KindOfTable,CurrNode,NewMarkedCell,NewGridLines,WinSize,NewTables,Shortcuts,
true,SysTabHidden,SortKey,Children);
{gs, _Id, click, show_info, _Args} ->
{Table, _Name, _Readable} = get_table_id(KindOfTable, element(3,MarkedCell),
Tables),
case start_tv_info(Table, CurrNode, CurrNode =:= node(), KindOfTable, Children) of
Children ->
{NewMarkedCell, NewTables, NewGridLines} =
refresh_window(MarkedCell,Tables,KindOfTable,CurrNode,GridLines,
UnreadHidden,SysTabHidden,SortKey, Children),
loop(KindOfTable,CurrNode,NewMarkedCell,NewGridLines,WinSize,NewTables,
Shortcuts,UnreadHidden,SysTabHidden,SortKey,Children);
NewChildren ->
loop(KindOfTable,CurrNode,MarkedCell,GridLines,WinSize,Tables,Shortcuts,
UnreadHidden,SysTabHidden,SortKey,NewChildren)
end;
{gs, _Id, click, sort_table_name, _Args} when SortKey =/= ?NAME_ELEM ->
gs:config(win, [{cursor,busy}]),
NewMarkedCell = unmark_cell(MarkedCell, Tables),
{NewTables, NewGridLines} =
update_grid(KindOfTable,CurrNode,GridLines,UnreadHidden,SysTabHidden,?NAME_ELEM),
gs:config(win, [{cursor,arrow}]),
loop(KindOfTable,CurrNode,NewMarkedCell,NewGridLines,WinSize,NewTables,Shortcuts,
UnreadHidden,SysTabHidden,?NAME_ELEM,Children);
{gs, _Id, click, sort_table_id, _Args} when SortKey =/= ?ID_ELEM ->
gs:config(win, [{cursor,busy}]),
NewMarkedCell = unmark_cell(MarkedCell, Tables),
{NewTables, NewGridLines} =
update_grid(KindOfTable,CurrNode,GridLines,UnreadHidden,SysTabHidden,?ID_ELEM),
gs:config(win, [{cursor,arrow}]),
loop(KindOfTable,CurrNode,NewMarkedCell,NewGridLines,WinSize,NewTables,Shortcuts,
UnreadHidden,SysTabHidden,?ID_ELEM,Children);
{gs, _Id, click, sort_owner_name, _Args} when SortKey =/= ?PROCNAME_ELEM ->
gs:config(win, [{cursor,busy}]),
NewMarkedCell = unmark_cell(MarkedCell, Tables),
{NewTables, NewGridLines} =
update_grid(KindOfTable,CurrNode,GridLines,UnreadHidden,SysTabHidden,
?PROCNAME_ELEM),
gs:config(win, [{cursor,arrow}]),
loop(KindOfTable,CurrNode,NewMarkedCell,NewGridLines,WinSize,NewTables,Shortcuts,
UnreadHidden,SysTabHidden,?PROCNAME_ELEM,Children);
{gs, _Id, click, sort_owner_pid, _Args} when SortKey =/= ?PID_ELEM ->
gs:config(win, [{cursor,busy}]),
NewMarkedCell = unmark_cell(MarkedCell, Tables),
{NewTables, NewGridLines} =
update_grid(KindOfTable,CurrNode,GridLines,UnreadHidden,SysTabHidden,?PID_ELEM),
gs:config(win, [{cursor,arrow}]),
loop(KindOfTable,CurrNode,NewMarkedCell,NewGridLines,WinSize,NewTables,Shortcuts,
UnreadHidden,SysTabHidden,?PID_ELEM,Children);
{gs, _Id, click, trace_process, _Args} ->
OwnerPid = element(?PID_ELEM, lists:nth(element(3,MarkedCell), Tables)),
NewChildren = start_pman(OwnerPid, Children),
loop(KindOfTable,CurrNode,MarkedCell,GridLines,WinSize,Tables,Shortcuts,
UnreadHidden,SysTabHidden,SortKey,NewChildren);
{gs, _Id, click, help_button, _Args} ->
HelpFile = filename:join([code:lib_dir(tv), "doc", "html", "index.html"]),
tool_utils:open_help(win, HelpFile),
loop(KindOfTable,CurrNode,MarkedCell,GridLines,WinSize,Tables,Shortcuts,
UnreadHidden,SysTabHidden,SortKey,Children);
{gs, _Id, click, otp_help_button, _Args} ->
IndexFile = filename:join([code:root_dir(), "doc", "index.html"]),
tool_utils:open_help(win, IndexFile),
loop(KindOfTable,CurrNode,MarkedCell,GridLines,WinSize,Tables,Shortcuts,
UnreadHidden,SysTabHidden,SortKey,Children);
{gs, win, configure, _Data, _Args} ->
loop(KindOfTable,CurrNode,MarkedCell,GridLines,WinSize,Tables,Shortcuts,
UnreadHidden,SysTabHidden,SortKey,Children);
{gs, _Id, click, exit_button, _Args} ->
lists:foreach(
fun({Pid,pman,_OP}) ->
exit(Pid,kill);
(_) ->
done
end,
Children),
exit(normal);
{gs, _Id, click, show_haiku, _Args} ->
gs:config(win, [{cursor,busy}]),
gs:config(show_haiku, [{data, hide_haiku}]),
lists:foreach(
fun({Pid,tv_info,_Data}) ->
Pid ! {error_msg_mode,haiku};
({Pid,tv_browser,_Data}) ->
Pid ! {error_msg_mode,haiku};
({Pid,tv_nodewin,_Data}) ->
Pid ! {error_msg_mode,haiku};
({Pid,tv_new_table,_Data}) ->
Pid ! {error_msg_mode,haiku};
(_Other) ->
done
end,
Children),
put(error_msg_mode, haiku),
gs:config(win, [{cursor,arrow}]),
loop(KindOfTable, CurrNode, MarkedCell, GridLines, WinSize, Tables, Shortcuts,
UnreadHidden,SysTabHidden,SortKey,Children);
{gs, _Id, click, hide_haiku, _Args} ->
gs:config(win, [{cursor,busy}]),
gs:config(show_haiku, [{data, show_haiku}]),
lists:foreach(
fun({Pid,tv_info,_Data}) ->
Pid ! {error_msg_mode,normal};
({Pid,tv_browser,_Data}) ->
Pid ! {error_msg_mode,normal};
({Pid,tv_nodewin,_Data}) ->
Pid ! {error_msg_mode,normal};
({Pid,tv_new_table,_Data}) ->
Pid ! {error_msg_mode,normal};
(_Other) ->
done
end,
Children),
put(error_msg_mode, normal),
gs:config(win, [{cursor,arrow}]),
loop(KindOfTable, CurrNode, MarkedCell, GridLines, WinSize, Tables, Shortcuts,
UnreadHidden,SysTabHidden,SortKey,Children);
{gs, win, destroy, _Data, _Args} ->
lists:foreach(
fun({Pid,pman,_OP}) ->
exit(Pid,kill);
(_) ->
done
end,
Children),
exit(normal);
{gs, win, keypress, _Data, [Key, _, _, 1 | _]} ->
case lists:keysearch(Key, 1, Shortcuts) of
{value, {Key, Value}} ->
handle_keypress(Value,KindOfTable,CurrNode,MarkedCell,
GridLines,WinSize,Tables, Shortcuts,
UnreadHidden,SysTabHidden,SortKey,Children);
false ->
loop(KindOfTable,CurrNode,MarkedCell,GridLines,WinSize,Tables,Shortcuts,
UnreadHidden,SysTabHidden,SortKey,Children)
end;
{gs, win, keypress, _Data, _Args} ->
loop(KindOfTable, CurrNode, MarkedCell, GridLines, WinSize, Tables, Shortcuts,
UnreadHidden,SysTabHidden,SortKey,Children);
{tv_new_node, _Sender, NewCurrNode} ->
gs:config(win, [{cursor,busy}]),
NewMarkedCell = unmark_cell(MarkedCell, Tables),
{NewTables, NewGridLines} =
update_grid(KindOfTable,NewCurrNode,GridLines,UnreadHidden,SysTabHidden,SortKey),
update_tv_info(Children),
update_tv_browser(Children),
NewChildren =
case replace_node_name(NewCurrNode, CurrNode) of
false ->
Children;
true ->
update_node_name(Children)
end,
gs:config(win, [{cursor,arrow}]),
loop(KindOfTable,NewCurrNode,NewMarkedCell,NewGridLines,WinSize,NewTables,Shortcuts,
UnreadHidden,SysTabHidden,SortKey,NewChildren);
{tv_start_infowin, Table, Node, LocalNode, TableType} ->
case start_tv_info(Table, Node, LocalNode, TableType, Children) of
Children ->
{NewMarkedCell, NewTables, NewGridLines} =
refresh_window(MarkedCell,Tables,KindOfTable,CurrNode,GridLines,
UnreadHidden,SysTabHidden,SortKey, Children),
loop(KindOfTable,CurrNode,NewMarkedCell,NewGridLines,WinSize,NewTables,
Shortcuts,UnreadHidden,SysTabHidden,SortKey,Children);
NewChildren ->
loop(KindOfTable,CurrNode,MarkedCell,GridLines,WinSize,Tables,Shortcuts,
UnreadHidden,SysTabHidden,SortKey,NewChildren)
end;
{tv_update_infowin, Table, Node, _Type} ->
case get_tv_info_pid(Table, Node, Children) of
undefined ->
done;
Pid ->
Pid ! #info_update_table_info{sender=self()}
end,
loop(KindOfTable,CurrNode,MarkedCell,GridLines,WinSize,
Tables,Shortcuts,UnreadHidden,SysTabHidden,SortKey,Children);
{tv_new_table, NewTabWinPid, Node, Name, Options, KindOfTableToCreate, _Readable, false} ->
case create_table(KindOfTableToCreate, Node, Node =:= node(), Name, Options,
NewTabWinPid) of
error ->
loop(KindOfTable,CurrNode,MarkedCell,GridLines,WinSize,
Tables,Shortcuts,UnreadHidden,SysTabHidden,SortKey,Children);
_TabId ->
case KindOfTable of
mnesia ->
done;
ets ->
self() ! {gs, tv_main, click, update, []}
end,
loop(KindOfTable,CurrNode,MarkedCell,GridLines,WinSize,Tables,
Shortcuts,UnreadHidden,SysTabHidden,SortKey,Children)
end;
{tv_new_table, NewTabWinPid, Node, Name, Options, KindOfTableToCreate, Readable, true} ->
case create_table(KindOfTableToCreate, Node, Node =:= node(), Name, Options,
NewTabWinPid) of
error ->
loop(KindOfTable,CurrNode,MarkedCell,GridLines,WinSize,
Tables,Shortcuts,UnreadHidden,SysTabHidden,SortKey,Children);
TabId ->
case start_tv_browser(TabId,Node,Name,KindOfTableToCreate,Readable,Children) of
Children ->
{FinalMarkedCell, NewTables, NewGridLines} =
case KindOfTable of
mnesia ->
{MarkedCell, Tables, GridLines};
ets ->
refresh_window(MarkedCell,Tables,KindOfTable,
CurrNode,GridLines,UnreadHidden,
SysTabHidden,SortKey, Children)
end,
loop(KindOfTable,CurrNode,FinalMarkedCell,NewGridLines,WinSize,
NewTables,Shortcuts,UnreadHidden,SysTabHidden,SortKey,Children);
NewChildren ->
case KindOfTable of
mnesia ->
done;
ets ->
self() ! {gs, tv_main, click, update, []}
end,
loop(KindOfTable,CurrNode,MarkedCell,GridLines,WinSize,Tables,
Shortcuts,UnreadHidden,SysTabHidden,SortKey,NewChildren)
end
end;
{'EXIT', Pid, _Reason} ->
case lists:keysearch(Pid, 1, Children) of
false ->
loop(KindOfTable,CurrNode,MarkedCell,GridLines,WinSize,
Tables,Shortcuts,UnreadHidden,SysTabHidden,SortKey,Children);
{value, {Pid,Prog,_Data}} ->
NewChildren =
case Prog of
tv_nodewin ->
lists:keydelete(Pid, 1, Children) ++ start_tv_nodewin(CurrNode);
_Other ->
lists:keydelete(Pid, 1, Children)
end,
loop(KindOfTable,CurrNode,MarkedCell,GridLines,WinSize,
Tables,Shortcuts,UnreadHidden,SysTabHidden,SortKey,NewChildren)
end;
_Other ->
loop(KindOfTable, CurrNode, MarkedCell, GridLines, WinSize, Tables, Shortcuts,
UnreadHidden,SysTabHidden,SortKey,Children)
end.
flush_msgs(Msg0 = {gs, Win, Op, _, _}) ->
receive Msg = {gs, Win,Op,_,_} ->
flush_msgs(Msg)
after 100 ->
Msg0
end.
handle_keypress(open_table,KindOfTable,CurrNode,MarkedCell,GridLines,
WinSize,Tables,Shortcuts,UnreadHidden,SysTabHidden,SortKey,Children) ->
NewChildren =
case MarkedCell of
{undefined,_,_} ->
case get(error_msg_mode) of
normal ->
gs:config(win, [beep]),
tv_utils:notify(win, "TV Notification", "No table selected!");
haiku ->
Msg = ["Rather than a beep",
"Or a rude error message",
"These words: make a choice."],
tv_utils:notify(win, "TV Notification", Msg)
end,
Children;
_OtherCell ->
{Table, Name, Readable} = get_table_id(KindOfTable, element(3, MarkedCell),
Tables),
start_tv_browser(Table, CurrNode, Name, KindOfTable, Readable, Children)
end,
case NewChildren of
Children ->
{NewMarkedCell, NewTables, NewGridLines} =
refresh_window(MarkedCell,Tables,KindOfTable,CurrNode,GridLines,UnreadHidden,
SysTabHidden, SortKey, Children),
loop(KindOfTable,CurrNode,NewMarkedCell,NewGridLines,WinSize,NewTables,
Shortcuts,UnreadHidden,SysTabHidden,SortKey,Children);
_Other ->
loop(KindOfTable,CurrNode,MarkedCell,GridLines,WinSize,Tables,Shortcuts,
UnreadHidden,SysTabHidden,SortKey,NewChildren)
end;
handle_keypress(update,KindOfTable,CurrNode,MarkedCell,GridLines,
WinSize,Tables,Shortcuts,UnreadHidden,SysTabHidden,SortKey,Children) ->
gs:config(win, [{cursor,busy}]),
NewMarkedCell = unmark_cell(MarkedCell, Tables),
{NewTabs, NewGrLines} =
update_grid(KindOfTable,CurrNode,GridLines,UnreadHidden,SysTabHidden,SortKey),
update_tv_info(Children),
gs:config(win, [{cursor,arrow}]),
loop(KindOfTable,CurrNode,NewMarkedCell,NewGrLines,WinSize,NewTabs,Shortcuts,
UnreadHidden,SysTabHidden,SortKey,Children);
handle_keypress(show_mnesia,ets,CurrNode,MarkedCell,GridLines,
WinSize,Tables,Shortcuts,UnreadHidden,SysTabHidden,SortKey,Children) ->
gs:config(win, [{cursor,busy}]),
NewMarkedCell = unmark_cell(MarkedCell, Tables),
gs:config(label2, [{fg, ?DISABLED_COLOR}]),
gs:config(label3, [{fg, ?DISABLED_COLOR}]),
gs:config(label4, [{fg, ?DISABLED_COLOR}]),
gs:config(show_unreadable, [{label, {text, " Unreadable Tables "}},
{data, show_unreadable}]),
%% gs:config(show_unreadable, [{enable, false},
%% {select, false}]),
gs:config(sort_table_id, [{enable, false}]),
NewSortKey =
case SortKey of
?ID_ELEM ->
gs:config(sort_table_name, [{select,true}]),
?NAME_ELEM;
_Other ->
SortKey
end,
{NewTables, NewGridLines} =
update_grid(mnesia,CurrNode,GridLines,UnreadHidden,SysTabHidden,NewSortKey),
gs:config(win, [{cursor,arrow}]),
loop(mnesia,CurrNode,NewMarkedCell,NewGridLines,WinSize,NewTables,Shortcuts,
UnreadHidden,SysTabHidden,NewSortKey,Children);
handle_keypress(show_ets,mnesia,CurrNode,MarkedCell,GridLines,
WinSize,Tables,Shortcuts,UnreadHidden,SysTabHidden,SortKey,Children) ->
gs:config(win, [{cursor,busy}]),
NewMarkedCell = unmark_cell(MarkedCell, Tables),
gs:config(label2, [{fg, ?NORMAL_FG_COLOR}]),
gs:config(label3, [{fg, ?NORMAL_FG_COLOR}]),
gs:config(label4, [{fg, ?NORMAL_FG_COLOR}]),
{NewTables, NewGridLines} =
update_grid(ets,CurrNode,GridLines,UnreadHidden,SysTabHidden,SortKey),
%% gs:config(show_unreadable, [{enable, true},
%% {select, not(UnreadHidden)}]),
gs:config(sort_table_id, [{enable, true}]),
gs:config(win, [{cursor,arrow}]),
loop(ets,CurrNode,NewMarkedCell,NewGridLines,WinSize,NewTables,Shortcuts,
UnreadHidden,SysTabHidden,SortKey,Children);
handle_keypress(trace_process,KindOfTable,CurrNode,MarkedCell,GridLines,
WinSize,Tables,Shortcuts,UnreadHidden,SysTabHidden,SortKey,Children) ->
NewChildren =
case MarkedCell of
{_Id, ?PID_COL, Row} ->
OwnerPid = element(?PID_ELEM, lists:nth(Row, Tables)),
start_pman(OwnerPid, Children);
{_Id, ?PROCNAME_COL, Row} ->
OwnerPid = element(?PID_ELEM, lists:nth(Row, Tables)),
start_pman(OwnerPid, Children);
_Other ->
Children
end,
loop(KindOfTable,CurrNode,MarkedCell,GridLines,WinSize,Tables,Shortcuts,
UnreadHidden,SysTabHidden,SortKey, NewChildren);
handle_keypress(select_node,KindOfTable,CurrNode,MarkedCell,GridLines,
WinSize,Tables,Shortcuts,UnreadHidden,SysTabHidden,SortKey,Children) ->
show_tv_nodewin(Children),
loop(KindOfTable,CurrNode,MarkedCell,GridLines,WinSize,Tables,Shortcuts,
UnreadHidden,SysTabHidden,SortKey,Children);
handle_keypress(show_info,KindOfTable,CurrNode,MarkedCell,GridLines,
WinSize,Tables,Shortcuts,UnreadHidden,SysTabHidden,SortKey,Children) ->
NewChildren =
case MarkedCell of
{_Id, ?NAME_COL, Row} ->
{Table, _Name, _Readable} = get_table_id(KindOfTable, Row, Tables),
start_tv_info(Table, CurrNode, CurrNode =:= node(), KindOfTable, Children);
{_Id, ?ID_COL, Row} ->
{Table, _Name, _Readable} = get_table_id(KindOfTable, Row, Tables),
start_tv_info(Table, CurrNode, CurrNode =:= node(), KindOfTable, Children);
{_Id, ?INFO_COL, Row} ->
{Table, _Name, _Readable} = get_table_id(KindOfTable, Row, Tables),
start_tv_info(Table, CurrNode, CurrNode =:= node(), KindOfTable, Children);
_OtherCell ->
Children
end,
case NewChildren of
Children ->
{NewMarkedCell, NewTables, NewGridLines} =
refresh_window(MarkedCell,Tables,KindOfTable,CurrNode,GridLines,UnreadHidden,
SysTabHidden, SortKey, Children),
loop(KindOfTable,CurrNode,NewMarkedCell,NewGridLines,WinSize,NewTables,
Shortcuts,UnreadHidden,SysTabHidden,SortKey,Children);
_Other ->
loop(KindOfTable,CurrNode,MarkedCell,GridLines,WinSize,Tables,Shortcuts,
UnreadHidden,SysTabHidden,SortKey,NewChildren)
end;
handle_keypress(help_button,KindOfTable,CurrNode,MarkedCell,GridLines,
WinSize,Tables,Shortcuts,UnreadHidden,SysTabHidden,SortKey,Children) ->
HelpFile = filename:join([code:lib_dir(tv), "doc", "html", "index.html"]),
tool_utils:open_help(win, HelpFile),
loop(KindOfTable,CurrNode,MarkedCell,GridLines,WinSize,Tables,Shortcuts,
UnreadHidden,SysTabHidden,SortKey,Children);
handle_keypress(exit_button,_KindOfTable,_CurrNode,_MarkedCell,_GridLines,
_WinSize,_Tables,_Shortcuts,_UnreadHidden,_SysTabHidden,_SortKey,Children) ->
lists:foreach(
fun({Pid,pman,_OP}) ->
exit(Pid,kill);
(_) ->
done
end,
Children),
exit(normal);
handle_keypress(_Any,KindOfTable,CurrNode,MarkedCell,GridLines,
WinSize,Tables,Shortcuts,UnreadHidden,SysTabHidden,SortKey,Children) ->
loop(KindOfTable,CurrNode,MarkedCell,GridLines,WinSize,Tables,Shortcuts,
UnreadHidden,SysTabHidden,SortKey,Children).
refresh_window(MarkedCell,Tables,KindOfTable,
CurrNode,GridLines,UnreadHidden,SysTabHidden, SortKey, Children) ->
gs:config(win, [{cursor,busy}]),
NewMarkedCell = unmark_cell(MarkedCell, Tables),
{NewTables, NewGridLines} =
update_grid(KindOfTable,CurrNode,GridLines,UnreadHidden,SysTabHidden,
SortKey),
update_tv_info(Children),
gs:config(win, [{cursor,arrow}]),
{NewMarkedCell, NewTables, NewGridLines}.
get_table_id(mnesia, Row, Tables) ->
TabTuple = lists:nth(Row, Tables),
Readable = element(?READABLE_ELEM, TabTuple),
Id = element(?NAME_ELEM, TabTuple),
{Id, Id, Readable};
get_table_id(ets, Row, Tables) ->
TabTuple = lists:nth(Row, Tables),
Readable = element(?READABLE_ELEM, TabTuple),
Name = element(?NAME_ELEM, TabTuple),
case element(?NAMED_TABLE_ELEM, TabTuple) of
false ->
{element(?ID_ELEM, TabTuple), Name, Readable};
_Other ->
{Name, Name, Readable}
end.
replace_node_name('nonode@nohost', 'nonode@nohost') ->
%% Still undistributed...
false;
replace_node_name(_Node, _OldNode) when node() =:= 'nonode@nohost' ->
%% No longer distributed, but previously was!
true;
replace_node_name(_Node, 'nonode@nohost') ->
%% The system has been distributed!
true;
replace_node_name(_Node, _OldNode) ->
false.
update_node_name(Children) when node() =:= 'nonode@nohost' ->
%% We have been distributed, but no longer are!
%% We change all node names stored to 'nonode@nohost'!
%% This works because we *will* receive exit signals
%% for those processes that have died on other nodes,
%% whereupon these processes will be removed from the
%% 'Children' list.
lists:map(fun({Pid, Prog, {Table,_Node}}) ->
{Pid, Prog, {Table,'nonode@nohost'}};
(H) ->
H
end,
Children);
update_node_name(Children) ->
%% We have become distributed!
%% Change all occurrences of 'nonode@nohost'
%% to the new current node name!
HomeNode = node(),
lists:map(fun({Pid, Prog, {Table,'nonode@nohost'}}) ->
{Pid, Prog, {Table,HomeNode}};
(H) ->
H
end,
Children).
show_tv_nodewin(Children) ->
{value, {Pid,tv_nodewin,_Node}} = lists:keysearch(tv_nodewin, 2, Children),
Pid ! show_window.
update_tv_info(Children) ->
Sender = self(),
lists:foreach(fun({Pid,tv_info,{_Table,_Node}}) ->
Pid ! #info_update_table_info{sender=Sender};
(_) ->
done
end,
Children).
update_tv_browser(Children) ->
lists:foreach(fun({Pid,tv_browser,{_Table,_Node}}) ->
Pid ! check_node;
(_) ->
done
end,
Children).
get_tv_info_pid(TabId,Node,Children) ->
TvInfoChildren = [X || X <- Children, element(2,X) =:= tv_info],
case lists:keysearch({TabId,Node}, 3, TvInfoChildren) of
{value, {Pid, tv_info, {_Table,Node}}} ->
Pid;
_Other ->
undefined
end.
start_tv_browser(Tab,Node,_Name,KindOfTable,false,Children) ->
gs:config(win, [beep]),
case get(error_msg_mode) of
normal ->
tv_utils:notify(win, "TV Notification",
["The selected table is unreadable!",
"Only table information may be viewed!"]);
haiku ->
Msg = ["Table protected.",
"The answers that you're seeking",
"will remain unknown."],
tv_utils:notify(win, "TV Notification", Msg)
end,
start_tv_info(Tab, Node, Node =:= node(), KindOfTable, Children);
start_tv_browser(Table,Node,Name,KindOfTable,_Readable,Children) ->
TvBrowserChildren = [X || X <- Children, element(2,X) =:= tv_browser],
case lists:keysearch({Table,Node}, 3, TvBrowserChildren) of
{value, {BPid,tv_browser,{Table,Node}}} ->
BPid ! raise,
Children;
_Other ->
%% Check that table still exists!
case table_still_there(KindOfTable, Node, Node =:= node(), Table, Name) of
true ->
LocalNode = (Node =:= node()),
NewBPid = tv:start_browser(Node, LocalNode, Table, KindOfTable, Name,
get(error_msg_mode)),
[{NewBPid, tv_browser, {Table,Node}} | Children];
_TableDead ->
gs:config(win, [beep]),
case get(error_msg_mode) of
normal ->
tv_utils:notify(win, "TV Notification",
["The table no longer exists!"]);
haiku ->
Msg = ["A table that big?",
"It might be very useful.",
"But now it is gone."],
tv_utils:notify(win, "TV Notification", Msg)
end,
Children
end
end.
table_still_there(ets, Node, LocalNode, Table, Name) ->
case catch tv_ets_rpc:all(Node, LocalNode) of
Tables when is_list(Tables) ->
case lists:member(Table, Tables) of
true ->
true;
false -> %% May be a named table...
lists:keymember(Name, 1, Tables)
end;
Error ->
analyze_error(Error, Node, Table),
false
end;
table_still_there(mnesia, Node, LocalNode, Table, Name) ->
case catch tv_mnesia_rpc:system_info(Node, LocalNode, tables) of
Tables when is_list(Tables) ->
lists:member(Name, Tables);
Error ->
analyze_error(Error, Node, Table),
false
end.
start_tv_info(Table, Node, LocalNode, KindOfTable, Children) ->
TvInfoChildren = [X || X <- Children, element(2,X) =:= tv_info],
case lists:keysearch({Table,Node}, 3, TvInfoChildren) of
{value, {Pid,tv_info,{Table,Node}}} ->
Pid ! #info_raise_window{sender = self()},
Children;
_Other ->
%% May have started a browser but no info window!
%% Info window may have been started from that browser, but
%% don't bother with checking *that*.
Pid = spawn_link(tv_info, info, [self(), Node, LocalNode, Table, KindOfTable,
get(error_msg_mode)]),
[{Pid, tv_info, {Table,Node}} | Children]
end.
start_tv_new_table(CurrNode, Children) ->
TvNewTableChild = [X || X <- Children, element(2,X) =:= tv_new_table],
case TvNewTableChild of
[{Pid,tv_new_table,undefined}] ->
Pid ! raise,
Children;
[] ->
Pid = tv_new_table:start(CurrNode, get(error_msg_mode)),
[{Pid, tv_new_table, undefined} | Children]
end.
create_table(mnesia, _Node, _LocalNode, _TabName, _Options, _NewTabWinPid) ->
error;
create_table(ets, Node, LocalNode, TabName, Options, NewTabWinPid) ->
case tv_table_owner:create(ets, Node, LocalNode, TabName, Options) of
{ok, TabId} ->
NewTabWinPid ! ok,
TabId;
error ->
NewTabWinPid ! error,
error
end.
start_pman(OwnerPid, Children) ->
Pid = pman_shell:start(OwnerPid),
[{Pid,pman,OwnerPid} | Children].
update_grid(TableType, CurrNode, GridLines, UnreadHidden, SysTabHidden,SortKey) ->
NewTables = get_tables(CurrNode, TableType, UnreadHidden, SysTabHidden,SortKey),
TabStr = case TableType of
mnesia ->
"Mnesia ";
ets ->
"ETS "
end,
NodeStr = atom_to_list(CurrNode),
gs:config(win, [{title, "[TV] " ++ TabStr ++ "tables on " ++ NodeStr}]),
gs:config(grid, [{rows, {1, get_nof_rows(length(NewTables), gs:read(grid,height))}}]),
NewGridLines = update_gridlines(NewTables, GridLines, 1),
{NewTables, NewGridLines}.
unmark_cell({undefined, AnyCol, AnyRow}, _Tables) ->
{undefined, AnyCol, AnyRow};
unmark_cell({Id, Col, Row}, Tables) ->
disable_menus(),
TabTuple = lists:nth(Row, Tables),
ReadableTable = element(?READABLE_ELEM, TabTuple),
NamedTable = element(?NAMED_TABLE_ELEM, TabTuple),
BgColor =
case ReadableTable of
false ->
?UNREADABLE_BG_COLOR;
_Other1 ->
?READABLE_BG_COLOR
end,
FgColor =
case NamedTable of
false when Col =:= ?NAME_COL ->
?UNNAMED_FG_COLOR;
_Other2 ->
?NORMAL_FG_COLOR
end,
gs:config(Id, [{bg, {Col, BgColor}},
{fg, {Col, FgColor}}]),
{undefined, undefined, undefined}.
mark_cell({Id,Col,Row}, {Id,Col,Row}, _Readable) ->
{undefined, undefined, undefined};
mark_cell({Id,Col,Row}, _Any, Readable) ->
case lists:member(Col, ?POSSIBLE_MARK_COLS) of
true ->
enable_menus(Col, Readable),
gs:config(Id, [{bg, {Col, ?GRID_MARK_COLOR}},
{fg, {Col, ?NORMAL_FG_COLOR}}]),
{Id, Col,Row};
false ->
{undefined, undefined, undefined}
end.
disable_menus() ->
disable_open_menu(),
disable_trace_menu(),
disable_info_menu().
enable_menus(?ID_COL, true) ->
enable_open_menu(),
enable_info_menu();
enable_menus(?ID_COL, {notext}) ->
enable_open_menu(),
enable_info_menu();
enable_menus(?ID_COL, false) ->
enable_info_menu();
enable_menus(?NAME_COL, true) ->
enable_open_menu(),
enable_info_menu();
enable_menus(?NAME_COL, {notext}) ->
enable_open_menu(),
enable_info_menu();
enable_menus(?NAME_COL, false) ->
enable_info_menu();
enable_menus(?PID_COL, _Any) ->
enable_trace_menu();
enable_menus(?PROCNAME_COL, _Any) ->
enable_trace_menu();
enable_menus(?INFO_COL, _Any) ->
enable_info_menu();
enable_menus(_Col, _Any) ->
done.
resize_window(Width, Height, NofElems) ->
WinWidth = lists:max([Width, ?MIN_WIN_WIDTH]),
WinHeight = lists:max([Height, ?MIN_WIN_HEIGHT]),
gs:config(win, [{width, WinWidth},
{height, WinHeight}
]),
{BgWidth, BgHeight, FgWidth, FgHeight} = get_frame_coords(WinWidth, WinHeight),
{GridWidth, GridHeight} = get_grid_coords(FgWidth, FgHeight),
ColWidths = get_col_widths(?COL_WIDTHS, GridWidth),
resize_header_labels(ColWidths,
[label1,label2,label3,label4,label5],
?GRID_XPOS),
gs:config(bgframe, [{width, BgWidth},
{height, BgHeight}
]),
gs:config(fgframe, [{width, FgWidth},
{height, FgHeight}
]),
gs:config(grid, [{width, GridWidth},
{height, GridHeight},
{columnwidths, ColWidths},
{rows, {1, get_nof_rows(NofElems, GridHeight)}}
]),
{WinWidth, WinHeight}.
create_window(Tables) ->
gs:window(win, gs:start(), [{width, ?WIN_WIDTH},
{height, ?WIN_HEIGHT},
{bg, ?DEFAULT_BG_COLOR},
{title, "[TV] ETS tables on " ++
atom_to_list(node())},
{destroy, true},
{configure, true},
{keypress, true}
]),
ShortcutList = create_menus(),
disable_menus(),
{BgFrameWidth, BgFrameHeight, FgFrameWidth, FgFrameHeight} =
get_frame_coords(?WIN_WIDTH, ?WIN_HEIGHT),
{GridWidth, GridHeight} = get_grid_coords(FgFrameWidth, FgFrameHeight),
ColWidths = get_col_widths(?COL_WIDTHS, GridWidth),
gs:frame(bgframe, win, [{width, BgFrameWidth},
{height, BgFrameHeight},
{x, ?GRID_XPOS},
{y, ?GRID_YPOS},
{bg, {0,0,0}}
]),
gs:frame(fgframe, bgframe, [{width, FgFrameWidth},
{height, FgFrameHeight},
{x, 0},
{y, 1},
{bg, ?DEFAULT_BG_COLOR}
]),
create_header_labels(ColWidths, ?HEADER_LABELS),
gs:grid(grid, fgframe, [{width, GridWidth},
{height, GridHeight},
{x, 0},
{y, -1},
{hscroll,bottom},
{vscroll,right},
{rows, {1, get_nof_rows(length(Tables), GridHeight)}},
{columnwidths, ColWidths},
{fg, ?NORMAL_FG_COLOR},
{bg, {255,255,255}},
{font, ?FONT}
]),
GridLines = update_gridlines(Tables, [], 1),
{{undefined,undefined,undefined}, GridLines, {?WIN_WIDTH,?WIN_HEIGHT}, ShortcutList}.
get_frame_coords(WinWidth, WinHeight) ->
BgWidth = WinWidth - 2 * ?GRID_XPOS,
BgHeight = WinHeight - ?GRID_YPOS - ?GRID_XPOS,
FgWidth = BgWidth,
FgHeight = BgHeight - 1,
{BgWidth, BgHeight, FgWidth, FgHeight}.
get_grid_coords(ParentWidth, ParentHeight) ->
{ParentWidth, ParentHeight + 1}.
get_col_widths(Cols, GridWidth) ->
SbWidth = 25, %% OK, OK, don't bother about it, this constant makes it work... :-/
FixColWidthSum = lists:sum(lists:map(fun(H) ->
lists:nth(H, Cols)
end,
?FIX_WIDTH_COLS)),
AvailableWidth = GridWidth - FixColWidthSum - SbWidth,
OriginalWidth = ?WIN_WIDTH - 2 * ?GRID_XPOS - FixColWidthSum - SbWidth,
get_col_widths(1, Cols, AvailableWidth, OriginalWidth).
get_col_widths(N, [H | T], AvailWidth, OrigWidth) ->
NewColWidth =
case lists:member(N, ?FIX_WIDTH_COLS) of
true ->
H;
_Other ->
round(H * (AvailWidth / OrigWidth) + 0.1)
end,
[NewColWidth | get_col_widths(N + 1, T, AvailWidth, OrigWidth)];
get_col_widths(_N, [], _AvailWidth, _OrigWidth) ->
[].
create_header_labels(ColWidths, Text) ->
create_header_labels(ColWidths, Text, 1, ?GRID_XPOS).
create_header_labels([W | T], [{Name, Text} | TextT], N, Xpos) ->
Ypos = ?GRID_YPOS - 20,
gs:label(Name, win, [{width, W + 1 - 3},
{height, 20},
{x, Xpos + 1 + 3},
{y, Ypos},
{bg, ?DEFAULT_BG_COLOR},
{fg, ?NORMAL_FG_COLOR},
{font, ?HEADER_FONT},
{align, w},
{label, {text, Text}}
]),
create_header_labels(T, TextT, N + 1, Xpos + 1 + W);
create_header_labels([], [], _N, _Xpos) ->
done.
resize_header_labels([W | T], [Name | NT], Xpos) ->
gs:config(Name, [{width, W + 1 - 3},
{x, Xpos + 1 + 3}
]),
resize_header_labels(T, NT, Xpos + 1 + W);
resize_header_labels([], [], _Xpos) ->
done.
disable_open_menu() ->
gs:config(open_table, [{enable,false}]).
disable_info_menu() ->
gs:config(show_info, [{enable,false}]).
disable_trace_menu() ->
gs:config(trace_process, [{enable,false}]).
enable_open_menu() ->
gs:config(open_table, [{enable,true}]).
enable_info_menu() ->
gs:config(show_info, [{enable,true}]).
enable_trace_menu() ->
gs:config(trace_process, [{enable,true}]).
create_menus() ->
gs:menubar(menubar, win, [{bg, ?DEFAULT_BG_COLOR}]),
HelpButt = gs:menubutton(menubar, [{bg, ?DEFAULT_BG_COLOR},
{fg, ?FIREBRICK}, % firebrick
{label, {text, " Help "}},
{underline, 1},
{side, right}
]),
FileButt = gs:menubutton(menubar, [{bg, ?DEFAULT_BG_COLOR},
{fg, ?FIREBRICK}, % firebrick
{label, {text, " File "}},
{underline, 1},
{side, left}
]),
ViewButt = gs:menubutton(menubar, [{bg, ?DEFAULT_BG_COLOR},
{fg, ?FIREBRICK}, % firebrick
{label, {text, " View "}},
{underline, 1},
{side, left}
]),
OptionsButt = gs:menubutton(menubar, [{bg, ?DEFAULT_BG_COLOR},
{fg, ?FIREBRICK}, % firebrick
{label, {text, " Options "}},
{underline, 1},
{side, left}
]),
HelpMenu = gs:menu(HelpButt, [{bg, ?DEFAULT_BG_COLOR},
{fg, ?FIREBRICK},
{disabledfg,?DISABLED_COLOR}
]),
FileMenu = gs:menu(FileButt, [{bg, ?DEFAULT_BG_COLOR},
{fg, ?FIREBRICK},
{disabledfg,?DISABLED_COLOR}
]),
OptionsMenu = gs:menu(OptionsButt, [{bg, ?DEFAULT_BG_COLOR},
{fg, ?FIREBRICK},
{disabledfg,?DISABLED_COLOR}
]),
ViewMenu = gs:menu(ViewButt, [{bg, ?DEFAULT_BG_COLOR},
{fg, ?FIREBRICK},
{disabledfg,?DISABLED_COLOR}
]),
ShortCutList =
create_menulist([{" Help ",normal,help_button,1,h},
separator,
{" OTP Documentation ",normal,otp_help_button,1,no_char}], HelpMenu) ++
create_menulist([{" Open Table ",normal,open_table,1,o},
{" New Table... ",normal,new_table,1,no_char},
{" Table Info ",normal,show_info,7,i},
separator,
{" Nodes... ",normal,select_node,1,n},
separator,
{" Trace Process ",normal,trace_process,1,t},
separator,
{" Exit ",normal, exit_button,2,x}], FileMenu) ++
[{c,exit_button}, {'C',exit_button}] ++
create_menulist([{" Refresh ",normal,update,1,r},
separator,
{" Unreadable Tables ",check,show_unreadable,1,no_char},
separator,
{" System Tables ",check,show_system,1,no_char},
separator,
{" Sort by Name ",radio,sort_table_name,9,no_char},
{" Sort by Id ",radio,sort_table_id,9,no_char},
{" Sort by Owner PID ",radio,sort_owner_pid,15,no_char},
{" Sort by Owner Name ",radio,sort_owner_name,9,no_char},
separator,
{" Error Messages in Haiku ",check,show_haiku,1,no_char}
],
OptionsMenu) ++
create_menulist([{" ETS Tables ",radio,show_ets,1,e},
{" Mnesia Tables ",radio,show_mnesia,1,m}], ViewMenu),
gs:config(show_unreadable, [{select,false}]),
gs:config(show_system, [{select,false}]),
gs:config(show_haiku, [{select,false}]),
%% Due to a bug (or some other reason), only one of the radiobuttons belonging
%% to a specified group can be selected, even if different processes have created
%% the radiobuttons! This means that, if we have started more than one tv_main
%% process, selecting one radiobutton will affect the radiobuttons in the other
%% tv_main process(es)!!! Since this is a highly undesirable bahaviour, we have to
%% create unique group names (i.e., atoms).
%% (We need to group the radiobuttons, since otherwise all created by one process
%% belongs to the same group, which also is undesirable...)
SelfStr = pid_to_list(self()),
SortGroup = list_to_atom("sorting" ++ SelfStr),
TypeGroup = list_to_atom("table_type" ++ SelfStr),
gs:config(sort_table_name, [{group,SortGroup},{select,true}]),
gs:config(sort_table_id, [{group,SortGroup}]),
gs:config(sort_owner_pid, [{group,SortGroup}]),
gs:config(sort_owner_name, [{group,SortGroup}]),
gs:config(show_ets, [{group,TypeGroup}, {select,true}]),
gs:config(show_mnesia, [{group,TypeGroup}]),
ShortCutList.
create_menulist(List, Menu) ->
MaxLength = get_length_of_longest_menu_text(List, 0),
create_menulist(List, Menu, MaxLength).
create_menulist([], _Menu, _MaxLength) ->
[];
create_menulist([{Text, Type, Data, AccCharPos, ShortcutChar} | Rest], Menu, MaxLength) ->
ShortcutCapitalChar =
if
ShortcutChar =:= no_char ->
no_char;
true ->
CharAsciiValue = lists:nth(1, atom_to_list(ShortcutChar)),
CapitalCharValue = CharAsciiValue - ($a - $A),
list_to_atom([CapitalCharValue])
end,
FinalText = if
ShortcutChar =:= no_char ->
Text;
true ->
Text ++ lists:duplicate(MaxLength - length(Text), " ") ++
" Ctrl+" ++ atom_to_list(ShortcutCapitalChar) ++ " "
end,
gs:menuitem(Data, Menu, [{bg, ?DEFAULT_BG_COLOR},
{fg, ?FIREBRICK},
{itemtype, Type},
{label, {text, FinalText}},
{underline, AccCharPos},
{data, Data}
]),
[{ShortcutChar, Data}, {ShortcutCapitalChar, Data} | create_menulist(Rest, Menu, MaxLength)];
create_menulist([separator | Rest], Menu, MaxLength) ->
gs:menuitem(Menu, [{itemtype, separator}]),
create_menulist(Rest, Menu, MaxLength).
get_length_of_longest_menu_text([], MaxLength) ->
MaxLength;
get_length_of_longest_menu_text([{Text, _Type, _Data, _APos, _SChar} | Rest], CurrMax) ->
L = length(Text),
if
L > CurrMax ->
get_length_of_longest_menu_text(Rest, L);
true ->
get_length_of_longest_menu_text(Rest, CurrMax)
end;
get_length_of_longest_menu_text([separator | Rest], CurrMax) ->
get_length_of_longest_menu_text(Rest, CurrMax).
get_nof_rows(NofElems, GridHeight) ->
lists:max([NofElems, round((GridHeight - 20) / 21) + 1]).
config_gridline(LineId, TabTuple) ->
Readable = element(?READABLE_ELEM, TabTuple),
NamedTable = element(?NAMED_TABLE_ELEM, TabTuple),
{FgColor, BgColor} =
case Readable of
true ->
{?NORMAL_FG_COLOR, ?READABLE_BG_COLOR};
false ->
{?UNREADABLE_FG_COLOR, ?UNREADABLE_BG_COLOR};
{notext} ->
{?NORMAL_FG_COLOR, ?READABLE_BG_COLOR}
end,
NameFgColor =
case NamedTable of
false ->
?UNNAMED_FG_COLOR;
_Other ->
?NORMAL_FG_COLOR
end,
gs:config(LineId, [{bg, BgColor},
{fg, FgColor},
{fg, {?NAME_COL, NameFgColor}},
{click, true},
{doubleclick, true},
{data, {grid,Readable}} |
lists:map(
fun({Elem,Col}) ->
case element(Elem, TabTuple) of
{notext} ->
{text, {Col, ""}};
Other when Elem =:= ?NAME_ELEM ->
case NamedTable of
false ->
{text, {Col, " " ++
lists:flatten(
io_lib:write(
Other)) ++ " "}};
_AnyOther ->
{text, {Col, " " ++ lists:flatten(
io_lib:write(
Other))}}
end;
Other ->
{text, {Col, " " ++ lists:flatten(
io_lib:write(
Other))}}
end
end,
[{?NAME_ELEM, ?NAME_COL},
{?ID_ELEM, ?ID_COL},
{?PID_ELEM, ?PID_COL},
{?PROCNAME_ELEM, ?PROCNAME_COL},
{?INFO_ELEM, ?INFO_COL}]
)
]).
update_gridlines([TabTuple | TT], [LineId | GT], CurrRow) ->
config_gridline(LineId, TabTuple),
[LineId | update_gridlines(TT, GT, CurrRow + 1)];
update_gridlines([TabTuple | TT], [], CurrRow) ->
LineId = gs:gridline(grid, [{row, CurrRow}]),
config_gridline(LineId, TabTuple),
[LineId | update_gridlines(TT, [], CurrRow + 1)];
update_gridlines([], [LineId | GT], _CurrRow) ->
gs:destroy(LineId),
update_gridlines([], GT, _CurrRow);
update_gridlines([], [], _CurrRow) ->
[].