From 84adefa331c4159d432d22840663c38f155cd4c1 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Fri, 20 Nov 2009 14:54:40 +0000 Subject: The R13B03 release. --- lib/tv/src/tv_main.erl | 1807 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1807 insertions(+) create mode 100644 lib/tv/src/tv_main.erl (limited to 'lib/tv/src/tv_main.erl') diff --git a/lib/tv/src/tv_main.erl b/lib/tv/src/tv_main.erl new file mode 100644 index 0000000000..2f743c2397 --- /dev/null +++ b/lib/tv/src/tv_main.erl @@ -0,0 +1,1807 @@ +%% +%% %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) -> + []. + + + + + + + + + -- cgit v1.2.3