%%
%% %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:      Code for pd, i.e., the data displaying part of the table 
%%%                     tool.
%%%
%%%*********************************************************************


-module(tv_pd).
-compile([{nowarn_deprecated_function,{gs,config,2}},
          {nowarn_deprecated_function,{gs,destroy,1}},
          {nowarn_deprecated_function,{gs,read,2}},
          {nowarn_deprecated_function,{gs,start,0}},
          {nowarn_deprecated_function,{gs,window,3}}]).



-export([pd/2]).




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





%%%*********************************************************************
%%% EXTERNAL FUNCTIONS
%%%*********************************************************************




%%======================================================================
%% Function:      pd.
%%
%% Return Value:  None.
%%
%% Description:   Process controlling the display part of the window,
%%                i.e., showing diagrams and handling the scale used for scrolling.
%%
%% Parameters:    None.
%%======================================================================


pd(Master, ErrMsgMode) ->
    process_flag(trap_exit, true),
    put(error_msg_mode, ErrMsgMode),
    PgPid = spawn_link(tv_pg, pg, [self()]),
    PbPid = spawn_link(tv_pb, pb, [self()]),

    ProcVars = #process_variables{master_pid = Master,
				  pg_pid     = PgPid,
				  pb_pid     = PbPid},
    blocked(ProcVars).







%%%********************************************************************
%%% INTERNAL FUNCTIONS
%%%********************************************************************





%%======================================================================
%% Function:      blocked.
%%
%% Return Value:  None.
%%
%% Description:   When started or explicitly blocked, pd enters this state,
%%                where nothing is performed until the module explicitly is 
%%                deblocked.
%%
%% Parameters:    
%%======================================================================


blocked(ProcVars) ->
    receive
	Msg ->
	    case Msg of 

		#pd_deblock{} ->
		    deblock(Msg, ProcVars);
		

		{error_msg_mode, Mode} ->
		    put(error_msg_mode, Mode),
		    blocked(ProcVars);
		

		_Other ->
		    blocked(ProcVars)
	    end
    end.
    








%%======================================================================
%% Function:      deblock.
%%
%% Return Value:  None.
%%
%% Description:   When deblocked, a canvas and scale shall be created according to 
%%                specification received in pd_deblock message.
%%
%% Parameters:    Rec:  received pd_deblock message.
%%======================================================================



deblock(Msg, ProcVars) ->
    #pd_deblock{win        = WindowId, 
		win_width  = WindowWidth,
		win_height = WindowHeight}          = Msg,

    NewProcVars = ?DISP_FUNC_FILE:init_display(WindowId, WindowWidth, WindowHeight,
					       ProcVars),
    receive 

	#pg_ready{} ->    
	    Sender = Msg#pd_deblock.sender,
	    Sender ! #pd_deblock_cfm{sender = self()},
	    deblocked_loop(NewProcVars)

    end.
    
    






%%======================================================================
%% Function:      deblocked_loop.
%%
%% Return Value:  None.
%%
%% Description:   Eternal (well, almost) loop, receiving messages and 
%%                handling them.
%%
%% Parameters:    Master:  Pid to the 'pc' process.
%%                Win:     Id of the window created.
%%======================================================================



deblocked_loop(ProcVars) ->
    receive
	Msg ->
	    case Msg of

		{gs, Id, Event, Data, Args} ->
		    NewProcVars = gs_messages({Id, Event, Data, Args}, ProcVars),
		    deblocked_loop(NewProcVars);

		_Other ->
		    NewProcVars = tv_messages(Msg, ProcVars),
		    deblocked_loop(NewProcVars)
	    end
    end.
	




