%%
%% %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%
-module(tv_info).
-compile([{nowarn_deprecated_function,{gs,config,2}},
          {nowarn_deprecated_function,{gs,create,3}},
          {nowarn_deprecated_function,{gs,frame,2}},
          {nowarn_deprecated_function,{gs,label,2}},
          {nowarn_deprecated_function,{gs,listbox,2}},
          {nowarn_deprecated_function,{gs,read,2}},
          {nowarn_deprecated_function,{gs,start,0}},
          {nowarn_deprecated_function,{gs,window,3}}]).



-export([info/6
	]).


-include("tv_int_def.hrl").
-include("tv_int_msg.hrl").




-define(DEFAULT_BG_COLOR, {217, 217, 217}).


-record(card_field_ids, {parent_pid,
			 window_id,
			 window_frame,
			 table_id,
			 table_type,
			 table_name,
			 named_table,
			 owner_pid,
			 owner_name,
			 bag_or_set,
			 arity,
			 attributes,
			 wild_pattern,
			 keypos,
			 index,
			 snmp,
			 protection,
			 size, 
			 memory,
			 storage_type,
			 disc_copies,
			 where_to_read,
			 ram_copies,
			 disc_only_copies,
			 where_to_write,
			 checkpoints,
			 node
			}).
	       


-define(WINDOW_WIDTH, 580).
-define(WINDOW_HEIGHT, 430).



-define(MNESIA_INFO_ITEMS, [type,
			    arity,
			    attributes,
			    index,
			    size,
			    memory,
			    storage_type,
			    where_to_read,
			    disc_copies,
			    disc_only_copies,
			    ram_copies,
			    where_to_write,
			    checkpoints
			   ]).




info(Master, Node, LocalNode, TabId, TabType, ErrMsgMode) ->
    process_flag(trap_exit,true),
    WinId = create_window(),
    {CardIds, MaskLabel} = init(Master, Node, LocalNode, TabId, TabType, WinId),
    put(error_msg_mode, ErrMsgMode),
    gs:config(WinId, [{map, true}]),
    loop(CardIds, MaskLabel, Node, LocalNode, TabId, TabType).






create_window() ->
    WinWidth = ?WINDOW_WIDTH,
    WinHeight = ?WINDOW_HEIGHT,
    Win = gs:window(win, gs:start(), [{width, WinWidth},
				      {height, WinHeight},
				      {bg, ?DEFAULT_BG_COLOR},
				      {destroy, true},
				      {configure, true},
				      {keypress, true}
				     ]),

    MenubarId = gs:create(menubar, Win, [{bg, ?DEFAULT_BG_COLOR}
					]),
    Mbutt = gs:create(menubutton, MenubarId, [{bg, ?DEFAULT_BG_COLOR},
					      {fg, {178, 34, 34}},  % firebrick
					      {label, {text, " File "}},
					      {underline, 1}
					     ]),
    Obutt = gs:create(menubutton, MenubarId, [{bg, ?DEFAULT_BG_COLOR},
					      {fg, {178, 34, 34}},  % firebrick
					      {label, {text, " Options "}},
					      {underline, 1}
					     ]),

       % Create the actual menu!
    FMenu = gs:create(menu, Mbutt, [{bg, ?DEFAULT_BG_COLOR},
				   {fg, {178, 34, 34}}]), 
    OMenu = gs:create(menu, Obutt, [{bg, ?DEFAULT_BG_COLOR},
				   {fg, {178, 34, 34}}]), 
    gs:create(menuitem, FMenu, [{bg, ?DEFAULT_BG_COLOR},
				{fg, {178, 34, 34}},
				{label, {text, " Close    Ctrl-C "}},
				{data, close_menu},
				{underline, 1}
			       ]),
    gs:create(menuitem, OMenu, [{bg, ?DEFAULT_BG_COLOR},
				{fg, {178, 34, 34}},
				{label, {text, " Refresh    Ctrl-R "}},
				{data, update},
				{underline, 1}
			       ]),
    Win.

    






