%%
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 1998-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_rec_edit).
-compile([{nowarn_deprecated_function,{gs,button,2}},
          {nowarn_deprecated_function,{gs,button,3}},
          {nowarn_deprecated_function,{gs,config,2}},
          {nowarn_deprecated_function,{gs,entry,3}},
          {nowarn_deprecated_function,{gs,frame,2}},
          {nowarn_deprecated_function,{gs,frame,3}},
          {nowarn_deprecated_function,{gs,label,2}},
          {nowarn_deprecated_function,{gs,read,2}},
          {nowarn_deprecated_function,{gs,start,0}},
          {nowarn_deprecated_function,{gs,window,3}}]).



-export([start/5,
	 start/6,
	 init/8
	]).


-include("tv_int_def.hrl").



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

-define(WIN_WIDTH, 375).
-define(WIN_HEIGHT, 341).
-define(ETS_WIN_HEIGHT, 154).

-define(FRAME_WIDTH, 375).
-define(FRAME_HEIGHT, 265).
-define(ETS_FRAME_HEIGHT, 74).

-define(MAX_LABEL_WIDTH, 165).
-define(X0, 15).
-define(Y0, 20).
-define(LABEL_HEIGHT, 30).
-define(ENTRY_HEIGHT, 30).
-define(FONT, {screen,12}).
-define(NEXT_BTN_WIDTH, 57).
-define(NEXT_BTN_HEIGHT, 22).
-define(NEXT_BTN_FG, {178,34,34}).
-define(INSERT_BTN_WIDTH, 80).
-define(INSERT_BTN_HEIGHT, 30).
-define(INSERT_BTN_DIST_BETWEEN, 23).
-define(INSERT_BTN_DIST_FROM_BOTTOM, 23).





start(TableType, TableName, AttributeList, ListsAsStr, ErrMsgMode) ->
    AttributeValues = lists:duplicate(length(AttributeList), undefined),
    spawn_link(?MODULE, init, [TableType, TableName, AttributeList, 
			       AttributeValues, ListsAsStr, ErrMsgMode, self(), true]).



start(TableType, TableName, AttributeList, AttributeValues, ListsAsStr, ErrMsgMode) ->
    spawn_link(?MODULE, init, [TableType, TableName, AttributeList, 
			       AttributeValues, ListsAsStr, ErrMsgMode, self(), false]).




init(TableType,TableName,AttributeList,AttributeValues,ListsAsStr,ErrMsgMode,MasterPid,Insert) ->
    process_flag(trap_exit, true),
    put(error_msg_mode, ErrMsgMode),
    Frames = create_window(TableType, TableName, AttributeList, AttributeValues, 
			   ListsAsStr, Insert),
    loop(TableType, TableName, Frames, AttributeList, AttributeValues, MasterPid, ListsAsStr).