tv_messages(Msg, ProcVars) ->
    WinId = ProcVars#process_variables.window_id,

    case Msg of
	#pg_cell_marked{} ->
	    mark_busy(WinId),
	    NewProcVars = handle_cell_marked(Msg, ProcVars),
	    mark_nonbusy(WinId),
	    NewProcVars;
	
	#pc_data{} ->
	    mark_busy(WinId),
	    NewProcVars = show_data(Msg, ProcVars),
	    mark_nonbusy(WinId),
	    NewProcVars;
	
	#pc_list_info{} ->
	    handle_list_info(Msg, ProcVars);

	#pb_col_marked{} ->
	    mark_busy(WinId),
	    NewProcVars = handle_col_marked(Msg, ProcVars),
	    mark_nonbusy(WinId),
	    NewProcVars;

	#pb_row_marked{} ->
	    mark_busy(WinId),
	    NewProcVars = handle_row_marked(Msg, ProcVars),
	    mark_nonbusy(WinId),
	    NewProcVars;
	
	#pb_new_colwidth{} ->
	    mark_busy(WinId),
	    NewProcVars = resize_column(Msg, ProcVars),
	    mark_nonbusy(WinId),
	    NewProcVars;
	
	#pd_get_sort_settings{sorting = Sorting, reverse = Reverse} ->
	    mark_busy(WinId),
	    NewProcVars =
		case send_sort_info_signal(Sorting, Reverse, ProcVars) of
		    ignore ->
			ProcVars;
		    TempNewProcVars ->
			set_sort_col(Sorting, TempNewProcVars)
		end,
	    mark_nonbusy(WinId),
	    NewProcVars;

	#pd_new_table{table_type=TabType,table_name=TabName,
		      record_name=RecName,writable=Writable} ->
	    mark_busy(WinId),		    
	    ToolP = ProcVars#process_variables.toolbar_params,
	    ?DISP_FUNC_FILE:update_toolbar_label(notext, ToolP, undefined, undefined, Writable),
	    mark_nonbusy(WinId),
	    ProcVars#process_variables{table_type  = TabType,
				       table_name  = TabName,
				       record_name = RecName,
				       writable    = Writable};

	#pd_win_conf{} ->
	    mark_busy(WinId),
	    NewProcVars = resize_window(Msg, ProcVars),
	    mark_nonbusy(WinId),
	    NewProcVars;
	
	#pd_rec_edit{} ->
	    mark_busy(WinId),
	    NewProcVars = open_rec_edit(Msg, ProcVars),
	    mark_nonbusy(WinId),
	    NewProcVars;


	{updated_object, UpdObj} ->
	    get_updated_elem2(true, UpdObj, ProcVars),
	    ProcVars;
	
	{new_object, NewObj} ->
	    get_updated_elem2(true, NewObj, ProcVars),
	    ProcVars;

	{error_msg_mode, Mode} ->
	    put(error_msg_mode, Mode),
	    ProcVars;
	
	{'EXIT', Pid, Reason} ->
	    exit_signals({Pid, Reason}, ProcVars);
	
	_Other ->
	    ProcVars
    end.






exit_signals(ExitInfo, ProcVars) ->
    #process_variables{master_pid = MasterPid,
		       pg_pid     = PgPid,
		       pb_pid     = PbPid,
		       rec_pid    = RecPid}  = ProcVars,
    
    case ExitInfo of
	{MasterPid, _Reason} ->
	    exit(normal);
	{PgPid, _Reason} ->
	    exit(normal);
	{PbPid, _Reason} ->
	    exit(normal);
	{RecPid, _Reason} ->
	    ProcVars#process_variables{rec_pid = undefined};
	_Other ->
	    ProcVars
    end.




open_rec_edit(Msg, ProcVars) ->
    #pd_rec_edit{attributes = AttrList}  = Msg,
    
    #process_variables{rec_pid          = RecPid,
		       table_type       = TabType,
		       table_name       = TabName,
		       record_name      = RecordName,
		       lists_as_strings = ListsAsStr,
		       mark_params      = MarkP}  = ProcVars,
    
    #mark_params{marked_object = MarkedObject}  = MarkP,
    
    TabOrRecName = 
	case TabType of 
	    mnesia ->
		RecordName;
	    ets ->
		TabName
	end,

    case RecPid of
	undefined ->
	    NewRecPid = 
		case MarkedObject of
		    undefined ->
			tv_rec_edit:start(TabType, TabOrRecName, AttrList, ListsAsStr, 
					  get(error_msg_mode));
		    _Other ->
			AttrVals = 
			    case TabType of
				mnesia ->
				    tl(tuple_to_list(MarkedObject));
				ets ->
				    [MarkedObject]
			    end,
			tv_rec_edit:start(TabType, TabOrRecName, AttrList, AttrVals, ListsAsStr,
					  get(error_msg_mode))
		end,
	    ProcVars#process_variables{rec_pid = NewRecPid};
	_AnyPid ->
	    RecPid ! raise,
	    ProcVars
    end.







