aboutsummaryrefslogtreecommitdiffstats
path: root/lib/tv/src/tv_nodewin.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/tv/src/tv_nodewin.erl')
-rw-r--r--lib/tv/src/tv_nodewin.erl403
1 files changed, 403 insertions, 0 deletions
diff --git a/lib/tv/src/tv_nodewin.erl b/lib/tv/src/tv_nodewin.erl
new file mode 100644
index 0000000000..3999d201d8
--- /dev/null
+++ b/lib/tv/src/tv_nodewin.erl
@@ -0,0 +1,403 @@
+%%
+%% %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_nodewin).
+
+
+
+-export([start/2, init/3]).
+
+
+-include("tv_int_msg.hrl").
+
+
+
+-define(WINDOW_WIDTH, 230).
+-define(WINDOW_HEIGHT, 260).
+-define(DEFAULT_BG_COLOR, {217,217,217}).
+-define(POLL_INTERVAL, 5000).
+
+
+
+%%%*********************************************************************
+%%% EXTERNAL FUNCTIONS
+%%%*********************************************************************
+
+
+
+
+start(CurrNode, ErrMsgMode) ->
+ spawn_link(?MODULE, init, [self(), CurrNode, ErrMsgMode]).
+
+
+
+
+
+init(Pid, CurrNode, ErrMsgMode) ->
+ process_flag(trap_exit, true),
+ net_kernel:monitor_nodes(true),
+ put(error_msg_mode, ErrMsgMode),
+ gs:start(),
+ NewCurrNode = update_node_listbox(CurrNode, false),
+ tell_master(NewCurrNode, CurrNode, Pid),
+ loop(Pid, NewCurrNode, node(), false).
+
+
+
+
+%%%*********************************************************************
+%%% INTERNAL FUNCTIONS
+%%%*********************************************************************
+
+
+
+init_window(CurrNode, Pid) ->
+ create_window(),
+ NewCurrNode = update_node_listbox(CurrNode, true),
+ tell_master(NewCurrNode, CurrNode, Pid),
+ gs:config(win, [{map,true}]),
+ NewCurrNode.
+
+
+
+
+handle_error(nodedown) ->
+ gs:window(errorwin, gs:start(), []),
+ gs:config(errorwin, [beep]),
+ case get(error_msg_mode) of
+ normal ->
+ tv_utils:notify(errorwin, "TV Notification", ["The selected node is down!"]);
+ haiku ->
+ Msg = ["With searching comes loss",
+ "And the presence of absence:",
+ "Node is down."],
+ tv_utils:notify(errorwin, "TV Notification", Msg)
+ end,
+ gs:destroy(errorwin);
+handle_error(distributed) ->
+ gs:window(errorwin, gs:start(), []),
+ gs:config(errorwin, [beep]),
+ case get(error_msg_mode) of
+ normal ->
+ tv_utils:notify(errorwin, "TV Notification",
+ ["The system has become distributed!"]);
+ haiku ->
+ Msg = [],
+ tv_utils:notify(errorwin, "TV Notification", Msg)
+ end,
+ gs:destroy(errorwin);
+handle_error(undistributed) ->
+ gs:window(errorwin, gs:start(), []),
+ gs:config(errorwin, [beep]),
+ case get(error_msg_mode) of
+ normal ->
+ tv_utils:notify(errorwin, "TV Notification",
+ ["The system is no longer distributed!"]);
+ haiku ->
+ Msg = ["The system you see",
+ "Is not a distributed",
+ "system anymore."],
+ tv_utils:notify(errorwin, "TV Notification", Msg)
+ end,
+ gs:destroy(errorwin).
+
+get_node_lists(CurrNode) ->
+ NodeDataList = lists:sort([node() | nodes()]),
+ NodeTextList = lists:map(fun(Item) ->
+ " " ++ atom_to_list(Item)
+ end,
+ NodeDataList),
+
+ %% It *may* be possible that CurrNode has disappeared!
+ %% If this is the case, use the node where TV resides
+ %% as new current node.
+ %% This also covers the case when our own node (or some
+ %% other node) suddenly goes distributed.
+
+ NewCurrNode = case lists:member(CurrNode, NodeDataList) of
+ true ->
+ CurrNode;
+ false ->
+ node()
+ end,
+
+ %% Now get the index that shall be marked in the node listbox.
+ %% Remember that the first item has number 0 (zero)!
+ NodeMarkIndex = get_node_mark_index(NewCurrNode, NodeDataList, 0),
+
+ {NewCurrNode, NodeDataList, NodeTextList, NodeMarkIndex}.
+
+
+
+
+%% We know that CurrNode is *somewhere* in the list, since we have checked.
+%% If the original CurrNode wasn't there, then we are using node() instead,
+%% which definitely is in the list. (node() may have gone distributed in the
+%% meantime, but it *IS* in the list!) :-)
+
+get_node_mark_index(CurrNode, [H | T], Acc) when CurrNode =/= H ->
+ get_node_mark_index(CurrNode, T, Acc + 1);
+get_node_mark_index(CurrNode, [CurrNode | _], Acc) ->
+ Acc. %% Acc tells the index of the current head. :-)
+
+
+
+
+
+check_selected_node('nonode@nohost', _OldNode, _WinCreated) when node() =:= 'nonode@nohost' ->
+ %% Not distributed, OK!
+ 'nonode@nohost';
+check_selected_node(_Node, _OldNode, WinCreated) when node() =:= 'nonode@nohost' ->
+ %% No longer distributed, but previously was!
+ handle_error(undistributed),
+ update_node_listbox('nonode@nohost', WinCreated);
+check_selected_node(Node, _OldNode, _WinCreated) when Node =:= node() ->
+ %% We are distributed, but on
+ %% our own node! Since we
+ % still are running, the node
+ %% is up.
+ Node;
+check_selected_node(Node, 'nonode@nohost', WinCreated) ->
+ %% The system has been distributed!
+ net_kernel:monitor_nodes(true),
+ handle_error(distributed),
+ update_node_listbox(Node, WinCreated);
+check_selected_node(Node, _OldNode, WinCreated) ->
+ %% We are distributed, and a new node has been chosen!
+ %% We better check this node!
+ case net_adm:ping(Node) of
+ pong ->
+ Node;
+ _Other ->
+ handle_error(nodedown),
+ update_node_listbox(Node, WinCreated)
+ end.
+
+
+
+available_nodes() ->
+ lists:sort([node() | nodes()]).
+
+
+
+loop(Pid, CurrNode, HomeNode, WinCreated) ->
+ receive
+
+ {nodedown, _Node} ->
+ flush_nodedown_messages(),
+ flush_nodeup_messages(),
+ case lists:member(CurrNode, available_nodes()) of
+ true ->
+ done;
+ false when node() =:= 'nonode@nohost', CurrNode =/= 'nonode@nohost' ->
+ handle_error(undistributed);
+ false ->
+ handle_error(nodedown)
+ end,
+ NewCurrNode = update_node_listbox(CurrNode, WinCreated),
+ tell_master(NewCurrNode, CurrNode, Pid),
+ loop(Pid, NewCurrNode, node(), WinCreated);
+
+
+ {nodeup, _Node} ->
+ flush_nodeup_messages(),
+ flush_nodedown_messages(),
+ case lists:member(CurrNode, available_nodes()) of
+ true ->
+ done;
+ false when node() =:= 'nonode@nohost', CurrNode =/= 'nonode@nohost' ->
+ handle_error(undistributed);
+ false when CurrNode =:= 'nonode@nohost' ->
+ net_kernel:monitor_nodes(true),
+ handle_error(distributed);
+ false ->
+ handle_error(nodedown)
+ end,
+ NewCurrNode = update_node_listbox(CurrNode, WinCreated),
+ tell_master(NewCurrNode, CurrNode, Pid),
+ loop(Pid, NewCurrNode, node(), WinCreated);
+
+
+ {gs, node_listbox, click, Data, [Idx, _Txt | _]} ->
+ NewCurrNode = check_selected_node(lists:nth(Idx + 1, Data), CurrNode, WinCreated),
+ tell_master(NewCurrNode, CurrNode, Pid),
+ loop(Pid, NewCurrNode, node(), WinCreated);
+
+
+ {gs, win, configure, _, _} ->
+ gs:config(win, [{width, ?WINDOW_WIDTH}, {height, ?WINDOW_HEIGHT}]),
+ loop(Pid, CurrNode, HomeNode, WinCreated);
+
+
+ show_window when WinCreated->
+ gs:config(win, [raise]),
+ loop(Pid, CurrNode, HomeNode, WinCreated);
+
+ show_window when not WinCreated ->
+ init_window(CurrNode, Pid),
+ loop(Pid, CurrNode, HomeNode, true);
+
+ {gs, _Id, click, close_menu, _Args} ->
+ gs:destroy(win),
+ loop(Pid, CurrNode, HomeNode, false);
+
+
+ {gs, _Id, keypress, _Data, [c, _, 0, 1 | _]} ->
+ gs:destroy(win),
+ loop(Pid, CurrNode, HomeNode, false);
+
+
+ {gs, _Id, keypress, _Data, ['C', _, 1, 1 | _]} ->
+ gs:destroy(win),
+ loop(Pid, CurrNode, HomeNode, false);
+
+
+ {gs, _Id, keypress, _Data, _Args} ->
+ loop(Pid, CurrNode, HomeNode, WinCreated);
+
+
+ {gs, _, destroy, _, _} ->
+ loop(Pid, CurrNode, HomeNode, false);
+
+
+ {error_msg_mode, Mode} ->
+ put(error_msg_mode, Mode),
+ loop(Pid, CurrNode, HomeNode, WinCreated);
+
+ {'EXIT', Pid, _Reason} ->
+ net_kernel:monitor_nodes(false),
+ exit(normal);
+
+
+ {'EXIT', _OtherPid, _Reason} ->
+ loop(Pid, CurrNode, HomeNode, WinCreated);
+
+
+ _Other ->
+ io:format("Node window received message ~p ~n", [_Other]),
+ loop(Pid, CurrNode, HomeNode, WinCreated)
+
+ after
+ 1000 ->
+ NewHomeNode = case node() of
+ HomeNode ->
+ HomeNode;
+ Other ->
+ self() ! {nodeup, Other}
+ end,
+ loop(Pid, CurrNode, NewHomeNode, WinCreated)
+ end.
+
+
+
+
+tell_master(NewNode, NewNode, _Pid) ->
+ done;
+tell_master(NewNode, _OldNode, Pid) ->
+ Pid ! {tv_new_node, self(), NewNode}.
+
+
+
+
+flush_nodedown_messages() ->
+ receive
+ {nodedown,_Node} ->
+ flush_nodedown_messages()
+ after
+ 0 ->
+ done
+ end.
+
+
+
+
+flush_nodeup_messages() ->
+ receive
+ {nodeup,_Node} ->
+ flush_nodeup_messages()
+ after
+ 0 ->
+ done
+ end.
+
+
+
+
+update_node_listbox(Node, WinCreated) ->
+ {NewNode, NodeDataList, NodeTextList, MarkIndex} = get_node_lists(Node),
+ case WinCreated of
+ false ->
+ done;
+ true ->
+ catch gs:config(node_listbox, [{data, NodeDataList},
+ {items, NodeTextList},
+ {selection, MarkIndex}
+ ])
+ end,
+ NewNode.
+
+
+
+
+
+create_window() ->
+ gs:window(win, gs:start(), [{width, ?WINDOW_WIDTH},
+ {height, ?WINDOW_HEIGHT},
+ {bg, ?DEFAULT_BG_COLOR},
+ {title, "[TV] Connected nodes"},
+ {configure, true},
+ {destroy, true},
+ {cursor, arrow},
+ {keypress, true}
+ ]),
+ gs:menubar(menubar, win, [{bg, ?DEFAULT_BG_COLOR}
+ ]),
+ gs:menubutton(mbutt, menubar, [{bg, ?DEFAULT_BG_COLOR},
+ {fg, {178, 34, 34}}, % firebrick
+ {label, {text, " File "}},
+ {underline, 1}
+ ]),
+
+ % Create the actual menu!
+ gs:menu(menu, mbutt, [{bg, ?DEFAULT_BG_COLOR},
+ {fg, {178, 34, 34}}]),
+ gs:menuitem(menu, [{bg, ?DEFAULT_BG_COLOR},
+ {fg, {178, 34, 34}},
+ {label, {text, " Close Ctrl-C "}},
+ {data, close_menu},
+ {underline, 1}
+ ]),
+
+ Xpos = 4,
+ Ypos = 40,
+ gs:listbox(node_listbox, win, [{x, Xpos},
+ {y, Ypos},
+ {width, ?WINDOW_WIDTH - 2 * Xpos},
+ {height, ?WINDOW_HEIGHT - Ypos - Xpos},
+ {bg, {255,255,255}},
+ {vscroll, right},
+ {hscroll, true},
+ {click, true}
+ ]).
+
+
+
+
+
+
+
+