loop(TabType, TabName, Frames, AttrList, AttrVals, MPid, ListsAsStr) ->
    receive
		
	{gs, insert, click, Insert, _Args} ->
	    gs:config(win, [{cursor, busy}]),
	    case get_record(TabType, TabName, AttrList, AttrList, Frames) of
		{ok, NewRec} ->
		    case Insert of
			insert ->
			    MPid ! {new_object, NewRec};
			change ->
			    MPid ! {updated_object, NewRec}
		    end;
		error ->
		    done
	    end,
	    gs:config(win, [{cursor, arrow}]),	    
	    loop(TabType, TabName, Frames, AttrList, AttrVals, MPid, ListsAsStr);
	

	{gs, cancel, click, _Data, _Args} ->
	    exit(normal);
	

	{gs, reset, click, _Data, _Args} ->
	    gs:config(win, [{cursor, busy}]),
	    set_entry_values(TabType, AttrList, AttrVals, ListsAsStr),
	    gs:config(win, [{cursor, arrow}]),
	    loop(TabType, TabName, Frames, AttrList, AttrVals, MPid, ListsAsStr);



	{gs, EntryId, keypress, _Data, ['Tab', _No, 0 | _T]} ->
	    {_Term, {NextEntry, NextFrame}} = 
		check_entry_content(EntryId, AttrList, Frames, forward),
	    case NextEntry of
		EntryId ->
		    gs:config(NextEntry, [{setfocus, true}]);
		_OtherId ->
		    gs:config(NextFrame, [raise]),
		    gs:config(NextEntry, [{setfocus, true},
					  {select, {0,100000000}}])
	    end,
	    loop(TabType, TabName, Frames, AttrList, AttrVals, MPid, ListsAsStr);
	

	{gs, EntryId, keypress, _Data, ['Down' | _T]} ->
	    {_Term, {NextEntry, NextFrame}} = 
		check_entry_content(EntryId, AttrList, Frames, forward),
	    case NextEntry of
		EntryId ->
		    gs:config(NextEntry, [{setfocus, true}]);
		_OtherId ->
		    gs:config(NextFrame, [raise]),
		    gs:config(NextEntry, [{setfocus, true},
					  {select, {0,100000000}}])
	    end,
	    loop(TabType, TabName, Frames, AttrList, AttrVals, MPid, ListsAsStr);
	

	{gs, EntryId, keypress, _Data, ['Tab', _No, 1 | _T]} ->
	    {_Term, {NextEntry, NextFrame}} = 
		check_entry_content(EntryId, AttrList, Frames, backward),
	    gs:config(NextFrame, [raise]),
	    case NextEntry of
		EntryId ->
		    gs:config(NextEntry, [{setfocus, true}]);
		_OtherId ->
		    gs:config(NextFrame, [raise]),
		    gs:config(NextEntry, [{setfocus, true},
					  {select, {0,100000000}}])
	    end,
	    loop(TabType, TabName, Frames, AttrList, AttrVals, MPid, ListsAsStr);
	
	
	{gs, EntryId, keypress, _Data, ['Up' | _T]} ->
	    {_Term, {NextEntry, NextFrame}} = 
		check_entry_content(EntryId, AttrList, Frames, backward),
	    gs:config(NextFrame, [raise]),
	    case NextEntry of
		EntryId ->
		    gs:config(NextEntry, [{setfocus, true}]);
		_OtherId ->
		    gs:config(NextFrame, [raise]),
		    gs:config(NextEntry, [{setfocus, true},
					  {select, {0,100000000}}])
	    end,
	    loop(TabType, TabName, Frames, AttrList, AttrVals, MPid, ListsAsStr);
	
	
	{gs, Id, keypress, _Data, ['Return' | _T]} ->
	    OldCursor = gs:read(Id, cursor),
	    gs:config(Id, [{cursor, busy}]),
	    gs:config(win, [{cursor, busy}]),
	    Insert = gs:read(insert, data),
	    case get_record(TabType, TabName, AttrList, AttrList, Frames) of
		{ok, NewRec} ->
		    case Insert of
			insert ->
			    MPid ! {new_object, NewRec};
			change ->
			    MPid ! {updated_object, NewRec}
		    end;
		error ->
		    done
	    end,
	    gs:config(win, [{cursor, arrow}]),
	    gs:config(Id, [{cursor, OldCursor}]),
	    loop(TabType, TabName, Frames, AttrList, AttrVals, MPid, ListsAsStr);


	
	{gs, _Id, click, FrameNo, _Args} ->
	    gs:config(lists:nth(FrameNo, Frames), [raise]),
	    loop(TabType, TabName, Frames, AttrList, AttrVals, MPid, ListsAsStr);


	{gs, win, configure, _Data, [Width | _T]} ->
	    resize_window(TabType, lists:max([Width, ?WIN_WIDTH]), Frames, AttrList),
	    loop(TabType, TabName, Frames, AttrList, AttrVals, MPid, ListsAsStr);
	    

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

	insert_mode ->
	    NewAttrVals = lists:duplicate(length(AttrList), undefined),
	    set_entry_values(TabType, AttrList, NewAttrVals, ListsAsStr),
	    loop(TabType, TabName, Frames, AttrList, NewAttrVals, MPid, ListsAsStr);

	
	{update_mode, Obj} ->
	    NewAttrVals = 
		case TabType of
		    mnesia ->
			case Obj of
			    undefined ->
				lists:duplicate(length(AttrList), undefined);
			    _AnyRec ->
				tl(tuple_to_list(Obj))
			end;
		    ets ->
			[Obj]
		end,
	    set_entry_values(TabType, AttrList, NewAttrVals, ListsAsStr),
	    loop(TabType, TabName, Frames, AttrList, NewAttrVals, MPid, ListsAsStr);
	    
	    
	{reset_info, Obj} ->
	       %% Info to use, instead of old info, when reset button is pressed.
	    NewAttrVals = 
		case TabType of
		    mnesia ->
			case Obj of
			    undefined ->
				lists:duplicate(length(AttrList), undefined);
			    _AnyRec ->
				tl(tuple_to_list(Obj))
			end;
		    ets ->
			[Obj]
		end,
	    loop(TabType, TabName, Frames, AttrList, NewAttrVals, MPid, ListsAsStr);
	    

	    raise ->
	    gs:config(win, [raise]),
	    loop(TabType, TabName, Frames, AttrList,AttrVals, MPid, ListsAsStr);


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


	_Other ->
	    loop(TabType, TabName, Frames, AttrList,AttrVals, MPid, ListsAsStr)
    end.