init(Master, Node, LocalNode, TabId, TabType, WinId) ->
    WinWidth  = ?WINDOW_WIDTH,
    WinHeight = ?WINDOW_HEIGHT,

    WinFrame = gs:frame(WinId, [{width, WinWidth},
				{height, WinHeight},
				{x, 0},
				{y, 30},
				{bg, ?DEFAULT_BG_COLOR},
				{bw, 0}
			       ]),

    TableIdFlap       = create_flap(1, "Table Id", WinFrame),
    BasicSettingsFlap = create_flap(2, "Basic Settings", WinFrame),
    SizeFlap          = create_flap(3, "Size", WinFrame),
    StorageFlap       = create_flap(4, "Storage", WinFrame),

    TableIdCard       = create_card(WinFrame, TableIdFlap),
    BasicSettingsCard = create_card(WinFrame, BasicSettingsFlap),
    SizeCard          = create_card(WinFrame, SizeFlap),
    StorageCard       = create_card(WinFrame, StorageFlap),
        

    set_flap_label(TableIdFlap, "Table Id"),
    set_flap_label(BasicSettingsFlap, "Basic Settings"),
    set_flap_label(SizeFlap, "Size"),
    set_flap_label(StorageFlap, "Storage"),


    gs:config(TableIdCard, [raise]),

    CardIds = print_cards(TabType, TableIdCard, BasicSettingsCard, SizeCard, StorageCard),

    {_CardId, FirstMaskXpos} = gs:read(TableIdFlap, data),
    Mask = gs:label(WinFrame, [{width, gs:read(TableIdFlap, width) - 2 * gs:read(TableIdFlap, bw) + 1},
			       {height, gs:read(TableIdCard, bw)},
			       {x, FirstMaskXpos},
			       {y, gs:read(TableIdCard, y)},
			       {bg, ?DEFAULT_BG_COLOR}
			      ]),

    update_info_flaps(TabType, Node, LocalNode, TabId, CardIds, Master),
    {CardIds#card_field_ids{parent_pid   = Master,
			    window_id    = WinId, 
			    window_frame = WinFrame}, Mask}.

    



check_node(OldNode, LocalNode) ->
    HomeNode = node(),
    case net_adm:ping(OldNode) of
	pong ->
	    OldNode;
	pang when LocalNode ->  
	       %% The system has gone either distributed or undistributed.
	       %% No matter which, HomeNode tells the current correct node.
	    HomeNode;
	pang ->
	    OldNode
    end.






update_data_field(notext, {label, Id}) ->
    gs:config(Id, [{label, {text, "" }}]);
update_data_field(notext, {listbox, Id}) ->
    gs:config(Id, [{items, []}]);
update_data_field({Data}, {label, Id}) ->
    gs:config(Id, [{label, {text, " " ++ lists:flatten(io_lib:write(Data))}}]);
update_data_field({Data}, {listbox, Id}) ->
    gs:config(Id, [{items, lists:map(fun(E) -> " " ++ lists:flatten(io_lib:write(E)) 
				     end, Data)}]).

    

    
