%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 1998-2011. 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). -compile([{nowarn_deprecated_function,{gs,config,2}}, {nowarn_deprecated_function,{gs,destroy,1}}, {nowarn_deprecated_function,{gs,frame,3}}, {nowarn_deprecated_function,{gs,grid,3}}, {nowarn_deprecated_function,{gs,gridline,2}}, {nowarn_deprecated_function,{gs,label,3}}, {nowarn_deprecated_function,{gs,menu,2}}, {nowarn_deprecated_function,{gs,menubar,3}}, {nowarn_deprecated_function,{gs,menubutton,2}}, {nowarn_deprecated_function,{gs,menuitem,2}}, {nowarn_deprecated_function,{gs,menuitem,3}}, {nowarn_deprecated_function,{gs,read,2}}, {nowarn_deprecated_function,{gs,start,0}}, {nowarn_deprecated_function,{gs,window,3}}]). -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) -> [].