resize_window(TabType, WinWidth, Frames, AttrList) ->
    WinHeight = 
	case TabType of 
	    mnesia ->
		get_window_height(length(AttrList));
	    ets ->
		?ETS_WIN_HEIGHT
	end,
    gs:config(win, [{width, WinWidth},
		    {height, WinHeight}
		   ]),
    FrameWidth = WinWidth,
    LblL = lists:map(fun(H) ->
			     gs:config(H, [{width, FrameWidth}]),
			     {LblW, BId, NId} = gs:read(H, data),
			     XNext = get_next_btn_xpos(FrameWidth),
			     XBack = XNext - ?NEXT_BTN_WIDTH,
			     gs:config(BId, [{x, XBack}]),
			     gs:config(NId, [{x, XNext}]),
			     LblW
		     end, 
		     Frames),
    LblW = hd(LblL),
    EntryW = get_entry_width(TabType, FrameWidth, LblW),
    lists:foreach(fun(H) ->
			  gs:config(H, [{width, EntryW}])
		  end,
		  AttrList),
    gs:config(btnframe, [{width, FrameWidth}]),
    {XInsert, XCancel, XReset} = get_insert_btn_coords(WinWidth),
    gs:config(insert, [{x, XInsert}]),
    gs:config(cancel, [{x, XCancel}]),
    gs:config(reset, [{x, XReset}]).




check_entry_content(EntryId, AttributeList, Frames, Direction) ->
    EditedStr = gs:read(EntryId, text),
    case tv_db_search:string_to_term(EditedStr) of
	{error, {_Reason, Msg}} ->
	    gs:config(EntryId, [beep]),
	    tv_utils:notify(gs:start(), "TV Notification", Msg),
	    {error, {EntryId, no_matter}};
	{ok, NewTerm} ->
	    {{ok,NewTerm}, get_next_entry_id(EntryId, AttributeList, Frames, Direction)}
    end.

    


get_next_entry_id(EntryId, AttributeList, Frames, Direction) ->
    OldPos = get_pos(EntryId, AttributeList),
    MaxPos = length(AttributeList),
    NewPos = case Direction of
		 forward when OldPos < MaxPos ->
		     OldPos + 1;
		 forward ->
		     1;
		 backward when OldPos > 1 ->
		     OldPos - 1;
		 backward ->
		     MaxPos;
		 stationary ->
		     OldPos
	     end,
    FramePos = get_next_frame_id(NewPos),
    {lists:nth(NewPos, AttributeList), lists:nth(FramePos, Frames)}.




get_next_frame_id(Pos) ->
    case Pos rem 5 of
	0 ->
	    Pos div 5;
	_Other ->
	    (Pos div 5) + 1
    end.
    



get_record(TabType, TabName, AttrList, AttrList, Frames) ->
    case get_record(AttrList, AttrList, Frames, []) of
	{ok, RecList} ->
	    case TabType of
		mnesia ->
		    NewRecList = [TabName | RecList],
		    {ok, list_to_tuple(NewRecList)};
		ets ->
		    {ok, hd(RecList)}   %% Only one element, a tuple!
	    end;
	error ->
	    error
    end.




get_record([H | T], AttrList, Frames, Acc) ->
    case check_entry_content(H, AttrList, Frames, forward) of
	{{ok, NewTerm}, _PosTuple} ->
	    get_record(T, AttrList, Frames, [NewTerm | Acc]);
	{error, _PosTuple} ->
	    {EntryId, FrameId} = get_next_entry_id(H, AttrList, Frames, stationary),
	    gs:config(FrameId, [raise]),
	    gs:config(EntryId, [{setfocus, true}]),
	    error
    end;