gs_messages(Msg, ProcVars) ->
    
    case Msg of

	{editentry, keypress, _Data, ['Tab' | _T]} ->
	    gs:config(editentry, [{select, {0,100000000}}]),
	    ProcVars;

	{editentry, keypress, _Data, ['Return' | _T]} ->
	    get_updated_elem(ProcVars),
	    ProcVars;

	{Id, enter, {toolbar, Btn, Str}, _} ->
	    gs:config(Id, [{motion, true}]),
	    NewProcVars = handle_toolbar_buttons(Id, Btn, Str, false, 0, 0, 
						 ProcVars),
	    NewProcVars;


	{_Id, buttonpress, _Data, [3 | _Rest]} ->
	    ProcVars;


	{_Id, buttonpress, vscale, [MouseBtn | _Tail]} ->
	    WinId = ProcVars#process_variables.window_id,
	    mark_busy(WinId),
	    NewProcVars = ?DISP_FUNC_FILE:scroll_vertically(MouseBtn, ProcVars),
	    mark_nonbusy(WinId),
	    NewProcVars;
	

	   % The order of messages from gs ought to be 
	   %   1. 'buttonpress' 
	   %   2. 'click'  and
	   %   3. 'buttonrelease'
	   % However, quite often the 'click' message comes last, meaning we have
	   % to check for this.  :-(

	{_Id, click, vscale, [NewScalePos | _Tail]} ->
	    WinId = ProcVars#process_variables.window_id,
	    mark_busy(WinId),
	    NewProcVars = ?DISP_FUNC_FILE:perform_vertical_scroll(NewScalePos, 
								  ProcVars),
	    mark_nonbusy(WinId),
	    NewProcVars;
	

	{_Id, buttonpress, hscale, [MouseBtn | _Tail]} ->
	    WinId = ProcVars#process_variables.window_id,
	    mark_busy(WinId),
	    NewProcVars = ?DISP_FUNC_FILE:scroll_horizontally(MouseBtn, ProcVars),
	    mark_nonbusy(WinId),
	    NewProcVars;
	

	{_Id, click, hscale, [NewScalePos | _Tail]} ->
	    WinId = ProcVars#process_variables.window_id,
	    mark_busy(WinId),
	    NewProcVars = ?DISP_FUNC_FILE:perform_horizontal_scroll(NewScalePos, 
								    ProcVars),
	    mark_nonbusy(WinId),
	    NewProcVars;
	

	{_Id, click, {toolbar, poll_table, _Str}, _Arg} ->
	    WinId = ProcVars#process_variables.window_id,
	    mark_busy(WinId),
	    PcPid = ProcVars#process_variables.master_pid,
	    PcPid ! #pc_poll_table{sender = self()},
	    mark_nonbusy(WinId),
	    ProcVars;


	{_Id, click, {toolbar, select_browser, _Str}, _Arg} ->
	    PcPid = ProcVars#process_variables.master_pid,
	    PcPid ! #pc_select{sender = self()},
	    ProcVars;


	{_Id, click, {toolbar, help_button, _Str}, _Arg} ->
	    PcPid = ProcVars#process_variables.master_pid,
	    PcPid ! #pc_help{sender = self()},
	    ProcVars;



	{_Id, click, {toolbar, insert_object, _Str}, _Arg} ->
	    WinId = ProcVars#process_variables.window_id,
	    mark_busy(WinId),
	    PcPid = ProcVars#process_variables.master_pid,
	    PcPid ! {pc_edit_object, self()},
	    mark_nonbusy(WinId),
	    ProcVars;


	{_Id, click, {toolbar, search_object, _Str}, _Arg} ->
	    WinId = ProcVars#process_variables.window_id,
	    mark_busy(WinId),
	    PcPid = ProcVars#process_variables.master_pid,
	    PcPid ! #pc_search_req{sender = self()},
	    mark_nonbusy(WinId),
	    ProcVars;


	{_Id, click, {toolbar, sort_rising_order, _Str}, _Arg} ->
	    WinId = ProcVars#process_variables.window_id,
	    mark_busy(WinId),
	    NewProcVars = case send_sort_info_signal(true, false, ProcVars) of
			      ignore ->
				  ProcVars;
			      TempNewProcVars ->
				  set_sort_col(true, TempNewProcVars)
			  end,
	    mark_nonbusy(WinId),
	    NewProcVars;


	{_Id, click, {toolbar, sort_falling_order, _Str}, _Arg} ->
	    WinId = ProcVars#process_variables.window_id,
	    mark_busy(WinId),
	    NewProcVars = case send_sort_info_signal(true, true, ProcVars) of
			      ignore ->
				  ProcVars;
			      TempNewProcVars ->
				  set_sort_col(true, TempNewProcVars)
			  end,
	    mark_nonbusy(WinId),
	    NewProcVars;


	{_Id, click, {toolbar, no_sorting, _Str}, _Arg} ->
	    NewProcVars = case send_sort_info_signal(false, false, ProcVars) of
			      ignore ->
				  ProcVars;
			      TempNewProcVars ->
				  set_sort_col(false, TempNewProcVars)
			  end,
	    NewProcVars;
	

	{Id, click, {toolbar, table_info, _Str}, _Arg} ->
	    ToolP = ProcVars#process_variables.toolbar_params,
	    F     = ToolP#toolbar_params.pop_up_frame_id,
	    gs:config(F, [{y, -30}]),
	    gs:config(Id, [{motion, false}]),
	    PcPid = ProcVars#process_variables.master_pid,
	    PcPid ! #pc_show_table_info{sender = self()},
	    ProcVars;
	

	{Id, click, {labelbtn, pop_up}, _Arg} ->
	    gs:config(Id, [{data, {labelbtn, pop_down}}]),
	    NewProcVars = ?DISP_FUNC_FILE:show_toolbar_editor(ProcVars),
	    NewProcVars;


	{Id, click, {labelbtn, pop_down}, _Arg} ->
	    gs:config(Id, [{data, {labelbtn, pop_up}}]),
	    NewProcVars = ?DISP_FUNC_FILE:hide_toolbar_editor(ProcVars),
	    NewProcVars;


	_OtherMessage ->
	    ProcVars

    end.
	    




get_updated_elem(ProcVars) ->
    EditedStr = gs:read(editentry, text),
    case tv_db_search:string_to_term(EditedStr) of
	{error, {_Reason, Msg}} ->
	    gs:config(editentry, [beep]),
	    gs:window(pdwin, gs:start(), []),
	    tv_utils:notify(pdwin, "TV Notification", Msg),
	    gs:destroy(pdwin),
	    ProcVars;
	{ok, NewTerm} ->
	    get_updated_elem2(false, NewTerm, ProcVars)
    end.





get_updated_elem2(FromRecEdit, NewTerm, ProcVars) ->
    #process_variables{table_type  = TableType,
		       record_name = RecordName,
		       mark_params = MarkP,
		       master_pid  = PcPid}  = ProcVars,

    #mark_params{marked_object  = ObjToUpdate,
		 marked_color   = ObjColor,
		 virtual_row_no = VirtualRow,
		 cell_col_no    = VirtualCol}  = MarkP,

	case ObjToUpdate of
	    undefined ->
		case new_object_ok(TableType, RecordName, NewTerm) of
		    true ->
			PcPid ! #pd_new_object{sender = self(),
					       object = NewTerm},
			ProcVars;
		    {false, Msg} ->
			gs:window(pdwin, gs:start(), []),
			tv_utils:notify(pdwin, "TV Notification", Msg),
			gs:destroy(pdwin),
			ProcVars
		end;
	    _AnyObj ->
		   %% We need to know if the object has been deleted!
		NewObj = 
		    case VirtualCol of 
			undefined ->
			    NewTerm;
			_AnyCol when FromRecEdit ->
			    NewTerm;
			_AnyCol ->
			    if 
				is_tuple(ObjToUpdate) ->
				    erlang:setelement(VirtualCol, ObjToUpdate, NewTerm);
				true ->
				    NewTerm
			    end
		    end,
		   %% Is the update OK?
		case update_ok(TableType, ObjToUpdate, NewObj) of
		    true ->
			PcPid ! #pd_updated_object{sender     = self(),
						   object     = NewObj,
						   old_object = ObjToUpdate,
						   old_color  = ObjColor,
						   obj_no     = VirtualRow},
			ProcVars;
		    false ->
			gs:window(pdwin, gs:start(), []),
			case get(error_msg_mode) of
			    normal ->
				tv_utils:notify(pdwin, "TV Notification", 
						["The record name cannot be changed!"]);
			    haiku ->
				tv_utils:notify(pdwin, "TV Notification", 
						["The attempt to change",
						 "The permanent record name",
						 "Is simply ignored."])
			end,
			gs:destroy(pdwin),
			ProcVars
		end
	end.




