layout method that assigns the position and size of widgets for the whole window before drawing. Add another method that responds to mouse button presses. Allow windows to have no title bar by specifying NULL as the title. Subversion-branch: /trunk/chocolate-doom Subversion-revision: 547
556 lines
13 KiB
C
556 lines
13 KiB
C
// Emacs style mode select -*- C++ -*-
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// $Id$
|
|
//
|
|
// Copyright(C) 1993-1996 Id Software, Inc.
|
|
// Copyright(C) 2006 Simon Howard
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License
|
|
// as published by the Free Software Foundation; either version 2
|
|
// of the License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
// 02111-1307, USA.
|
|
//
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "doomkeys.h"
|
|
|
|
#include "txt_desktop.h"
|
|
#include "txt_gui.h"
|
|
#include "txt_main.h"
|
|
#include "txt_separator.h"
|
|
#include "txt_table.h"
|
|
|
|
static void TXT_TableDestructor(TXT_UNCAST_ARG(table))
|
|
{
|
|
TXT_CAST_ARG(txt_table_t, table);
|
|
int i;
|
|
|
|
// Free all widgets
|
|
|
|
for (i=0; i<table->num_widgets; ++i)
|
|
{
|
|
if (table->widgets[i] != NULL)
|
|
{
|
|
TXT_DestroyWidget(table->widgets[i]);
|
|
}
|
|
}
|
|
|
|
// Free table resources
|
|
|
|
free(table->widgets);
|
|
}
|
|
|
|
static int TableRows(txt_table_t *table)
|
|
{
|
|
return (table->num_widgets + table->columns - 1) / table->columns;
|
|
}
|
|
|
|
static void CalcRowColSizes(txt_table_t *table,
|
|
int *row_heights,
|
|
int *col_widths)
|
|
{
|
|
int table_height;
|
|
int x, y;
|
|
int rows;
|
|
txt_widget_t *widget;
|
|
|
|
rows = TableRows(table);
|
|
|
|
memset(col_widths, 0, sizeof(int) * table->columns);
|
|
|
|
for (y=0; y<rows; ++y)
|
|
{
|
|
row_heights[y] = 0;
|
|
|
|
for (x=0; x<table->columns; ++x)
|
|
{
|
|
if (y * table->columns + x >= table->num_widgets)
|
|
break;
|
|
|
|
widget = table->widgets[y * table->columns + x];
|
|
|
|
// NULL represents an empty spacer
|
|
|
|
if (widget != NULL)
|
|
{
|
|
TXT_CalcWidgetSize(widget);
|
|
if (widget->h > row_heights[y])
|
|
row_heights[y] = widget->h;
|
|
if (widget->w > col_widths[x])
|
|
col_widths[x] = widget->w;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void TXT_CalcTableSize(TXT_UNCAST_ARG(table))
|
|
{
|
|
TXT_CAST_ARG(txt_table_t, table);
|
|
int *column_widths;
|
|
int *row_heights;
|
|
int x, y;
|
|
int rows;
|
|
|
|
rows = TableRows(table);
|
|
|
|
row_heights = malloc(sizeof(int) * rows);
|
|
column_widths = malloc(sizeof(int) * table->columns);
|
|
|
|
CalcRowColSizes(table, row_heights, column_widths);
|
|
|
|
table->widget.w = 0;
|
|
|
|
for (x=0; x<table->columns; ++x)
|
|
{
|
|
table->widget.w += column_widths[x];
|
|
}
|
|
|
|
table->widget.h = 0;
|
|
|
|
for (y=0; y<rows; ++y)
|
|
{
|
|
table->widget.h += row_heights[y];
|
|
}
|
|
|
|
free(row_heights);
|
|
free(column_widths);
|
|
}
|
|
|
|
void TXT_AddWidget(TXT_UNCAST_ARG(table), TXT_UNCAST_ARG(widget))
|
|
{
|
|
TXT_CAST_ARG(txt_table_t, table);
|
|
TXT_CAST_ARG(txt_widget_t, widget);
|
|
|
|
if (table->num_widgets > 0)
|
|
{
|
|
txt_widget_t *last_widget;
|
|
|
|
last_widget = table->widgets[table->num_widgets - 1];
|
|
|
|
if (widget != NULL && last_widget != NULL
|
|
&& widget->widget_class == &txt_separator_class
|
|
&& last_widget->widget_class == &txt_separator_class)
|
|
{
|
|
// The previous widget added was a separator; replace
|
|
// it with this one.
|
|
//
|
|
// This way, if the first widget added to a window is
|
|
// a separator, it replaces the "default" separator that
|
|
// the window itself adds on creation.
|
|
|
|
table->widgets[table->num_widgets - 1] = widget;
|
|
|
|
TXT_DestroyWidget(last_widget);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
table->widgets = realloc(table->widgets,
|
|
sizeof(txt_widget_t *) * (table->num_widgets + 1));
|
|
table->widgets[table->num_widgets] = widget;
|
|
++table->num_widgets;
|
|
}
|
|
|
|
static int SelectableWidget(txt_table_t *table, int x, int y)
|
|
{
|
|
txt_widget_t *widget;
|
|
int i;
|
|
|
|
i = y * table->columns + x;
|
|
|
|
if (i >= 0 && i < table->num_widgets)
|
|
{
|
|
widget = table->widgets[i];
|
|
return widget != NULL && widget->selectable && widget->visible;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Tries to locate a selectable widget in the given row, returning
|
|
// the column number of the first column available or -1 if none are
|
|
// available in the given row.
|
|
//
|
|
// Starts from start_col, then searches nearby columns.
|
|
|
|
static int FindSelectableColumn(txt_table_t *table, int row, int start_col)
|
|
{
|
|
int x;
|
|
int i;
|
|
|
|
for (x=0; x<table->columns; ++x)
|
|
{
|
|
// Search to the right
|
|
|
|
if (SelectableWidget(table, start_col + x, row))
|
|
{
|
|
return start_col + x;
|
|
}
|
|
|
|
// Search to the left
|
|
|
|
if (SelectableWidget(table, start_col - x, row))
|
|
{
|
|
return start_col - x;
|
|
}
|
|
}
|
|
|
|
// None available
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int TXT_TableKeyPress(TXT_UNCAST_ARG(table), int key)
|
|
{
|
|
TXT_CAST_ARG(txt_table_t, table);
|
|
int selected;
|
|
int rows;
|
|
|
|
rows = TableRows(table);
|
|
|
|
// Send to the currently selected widget first
|
|
|
|
selected = table->selected_y * table->columns + table->selected_x;
|
|
|
|
if (selected >= 0 && selected < table->num_widgets)
|
|
{
|
|
if (table->widgets[selected] != NULL
|
|
&& TXT_WidgetKeyPress(table->widgets[selected], key))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (key == KEY_DOWNARROW)
|
|
{
|
|
int new_x, new_y;
|
|
|
|
// Move cursor down to the next selectable widget
|
|
|
|
for (new_y = table->selected_y + 1; new_y < rows; ++new_y)
|
|
{
|
|
new_x = FindSelectableColumn(table, new_y, table->selected_x);
|
|
|
|
if (new_x >= 0)
|
|
{
|
|
// Found a selectable widget in this column!
|
|
|
|
table->selected_x = new_x;
|
|
table->selected_y = new_y;
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (key == KEY_UPARROW)
|
|
{
|
|
int new_x, new_y;
|
|
|
|
// Move cursor up to the next selectable widget
|
|
|
|
for (new_y = table->selected_y - 1; new_y >= 0; --new_y)
|
|
{
|
|
new_x = FindSelectableColumn(table, new_y, table->selected_x);
|
|
|
|
if (new_x >= 0)
|
|
{
|
|
// Found a selectable widget in this column!
|
|
|
|
table->selected_x = new_x;
|
|
table->selected_y = new_y;
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (key == KEY_LEFTARROW)
|
|
{
|
|
int new_x;
|
|
int i;
|
|
|
|
// Move cursor left
|
|
|
|
for (new_x = table->selected_x - 1; new_x >= 0; --new_x)
|
|
{
|
|
if (SelectableWidget(table, new_x, table->selected_y))
|
|
{
|
|
// Found a selectable widget!
|
|
|
|
table->selected_x = new_x;
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (key == KEY_RIGHTARROW)
|
|
{
|
|
int new_x;
|
|
int i;
|
|
|
|
// Move cursor left
|
|
|
|
for (new_x = table->selected_x + 1; new_x < table->columns; ++new_x)
|
|
{
|
|
if (SelectableWidget(table, new_x, table->selected_y))
|
|
{
|
|
// Found a selectable widget!
|
|
|
|
table->selected_x = new_x;
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Check the currently selected widget in the table is valid.
|
|
|
|
static void CheckValidSelection(txt_table_t *table)
|
|
{
|
|
int rows;
|
|
int new_x, new_y;
|
|
|
|
rows = TableRows(table);
|
|
|
|
for (new_y = table->selected_y; new_y < rows; ++new_y)
|
|
{
|
|
new_x = FindSelectableColumn(table, new_y, table->selected_x);
|
|
|
|
if (new_x >= 0)
|
|
{
|
|
// Found a selectable column.
|
|
|
|
table->selected_x = new_x;
|
|
table->selected_y = new_y;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void LayoutCell(txt_table_t *table, int x, int y, int col_width,
|
|
int draw_x, int draw_y)
|
|
{
|
|
txt_widget_t *widget;
|
|
|
|
widget = table->widgets[y * table->columns + x];
|
|
|
|
// Adjust x position based on alignment property
|
|
|
|
switch (widget->align)
|
|
{
|
|
case TXT_HORIZ_LEFT:
|
|
widget->w = col_width;
|
|
break;
|
|
|
|
case TXT_HORIZ_CENTER:
|
|
TXT_CalcWidgetSize(widget);
|
|
|
|
// Separators are always drawn left-aligned.
|
|
|
|
if (widget->widget_class != &txt_separator_class)
|
|
{
|
|
draw_x += (col_width - widget->w) / 2;
|
|
}
|
|
|
|
break;
|
|
|
|
case TXT_HORIZ_RIGHT:
|
|
TXT_CalcWidgetSize(widget);
|
|
|
|
if (widget->widget_class != &txt_separator_class)
|
|
{
|
|
draw_x += col_width - widget->w;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// Set the position for this widget
|
|
|
|
widget->x = draw_x;
|
|
widget->y = draw_y;
|
|
|
|
// Recursively lay out any widgets contained in the widget
|
|
|
|
TXT_LayoutWidget(widget);
|
|
}
|
|
|
|
static void TXT_TableLayout(TXT_UNCAST_ARG(table))
|
|
{
|
|
TXT_CAST_ARG(txt_table_t, table);
|
|
int *column_widths;
|
|
int *row_heights;
|
|
int draw_x, draw_y;
|
|
int x, y;
|
|
int i;
|
|
int rows;
|
|
|
|
// Work out the column widths and row heights
|
|
|
|
rows = TableRows(table);
|
|
|
|
column_widths = malloc(sizeof(int) * table->columns);
|
|
row_heights = malloc(sizeof(int) * rows);
|
|
|
|
CalcRowColSizes(table, row_heights, column_widths);
|
|
|
|
// If this table only has one column, expand column size to fit
|
|
// the display width. Ensures that separators reach the window edges
|
|
// when drawing windows.
|
|
|
|
if (table->columns == 1)
|
|
{
|
|
column_widths[0] = table->widget.w;
|
|
}
|
|
|
|
// Draw all cells
|
|
|
|
draw_y = table->widget.y;
|
|
|
|
for (y=0; y<rows; ++y)
|
|
{
|
|
draw_x = table->widget.x;
|
|
|
|
for (x=0; x<table->columns; ++x)
|
|
{
|
|
i = y * table->columns + x;
|
|
|
|
if (i >= table->num_widgets)
|
|
break;
|
|
|
|
if (table->widgets[i] != NULL)
|
|
{
|
|
LayoutCell(table, x, y, column_widths[x],
|
|
draw_x, draw_y);
|
|
}
|
|
|
|
draw_x += column_widths[x];
|
|
}
|
|
|
|
draw_y += row_heights[y];
|
|
}
|
|
|
|
free(row_heights);
|
|
free(column_widths);
|
|
}
|
|
|
|
static void TXT_TableDrawer(TXT_UNCAST_ARG(table), int selected)
|
|
{
|
|
TXT_CAST_ARG(txt_table_t, table);
|
|
txt_widget_t *widget;
|
|
int selected_cell;
|
|
int i;
|
|
|
|
// Check the table's current selection points at something valid before
|
|
// drawing.
|
|
|
|
CheckValidSelection(table);
|
|
|
|
// Find the index of the currently-selected widget.
|
|
|
|
selected_cell = table->selected_y * table->columns + table->selected_x;
|
|
|
|
// Draw all cells
|
|
|
|
for (i=0; i<table->num_widgets; ++i)
|
|
{
|
|
widget = table->widgets[i];
|
|
|
|
if (widget != NULL)
|
|
{
|
|
TXT_GotoXY(widget->x, widget->y);
|
|
TXT_DrawWidget(widget, selected && i == selected_cell);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Responds to mouse presses
|
|
|
|
static void TXT_TableMousePress(TXT_UNCAST_ARG(table), int x, int y, int b)
|
|
{
|
|
TXT_CAST_ARG(txt_table_t, table);
|
|
txt_widget_t *widget;
|
|
int i;
|
|
|
|
for (i=0; i<table->num_widgets; ++i)
|
|
{
|
|
widget = table->widgets[i];
|
|
|
|
// NULL widgets are spacers
|
|
|
|
if (widget != NULL)
|
|
{
|
|
if (x >= widget->x && x < widget->x + widget->w
|
|
&& y >= widget->y && y < widget->y + widget->h)
|
|
{
|
|
// This is the widget that was clicked!
|
|
|
|
// Select the cell if the widget is selectable
|
|
|
|
if (widget->selectable)
|
|
{
|
|
table->selected_x = i % table->columns;
|
|
table->selected_y = i / table->columns;
|
|
}
|
|
|
|
// Propagate click
|
|
|
|
TXT_WidgetMousePress(widget, x, y, b);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
txt_widget_class_t txt_table_class =
|
|
{
|
|
TXT_CalcTableSize,
|
|
TXT_TableDrawer,
|
|
TXT_TableKeyPress,
|
|
TXT_TableDestructor,
|
|
TXT_TableMousePress,
|
|
TXT_TableLayout,
|
|
};
|
|
|
|
void TXT_InitTable(txt_table_t *table, int columns)
|
|
{
|
|
TXT_InitWidget(table, &txt_table_class);
|
|
table->columns = columns;
|
|
table->widgets = NULL;
|
|
table->num_widgets = 0;
|
|
table->selected_x = 0;
|
|
table->selected_y = 0;
|
|
}
|
|
|
|
txt_table_t *TXT_NewTable(int columns)
|
|
{
|
|
txt_table_t *table;
|
|
|
|
table = malloc(sizeof(txt_table_t));
|
|
|
|
TXT_InitTable(table, columns);
|
|
|
|
return table;
|
|
}
|
|
|