%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1997-2010. 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_pg_gridfcns).
-compile([{nowarn_deprecated_function,{gs,config,2}},
{nowarn_deprecated_function,{gs,frame,2}},
{nowarn_deprecated_function,{gs,label,2}},
{nowarn_deprecated_function,{gs,read,2}}]).
-export([init_grid/8,
resize_grid/3,
resize_grid_column/4,
update_grid_data/3,
scroll_grid_horizontally/2,
mark_cell_and_notify/4,
remove_marks/1,
mark_col/2,
mark_row/2,
handle_list_info/2
]).
-include("tv_pd_int_msg.hrl").
-include("tv_pg_int_def.hrl").
%%%*********************************************************************
%%% EXTERNAL FUNCTIONS
%%%*********************************************************************
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
init_grid(GridParentId, GridWidth,
GridHeight, GridXpos, GridYpos, NofRows, RowHeight, ProcVars) ->
% Get the size and ID of the grid-parent frame, i.e., the
% grid-frame! Do not confuse the base-frames below with
% the grid-frame!
#process_variables{parent_pid = ParentPid,
grid_params = GridP} = ProcVars,
#grid_params{fg_color = GridFgColor,
nof_cols = NofCols,
col_width = DefaultColWidth,
first_col_shown = FirstColShown,
col_widths = ColWidths} = GridP,
% Create the two frames the column frames are placed on!
% These two frames defines the size of the grid.
BgFrame = create_base_frame(GridParentId, GridWidth, GridHeight,
GridXpos, GridYpos, GridFgColor),
FgFrame = create_base_frame(BgFrame, GridWidth - 1, GridHeight - 1,
0, 0, GridFgColor),
% Compute the the colwidths necessary to cover the grid.
ColsShown = compute_cols_shown(FirstColShown, ColWidths, GridWidth, NofCols,
DefaultColWidth),
NofRowsShown = compute_rows_shown(GridHeight, RowHeight),
% Tell parent about the width of columns shown!
ParentPid ! #pg_col_info{sender = self(),
first_col_shown = FirstColShown,
width_of_cols_shown = ColsShown,
nof_rows_shown = NofRowsShown
},
NewNofCols = erlang:max(length(ColsShown), NofCols),
% The GridColWidths list shall contain the current width of each frame.
NewColWidths = update_col_widths(ColsShown, ColWidths, FirstColShown,
DefaultColWidth),
% Create column frames, one for each column, and rows (labels) on each frame.
{FrameIdList, ColLabelList} = create_col_frames(NewNofCols, NofRows, RowHeight,
FgFrame, GridP, [], []),
% Get lists of label-ID's for each row. (When we created the column frames,
% we got the id's of labels placed on each column, i.e., vertically.
% However, most often we want the id's for one row, i.e., label id's
% horisontally.)
RowIdList = get_row_ids(NofRows, ColLabelList, []),
% Update the grid_params record with the new values!
NewGridP = GridP#grid_params{bg_frame = BgFrame,
fg_frame = FgFrame,
grid_width = GridWidth,
grid_height = GridHeight,
grid_xpos = GridXpos,
grid_ypos = GridYpos,
nof_cols = NewNofCols,
col_widths = NewColWidths,
cols_shown = ColsShown,
nof_rows = NofRows,
row_height = RowHeight,
nof_rows_shown = NofRowsShown,
col_frame_ids = FrameIdList,
col_ids = ColLabelList,
row_ids = RowIdList,
row_data_list = lists:duplicate(NofRows, notext)
},
ProcVars#process_variables{grid_parent_id = GridParentId,
grid_params = NewGridP}.
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
resize_grid(NewWidth, NewHeight, ProcVars) ->
#process_variables{parent_pid = ParentPid,
grid_params = GridP,
mark_params = MarkP} = ProcVars,
#grid_params{bg_frame = BgFrame,
fg_frame = FgFrame,
nof_cols = NofCols,
nof_rows = NofRows,
col_width = DefaultColWidth,
first_col_shown = FirstColShown,
col_widths = ColWidths,
row_height = RowHeight,
col_frame_ids = ColFrameIds,
col_ids = ColIds,
row_ids = RowIds,
bg_color = BgColor,
fg_color = FgColor,
row_data_list = RowDataList,
lists_as_strings = ListAsStr} = GridP,
gs:config(BgFrame, [{width, NewWidth},
{height, NewHeight}
]),
gs:config(FgFrame, [{width, NewWidth - 1},
{height, NewHeight - 1}
]),
ColsShown = compute_cols_shown(FirstColShown, ColWidths, NewWidth, NofCols,
DefaultColWidth),
NofRowsShown = compute_rows_shown(NewHeight, RowHeight),
% Tell parent about the width of columns shown!
ParentPid ! #pg_col_info{sender = self(),
first_col_shown = FirstColShown,
width_of_cols_shown = ColsShown,
nof_rows_shown = NofRowsShown
},
NewColWidths = update_col_widths(ColsShown, ColWidths, FirstColShown,
DefaultColWidth),
NofColsShown = length(ColsShown),
{NewNofCols, NewColFrameIds, NewColIds, NewRowIds} =
check_nof_cols(ColsShown, (NofColsShown - NofCols), ColFrameIds, ColIds,
RowIds, NofRows, RowHeight, FgColor, BgColor ),
clear_fields(safe_nthtail(NofColsShown, NewColIds),
safe_nthtail(NofRowsShown, NewRowIds)),
RowsToUpdate = lists:sublist(NewRowIds, NofRowsShown),
refresh_visible_rows(RowsToUpdate, FirstColShown, NofColsShown, RowDataList, ListAsStr),
NewGridP = GridP#grid_params{grid_width = NewWidth,
grid_height = NewHeight,
nof_cols = NewNofCols,
nof_rows_shown = NofRowsShown,
cols_shown = ColsShown,
col_widths = NewColWidths,
col_frame_ids = NewColFrameIds,
col_ids = NewColIds,
row_ids = NewRowIds
},
refresh_marks(NewGridP, MarkP),
ProcVars#process_variables{grid_params = NewGridP}.
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
resize_grid_column(RealCol, VirtualCol, Xdiff, ProcVars) ->
#process_variables{parent_pid = ParentPid,
grid_params = GridP,
mark_params = MarkP} = ProcVars,
#grid_params{grid_width = GridWidth,
first_col_shown = FirstColShown,
nof_cols = NofCols,
col_widths = ColWidths,
col_frame_ids = ColFrameIds,
col_ids = ColIds,
col_width = DefaultColWidth,
row_ids = RowIds,
max_col_width = MaxColWidth,
min_col_width = MinColWidth,
nof_rows = NofRows,
nof_rows_shown = NofRowsShown,
row_height = RowHeight,
bg_color = BgColor,
fg_color = FgColor,
row_data_list = RowDataList,
lists_as_strings = ListAsStr} = GridP,
% Get new width!
Width = erlang:min(MaxColWidth, erlang:max((lists:nth(VirtualCol, ColWidths) + Xdiff),
MinColWidth)),
% Resize the column.
NewWidthOfCol = resize_one_column(RealCol, Width, ColFrameIds, MaxColWidth,
MinColWidth),
% Update the ColWidths list.
TempColWidths = lists:sublist(ColWidths, VirtualCol - 1) ++
[NewWidthOfCol | safe_nthtail(VirtualCol, ColWidths)],
% Check the other columns, whether a new column has to be created.
ColsShown = compute_cols_shown(FirstColShown, TempColWidths, GridWidth,
NofCols, DefaultColWidth),
% Get the final ColWidths list, after all updates!
NewColWidths = update_col_widths(ColsShown, TempColWidths, FirstColShown,
DefaultColWidth),
% Tell parent about the width of columns shown!
ParentPid ! #pg_col_info{sender = self(),
first_col_shown = FirstColShown,
width_of_cols_shown = ColsShown,
nof_rows_shown = NofRowsShown
},
% Get the new number of columns (may have changed).
NofColsShown = length(ColsShown),
{NewNofCols, NewColFrameIds, NewColIds, NewRowIds} =
check_nof_cols(ColsShown, (NofColsShown - NofCols), ColFrameIds, ColIds,
RowIds, NofRows, RowHeight, FgColor, BgColor ),
RowsToUpdate = lists:sublist(NewRowIds, NofRowsShown),
refresh_visible_rows(RowsToUpdate, FirstColShown, NofColsShown, RowDataList, ListAsStr),
NewGridP = GridP#grid_params{nof_cols = NewNofCols,
cols_shown = ColsShown,
col_widths = NewColWidths,
col_frame_ids = NewColFrameIds,
col_ids = NewColIds,
row_ids = NewRowIds
},
refresh_marks(NewGridP, MarkP),
ProcVars#process_variables{grid_params = NewGridP}.
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
handle_list_info(ListAsStr, ProcVars) ->
#process_variables{grid_params = GridP} = ProcVars,
#grid_params{first_col_shown = FirstColShown,
cols_shown = ColsShown,
nof_rows_shown = NofRowsShown,
row_data_list = RowDataList,
row_ids = RowIds,
lists_as_strings = OldListAsStr} = GridP,
case ListAsStr of
OldListAsStr ->
ProcVars;
_NewValue ->
NofColsShown = length(ColsShown),
RowsToUpdate = lists:sublist(RowIds, NofRowsShown),
refresh_visible_rows(RowsToUpdate, FirstColShown, NofColsShown,
RowDataList, ListAsStr),
NewGridP = GridP#grid_params{lists_as_strings = ListAsStr},
ProcVars#process_variables{grid_params = NewGridP}
end.
update_grid_data(Data, FirstRowShown, ProcVars) ->
#process_variables{grid_params = GridP,
mark_params = MarkP} = ProcVars,
#grid_params{first_col_shown = FirstColShown,
cols_shown = ColsShown,
nof_rows = NofRows,
nof_rows_shown = NofRowsShown,
row_ids = RowIds,
lists_as_strings = ListAsStr} = GridP,
NofColsShown = length(ColsShown),
RowsToUpdate = lists:sublist(RowIds, NofRowsShown),
NewMarkP = move_marks(FirstColShown, FirstRowShown, GridP, MarkP),
update_visible_rows(RowsToUpdate, FirstColShown, NofColsShown, Data, ListAsStr),
NewRowDataList = make_row_data_list(1, NofRows, Data),
NewGridP = GridP#grid_params{first_row_shown = FirstRowShown,
row_data_list = NewRowDataList},
ProcVars#process_variables{grid_params = NewGridP,
mark_params = NewMarkP}.
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
scroll_grid_horizontally(NewFirstColShown, ProcVars) ->
#process_variables{parent_pid = ParentPid,
grid_params = GridP,
mark_params = MarkP} = ProcVars,
#grid_params{grid_width = Width,
nof_cols = NofCols,
nof_rows = NofRows,
nof_rows_shown = NofRowsShown,
first_row_shown = FirstRowShown,
col_width = DefaultColWidth,
max_col_width = MaxColWidth,
min_col_width = MinColWidth,
col_widths = ColWidths,
row_height = RowHeight,
col_frame_ids = ColFrameIds,
col_ids = ColIds,
row_ids = RowIds,
bg_color = BgColor,
fg_color = FgColor,
row_data_list = RowDataList,
lists_as_strings = ListAsStr} = GridP,
% Probably it is unnecessary to check whether any new columns shall be
% created or not, but what the heck, we don't want to crash...
ColsShown = compute_cols_shown(NewFirstColShown, ColWidths, Width, NofCols,
DefaultColWidth),
NofColsShown = length(ColsShown),
ParentPid ! #pg_col_info{sender = self(),
first_col_shown = NewFirstColShown,
width_of_cols_shown = ColsShown,
nof_rows_shown = NofRowsShown
},
NewMarkP = move_marks(NewFirstColShown, FirstRowShown, GridP, MarkP),
NewColWidths = update_col_widths(ColsShown, ColWidths, NewFirstColShown,
DefaultColWidth),
{NewNofCols, NewColFrameIds, NewColIds, NewRowIds} =
check_nof_cols(ColsShown, (NofColsShown - NofCols), ColFrameIds, ColIds,
RowIds, NofRows, RowHeight, FgColor, BgColor ),
RowsToUpdate = lists:sublist(NewRowIds, NofRowsShown),
resize_all_grid_columns(1, ColsShown, NewColFrameIds, MaxColWidth, MinColWidth),
refresh_visible_rows(RowsToUpdate, NewFirstColShown, NofColsShown, RowDataList, ListAsStr),
% Clear fields currently not visible.
clear_fields(safe_nthtail(NofColsShown, NewColIds),
safe_nthtail(NofRowsShown, NewRowIds)),
NewGridP = GridP#grid_params{nof_cols = NewNofCols,
cols_shown = ColsShown,
col_widths = NewColWidths,
col_frame_ids = NewColFrameIds,
col_ids = NewColIds,
row_ids = NewRowIds,
first_col_shown = NewFirstColShown
},
ProcVars#process_variables{grid_params = NewGridP,
mark_params = NewMarkP}.
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
mark_row(VirtualRow, ProcVars) ->
#process_variables{grid_params = GridP,
mark_params = MarkP} = ProcVars,
#grid_params{first_row_shown = FirstRowShown,
nof_rows_shown = NofRowsShown,
row_ids = RowIds} = GridP,
mark_row(VirtualRow, FirstRowShown, FirstRowShown + NofRowsShown - 1, RowIds,
?GRID_MARK_COLOR),
NewMarkP = MarkP#mark_params{cell_id = undefined,
virtual_col = undefined,
virtual_row = VirtualRow
},
ProcVars#process_variables{mark_params = NewMarkP}.
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
mark_col(VirtualCol, ProcVars) ->
#process_variables{grid_params = GridP,
mark_params = MarkP} = ProcVars,
#grid_params{first_col_shown = FirstColShown,
cols_shown = ColsShown,
col_ids = ColIds} = GridP,
NofColsShown = length(ColsShown),
mark_col(VirtualCol, FirstColShown, FirstColShown + NofColsShown - 1, ColIds,
?GRID_MARK_COLOR),
NewMarkP = MarkP#mark_params{cell_id = undefined,
virtual_col = VirtualCol,
virtual_row = undefined
},
ProcVars#process_variables{mark_params = NewMarkP}.
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
mark_cell_and_notify(CellId, RealCol, RealRow, ProcVars) ->
#process_variables{parent_pid = ParentPid,
grid_params = GridP,
mark_params = MarkP} = ProcVars,
#grid_params{first_col_shown = FirstColShown,
first_row_shown = FirstRowShown} = GridP,
OldCellId = MarkP#mark_params.cell_id,
VirtualCol = FirstColShown + RealCol - 1,
VirtualRow = FirstRowShown + RealRow - 1,
%% Right now, when the table tool only is passive, i.e., we cannot edit
%% the table content, we don't want to be able to mark empty cells.
{text, CellText} = gs:read(CellId, label),
CellMarked = case CellText of
"" -> false;
_AnyText when CellId=:=OldCellId -> false;
_AnyText -> true
end,
remove_marks(ProcVars),
update_marked_cells(CellId, OldCellId, CellMarked),
notify_about_cell_marked(ParentPid, CellMarked, RealCol, RealRow,
VirtualCol, VirtualRow, CellText),
NewMarkP = case CellMarked of
true ->
MarkP#mark_params{cell_id = CellId,
virtual_col = VirtualCol,
virtual_row = VirtualRow
};
false ->
MarkP#mark_params{cell_id = undefined,
virtual_col = 0,
virtual_row = undefined
}
end,
ProcVars#process_variables{mark_params = NewMarkP}.
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
remove_marks(ProcVars) ->
#process_variables{mark_params = MarkP,
grid_params = GridP} = ProcVars,
#grid_params{first_col_shown = FirstColShown,
cols_shown = ColsShown,
col_ids = ColIds,
first_row_shown = FirstRowShown,
nof_rows_shown = NofRowsShown,
row_ids = RowIds} = GridP,
#mark_params{cell_id = CellId,
virtual_col = VirtualCol,
virtual_row = VirtualRow} = MarkP,
case {VirtualCol, VirtualRow} of
{undefined, undefined} ->
update_marked_cells(CellId, CellId, false);
{_AnyCol, undefined} ->
NofColsShown = length(ColsShown),
unmark_col(VirtualCol, FirstColShown, FirstColShown + NofColsShown - 1,
ColIds);
{undefined, _AnyRow} ->
unmark_row(VirtualRow, FirstRowShown, FirstRowShown + NofRowsShown - 1,
RowIds);
_Other ->
update_marked_cells(CellId, CellId, false)
end,
NewMarkP = MarkP#mark_params{cell_id = undefined,
virtual_col = 0,
virtual_row = undefined
},
ProcVars#process_variables{mark_params = NewMarkP}.
%%%*********************************************************************
%%% INTERNAL FUNCTIONS
%%%*********************************************************************
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
move_marks(FirstCol, FirstRow, GridP, MarkP) ->
#grid_params{first_col_shown = OldFirstCol,
cols_shown = ColsShown,
first_row_shown = OldFirstRow,
nof_rows_shown = NofRowsShown,
col_ids = ColIds,
row_ids = RowIds} = GridP,
#mark_params{virtual_col = VirtualCol,
virtual_row = VirtualRow} = MarkP,
case {VirtualCol, VirtualRow} of
{undefined, undefined} ->
NofColsShown = length(ColsShown),
move_marked_cell(FirstCol, FirstRow, NofColsShown,
NofRowsShown, RowIds, MarkP);
{_AnyCol, undefined} ->
NofColsShown = length(ColsShown),
OldLastCol = OldFirstCol + NofColsShown - 1,
LastCol = FirstCol + NofColsShown - 1,
move_marked_col(VirtualCol, OldFirstCol, OldLastCol,
FirstCol, LastCol, ColIds, MarkP);
{undefined, _AnyRow} ->
OldLastRow = OldFirstRow + NofRowsShown - 1,
LastRow = FirstRow + NofRowsShown - 1,
move_marked_row(VirtualRow, OldFirstRow, OldLastRow,
FirstRow, LastRow, RowIds, MarkP);
{_CellCol, _CellRow} ->
NofColsShown = length(ColsShown),
move_marked_cell(FirstCol, FirstRow, NofColsShown,
NofRowsShown, RowIds, MarkP)
end.
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
refresh_marks(GridP, MarkP) ->
#grid_params{first_col_shown = FirstCol,
cols_shown = ColsShown,
first_row_shown = FirstRow,
nof_rows_shown = NofRowsShown,
col_ids = ColIds,
row_ids = RowIds} = GridP,
#mark_params{virtual_col = VirtualCol,
virtual_row = VirtualRow} = MarkP,
case {VirtualCol, VirtualRow} of
{undefined, undefined} ->
NofColsShown = length(ColsShown),
move_marked_cell(FirstCol, FirstRow, NofColsShown, NofRowsShown,
RowIds, MarkP);
{_AnyCol, undefined} ->
NofColsShown = length(ColsShown),
LastCol = FirstCol + NofColsShown - 1,
mark_col(VirtualCol, FirstCol, LastCol, ColIds, ?GRID_MARK_COLOR);
{undefined, _AnyRow} ->
LastRow = FirstRow + NofRowsShown - 1,
mark_row(VirtualRow, FirstRow, LastRow, RowIds, ?GRID_MARK_COLOR);
{_CellCol, _CellRow} ->
NofColsShown = length(ColsShown),
move_marked_cell(FirstCol, FirstRow, NofColsShown, NofRowsShown,
RowIds, MarkP)
end.
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
move_marked_col(VirtualCol,
OldFirstCol, OldLastCol, FirstCol, LastCol, ColIds, MarkP) ->
unmark_col(VirtualCol, OldFirstCol, OldLastCol, ColIds),
mark_col(VirtualCol, FirstCol, LastCol, ColIds, ?GRID_MARK_COLOR),
MarkP#mark_params{cell_id = undefined}.
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
mark_col(VirtualCol, FirstCol, _LastCol, _ColIds, _Color) when VirtualCol < FirstCol ->
done;
mark_col(VirtualCol, _FirstCol, LastCol, _ColIds, _Color) when VirtualCol > LastCol ->
done;
mark_col(VirtualCol, FirstCol, _LastCol, ColIds, Color) ->
RealCol = VirtualCol - FirstCol + 1,
MarkedColIds = lists:nth(RealCol, ColIds),
mark_all_cells(MarkedColIds, Color).
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
unmark_col(VirtualCol, FirstCol, LastCol, ColIds) ->
mark_col(VirtualCol, FirstCol, LastCol, ColIds, ?DEFAULT_GRID_BGCOLOR).
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
mark_all_cells([], _Color) ->
done;
mark_all_cells([CellId | T], Color) ->
gs:config(CellId, [{bg, Color}]),
mark_all_cells(T, Color).
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
mark_row(VirtualRow, FirstRow, _LastRow, _RowIds, _Color) when VirtualRow < FirstRow ->
done;
mark_row(VirtualRow, _FirstRow, LastRow, _RowIds, _Color) when VirtualRow > LastRow ->
done;
mark_row(VirtualRow, FirstRow, _LastRow, RowIds, Color) ->
RealRow = VirtualRow - FirstRow + 1,
MarkedRowIds = lists:nth(RealRow, RowIds),
mark_all_cells(MarkedRowIds, Color).
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
unmark_row(VirtualRow, FirstRow, LastRow, RowIds) ->
mark_row(VirtualRow, FirstRow, LastRow, RowIds, ?DEFAULT_GRID_BGCOLOR).
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
move_marked_row(VirtualRow,
OldFirstRow, OldLastRow, FirstRow, LastRow, RowIds, MarkP) ->
unmark_row(VirtualRow, OldFirstRow, OldLastRow, RowIds),
mark_row(VirtualRow, FirstRow, LastRow, RowIds, ?GRID_MARK_COLOR),
MarkP#mark_params{cell_id = undefined}.
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
move_marked_cell(FirstColShown,
FirstRowShown, NofColsShown, NofRowsShown, RowIds, MarkP) ->
#mark_params{cell_id = OldCellId,
virtual_col = VirtualCol,
virtual_row = VirtualRow} = MarkP,
case OldCellId of
undefined ->
MarkP;
_OtherId ->
NewRealCol = VirtualCol - FirstColShown + 1,
NewRealRow = VirtualRow - FirstRowShown + 1,
update_marked_cells(undefined, OldCellId, false),
case check_if_new_mark_visible(NewRealCol, NewRealRow,
NofColsShown, NofRowsShown) of
false ->
MarkP;
true ->
NewCellId = lists:nth(NewRealCol,
lists:nth(NewRealRow, RowIds)),
update_marked_cells(NewCellId, undefined, true),
MarkP#mark_params{cell_id = NewCellId}
end
end.
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
check_if_new_mark_visible(Col, _Row, NofCols, _NofRows) when Col > NofCols ->
false;
check_if_new_mark_visible(Col, _Row, _NofCols, _NofRows) when Col =< 0 ->
false;
check_if_new_mark_visible(_Col, Row, _NofCols, NofRows) when Row > NofRows ->
false;
check_if_new_mark_visible(_Col, Row, _NofCols, _NofRows) when Row =< 0 ->
false;
check_if_new_mark_visible(_Col, _Row, _NofCols, _NofRows) ->
true.
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
update_marked_cells(CellId, OldCellId, _MarkedCell) when CellId =:= OldCellId ->
gs:config(CellId, [{bg, ?DEFAULT_GRID_BGCOLOR}]);
update_marked_cells(_CellId, undefined, false) ->
done;
update_marked_cells(CellId, undefined, true) ->
gs:config(CellId, [{bg, ?GRID_MARK_COLOR}]);
update_marked_cells(CellId, OldCellId, true) ->
gs:config(OldCellId, [{bg, ?DEFAULT_GRID_BGCOLOR}]),
gs:config(CellId, [{bg, ?GRID_MARK_COLOR}]);
update_marked_cells(_CellId, OldCellId, false) ->
gs:config(OldCellId, [{bg, ?DEFAULT_GRID_BGCOLOR}]).
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
notify_about_cell_marked(Pid, Marked, RealCol, RealRow, VirtCol, VirtRow, Text) ->
Pid ! #pg_cell_marked{sender = self(),
cell_marked = Marked,
real_col = RealCol,
real_row = RealRow,
virtual_col = VirtCol,
virtual_row = VirtRow,
cell_text = Text
}.
%%%---------------------------------------------------------------------
%%% START of functions used to print data in the grid fields.
%%%---------------------------------------------------------------------
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
refresh_visible_rows([], _FirstColShown, _NofColsShown, _DataList, _ListAsStr) ->
done;
refresh_visible_rows(RowIds, _FirstColShown, _NofColsShown, [], _ListAsStr) ->
clear_cols_or_rows(RowIds);
refresh_visible_rows([OneRowIds | RemRowIds], FirstColShown, NofColsShown,
[DataItemList | RemDataItemLists], ListAsStr) ->
NewDataItemList = get_data_sublist(DataItemList, FirstColShown, NofColsShown),
update_one_row(lists:sublist(OneRowIds, NofColsShown), NewDataItemList, ListAsStr),
refresh_visible_rows(RemRowIds, FirstColShown, NofColsShown, RemDataItemLists, ListAsStr).
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
update_visible_rows([], _FirstColShown, _NofColsShown, _DataList, _ListAsStr) ->
done;
update_visible_rows(RowIds, _FirstColShown, _NofColsShown, [], _ListAsStr) ->
clear_cols_or_rows(RowIds);
update_visible_rows([OneRowIds | RemRowIds], FirstColShown, NofColsShown,
[DataItem | RemData], ListAsStr) ->
% We convert the received item to a list! This way we know that
% '[notext]' shall be printed as 'notext', while 'notext' shall
% be printed as ''.
TempDataItemList = item_to_list(DataItem),
DataItemList = get_data_sublist(TempDataItemList, FirstColShown,
NofColsShown),
update_one_row(lists:sublist(OneRowIds, NofColsShown), DataItemList, ListAsStr),
update_visible_rows(RemRowIds, FirstColShown, NofColsShown, RemData, ListAsStr).
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
update_one_row(OneRowIds, [], _ListAsStr) ->
clear_one_col_or_row(OneRowIds);
update_one_row([], _DataItemList, _ListAsStr) ->
done;
update_one_row([LabelId | RemLabelIds], [notext | T], ListAsStr) ->
gs:config(LabelId, [{label, {text, ""}}
]),
update_one_row(RemLabelIds, T, ListAsStr);
update_one_row([LabelId | RemLabelIds], [DataElem | T], ListAsStr) ->
Str = case ListAsStr of
true ->
tv_io_lib:format(" ~p", [DataElem]);
false ->
" " ++ lists:flatten(tv_io_lib:write(DataElem))
end,
gs:config(LabelId, [{label, {text, Str}}
]),
update_one_row(RemLabelIds, T, ListAsStr).
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
make_row_data_list(N, NofRows, []) when N > NofRows ->
[];
make_row_data_list(N, NofRows, []) ->
% If NofRows == N, we get the empty list here!
lists:duplicate(NofRows- N, notext);
make_row_data_list(N, NofRows, [_DataItem | _RemData]) when N > NofRows ->
[];
make_row_data_list(N, NofRows, [DataItem | RemData]) ->
% We convert the received item to a list! This way we know that
% '[notext]' shall be printed as 'notext', while 'notext' shall
% be printed as ''.
[item_to_list(DataItem) | make_row_data_list(N + 1, NofRows, RemData)].
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
item_to_list(Item) when is_tuple(Item) ->
tuple_to_list(Item);
item_to_list(Item) ->
[Item].
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
get_data_sublist(DataList, StartPos, Length) ->
case catch lists:sublist(DataList, StartPos, Length) of
{'EXIT', _Reason} ->
[];
Sublist ->
Sublist
end.
%%%---------------------------------------------------------------------
%%% END of functions used to print data in the grid fields.
%%%---------------------------------------------------------------------
%%%---------------------------------------------------------------------
%%% START of functions used to resize the grid columns.
%%%---------------------------------------------------------------------
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
resize_all_grid_columns(_RealCol, [], _ColFrameIds, _MaxColWidth, _MinColWidth) ->
done;
resize_all_grid_columns(RealCol, [ColWidth | Tail], ColFrameIds, MaxColWidth, MinColWidth) ->
resize_one_column(RealCol, ColWidth, ColFrameIds, MaxColWidth, MinColWidth),
resize_all_grid_columns(RealCol + 1, Tail, ColFrameIds, MaxColWidth,
MinColWidth).
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
resize_one_column(RealCol, Width, ColFrameIds, MaxW, MinW) ->
NewWidthOfCol = erlang:min(MaxW, erlang:max(Width, MinW)),
case length(ColFrameIds) of
RealCol ->
done;
_Other ->
FrameId = lists:nth(RealCol + 1, ColFrameIds),
gs:config(FrameId, [{x, NewWidthOfCol + 1}])
end,
NewWidthOfCol.
%%%---------------------------------------------------------------------
%%% END of functions used to resize the grid columns.
%%%---------------------------------------------------------------------
%%%---------------------------------------------------------------------
%%% START of functions used to update the grid.
%%%---------------------------------------------------------------------
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
clear_fields(ColIds, RowIds) ->
clear_cols_or_rows(ColIds),
clear_cols_or_rows(RowIds).
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
clear_cols_or_rows([]) ->
done;
clear_cols_or_rows([IdList | RemIdLists]) ->
clear_one_col_or_row(IdList),
clear_cols_or_rows(RemIdLists).
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
clear_one_col_or_row([]) ->
done;
clear_one_col_or_row([LabelId | RemLabelIds]) ->
gs:config(LabelId, [{label, {text, ""}}
]),
clear_one_col_or_row(RemLabelIds).
%%%---------------------------------------------------------------------
%%% END of functions used to update the grid.
%%%---------------------------------------------------------------------
%%%---------------------------------------------------------------------
%%% START of functions used to compute the part of the grid that has to
%%% be updated, as well as deciding whether a new column has to be added.
%%% Old columns (i.e., columns not visible) are not removed, but they
%%% shall not be updated until they once again becomes visible.
%%%---------------------------------------------------------------------
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
check_nof_cols(_ColsShown, NofNewCols, ColFrameIds, ColIds, RowIds,
_NofRows, _RowHeight, _FgColor, _BgColor) when NofNewCols =< 0 ->
{length(ColFrameIds), ColFrameIds, ColIds, RowIds};
check_nof_cols(ColsShown, NofNewCols, ColFrameIds, ColIds,
RowIds, NofRows, RowHeight, FgColor, BgColor) ->
NewColNo = length(ColFrameIds) + 1,
% We don't care about the pathological case where no columns have been
% created. If the gridwidth, or the columnwidth, was set to =< 0 during
% initialisation, then no columns will have been created. The program
% will probably also have crashed. If any smart jackass has set invalid
% values on these important parameters, then he can only blame himself.
ParentId = lists:nth((NewColNo - 1), ColFrameIds),
ParentColWidth = lists:nth((NewColNo - 1), ColsShown),
Xpos = ParentColWidth + 1,
{ColFrameId, LabelIds} = add_one_col_frame(ParentId, NewColNo, Xpos, FgColor,
BgColor, NofRows, RowHeight),
NewColFrameIds = ColFrameIds ++ [ColFrameId],
NewColIds = ColIds ++ [LabelIds],
NewRowIds = update_row_ids(RowIds, LabelIds),
check_nof_cols(ColsShown, NofNewCols - 1, NewColFrameIds, NewColIds, NewRowIds,
NofRows, RowHeight, FgColor, BgColor).
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
update_row_ids([], _LabelIds) ->
[];
update_row_ids([OneRowIds | RemainingRows], [NewElemId | RemainingElemIds]) ->
[OneRowIds ++ [NewElemId] | update_row_ids(RemainingRows, RemainingElemIds)].
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
update_col_widths(ColsShown, ColWidths, FirstColShown, DefaultColWidth) ->
% What we do here is that we first (if necessary) add default
% column widths to the ColWidth list until it reaches to where
% ColsShown starts (vitually seen).
% In the second step we take the appropriate elements from the
% ColsShown list and add them to the ColWidths list, until it is
% of sufficient length.
% Of course this may seem unnecessary - it would suffice to just
% add default widths to the ColWidths list until it is long enough,
% since the compute_cols_shown function right now just adds default
% width columns to the ColsShown list, when the ColWidths list is empty.
% However, this could change (maybe we some other time want the last
% column to carry all remaining width, instead of adding new columns).
% Besides, we don't like hidden dependencies between functions!!!
NofColsShown = length(ColsShown),
NewColWidths = set_necessary_col_widths_length(FirstColShown, ColWidths,
DefaultColWidth),
% Now NofVirtualCols will always be equal to, or greater
% than, FirstColShown - 1.
NofVirtualCols = length(NewColWidths),
NecessaryNofVirtualCols = FirstColShown + (NofColsShown - 1),
if
NecessaryNofVirtualCols > NofVirtualCols ->
TailNo = NofVirtualCols - FirstColShown + 1, % Always >= 0 !!!
NewColWidths ++ safe_nthtail(TailNo, ColsShown);
true ->
NewColWidths
end.
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
set_necessary_col_widths_length(FirstColShown, ColWidths, DefaultColWidth) ->
% First check that (length(ColWidths) - FirstColShown) >= -1.
% If not, add elements so the relation holds true!
MissingDefaultWidthElems = FirstColShown - length(ColWidths),
if
MissingDefaultWidthElems > 1 ->
ColWidths ++ lists:duplicate(MissingDefaultWidthElems - 1,
DefaultColWidth);
true ->
ColWidths
end.
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
compute_rows_shown(GridHeight, RowHeight) ->
(GridHeight div RowHeight) + 1.
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
compute_cols_shown(FirstColShown, ColWidths, GridWidth, _NofCols, DefaultColWidth) ->
ColWidthsLength = length(ColWidths),
% Normally ColWidths shall be long enough, but just to make sure...
% (We could have chosen to update ColWidths here to, but right now
% we do it instead explicitly when resizeing the grid, changing the
% column size(s), and scrolling horizontally.)
UsedColWidths = if
ColWidthsLength < FirstColShown ->
[];
true ->
safe_nthtail(FirstColShown - 1, ColWidths)
end,
compute_cols_shown(UsedColWidths, GridWidth, DefaultColWidth).
compute_cols_shown(_ColWidths, RemainingWidth, _DefColW) when RemainingWidth =< 0 ->
[];
compute_cols_shown([], RemainingWidth, DefaultColWidth) ->
[DefaultColWidth | compute_cols_shown([], RemainingWidth - DefaultColWidth,
DefaultColWidth)];
compute_cols_shown([VirtualColWidth | T], RemainingWidth, DefaultColWidth) ->
[VirtualColWidth | compute_cols_shown(T, RemainingWidth - VirtualColWidth,
DefaultColWidth)].
%%%---------------------------------------------------------------------
%%% END of functions used to compute the part of the grid that has to
%%% be updated, as well as deciding whether a new column has to be added.
%%%---------------------------------------------------------------------
%%%---------------------------------------------------------------------
%%% START of functions used to create the grid (baseframes, columns
%%% and rows), as well as sorting the ID's appropriately.
%%%---------------------------------------------------------------------
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
create_base_frame(ParentId, Width, Height, Xpos, Ypos, BgColor) ->
gs:frame(ParentId, [{width, Width},
{height, Height},
{x, Xpos},
{y, Ypos},
{bg, BgColor}
]).
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
create_col_frames(0, _NofRows, _RowHeight, _ParentId, _GridP, ColFrameAcc, LabelAcc) ->
{lists:reverse(ColFrameAcc), lists:reverse(LabelAcc)};
create_col_frames(N, NofRows, RowHeight, ParentId, GridP, ColFrameAcc, LabelAcc) ->
% Yes, it *IS* inefficient to copy GridP for each loop.
% However, it is only done once, and for a limited number of times,
% and we avoid having a lot of parameters!
#grid_params{bg_color = BgColor,
fg_color = FgColor,
nof_cols = NofCols,
col_width = ColWidth} = GridP,
Xpos = if
N =:= NofCols ->
0;
true ->
ColWidth + 1
end,
ColNo = NofCols - N + 1,
{ColFrameId, LabelIds} = add_one_col_frame(ParentId, ColNo, Xpos, FgColor,
BgColor, NofRows, RowHeight),
create_col_frames(N - 1, NofRows, RowHeight, ColFrameId, GridP,
[ColFrameId | ColFrameAcc], [LabelIds | LabelAcc]).
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
add_one_col_frame(ParentId, ColNo, Xpos, FgColor, BgColor, NofRows, RowHeight) ->
ColFrameId = create_one_col_frame(ParentId, Xpos, FgColor),
FirstRowYpos = 1,
FirstRowNo = 1,
LabelIds = create_rows_on_frame(ColFrameId, FirstRowNo, NofRows, RowHeight,
FirstRowYpos, FgColor, BgColor, ColNo, []),
{ColFrameId, LabelIds}.
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
create_one_col_frame(ParentId, Xpos, BgColor) ->
ColFrameWidth = 1200,
ColFrameHeight = 900,
Ypos = 0,
gs:frame(ParentId, [{width, ColFrameWidth},
{height, ColFrameHeight},
{x, Xpos},
{y, Ypos},
{bg, BgColor}
]).
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
create_rows_on_frame(_FrameId, RowNo, NofRows, _H, _Y, _Fg, _Bg, _ColNo, Acc) when RowNo > NofRows ->
lists:reverse(Acc);
create_rows_on_frame(FrameId, RowNo, NofRows, H, Y, Fg, Bg, ColNo, RAcc) ->
Width = 1200,
R = gs:label(FrameId, [{width, Width},
{height, H},
{x, 1},
{y, Y},
{bg, Bg},
{fg, Fg},
{align, w},
{buttonpress, true},
{data, {gridcell, ColNo, RowNo, FrameId}}
]),
NextRowNo = RowNo + 1,
NextY = Y + H +1,
create_rows_on_frame(FrameId, NextRowNo, NofRows, H, NextY, Fg, Bg, ColNo,
[R | RAcc]).
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
get_row_ids(0, _Cols, RowAcc) ->
RowAcc;
get_row_ids(RowNo, Cols, RowAcc) ->
Row = extract_ids_for_one_row(RowNo, Cols),
get_row_ids(RowNo - 1, Cols, [Row | RowAcc]).
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
extract_ids_for_one_row(_N, []) ->
[];
extract_ids_for_one_row(N, [ColIds | Tail]) ->
[lists:nth(N, ColIds) | extract_ids_for_one_row(N, Tail)].
%%%---------------------------------------------------------------------
%%% END of functions used to create the grid.
%%%---------------------------------------------------------------------
%%======================================================================
%% Function:
%%
%% Return Value:
%%
%% Description:
%%
%% Parameters:
%%======================================================================
safe_nthtail(_, []) -> [];
safe_nthtail(1, [_|T]) -> T;
safe_nthtail(N, [_|T]) when N > 1 ->
safe_nthtail(N - 1, T);
safe_nthtail(0, L) when is_list(L) -> L.