new_object_ok(ets, _RecordName, NewTerm) when is_tuple(NewTerm) ->
    true;
new_object_ok(ets, _RecordName, _NewTerm) ->
    Msg = case get(error_msg_mode) of
	      normal ->
		  ["Object is not a tuple!"];
	      haiku ->
		  ["Yes, it is a term.",
		   "It is pretty, but it's not",
		   "A proper tuple."]
	  end,
    {false, Msg};    
new_object_ok(mnesia, RecordName, NewTerm) when is_tuple(NewTerm) ->
    NewRecName = element(1, NewTerm),
    case NewRecName of
	RecordName ->
	    true;
	_OtherName ->
	    Msg = case get(error_msg_mode) of
		      normal ->
			  ["Erroneous record name!"];
		      haiku ->
			  ["The attempt to use",
			   "An invalid record name",
			   "Is simply ignored."]
		  end,
	    {false, Msg}
    end;
new_object_ok(mnesia, _RecordName, _NewTerm) ->
    Msg = case get(error_msg_mode) of
	      normal ->
		  ["Object is not a record!"];
	      haiku ->
		  ["Yes, it is a term.",
		   "It is pretty, but it's not",
		   "The proper record."]
	  end,
    {false, Msg}.




update_ok(ets, _ObjectToUpdate, _NewObject) ->
    true;