get_record([], _AttrList, _Frames, Acc) ->
    {ok, lists:reverse(Acc)}.
	    
		




get_pos(Elem, L) ->
    get_pos(Elem, L, 1).


get_pos(Elem, [Elem | _T], N) ->
    N;
get_pos(Elem, [_H | T], N) ->
    get_pos(Elem, T, N + 1).




create_window(mnesia, TableName, AttrList, AttrValues, ListsAsStr, Insert) ->
    NofAttr       = length(AttrList),
    NofFrames =
	case NofAttr rem 5 of
	    0 ->
		NofAttr div 5;
	    _Rem ->
		(NofAttr div 5) + 1
	end,

    WinHeight   = get_window_height(NofAttr),
    FrameHeight = get_frame_height(NofAttr),

    Attr = get_longest_attribute_name(AttrList),
    LabelWidth = lists:min([?MAX_LABEL_WIDTH,
			    element(1, gs:read(gs:start(), 
					       {font_wh, {?FONT, atom_to_list(Attr)}}))]),
    
    gs:window(win, gs:start(), [{width, ?WIN_WIDTH},
				{height, WinHeight},
				{title, "[TV]   Record Editor:   '" ++ 
				 atom_to_list(TableName) ++ "'"},
				{bg, ?DEFAULT_BG_COLOR},
				{configure, true},
				{destroy, true},
				{cursor, arrow}
			       ]),
    
    create_insert_and_cancel_btns(Insert, WinHeight, FrameHeight),
    FrameList = create_frames(NofFrames, LabelWidth, AttrList, AttrValues, NofFrames, 
			      ListsAsStr, FrameHeight),
    gs:config(hd(FrameList), [raise]),
    gs:config(hd(AttrList), [{setfocus, true},
			     {select, {0,100000000}}]),
    gs:config(win, [{map,true}]),
    FrameList;
create_window(ets, TableName, [Attr], [AttrVal], ListsAsStr, Insert) ->
    gs:window(win, gs:start(), [{width, ?WIN_WIDTH},
				{height, ?ETS_WIN_HEIGHT},
				{title, "[TV]   Tuple Editor, table  '" ++ 
				 atom_to_list(TableName) ++ "'"},
				{bg, ?DEFAULT_BG_COLOR},
				{configure, true},
				{destroy, true},
				{cursor, arrow}
			       ]),
    
    F = gs:frame(win, [{width, ?FRAME_WIDTH},
		       {height, ?ETS_FRAME_HEIGHT},
		       {x, 0},
		       {y, 0},
		       {bg, ?DEFAULT_BG_COLOR},
		       {bw,2},
		       {data, {0, undefined, undefined}}
		      ]),
    
    create_insert_and_cancel_btns(Insert, ?ETS_WIN_HEIGHT, ?ETS_FRAME_HEIGHT),

    EntryW = get_entry_width(ets, ?FRAME_WIDTH, 0),
    EntryX = ?X0 - 2,
    
    EntryText = 
	case AttrVal of
	    undefined ->
		"";
	    _OtherVal ->
		case ListsAsStr of
		    true ->
			tv_io_lib:format("~p", [AttrVal]);
		    false ->
			lists:flatten(io_lib:write(AttrVal))
		end
	end,
    gs:entry(Attr, F, [{width, EntryW},
		       {height, ?LABEL_HEIGHT},
		       {x, EntryX},
		       {y, ?Y0},
		       {bg, {255,255,255}},
		       {fg, {0,0,0}},
		       {bw, 1},
		       {font, ?FONT},
		       {justify, left},
		       {text, EntryText},
		       {cursor, text},
		       {setfocus, true},
		       {enable, true},
		       {keypress,true},
		       {select, {0,100000000}}
		      ]),
    gs:config(win, [{map,true}]),
    [F].
    
    
    

get_insert_btn_coords(WinWidth) ->
    Middle        = round(WinWidth / 2),
    XInsert       = Middle - round(1.5 * ?INSERT_BTN_WIDTH) - ?INSERT_BTN_DIST_BETWEEN,
    XCancel       = Middle - round(0.5 * ?INSERT_BTN_WIDTH),
    XReset        = Middle + round(0.5 * ?INSERT_BTN_WIDTH) + ?INSERT_BTN_DIST_BETWEEN,
    {XInsert, XCancel, XReset}.