print_info(mnesia, Node, LocalNode, TabId, CardIds) ->
    update_data_field({mnesia}, 
		      CardIds#card_field_ids.table_type),
    update_data_field({TabId}, 
		      CardIds#card_field_ids.table_name),
    update_data_field({tv_mnesia_rpc:table_info(Node, LocalNode, TabId, type)}, 
		      CardIds#card_field_ids.bag_or_set),
    update_data_field({tv_mnesia_rpc:table_info(Node, LocalNode, TabId, arity) - 1}, 
		      CardIds#card_field_ids.arity),

    AttributesList = tv_mnesia_rpc:table_info(Node, LocalNode, TabId, attributes),
    update_data_field({AttributesList}, 
		      CardIds#card_field_ids.attributes),
    update_data_field({lists:map(fun(N) -> 
					 lists:nth(N - 1, AttributesList) 
				 end, 
				 [2] ++ tv_mnesia_rpc:table_info(Node, 
								 LocalNode, 
								 TabId, 
								 index)
				)
		      }, 
		      CardIds#card_field_ids.index),

    update_data_field({tv_mnesia_rpc:table_info(Node, LocalNode, TabId, size)}, 
		      CardIds#card_field_ids.size),
    update_data_field({tv_mnesia_rpc:table_info(Node, LocalNode, TabId, memory)}, 
		      CardIds#card_field_ids.memory),
    update_data_field({tv_mnesia_rpc:table_info(Node, LocalNode, TabId, storage_type)}, 
		      CardIds#card_field_ids.storage_type),
    update_data_field({tv_mnesia_rpc:table_info(Node, LocalNode, TabId, where_to_read)}, 
		      CardIds#card_field_ids.where_to_read),
    update_data_field({tv_mnesia_rpc:table_info(Node, LocalNode, TabId, disc_copies)}, 
		      CardIds#card_field_ids.disc_copies),
    update_data_field({tv_mnesia_rpc:table_info(Node, LocalNode, TabId, disc_only_copies)}, 
		      CardIds#card_field_ids.disc_only_copies),
    update_data_field({tv_mnesia_rpc:table_info(Node, LocalNode, TabId, ram_copies)}, 
		      CardIds#card_field_ids.ram_copies),
    update_data_field({tv_mnesia_rpc:table_info(Node, LocalNode, TabId, where_to_write)}, 
		      CardIds#card_field_ids.where_to_write),
    update_data_field({tv_mnesia_rpc:table_info(Node, LocalNode, TabId, checkpoints)}, 
		      CardIds#card_field_ids.checkpoints),
    {ok, TabId};
print_info(ets, Node, LocalNode, TabId, CardIds) ->
    update_data_field({ets}, 
		      CardIds#card_field_ids.table_type),
    update_data_field({TabId}, 
		      CardIds#card_field_ids.table_id),
    TabName = tv_ets_rpc:info(Node, LocalNode, TabId, name),
    update_data_field({TabName}, 
		      CardIds#card_field_ids.table_name),
    update_data_field({tv_ets_rpc:info(Node, LocalNode, TabId, named_table)}, 
		      CardIds#card_field_ids.named_table),

    OwnerPid              = tv_ets_rpc:info(Node, LocalNode, TabId, owner),
    OwnerNameSearchResult = lists:keysearch(registered_name, 
					    1, 
					    rpc:block_call(Node, 
							   erlang, 
							   process_info, 
							   [OwnerPid])),
    OwnerName             = case OwnerNameSearchResult of
				false ->
				    notext;
				{value, {registered_name, WantedName}} ->
				    {WantedName}
			    end,
    update_data_field({OwnerPid}, 
		      CardIds#card_field_ids.owner_pid),
    update_data_field(OwnerName, 
		      CardIds#card_field_ids.owner_name),
    
    update_data_field({tv_ets_rpc:info(Node, LocalNode, TabId, keypos)},  
		      CardIds#card_field_ids.keypos),
    update_data_field({tv_ets_rpc:info(Node, LocalNode, TabId, type)},  
		      CardIds#card_field_ids.bag_or_set),
    update_data_field({tv_ets_rpc:info(Node, LocalNode, TabId, protection)},  
		      CardIds#card_field_ids.protection),
    update_data_field({tv_ets_rpc:info(Node, LocalNode, TabId, size)}, 
		      CardIds#card_field_ids.size),
    update_data_field({tv_ets_rpc:info(Node, LocalNode, TabId, memory)},  
		      CardIds#card_field_ids.memory),
    update_data_field({tv_ets_rpc:info(Node, LocalNode, TabId, node)}, 
		      CardIds#card_field_ids.node),
    {ok, TabName}.
    





print_cards(mnesia, Card1, Card2, Card3, Card4) ->
    create_card_text(1, "Table Type:", Card1),
    create_card_text(2, "Table Name:", Card1),
    
    create_card_text(1, "Table Type:", Card2),
    create_card_text(2, "Number of Attributes:", Card2),
%    create_card_text(3, "Attribute Names:", Card2),
%    create_card_text(4, "Index Positions:", Card2),

    create_card_text(1, "Number of Elements Stored:", Card3),
    create_card_text(2, "Number of Words Allocated:", Card3),

    create_card_text(1, "Local Storage Type:", Card4),
    create_card_text(2, "Table Readable at Node:", Card4),
%    create_card_text(3, "Disc Copy Nodes:", Card4),
%    create_card_text(4, "Disc Copy Only Nodes:", Card4),
%    create_card_text(5, "RAM Copy Nodes:", Card4),
%    create_card_text(6, "Active Table Replica Nodes:", Card4),
%    create_card_text(7, "Active Checkpoints:", Card4),
    
    {AttributesId, IndexId} = create_special_fields(mnesia, size_card, Card2),

    
    {DiscCopiesId, DiscOnlyCopiesId, RamCopiesId, WhereToWriteId, CheckpointsId} = 
	create_special_fields(mnesia, storage_card, Card4),

    #card_field_ids{table_name       = {label, create_card_data_field(2, Card1)},
		    table_type       = {label, create_card_data_field(1, Card1)},
		    bag_or_set       = {label, create_card_data_field(1, Card2)},
		    arity            = {label, create_card_data_field(2, Card2)},
		    attributes       = AttributesId,
		    index            = IndexId,
		    size             = {label, create_card_data_field(1, Card3)},
		    memory           = {label, create_card_data_field(2, Card3)},
		    storage_type     = {label, create_card_data_field(1, Card4)},
		    where_to_read    = {label, create_card_data_field(2, Card4)},
		    disc_copies      = DiscCopiesId,
		    disc_only_copies = DiscOnlyCopiesId,
		    ram_copies       = RamCopiesId,
		    where_to_write   = WhereToWriteId,
		    checkpoints      = CheckpointsId
		   };
print_cards(ets, Card1, Card2, Card3, Card4) ->
    create_card_text(1, "Table Type:", Card1),
    create_card_text(2, "Table Id:", Card1),
    create_card_text(3, "Table Name:", Card1),
    create_card_text(4, "Table Name Registered:", Card1),
    create_card_text(5, "Process Owning the Table:", Card1),
    create_card_text(6, "Name of Owning Process:", Card1),

    create_card_text(1, "Index Position:", Card2),
    create_card_text(2, "Table Type:", Card2),
    create_card_text(3, "Protection Mode:", Card2),
    
    create_card_text(1, "Number of Elements Stored:", Card3),
    create_card_text(2, "Number of Words Allocated:", Card3),
    
    create_card_text(1, "Table Stored at Node:", Card4),
    
    #card_field_ids{table_id    = {label, create_card_data_field(2, Card1)},
		    table_type  = {label, create_card_data_field(1, Card1)},
		    table_name  = {label, create_card_data_field(3, Card1)},
		    named_table = {label, create_card_data_field(4, Card1)},
		    owner_pid   = {label, create_card_data_field(5, Card1)},
		    owner_name  = {label, create_card_data_field(6, Card1)},
		    	       			      
		    keypos      = {label, create_card_data_field(1, Card2)},
		    bag_or_set  = {label, create_card_data_field(2, Card2)},
		    protection  = {label, create_card_data_field(3, Card2)},
			       			      
		    size        = {label, create_card_data_field(1, Card3)},
		    memory      = {label, create_card_data_field(2, Card3)},
			       			      
		    node        = {label, create_card_data_field(1, Card4)}
		   }.



    


create_special_fields(mnesia, size_card, CardId) ->
    LabelWidth      = 195,
    LabelHeight     = 24,
    ListboxWidth    = 210,
    ListboxHeight   = 160,
    VerticalSpacing = 20,
    LXpos           = 30,
    RXpos           = 330,
    Ypos            = 40 + (LabelHeight + VerticalSpacing) * 2 + 25,
    gs:label(CardId, [{width, LabelWidth},
		      {height, LabelHeight},
		      {x, LXpos},
		      {y, Ypos},
		      {bg, ?DEFAULT_BG_COLOR},
		      {fg, {0, 0, 0}},
		      {align, center},
		      {label, {text, "Attribute Names:"}}
		    ]),
    
    gs:label(CardId, [{width, LabelWidth},
		      {height, LabelHeight},
		      {x, RXpos},
		      {y, Ypos},
		      {bg, ?DEFAULT_BG_COLOR},
		      {fg, {0, 0, 0}},
		      {align, center},
		      {label, {text, "Attributes Used as Indices:"}}
		     ]),
    
    AttributesId = gs:listbox(CardId, [{width, ListboxWidth},
				       {height, ListboxHeight},
				       {x, LXpos},
				       {y, Ypos + LabelHeight - 3},
				       {bg, {255, 255, 255}},
				       {fg, {0, 0, 0}},
				       {hscroll, bottom},
				       {vscroll, right},
				       {selectmode, single},
				       {click, true},
				       {doubleclick, true},
				       {data, listbox}
				      ]),
    
    IndexId = gs:listbox(CardId, [{width, ListboxWidth},
				  {height, ListboxHeight},
				  {x, RXpos},
				  {y, Ypos + LabelHeight - 3},
				  {bg, {255, 255, 255}},
				  {fg, {0, 0, 0}},
				  {hscroll, bottom},
				  {vscroll, right},
				  {selectmode, single},
				  {click, true},
				  {doubleclick, true},
				  {data, listbox}				  
				]),
    
    {{listbox, AttributesId}, 
     {listbox, IndexId}
    };
create_special_fields(mnesia, storage_card, CardId) ->
    LabelWidth      = 155,
    LabelHeight     = 24,
    ListboxHeight   = 80,
    ListboxWidth    = 170,
    VerticalSpacing = 20,
    LXpos           = 10,
    MXpos           = 197,
    RXpos           = 385,
    % Y-positions for upper and lower row.
    UYpos           = 40 + (LabelHeight + VerticalSpacing) * 2,
    LYpos           = UYpos +  ListboxHeight + 37,
    gs:label(CardId, [{width, LabelWidth},
		      {height, LabelHeight},
		      {x, LXpos},
		      {y, UYpos},
		      {bg, ?DEFAULT_BG_COLOR},
		      {fg, {0, 0, 0}},
		      {align, center},
		      {label, {text, "Disc Copy Nodes:"}}
		     ]),

    gs:label(CardId, [{width, LabelWidth},
		      {height, LabelHeight},
		      {x, MXpos},
		      {y, UYpos},
		      {bg, ?DEFAULT_BG_COLOR},
		      {fg, {0, 0, 0}},
		      {align, center},
		      {label, {text, "Disc Only Copy Nodes:"}}
		     ]),
    
    gs:label(CardId, [{width, LabelWidth},
		      {height, LabelHeight},
		      {x, RXpos},
		      {y, UYpos},
		      {bg, ?DEFAULT_BG_COLOR},
		      {fg, {0, 0, 0}},
		      {align, center},
		      {label, {text, "RAM Copy Nodes:"}}
		     ]),


    gs:label(CardId, [{width, LabelWidth},
		      {height, LabelHeight},
		      {x, LXpos},
		      {y, LYpos},
		      {bg, ?DEFAULT_BG_COLOR},
		      {fg, {0, 0, 0}},
		      {align, center},
		      {label, {text, "Table Replica Nodes:"}}
		     ]),

    gs:label(CardId, [{width, LabelWidth},
		      {height, LabelHeight},
		      {x, MXpos},
		      {y, LYpos},
		      {bg, ?DEFAULT_BG_COLOR},
		      {fg, {0, 0, 0}},
		      {align, center},
		      {label, {text, "Active Checkpoints:"}}
		    ]),
    

    DiscCopiesId = gs:listbox(CardId, [{width, ListboxWidth},
				       {height, ListboxHeight},
				       {x, LXpos},
				       {y, UYpos + LabelHeight - 3},
				       {bg, {255, 255, 255}},
				       {fg, {0, 0, 0}},
				       {hscroll, bottom},
				       {vscroll, right},
				       {selectmode, single},
				       {click, true},
				       {doubleclick, true},
				       {data, listbox}				       
				      ]),
    
    DiscCopiesOnlyId = gs:listbox(CardId, [{width, ListboxWidth},
					   {height, ListboxHeight},
					   {x, MXpos},
					   {y, UYpos + LabelHeight - 3},
					   {bg, {255, 255, 255}},
					   {fg, {0, 0, 0}},
					   {hscroll, bottom},
					   {vscroll, right},
					   {selectmode, single},
					   {click, true},
					   {doubleclick, true},
					   {data, listbox}
					  ]),

    RamCopiesId = gs:listbox(CardId, [{width, ListboxWidth},
				      {height, ListboxHeight},
				      {x, RXpos},
				      {y, UYpos + LabelHeight - 3},
				      {bg, {255, 255, 255}},
				      {fg, {0, 0, 0}},
				      {hscroll, bottom},
				      {vscroll, right},
				      {selectmode, single},
				      {click, true},
				      {doubleclick, true},
				      {data, listbox}
				     ]),
    


    WhereToWriteId = gs:listbox(CardId, [{width, ListboxWidth},
					 {height, ListboxHeight},
					 {x, LXpos},
					 {y, LYpos + LabelHeight - 3},
					 {bg, {255, 255, 255}},
					 {fg, {0, 0, 0}},
					 {hscroll, bottom},
					 {vscroll, right},
					 {selectmode, single},
					 {click, true},
					 {doubleclick, true},
					 {data, listbox}
					]),
    
    CheckpointsId = gs:listbox(CardId, [{width, ListboxWidth},
					{height, ListboxHeight},
					{x, MXpos},
					{y, LYpos + LabelHeight - 3},
					{bg, {255, 255, 255}},
					{fg, {0, 0, 0}},
					{hscroll, bottom},
					{vscroll, right},
					{selectmode, single},
					{click, true},
					{doubleclick, true},
					{data, listbox}
				       ]),
    
    {{listbox, DiscCopiesId}, 
     {listbox, DiscCopiesOnlyId}, 
     {listbox, RamCopiesId}, 
     {listbox, WhereToWriteId}, 
     {listbox, CheckpointsId}
    }.







create_card_data_field(N, ParentId) ->
    Width           = 345,
    Height          = 24,
    VerticalSpacing = 20,
    Xpos            = 210,
    Ypos            = 40 + (Height + VerticalSpacing) * (N - 1),

    BgFrame = gs:frame(ParentId, [{width, Width},
				  {height, Height},
				  {x, Xpos},
				  {y, Ypos},
				  {bg, {0, 0, 0}},
				  {bw, 0}
				 ]),
    gs:label(BgFrame, [{width, Width - 2},
		       {height, Height - 2},
		       {x, 1},
		       {y, 1},
		       {bg, {255, 255, 255}},
		       {fg, {0, 0, 0}},
		       {align, w}
		      ]).






create_card_text(N, Text, ParentId) ->
    LabelWidth      = 205,
    LabelHeight     = 24,
    VerticalSpacing = 20,
    Xpos            = 10,
    Ypos            = 40 + (LabelHeight + VerticalSpacing) * (N - 1),
    gs:label(ParentId, [{width, LabelWidth},
			{height, LabelHeight},
			{x, Xpos},
			{y, Ypos},
			{bg, ?DEFAULT_BG_COLOR},
			{fg, {0, 0, 0}},
			{align, w},
			{label, {text, Text}}
		       ]).
    



create_card(ParentId, FlapId) ->
    CardId = gs:frame(ParentId, [{width, 570},
				 {height, 360},
				 {x, 5},
				 {y, 35},
				 {bg, ?DEFAULT_BG_COLOR},
				 {bw, 2}
				]),
    FlapXpos = gs:read(FlapId, data),
    gs:config(FlapId, [{data, {CardId, FlapXpos}}
		      ]),
    CardId.
    
    



set_flap_label(ParentId, Text) ->
    Bw     = gs:read(ParentId, bw),     % It is assumed that the parent is a frame!  :-)
    Width  = gs:read(ParentId, width) - 2 * Bw - 2,
    Height = gs:read(ParentId, height) - 2 * Bw - 6,
    Xpos   = 0,
    Ypos   = 0,
    Data   = gs:read(ParentId, data),
    
    gs:label(ParentId, [{width, Width},
			{height, Height},
			{x, Xpos},
			{y, Ypos},
			% {fg, {178, 34, 34}},
			{bg, ?DEFAULT_BG_COLOR},
			{fg, {0, 0, 0}},
			{label, {text, Text}},
			{align, center},
			{buttonpress, true},
			{data, Data}
		       ]).



create_flap(N, _Text, ParentId) ->
    Width       = 120,
    Height      = 40,
    Spacing     = 2,
    FirstXpos   = 5,
    Xpos        = FirstXpos + ((Width + Spacing) * (N - 1)),
    Ypos        = 5,
    BorderWidth = 2,
    
    gs:frame(ParentId, [{width, Width},
			{height, Height},
			{x, Xpos},
			{y, Ypos},
			{bg, ?DEFAULT_BG_COLOR},
			{bw, BorderWidth},
			{cursor, hand},
			{buttonpress, true},
			{data, Xpos + BorderWidth}
		       ]).
				   


update_info_flaps(TabType, Node, LocalNode, TabId, CardIds, MasterPid) ->
    case catch print_info(TabType, Node, LocalNode, TabId, CardIds) of
	{ok, TabName} ->
	    WinTitle = tv_pc_menu_handling:get_window_title(TabType,Node,TabId,TabName),
	    gs:config(win, [{title, "[TV]   " ++ WinTitle}]),
	    done;
	nodedown ->
	    nodedown;
	no_table ->
	    gs:config(win, [beep]),
	    case get(error_msg_mode) of
		normal ->
		    Msg = ["The table " ++ lists:flatten(io_lib:write(TabId)) ++ " on node",
			   lists:flatten(io_lib:write(Node)) ++ " no longer exists!"],
		    tv_utils:notify(win, "TV Notification", Msg);
		haiku ->
		    Msg = ["Three things are certain:",
			   "Death, taxes, and lost tables.",
			   "Guess which has occurred."],
		    tv_utils:notify(win, "TV Notification", Msg)
	    end,
	    MasterPid ! #ip_dead_table{sender = self()};
	mnesia_not_started ->
	    gs:config(win, [beep]),
	    case get(error_msg_mode) of
		normal ->
		    Msg = ["The table " ++ lists:flatten(io_lib:write(TabId)) ++ " on node",
			   lists:flatten(io_lib:write(Node)) ++ " no longer exists!"],
		    tv_utils:notify(win, "TV Notification", Msg);
		haiku ->
		    Msg = ["A table that big?",
			   "It might be very useful.",
			   "But now it is gone."],
		    tv_utils:notify(win, "TV Notification", Msg)
	    end,
	    MasterPid ! #ip_dead_table{sender = self()};
	{unexpected_error,Reason} ->
	    io:format("Unexpected error:  ~p~n", [Reason]);
	_Other ->
	    io:format("Unexpected return value: ~p~n", [_Other]),
	    done
    end.



    
loop(CardIds, MaskLabel, Node, LocalNode, TabId, TabType) ->				 
    receive
	#info_update_table_info{sender = Sender} ->
	    NewNode = check_node(Node, LocalNode),
	    update_info_flaps(TabType, NewNode, LocalNode, TabId, CardIds, Sender),
	    loop(CardIds, MaskLabel, NewNode, LocalNode, TabId, TabType);


	#info_raise_window{sender = Sender} ->
	    gs:config(CardIds#card_field_ids.window_id, [raise]),
	    NewNode = check_node(Node, LocalNode),
	    chk(update_info_flaps(TabType, NewNode, LocalNode, TabId, CardIds, Sender)),
	    loop(CardIds, MaskLabel, NewNode, LocalNode, TabId, TabType);
	

	#info_quit{} ->
	    exit(normal);

	{gs, _FlapId, buttonpress, {CardId, Xpos}, [1 | _]} ->
	    gs:config(CardId, [raise
			      ]),
	    gs:config(MaskLabel, [raise,
				  {x, Xpos}
				 ]),
	    loop(CardIds, MaskLabel, Node, LocalNode, TabId, TabType);

	{gs, _Id, buttonpress, {_CardId, _Xpos}, _Args} ->
	    loop(CardIds, MaskLabel, Node, LocalNode, TabId, TabType);

	{gs, _LblId, enter, _Data, _Args} ->
	    loop(CardIds, MaskLabel, Node, LocalNode, TabId, TabType);
	
	{gs, WinId, configure, _Data, _Args} ->
	    gs:config(WinId, [{width, ?WINDOW_WIDTH},
			      {height, ?WINDOW_HEIGHT}
			     ]),
	    loop(CardIds, MaskLabel, Node, LocalNode, TabId, TabType);

	{gs, ListboxId, click, listbox, _Args} ->
	    gs:config(ListboxId, [{selection, clear}]),
	    loop(CardIds, MaskLabel, Node, LocalNode, TabId, TabType);

	{gs, ListboxId, doubleclick, listbox, _Args} ->
	    gs:config(ListboxId, [{selection, clear}]),
	    loop(CardIds, MaskLabel, Node, LocalNode, TabId, TabType);

	{gs, _Id, click, update, _Args} ->
	    NewNode = check_node(Node, LocalNode),
	    chk(update_info_flaps(TabType,NewNode,LocalNode,TabId,CardIds,
				  CardIds#card_field_ids.parent_pid)),
	    loop(CardIds, MaskLabel, NewNode, LocalNode, TabId, TabType);

	{gs, _Id, keypress, _, [r, _, 0, 1 | _]} ->
	    NewNode = check_node(Node, LocalNode),
	    chk(update_info_flaps(TabType,NewNode,LocalNode,TabId,CardIds,
				  CardIds#card_field_ids.parent_pid)),
	    loop(CardIds, MaskLabel, NewNode, LocalNode, TabId, TabType);

	{gs, _Id, keypress, _, ['R', _, 1, 1 | _]} ->
	    NewNode = check_node(Node, LocalNode),
	    chk(update_info_flaps(TabType,NewNode,LocalNode,TabId,CardIds,
				  CardIds#card_field_ids.parent_pid)),
	    loop(CardIds, MaskLabel, NewNode, LocalNode, TabId, TabType);

	{gs, _Id, click, close_menu, _Args} ->
	    exit(normal);

	{gs, _Id, keypress, _, [c, _, 0, 1 | _]} ->
	    exit(normal);
	
	{gs, _Id, keypress, _, ['C', _, 1, 1 | _]} ->
	    exit(normal);

	{gs, _Id, destroy, _Data, _Args} ->
	    exit(normal);

	{'EXIT', _Pid, _Reason} ->
	    exit(normal);

	{error_msg_mode, Mode} ->
	    put(error_msg_mode, Mode),
	    loop(CardIds, MaskLabel, Node, LocalNode, TabId, TabType);

	_Other ->
	    loop(CardIds, MaskLabel, Node, LocalNode, TabId, TabType)  
    end.
    





chk(nodedown) ->
    gs:config(win, [beep]),
    case get(error_msg_mode) of
	normal ->
	    tv_utils:notify(win, "TV Notification", 
			    ["The node is down, and the",
			     "table cannot be reached."]);
	haiku ->
	    ErrMsg1 = ["With searching comes loss",
		       "And the presence of absence:",
		       "Node is down."],
	    tv_utils:notify(win, "TV Notification", ErrMsg1)
    end;
chk(_Other) ->
    done.