update_ok(mnesia, ObjectToUpdate, NewObject) ->
    OldRecName = element(1, ObjectToUpdate),
    NewRecName = element(1, NewObject),
    case NewRecName of
	OldRecName ->
	    true;
	_Other ->
	    false
    end.




handle_toolbar_buttons(Id, Btn, Str, LabelShown, X, Y, ProcVars) ->
    WinId = ProcVars#process_variables.window_id,
    ToolP = ProcVars#process_variables.toolbar_params,
    F     = ToolP#toolbar_params.pop_up_frame_id,

    receive

	{gs, Id, motion, _Data, [NewX, NewY | _]} ->
	    handle_toolbar_buttons(Id, Btn, Str, LabelShown, NewX, NewY, 
				   ProcVars);

	{gs, editentry, keypress, _Data, ['Tab' | _T]} ->
	    gs:config(editentry, [{select, {0,100000000}}]),
	    handle_toolbar_buttons(Id, Btn, Str, LabelShown, X, Y, ProcVars);

	{gs, editentry, keypress, _Data, ['Return' | _T]} ->
	    get_updated_elem(ProcVars),
	    handle_toolbar_buttons(Id, Btn, Str, LabelShown, X, Y, ProcVars);

	{gs, Id, leave, {toolbar, Btn, Str}, _Arg} ->
	    gs:config(F, [{y, -30}]),
	    gs:config(Id, [{motion, false}]),
	    ProcVars;
	
	{gs, Id, click, {toolbar, poll_table, _Str}, _Arg} ->
	    mark_busy(WinId),
	    gs:config(F, [{y, -30}]),
	    gs:config(Id, [{motion, false}]),
	    PcPid = ProcVars#process_variables.master_pid,
	    PcPid ! #pc_poll_table{sender = self()},
	    mark_nonbusy(WinId),
	    ProcVars;

	{gs, Id, click, {toolbar, select_browser, _Str}, _Arg} ->
	    gs:config(F, [{y, -30}]),
	    gs:config(Id, [{motion, false}]),
	    PcPid = ProcVars#process_variables.master_pid,
	    PcPid ! #pc_select{sender = self()},
	    ProcVars;

	{gs, Id, click, {toolbar, help_button, _Str}, _Arg} ->
	    gs:config(F, [{y, -30}]),
	    gs:config(Id, [{motion, false}]),
	    PcPid = ProcVars#process_variables.master_pid,
	    PcPid ! #pc_help{sender = self()},
	    ProcVars;

	{gs, Id, click, {toolbar, insert_object, _Str}, _Arg} ->
	    mark_busy(WinId),
	    gs:config(F, [{y, -30}]),
	    gs:config(Id, [{motion, false}]),
	    PcPid = ProcVars#process_variables.master_pid,
	    PcPid ! {pc_edit_object, self()},
	    mark_nonbusy(WinId),
	    ProcVars;


	{gs, Id, click, {toolbar, search_object, _Str}, _Arg} ->
	    mark_busy(WinId),
	    gs:config(F, [{y, -30}]),
	    gs:config(Id, [{motion, false}]),
	    PcPid = ProcVars#process_variables.master_pid,
	    PcPid ! #pc_search_req{sender = self()},
	    mark_nonbusy(WinId),
	    ProcVars;

	{gs, Id, click, {toolbar, sort_rising_order, _Str}, _Arg} ->
	    mark_busy(WinId),
	    gs:config(F, [{y, -30}]),
	    gs:config(Id, [{motion, false}]),
	    NewProcVars =
		case send_sort_info_signal(true, false, ProcVars) of
		    ignore ->
			ProcVars;
		    TempNewProcVars ->
			set_sort_col(true, TempNewProcVars)
		end,
	    mark_nonbusy(WinId),
	    NewProcVars;

	{gs, Id, click, {toolbar, sort_falling_order, _Str}, _Arg} ->
	    mark_busy(WinId),
	    gs:config(F, [{y, -30}]),
	    gs:config(Id, [{motion, false}]),
	    NewProcVars =
		case send_sort_info_signal(true, true, ProcVars) of
		    ignore ->
			ProcVars;
		    TempNewProcVars ->
			set_sort_col(true, TempNewProcVars)
		end,
	    mark_nonbusy(WinId),
	    NewProcVars;

	{gs, Id, click, {toolbar, no_sorting, _Str}, _Arg} ->
	    gs:config(F, [{y, -30}]),
	    gs:config(Id, [{motion, false}]),
	    NewProcVars =
		case send_sort_info_signal(false, false, ProcVars) of
		    ignore ->
			ProcVars;
		    TempNewProcVars ->
			set_sort_col(false, TempNewProcVars)
		end,
	    NewProcVars;

	{gs, Id, click, {toolbar, table_info, _Str}, _Arg} ->
	    gs:config(F, [{y, -30}]),
	    gs:config(Id, [{motion, false}]),
	    PcPid = ProcVars#process_variables.master_pid,
	    PcPid ! #pc_show_table_info{sender = self()},
	    ProcVars;

	{'EXIT', Pid, Reason} ->
	    exit_signals({Pid, Reason}, ProcVars),
	    handle_toolbar_buttons(Id, Btn, Str, LabelShown, X, Y, ProcVars);

	OtherMsg ->
	    NewProcVars = tv_messages(OtherMsg, ProcVars),
	    handle_toolbar_buttons(Id, Btn, Str, LabelShown, X, Y, NewProcVars)

    after 600 ->
	    case LabelShown of
		false ->
		    FrameP = ProcVars#process_variables.frame_params,
		    L = ToolP#toolbar_params.pop_up_label_id,
		    
		    #frame_params{toolbar_frame_width  = TWidth,
				  toolbar_frame_height = THeight} = FrameP,

		    BtnHeight         = gs:read(Id, height),
		    BtnXpos           = gs:read(Id, x),
		    BtnYpos           = gs:read(Id, y),
		    FrameHeight       = gs:read(F, height),
		    FontUsed          = gs:read(L, font),
		    {StringWidth, _H} = gs:read(L, {font_wh, {FontUsed, Str}}),

		    Width   = StringWidth + 6,
		    Xpos    = BtnXpos + X,
		    LblXpos = if
				  Xpos + Width > TWidth ->
				      Xpos - Width;
				  true ->
				      Xpos
			      end,
		  % Ypos    = BtnYpos + Y + 15,
		    Ypos    = BtnYpos + BtnHeight + 6,
		    LblYpos = if
				  Ypos + FrameHeight > THeight ->
				      Ypos - FrameHeight - 25;
				  true ->
				      Ypos
			      end,
		    gs:config(L, [{width, Width - 2},
				  {label, {text, Str}}]),
		    gs:config(F, [{width, Width},
				  {x, LblXpos},
				  {y, LblYpos}
				 ]);
		true ->
		    done
	    end,
	    handle_toolbar_buttons(Id, Btn, Str, true, X, Y, ProcVars)
    end.



	    