create_insert_and_cancel_btns(Insert, WinHeight, FrameHeight) ->
    LowerFrameHeight           = WinHeight - FrameHeight,
    Y                          = ?INSERT_BTN_DIST_FROM_BOTTOM,
    {XInsert, XCancel, XReset} = get_insert_btn_coords(?WIN_WIDTH),
    
    {InsertBtnText, InsertBtnData} = 
	case Insert of
	    true ->
		{"Insert", insert};
	    false ->
		{"Change", change}
	end,

    gs:frame(btnframe, win, [{width, ?FRAME_WIDTH},
			     {height, LowerFrameHeight},
			     {x, 0},
			     {y, FrameHeight},
			     {bg, ?DEFAULT_BG_COLOR},
			     {bw,2}
			    ]),
    gs:button(insert, btnframe, [{width, ?INSERT_BTN_WIDTH},
				 {height, ?INSERT_BTN_HEIGHT},
				 {x, XInsert},
				 {y, Y},
				 {bg, ?DEFAULT_BG_COLOR},
				 {fg, {0,0,0}},
				 {font, ?FONT},
				 {label, {text, InsertBtnText}},
				 {align, center},
				 {data, InsertBtnData}
				]),
    gs:button(cancel, btnframe, [{width, ?INSERT_BTN_WIDTH},
				 {height, ?INSERT_BTN_HEIGHT},
				 {x, XCancel},
				 {y, Y},
				 {bg, ?DEFAULT_BG_COLOR},
				 {fg, {0,0,0}},
				 {font, ?FONT},
				 {label, {text, "Cancel"}},
				 {align, center}
				]),
    gs:button(reset, btnframe, [{width, ?INSERT_BTN_WIDTH},
				{height, ?INSERT_BTN_HEIGHT},
				{x, XReset},
				{y, Y},
				{bg, ?DEFAULT_BG_COLOR},
				{fg, {0,0,0}},
				{font, ?FONT},
				{label, {text, "Reset"}},
				{align, center}
			       ]).
    




create_frames(0, _LblW, _AttrList, _AttrValues, _NofFrames, _ListsAsStr, _FrameHeight) ->    
    [];
create_frames(N, LblW, AttrList, AttrValues, NofFrames, ListsAsStr, FrameHeight) ->
    F = gs:frame(win, [{width, ?FRAME_WIDTH},
		       {height, FrameHeight},
		       {x, 0},
		       {y, 0},
		       {bg, ?DEFAULT_BG_COLOR},
		       {bw,2}
		      ]),
    {BId, NId} = create_back_and_next_btns(F, 5, N, NofFrames),
    gs:config(F, [{data, {LblW, BId, NId}}]),
    {RemAttrList, RemAttrValues} = 
	create_labels_and_entries(5, AttrList, AttrValues, LblW, F, ListsAsStr),
    [F | create_frames(N - 1,LblW,RemAttrList,RemAttrValues,NofFrames,ListsAsStr,FrameHeight)].






create_back_and_next_btns(FrameId, NofEntries, FrameNo, NofFrames) ->
    Y        = ?Y0 + NofEntries * (?LABEL_HEIGHT + 10) + 8,
    XNext    = get_next_btn_xpos(?FRAME_WIDTH),
    XBack    = XNext - ?NEXT_BTN_WIDTH,
    DataNext = (NofFrames - FrameNo + 1) + 1,
    DataBack = (NofFrames - FrameNo + 1) - 1,
    BId = 
	if 
	    DataBack =< 0 ->
		undefined;
	    true ->
		gs:button(FrameId, [{width, ?NEXT_BTN_WIDTH},
				    {height, ?NEXT_BTN_HEIGHT},
				    {x, XBack},
				    {y, Y},
				    {bg, ?DEFAULT_BG_COLOR},
				    {fg, ?NEXT_BTN_FG},
				    {font, ?FONT},
				    {align, center},
				    {label, {text, "< Back"}},
				    %% {underline, 2},
				    {data, DataBack}
				   ])
	end,
    NId = 
	if
	    DataNext > NofFrames ->
		undefined;
	    true ->
		gs:button(FrameId, [{width, ?NEXT_BTN_WIDTH},
				    {height, ?NEXT_BTN_HEIGHT},
				    {x, XNext},
				    {y, Y},
				    {bg, ?DEFAULT_BG_COLOR},
				    {fg, ?NEXT_BTN_FG},
				    {font, ?FONT},
				    {align, center},
				    {label, {text, " Next >"}},
				    %% {underline, 1},
				    {data, DataNext}
				   ])
	end,
    {BId, NId}.




