%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1997-2012. 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%
%%%*********************************************************************
%%%
%%% Description: Part of pc handling the creation of menus, as well as
%%% treating the signals these menus results in,
%%% when chosen.
%%%
%%%*********************************************************************
-module(tv_pc_menu_handling).
-compile([{nowarn_deprecated_function,{gs,config,2}},
{nowarn_deprecated_function,{gs,destroy,1}},
{nowarn_deprecated_function,{gs,start,0}},
{nowarn_deprecated_function,{gs,window,3}}]).
-export([create_menus/1,
exit_button/1,
insert_object/1,
delete_object/1,
search_object/1,
open_table/7,
set_poll_interval/1,
poll_table/1,
sort_rising_order/1,
sort_falling_order/1,
no_sorting/1,
lists_as_strings/1,
lists_as_lists/1,
table_info/1,
help_button/1,
otp_help_button/1,
get_window_title/4]).
-include("tv_int_def.hrl").
-include("tv_int_msg.hrl").
-include("tv_pc_int_def.hrl").
%%%*********************************************************************
%%% EXTERNAL FUNCTIONS
%%%*********************************************************************
%% Shortcuts currently used, in alphabetical order:
%%
%% c -> "Exit"
%% d -> "Delete Object"
%% f -> "Sort Falling Order"
%% h -> "Help"
%% i -> "Table Info"
%% n -> "No Sorting"
%% o -> "Edit Object"
%% p -> "Poll Table"
%% r -> "Sort Rising Order"
%% s -> "Search Object"
%% v -> "Set Poll Interval"
%% x -> "Exit"
create_menus(PwPid) ->
%% 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),
ListGroup = list_to_atom("lists" ++ SelfStr),
% Order pw to create the 'File' menu.
?GRAPH_FUNC_FILE:create_menu(PwPid,
" File ",
1,
[{" Table Info ", normal, table_info, 7, i},
separator,
{" Close ", normal, exit_button, 1, c}
]),
?GRAPH_FUNC_FILE:create_menu(PwPid,
" Edit ",
1,
[{" Edit Object... ", normal, insert_object, 1, o},
{" Delete Object ", normal, delete_object, 1, d}
]),
?GRAPH_FUNC_FILE:create_menu(PwPid,
" View ",
1,
[{" Lists as Lists ",{radio,false,ListGroup},lists_as_lists,10,no_char},
{" Lists as Strings ",{radio,true,ListGroup},lists_as_strings,10,no_char}
]),
% Order pw to create the 'Options' menu.
?GRAPH_FUNC_FILE:create_menu(PwPid,
" Options ",
1,
[{" Poll Table ", normal, poll_table, 1, p},
{" Poll Interval... ",normal,set_poll_interval,6,no_char},
separator,
{" Search Object ", normal, search_object, 1, s},
separator,
{" Sort Ascending Order ",{radio,false,SortGroup},sort_rising_order,6,no_char},
{" Sort Descending Order ",{radio,false,SortGroup},sort_falling_order,6,no_char},
{" No Sorting ",{radio,true,SortGroup},no_sorting,1,no_char}
]).
exit_button(_ProcVars) ->
exit(normal).
help_button(ProcVars) ->
WinP = ProcVars#process_variables.window_params,
HelpFile = filename:join([code:lib_dir(tv), "doc", "html", "index.html"]),
tool_utils:open_help(WinP#window_params.window_id, HelpFile),
ProcVars.
otp_help_button(ProcVars) ->
WinP = ProcVars#process_variables.window_params,
IndexFile = filename:join([code:root_dir(), "doc", "index.html"]),
tool_utils:open_help(WinP#window_params.window_id, IndexFile),
ProcVars.
table_info(ProcVars) ->
#process_variables{table_id = TableId,
current_node = Node,
local_node = LocalNode,
table_type = Type,
parent_pid = ParentPid} = ProcVars,
case TableId of
undefined ->
done;
_OtherValue ->
ParentPid ! {tv_start_infowin, TableId, Node, LocalNode, Type}
end,
ProcVars.
sort_rising_order(ProcVars) ->
request_sort_settings(ProcVars#process_variables.pd_pid, true, false),
ProcVars.
sort_falling_order(ProcVars) ->
request_sort_settings(ProcVars#process_variables.pd_pid, true, true),
ProcVars.
no_sorting(ProcVars) ->
request_sort_settings(ProcVars#process_variables.pd_pid, false, false),
ProcVars.
set_poll_interval(ProcVars) ->
#process_variables{etsread_pid = EtsreadPid,
poll_interval = PollInterval} = ProcVars,
case tv_poll_dialog:start(PollInterval) of
cancel ->
ProcVars;
NewPollInterval ->
EtsreadPid ! #etsread_set_poll_interval{sender = self(),
interval = NewPollInterval},
ProcVars#process_variables{poll_interval = NewPollInterval}
end.
poll_table(ProcVars) ->
EtsreadPid = ProcVars#process_variables.etsread_pid,
EtsreadPid ! #etsread_poll_table{sender = self()},
ProcVars.
search_object(ProcVars) ->
DbsPid = ProcVars#process_variables.dbs_pid,
DbsPid ! #dbs_search_req{sender=self()},
ProcVars.
lists_as_strings(ProcVars) ->
PdPid = ProcVars#process_variables.pd_pid,
PdPid ! #pc_list_info{sender=self(), lists_as_strings=true},
DbsPid = ProcVars#process_variables.dbs_pid,
DbsPid ! #pc_list_info{sender=self(), lists_as_strings=true},
ProcVars#process_variables{lists_as_strings=true}.
lists_as_lists(ProcVars) ->
PdPid = ProcVars#process_variables.pd_pid,
PdPid ! #pc_list_info{sender=self(), lists_as_strings=false},
DbsPid = ProcVars#process_variables.dbs_pid,
DbsPid ! #pc_list_info{sender=self(), lists_as_strings=false},
ProcVars#process_variables{lists_as_strings=false}.
insert_object(ProcVars) ->
#process_variables{pd_pid = PdPid,
current_node = Node,
local_node = LocalNode,
table_type = TabType,
table_name = TabName,
table_protection = Protection,
window_params = WinP} = ProcVars,
case Protection of
public ->
case TabType of
mnesia ->
case catch tv_mnesia_rpc:table_info(Node, LocalNode, TabName, attributes) of
nodedown ->
handle_error(nodedown);
no_table ->
handle_error(nodedown);
mnesia_not_started ->
handle_error(mnesia_not_started);
{unexpected_error,Reason} ->
handle_error({unexpected_error,Reason});
AttrList ->
PdPid ! #pd_rec_edit{sender = self(),
attributes = AttrList
}
end;
ets ->
PdPid ! #pd_rec_edit{sender = self(),
attributes = [tuple]
}
end;
_OtherProtection ->
WinId = WinP#window_params.window_id,
gs:config(WinId, [beep]),
ErrMsg =
case get(error_msg_mode) of
normal ->
["The table is protected and",
" cannot be edited."];
haiku ->
["The table you see",
"Is cunningly protected:",
"You can only watch."]
end,
tv_utils:notify(WinId, "TV Notification", ErrMsg)
end,
ProcVars.
delete_object(ProcVars) ->
#process_variables{dbs_pid = DbsPid,
table_protection = Protection,
marked_row = MarkedRow,
marked_object = MarkedObject,
marked_color = MarkedColor,
window_params = WinP} = ProcVars,
case MarkedRow of
undefined ->
done;
_AnyRow ->
case Protection of
public ->
DbsPid ! #dbs_delete_object{sender = self(),
object = MarkedObject,
color = MarkedColor,
obj_no = MarkedRow};
_OtherProtection ->
WinId = WinP#window_params.window_id,
gs:config(WinId, [beep]),
ErrMsg =
case get(error_msg_mode) of
normal ->
["The table is protected and",
" cannot be edited."];
haiku ->
["The table you see",
"Is cunningly protected:",
"You can only watch."]
end,
tv_utils:notify(WinId, "TV Notification", ErrMsg)
end
end,
ProcVars.
open_table(CurrNode, LocalNode, TableId, TableType, TableName, Raise, ProcVars) ->
#process_variables{dbs_pid = DbsPid,
etsread_pid = EtsreadPid,
pw_pid = PwPid,
pd_pid = PdPid,
poll_interval = PollInterval,
window_params = WinP} = ProcVars,
case Raise of
true ->
gs:config(WinP#window_params.window_id, [raise]);
false ->
done
end,
{Type, KeyPos, Protection} = init_etsread(EtsreadPid, DbsPid, CurrNode, LocalNode, TableId,
TableType, PollInterval),
WinTitle = get_window_title(TableType, CurrNode, TableId, TableName),
PwPid ! #pw_set_window_title{sender = self(),
win_title = WinTitle},
Writable =
case Protection of
public ->
true;
_Other ->
false
end,
RecordName =
case TableType of
mnesia ->
tv_mnesia_rpc:table_info(CurrNode, LocalNode, TableId, record_name);
ets ->
undefined
end,
PdPid ! #pd_new_table{sender = self(),
table_type = TableType,
table_name = TableName,
record_name = RecordName,
writable = Writable},
init_dbs(DbsPid, Type, KeyPos, EtsreadPid),
ProcVars#process_variables{current_node = CurrNode,
local_node = LocalNode,
table_id = TableId,
table_type = TableType,
table_name = TableName,
table_protection = Protection}.
get_window_title(ets, Node, TableId, TableName) ->
NameStr = lists:flatten(io_lib:write(TableName)),
TableStr = case TableId of
{TableName, _Pid} ->
NameStr;
TableName ->
NameStr;
_Other ->
lists:flatten(io_lib:write(TableId)) ++ " (" ++ NameStr ++ ")"
end,
WinTitleSuffix = " Node: " ++ atom_to_list(Node),
"ETS: " ++ TableStr ++ WinTitleSuffix;
get_window_title(mnesia, Node, _TableId, TableName) ->
TableNameStr = lists:flatten(io_lib:write(TableName)),
WinTitleSuffix = " Node: " ++ atom_to_list(Node),
"Mnesia: " ++ TableNameStr ++ WinTitleSuffix.
%%%*********************************************************************
%%% INTERNAL FUNCTIONS
%%%*********************************************************************
init_etsread(EtsreadPid, DbsPid, Node, LocalNode, TableId, TableType, PollInterval) ->
EtsreadPid ! #etsread_deblock{sender = self(),
dbs_pid = DbsPid,
node = Node,
local_node = LocalNode,
table_id = TableId,
table_type = TableType,
poll_interval = PollInterval
},
receive
#etsread_deblock_cfm{type=Type, keypos=KeyPos, protection=Protection} ->
{Type, KeyPos, Protection}
after 10000 ->
exit(error)
end.
init_dbs(DbsPid, Type, KeyPos, EtsreadPid) ->
DbsPid ! #dbs_deblock{sender = self(),
etsread_pid = EtsreadPid,
type = Type,
keypos = KeyPos,
sublist_length = ?ITEMS_TO_DISPLAY
},
receive
#dbs_deblock_cfm{} ->
done
after 10000 ->
exit(error)
end.
request_sort_settings(PdPid, Sorting, Reverse) ->
PdPid ! #pd_get_sort_settings{sender = self(),
sorting = Sorting,
reverse = Reverse
}.
handle_error(mnesia_not_started) ->
gs:window(errorwin, gs:start(), []),
gs:config(errorwin, [beep]),
case get(error_msg_mode) of
normal ->
tv_utils:notify(errorwin, "TV Notification", ["Mnesia not started!"]);
haiku ->
tv_utils:notify(errorwin, "TV Notification", ["Mnesia is stopped.",
"We wish to reach all data",
"But we never will."])
end,
gs:destroy(errorwin);
handle_error(nodedown) ->
done; %% Main process handles this!
handle_error({unexpected_error,Cause}) ->
gs:window(errorwin, gs:start(), []),
io:format("Unexpected error: ~p~n", [Cause]),
gs:config(errorwin, [beep]),
gs:destroy(errorwin).