This change rewrites and simplifies the copyright headers at the top of all source files: * Remove "Emacs style mode select" line; this line was included in the headers for the originally released source files and appears to be to set the file type for old versions of Emacs. I'm not sure entirely why it was required but I don't think it is any more. * Remove "You should have received a copy of..." text from copyright header. This refers to the old 59 Temple Place address where the FSF headquarters used to be located and is no longer correct. Rather than change to the new address, just remove the paragraph as it is superfluous anyway. This fixes #311. * Remove ---- separator lines so that the file headers are barer and more simplified.
520 lines
12 KiB
C
520 lines
12 KiB
C
//
|
|
// Copyright(C) 2005-2014 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.
|
|
//
|
|
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
|
|
#include "doomkeys.h"
|
|
|
|
#include "txt_label.h"
|
|
#include "txt_desktop.h"
|
|
#include "txt_gui.h"
|
|
#include "txt_io.h"
|
|
#include "txt_main.h"
|
|
#include "txt_separator.h"
|
|
#include "txt_window.h"
|
|
|
|
void TXT_SetWindowAction(txt_window_t *window,
|
|
txt_horiz_align_t position,
|
|
txt_window_action_t *action)
|
|
{
|
|
if (window->actions[position] != NULL)
|
|
{
|
|
TXT_DestroyWidget(window->actions[position]);
|
|
}
|
|
|
|
window->actions[position] = action;
|
|
|
|
// Maintain parent pointer.
|
|
|
|
if (action != NULL)
|
|
{
|
|
action->widget.parent = &window->table.widget;
|
|
}
|
|
}
|
|
|
|
txt_window_t *TXT_NewWindow(char *title)
|
|
{
|
|
int i;
|
|
|
|
txt_window_t *win;
|
|
|
|
win = malloc(sizeof(txt_window_t));
|
|
|
|
TXT_InitTable(&win->table, 1);
|
|
|
|
if (title == NULL)
|
|
{
|
|
win->title = NULL;
|
|
}
|
|
else
|
|
{
|
|
win->title = strdup(title);
|
|
}
|
|
|
|
win->x = TXT_SCREEN_W / 2;
|
|
win->y = TXT_SCREEN_H / 2;
|
|
win->horiz_align = TXT_HORIZ_CENTER;
|
|
win->vert_align = TXT_VERT_CENTER;
|
|
win->key_listener = NULL;
|
|
win->mouse_listener = NULL;
|
|
|
|
TXT_AddWidget(win, TXT_NewSeparator(NULL));
|
|
|
|
for (i=0; i<3; ++i)
|
|
win->actions[i] = NULL;
|
|
|
|
TXT_AddDesktopWindow(win);
|
|
|
|
// Default actions
|
|
|
|
TXT_SetWindowAction(win, TXT_HORIZ_LEFT, TXT_NewWindowEscapeAction(win));
|
|
TXT_SetWindowAction(win, TXT_HORIZ_RIGHT, TXT_NewWindowSelectAction(win));
|
|
|
|
return win;
|
|
}
|
|
|
|
void TXT_CloseWindow(txt_window_t *window)
|
|
{
|
|
int i;
|
|
|
|
TXT_EmitSignal(window, "closed");
|
|
TXT_RemoveDesktopWindow(window);
|
|
|
|
free(window->title);
|
|
|
|
// Destroy all actions
|
|
|
|
for (i=0; i<3; ++i)
|
|
{
|
|
if (window->actions[i] != NULL)
|
|
{
|
|
TXT_DestroyWidget(window->actions[i]);
|
|
}
|
|
}
|
|
|
|
// Destroy table and window
|
|
|
|
TXT_DestroyWidget(window);
|
|
}
|
|
|
|
static void CalcWindowPosition(txt_window_t *window)
|
|
{
|
|
switch (window->horiz_align)
|
|
{
|
|
case TXT_HORIZ_LEFT:
|
|
window->window_x = window->x;
|
|
break;
|
|
case TXT_HORIZ_CENTER:
|
|
window->window_x = window->x - (window->window_w / 2);
|
|
break;
|
|
case TXT_HORIZ_RIGHT:
|
|
window->window_x = window->x - (window->window_w - 1);
|
|
break;
|
|
}
|
|
|
|
switch (window->vert_align)
|
|
{
|
|
case TXT_VERT_TOP:
|
|
window->window_y = window->y;
|
|
break;
|
|
case TXT_VERT_CENTER:
|
|
window->window_y = window->y - (window->window_h / 2);
|
|
break;
|
|
case TXT_VERT_BOTTOM:
|
|
window->window_y = window->y - (window->window_h - 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void LayoutActionArea(txt_window_t *window)
|
|
{
|
|
txt_widget_t *widget;
|
|
int space_available;
|
|
int space_left_offset;
|
|
|
|
// We need to calculate the available horizontal space for the center
|
|
// action widget, so that we can center it within it.
|
|
// To start with, we have the entire action area available.
|
|
|
|
space_available = window->window_w;
|
|
space_left_offset = 0;
|
|
|
|
// Left action
|
|
|
|
if (window->actions[TXT_HORIZ_LEFT] != NULL)
|
|
{
|
|
widget = (txt_widget_t *) window->actions[TXT_HORIZ_LEFT];
|
|
|
|
TXT_CalcWidgetSize(widget);
|
|
|
|
widget->x = window->window_x + 1;
|
|
widget->y = window->window_y + window->window_h - widget->h - 1;
|
|
|
|
// Adjust available space:
|
|
|
|
space_available -= widget->w;
|
|
space_left_offset += widget->w;
|
|
}
|
|
|
|
// Draw the right action
|
|
|
|
if (window->actions[TXT_HORIZ_RIGHT] != NULL)
|
|
{
|
|
widget = (txt_widget_t *) window->actions[TXT_HORIZ_RIGHT];
|
|
|
|
TXT_CalcWidgetSize(widget);
|
|
|
|
widget->x = window->window_x + window->window_w - 1 - widget->w;
|
|
widget->y = window->window_y + window->window_h - widget->h - 1;
|
|
|
|
// Adjust available space:
|
|
|
|
space_available -= widget->w;
|
|
}
|
|
|
|
// Draw the center action
|
|
|
|
if (window->actions[TXT_HORIZ_CENTER] != NULL)
|
|
{
|
|
widget = (txt_widget_t *) window->actions[TXT_HORIZ_CENTER];
|
|
|
|
TXT_CalcWidgetSize(widget);
|
|
|
|
// The left and right widgets have left a space sandwiched between
|
|
// them. Center this widget within that space.
|
|
|
|
widget->x = window->window_x
|
|
+ space_left_offset
|
|
+ (space_available - widget->w) / 2;
|
|
widget->y = window->window_y + window->window_h - widget->h - 1;
|
|
}
|
|
}
|
|
|
|
static void DrawActionArea(txt_window_t *window)
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<3; ++i)
|
|
{
|
|
if (window->actions[i] != NULL)
|
|
{
|
|
TXT_DrawWidget(window->actions[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void CalcActionAreaSize(txt_window_t *window,
|
|
unsigned int *w, unsigned int *h)
|
|
{
|
|
txt_widget_t *widget;
|
|
int i;
|
|
|
|
*w = 0;
|
|
*h = 0;
|
|
|
|
// Calculate the width of all the action widgets and use this
|
|
// to create an overall min. width of the action area
|
|
|
|
for (i=0; i<3; ++i)
|
|
{
|
|
widget = (txt_widget_t *) window->actions[i];
|
|
|
|
if (widget != NULL)
|
|
{
|
|
TXT_CalcWidgetSize(widget);
|
|
*w += widget->w;
|
|
|
|
if (widget->h > *h)
|
|
{
|
|
*h = widget->h;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sets size and position of all widgets in a window
|
|
|
|
void TXT_LayoutWindow(txt_window_t *window)
|
|
{
|
|
txt_widget_t *widgets = (txt_widget_t *) window;
|
|
unsigned int widgets_w;
|
|
unsigned int actionarea_w, actionarea_h;
|
|
|
|
// Calculate size of table
|
|
|
|
TXT_CalcWidgetSize(window);
|
|
|
|
// Widgets area: add one character of padding on each side
|
|
widgets_w = widgets->w + 2;
|
|
|
|
// Calculate the size of the action area
|
|
// Make window wide enough to action area
|
|
|
|
CalcActionAreaSize(window, &actionarea_w, &actionarea_h);
|
|
|
|
if (actionarea_w > widgets_w)
|
|
widgets_w = actionarea_w;
|
|
|
|
// Set the window size based on widgets_w
|
|
|
|
window->window_w = widgets_w + 2;
|
|
window->window_h = widgets->h + 1;
|
|
|
|
// If the window has a title, add an extra two lines
|
|
|
|
if (window->title != NULL)
|
|
{
|
|
window->window_h += 2;
|
|
}
|
|
|
|
// If the window has an action area, add extra lines
|
|
|
|
if (actionarea_h > 0)
|
|
{
|
|
window->window_h += actionarea_h + 1;
|
|
}
|
|
|
|
// Use the x,y position as the centerpoint and find the location to
|
|
// draw the window.
|
|
|
|
CalcWindowPosition(window);
|
|
|
|
// Set the table size and position
|
|
|
|
widgets->w = widgets_w - 2;
|
|
// widgets->h (already set)
|
|
widgets->x = window->window_x + 2;
|
|
widgets->y = window->window_y;
|
|
|
|
if (window->title != NULL)
|
|
{
|
|
widgets->y += 2;
|
|
}
|
|
|
|
// Layout the table and action area
|
|
|
|
LayoutActionArea(window);
|
|
TXT_LayoutWidget(widgets);
|
|
}
|
|
|
|
void TXT_DrawWindow(txt_window_t *window)
|
|
{
|
|
txt_widget_t *widgets;
|
|
|
|
TXT_LayoutWindow(window);
|
|
|
|
if (window->table.widget.focused)
|
|
{
|
|
TXT_BGColor(TXT_ACTIVE_WINDOW_BACKGROUND, 0);
|
|
}
|
|
else
|
|
{
|
|
TXT_BGColor(TXT_INACTIVE_WINDOW_BACKGROUND, 0);
|
|
}
|
|
|
|
TXT_FGColor(TXT_COLOR_BRIGHT_WHITE);
|
|
|
|
// Draw the window
|
|
|
|
TXT_DrawWindowFrame(window->title,
|
|
window->window_x, window->window_y,
|
|
window->window_w, window->window_h);
|
|
|
|
// Draw all widgets
|
|
|
|
TXT_DrawWidget(window);
|
|
|
|
// Draw an action area, if we have one
|
|
|
|
widgets = (txt_widget_t *) window;
|
|
|
|
if (widgets->y + widgets->h < window->window_y + window->window_h - 1)
|
|
{
|
|
// Separator for action area
|
|
|
|
TXT_DrawSeparator(window->window_x, widgets->y + widgets->h,
|
|
window->window_w);
|
|
|
|
// Action area at the window bottom
|
|
|
|
DrawActionArea(window);
|
|
}
|
|
}
|
|
|
|
void TXT_SetWindowPosition(txt_window_t *window,
|
|
txt_horiz_align_t horiz_align,
|
|
txt_vert_align_t vert_align,
|
|
int x, int y)
|
|
{
|
|
window->vert_align = vert_align;
|
|
window->horiz_align = horiz_align;
|
|
window->x = x;
|
|
window->y = y;
|
|
}
|
|
|
|
static void MouseButtonPress(txt_window_t *window, int b)
|
|
{
|
|
int x, y;
|
|
int i;
|
|
txt_widget_t *widgets;
|
|
txt_widget_t *widget;
|
|
|
|
// Lay out the window, set positions and sizes of all widgets
|
|
|
|
TXT_LayoutWindow(window);
|
|
|
|
// Get the current mouse position
|
|
|
|
TXT_GetMousePosition(&x, &y);
|
|
|
|
// Try the mouse button listener
|
|
// This happens whether it is in the window range or not
|
|
|
|
if (window->mouse_listener != NULL)
|
|
{
|
|
// Mouse listener can eat button presses
|
|
|
|
if (window->mouse_listener(window, x, y, b,
|
|
window->mouse_listener_data))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Is it within the table range?
|
|
|
|
widgets = (txt_widget_t *) window;
|
|
|
|
if (x >= widgets->x && x < (signed) (widgets->x + widgets->w)
|
|
&& y >= widgets->y && y < (signed) (widgets->y + widgets->h))
|
|
{
|
|
TXT_WidgetMousePress(window, x, y, b);
|
|
}
|
|
|
|
// Was one of the action area buttons pressed?
|
|
|
|
for (i=0; i<3; ++i)
|
|
{
|
|
widget = (txt_widget_t *) window->actions[i];
|
|
|
|
if (widget != NULL
|
|
&& x >= widget->x && x < (signed) (widget->x + widget->w)
|
|
&& y >= widget->y && y < (signed) (widget->y + widget->h))
|
|
{
|
|
int was_focused;
|
|
|
|
// Main table temporarily loses focus when action area button
|
|
// is clicked. This way, any active input boxes that depend
|
|
// on having focus will save their values before the
|
|
// action is performed.
|
|
|
|
was_focused = window->table.widget.focused;
|
|
TXT_SetWidgetFocus(window, 0);
|
|
TXT_SetWidgetFocus(window, was_focused);
|
|
|
|
// Pass through mouse press.
|
|
|
|
TXT_WidgetMousePress(widget, x, y, b);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void TXT_WindowKeyPress(txt_window_t *window, int c)
|
|
{
|
|
int i;
|
|
|
|
// Is this a mouse button ?
|
|
|
|
if (c >= TXT_MOUSE_BASE && c < TXT_MOUSE_BASE + TXT_MAX_MOUSE_BUTTONS)
|
|
{
|
|
MouseButtonPress(window, c);
|
|
return;
|
|
}
|
|
|
|
// Try the window key spy
|
|
|
|
if (window->key_listener != NULL)
|
|
{
|
|
// key listener can eat keys
|
|
|
|
if (window->key_listener(window, c, window->key_listener_data))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Send to the currently selected widget
|
|
|
|
if (TXT_WidgetKeyPress(window, c))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Try all of the action buttons
|
|
|
|
for (i=0; i<3; ++i)
|
|
{
|
|
if (window->actions[i] != NULL
|
|
&& TXT_WidgetKeyPress(window->actions[i], c))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void TXT_SetKeyListener(txt_window_t *window, TxtWindowKeyPress key_listener,
|
|
void *user_data)
|
|
{
|
|
window->key_listener = key_listener;
|
|
window->key_listener_data = user_data;
|
|
}
|
|
|
|
void TXT_SetMouseListener(txt_window_t *window,
|
|
TxtWindowMousePress mouse_listener,
|
|
void *user_data)
|
|
{
|
|
window->mouse_listener = mouse_listener;
|
|
window->mouse_listener_data = user_data;
|
|
}
|
|
|
|
void TXT_SetWindowFocus(txt_window_t *window, int focused)
|
|
{
|
|
TXT_SetWidgetFocus(window, focused);
|
|
}
|
|
|
|
txt_window_t *TXT_MessageBox(char *title, char *message, ...)
|
|
{
|
|
txt_window_t *window;
|
|
char buf[256];
|
|
va_list args;
|
|
|
|
va_start(args, message);
|
|
TXT_vsnprintf(buf, sizeof(buf), message, args);
|
|
va_end(args);
|
|
|
|
window = TXT_NewWindow(title);
|
|
TXT_AddWidget(window, TXT_NewLabel(buf));
|
|
|
|
TXT_SetWindowAction(window, TXT_HORIZ_LEFT, NULL);
|
|
TXT_SetWindowAction(window, TXT_HORIZ_CENTER,
|
|
TXT_NewWindowEscapeAction(window));
|
|
TXT_SetWindowAction(window, TXT_HORIZ_RIGHT, NULL);
|
|
|
|
return window;
|
|
}
|
|
|