get_next_btn_xpos(FrameWidth) ->
    FrameWidth - ?X0 - ?NEXT_BTN_WIDTH.



get_entry_width(TableType, FrameWidth, LblWidth) ->
    HorizontalSpacing = 
	case TableType of
	    mnesia ->
		10;
	    ets ->
		0
	end,
    FrameWidth - LblWidth - 2 * ?X0 - HorizontalSpacing.
    


create_labels_and_entries(N, [H | T], [VH | VT], LblW, F, ListsAsStr) when N > 0 ->
    Y      = ?Y0 + (5 - N) * (?LABEL_HEIGHT + 10),
    EntryW = get_entry_width(mnesia, ?FRAME_WIDTH, LblW),
    EntryX = ?FRAME_WIDTH - EntryW - ?X0 - 2,

    EntryText = 
	case ListsAsStr of
	    true ->
		tv_io_lib:format("~p", [VH]);
	    false ->
		lists:flatten(io_lib:write(VH))
	end,
    gs:label(F, [{width, LblW},
		 {height, ?LABEL_HEIGHT},
		 {x, ?X0},
		 {y, Y},
		 {bg, ?DEFAULT_BG_COLOR},
		 {fg, {0,0,0}},
		 {align,w},
		 {font, ?FONT},
		 {label, {text, atom_to_list(H)}}
		]),
    gs:entry(H, F, [{width, EntryW},
		    {height, ?LABEL_HEIGHT},
		    {x, EntryX},
		    {y, Y},
		    {bg, {255,255,255}},
		    {fg, {0,0,0}},
		    {bw, 1},
		    {font, ?FONT},
		    {justify, left},
		    {text, EntryText},
		    {cursor, text},
		    {setfocus, false},
		    {enable, true},
		    {keypress,true}
		   ]),
    create_labels_and_entries(N - 1, T, VT, LblW, F, ListsAsStr);
create_labels_and_entries(0, RemAttrList, RemAttrValues, _LblW, _F, _ListsAsStr) ->
    {RemAttrList, RemAttrValues};
create_labels_and_entries(_N, [], [], _LblW, _F, _ListsAsStr) ->
    {[], []}.




get_longest_attribute_name(AttrList) ->
    get_longest_attribute_name(AttrList, 0, undefined).


get_longest_attribute_name([H | T], Max, Attr) ->
    CurrLength = length(atom_to_list(H)),
    if
	CurrLength >= Max ->
	    get_longest_attribute_name(T, CurrLength, H);
	true ->
	    get_longest_attribute_name(T, Max, Attr)
    end;
get_longest_attribute_name([], _Max, Attr) ->
    Attr.




get_window_height(N) ->
    if 
	N >= 5 ->
	    ?WIN_HEIGHT;
	true ->
	    ?WIN_HEIGHT - ((5 - N) * (?LABEL_HEIGHT + 10) + ?NEXT_BTN_HEIGHT + 8)
    end.



get_frame_height(N) ->
    if 
	N >= 5 ->
	    ?FRAME_HEIGHT;
	true ->
	    ?FRAME_HEIGHT - ((5 - N) * (?LABEL_HEIGHT + 10) + ?NEXT_BTN_HEIGHT + 8)
    end.
	    
    


set_entry_values(TabType, [H | T], [VH | VT], ListsAsStr) ->
    EntryText = 
	case VH of
	    undefined when TabType =:= ets ->
		"";
	    _AnyValue ->
		case ListsAsStr of
		    true ->
			tv_io_lib:format("~p", [VH]);
		    false ->
			lists:flatten(io_lib:write(VH))
		end
	end,
    gs:config(H, [{text, EntryText}]),
    set_entry_values(TabType, T, VT, ListsAsStr);
set_entry_values(_TabType, [], [], _ListsAsStr) ->
    done.