set_sort_col(SortingOn, ProcVars) ->
    #process_variables{pb_pid      = PbPid,
		       mark_params = MarkP} = ProcVars,
    
    SortCol = case SortingOn of
		  true ->
		      MarkP#mark_params.col_no;
		  false ->
		      undefined
	      end,
    PbPid ! #pb_set_sort_col{sender      = self(),
			     virtual_col = SortCol
			    },
    remove_all_marks(SortCol, ProcVars).





send_sort_info_signal(Sorting, Reverse, ProcVars) ->
    #process_variables{master_pid  = PcPid,
		       mark_params = MarkP}  = ProcVars,

    SortColNo = MarkP#mark_params.col_no,

    PcPid ! #pc_set_sorting_mode{sender      = self(),
				 sorting     = Sorting,
				 reverse     = Reverse,
				 sort_key_no = SortColNo
				},
    receive
	#pc_set_sorting_mode_cfm{sort_key_no = FinalSortColNo} ->
	    NewMarkP = MarkP#mark_params{col_no = FinalSortColNo},
	    ProcVars#process_variables{mark_params = NewMarkP};

	#pd_ignore{} ->
	    ignore

    end.
	    




show_data(Msg, ProcVars) ->
    #pc_data{scale_pos        = Pos,
	     scale_range      = Range, 
	     list_range       = MaxValue, 
	     elementlist      = List,
	     list_of_keys     = KeyList,
	     max_elem_size    = MaxElemSize,
	     marked_row       = MarkedRowData}     = Msg,
    
    ?DISP_FUNC_FILE:display_data(Pos, Range, MaxValue, List, KeyList, MaxElemSize, 
				 MarkedRowData, ProcVars).






handle_list_info(Msg, ProcVars) ->
    ListAsStr = Msg#pc_list_info.lists_as_strings,
    PgPid     = ProcVars#process_variables.pg_pid,
    PgPid ! #pg_list_info{sender           = self(),
			  lists_as_strings = ListAsStr},
    ProcVars#process_variables{lists_as_strings = ListAsStr}.
    




handle_col_marked(Msg, ProcVars) ->
    #pb_col_marked{col_marked  = ColMarked,
		   virtual_col = VirtualCol}  = Msg,

    #process_variables{master_pid     = MasterPid,
		       pg_pid         = PgPid,
		       rec_pid        = RecPid,
		       writable       = Writable,
		       toolbar_params = ToolP,
		       mark_params    = MarkP}  = ProcVars,
    SortCol = MarkP#mark_params.sort_col_no,
    
    PgPid ! #pg_remove_marks{sender = self()},

    case ColMarked of
	true ->
	    PgPid ! #pg_col_marked{sender      = self(),
				   virtual_col = VirtualCol};
	false ->
	    done
    end,

    MasterPid ! #pc_marked_row{sender = self(),
			       row_no = undefined,
			       object = undefined,
			       color  = undefined
			      },

    ?DISP_FUNC_FILE:update_toolbar_label(notext, ToolP, undefined, undefined, Writable),
    send_to_rec_edit(RecPid, insert_mode),
    
    NewMarkP = 
	if
	    ColMarked ->
		MarkP#mark_params{col_no = VirtualCol};
	    true ->
		if 
		    SortCol =:= undefined ->
			MarkP;
		    true ->
			MarkP#mark_params{col_no = SortCol}
		end
	end,
    ProcVars#process_variables{mark_params = NewMarkP}.
		      
    




remove_all_marks(SortCol, ProcVars) ->
    #process_variables{master_pid     = MasterPid,
		       pb_pid         = PbPid,
		       pg_pid         = PgPid,
		       toolbar_params = ToolP}  = ProcVars,
    
    PgPid ! #pg_remove_marks{sender = self()},
    PbPid ! #pb_remove_marks{sender = self()},
    MasterPid ! #pc_marked_row{sender = self(),
			       row_no = undefined,
			       object = undefined,
			       color  = undefined
			      },
%%    ?DISP_FUNC_FILE:update_toolbar_label(notext, ToolP, undefined, undefined, Writable),
    ?DISP_FUNC_FILE:update_toolbar_editor(ToolP#toolbar_params.editor_id, notext),
%%    send_to_rec_edit(RecPid, insert_mode),
    ProcVars#process_variables{mark_params = #mark_params{sort_col_no    = SortCol,
							  cell_col_no    = undefined,
							  row_no         = undefined,
							  virtual_row_no = undefined,
							  marked_object  = undefined,
							  marked_color   = undefined}
			      }.






handle_row_marked(Msg, ProcVars) ->
    #pb_row_marked{row_marked  = RowMarked,
		   virtual_row = VirtualRow,
		   real_row    = RealRow}  = Msg,

    #process_variables{master_pid     = MasterPid,
		       rec_pid        = RecPid,
		       pg_pid         = PgPid,
		       data_list      = DataList,
		       color_list     = ColorList,
		       writable       = Writable,
		       toolbar_params = ToolP,
		       mark_params    = MarkP}  = ProcVars,
		       
    PgPid ! #pg_remove_marks{sender = self()},

    case RowMarked of
	true ->
	    PgPid ! #pg_row_marked{sender      = self(),
				   virtual_row = VirtualRow};
	false ->
	    done
    end,

    {DataElement, NewMarkP} = 
	if
	    RowMarked ->
		{MarkedRowOrCol, RowObj} = 
		    ?DISP_FUNC_FILE:get_data_element(row, DataList, RealRow, undefined),
		
		MarkedRowColor =
		    case MarkedRowOrCol of
			notext ->
			    undefined;
			_OtherObject ->
			    lists:nth(RealRow, ColorList)
		    end,
		MasterPid ! #pc_marked_row{sender = self(),
					   row_no = VirtualRow,
					   object = RowObj,
					   color  = MarkedRowColor
					  },
		send_to_rec_edit(RecPid, {update_mode,RowObj}),
		{MarkedRowOrCol, MarkP#mark_params{virtual_row_no = VirtualRow,
						   row_no         = RealRow,
						   cell_col_no    = undefined,
						   col_no         = undefined,
						   marked_object  = RowObj,
						   marked_color   = MarkedRowColor}};
	    true ->
		 MasterPid ! #pc_marked_row{sender = self(),
					    row_no = undefined,
					    object = undefined,
					    color  = undefined
					   },
		send_to_rec_edit(RecPid, insert_mode),
		{notext, MarkP#mark_params{virtual_row_no = undefined,
					   row_no         = undefined,
					   cell_col_no    = undefined,
					   col_no         = undefined,
					   marked_object  = undefined,
					   marked_color   = undefined}}
	end,
    
    ?DISP_FUNC_FILE:update_toolbar_label(DataElement, ToolP, VirtualRow, 
					 undefined, Writable),
    ProcVars#process_variables{mark_params = NewMarkP}.





handle_cell_marked(Msg, ProcVars) ->
    #pg_cell_marked{cell_marked = CellMarked,
		    virtual_col = VirtualCol,
		    real_row    = RealRow,
		    virtual_row = VirtualRow}    = Msg,

       % We are interested in the real row number, since we only have a sublist 
       % stored in pd.
    ?DISP_FUNC_FILE:marked_cell(CellMarked, VirtualCol, RealRow, VirtualRow,
				ProcVars).
				      



resize_window(Msg, ProcVars) ->
    #pd_win_conf{width = NewWindowWidth, 
		 height = NewWindowHeight} = Msg,

    ?DISP_FUNC_FILE:resize_display(NewWindowWidth, NewWindowHeight, ProcVars).




resize_column(Msg, ProcVars) ->
    #pb_new_colwidth{real_col    = RealCol,
		     virtual_col = VirtualCol,
		     xdiff       = Xdiff}       = Msg,
    
    ?DISP_FUNC_FILE:resize_column(RealCol, VirtualCol, Xdiff, ProcVars).




mark_busy(Id) ->
    gs:config(Id, [{cursor, busy}]).
    



mark_nonbusy(Id) ->
    gs:config(Id, [{cursor, arrow}]).




send_to_rec_edit(undefined, _Msg) ->
    done;
send_to_rec_edit(RecPid, Msg) ->
    RecPid ! Msg.

