This repository has been archived on 2023-07-11. You can view files and clone it, but cannot push or open issues or pull requests.
chocolate-doom-wii/textscreen/txt_spinctrl.c

421 lines
9.8 KiB
C
Raw Permalink Normal View History

//
// 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 <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "doomkeys.h"
#include "txt_spinctrl.h"
#include "txt_gui.h"
#include "txt_io.h"
#include "txt_main.h"
#include "txt_window.h"
// Generate the format string to be used for displaying floats
static void FloatFormatString(float step, char *buf, size_t buf_len)
{
int precision;
precision = (int) ceil(-log(step) / log(10));
if (precision > 0)
{
TXT_snprintf(buf, buf_len, "%%.%if", precision);
}
else
{
TXT_StringCopy(buf, "%.1f", buf_len);
}
}
// Number of characters needed to represent a character
static unsigned int IntWidth(int val)
{
char buf[25];
TXT_snprintf(buf, sizeof(buf), "%i", val);
return strlen(buf);
}
static unsigned int FloatWidth(float val, float step)
{
unsigned int precision;
unsigned int result;
// Calculate the width of the int value
result = IntWidth((int) val);
// Add a decimal part if the precision specifies it
precision = (unsigned int) ceil(-log(step) / log(10));
if (precision > 0)
{
result += precision + 1;
}
return result;
}
// Returns the minimum width of the input box
static unsigned int SpinControlWidth(txt_spincontrol_t *spincontrol)
{
unsigned int minw, maxw;
switch (spincontrol->type)
{
case TXT_SPINCONTROL_FLOAT:
minw = FloatWidth(spincontrol->min.f, spincontrol->step.f);
maxw = FloatWidth(spincontrol->max.f, spincontrol->step.f);
break;
default:
case TXT_SPINCONTROL_INT:
minw = IntWidth(spincontrol->min.i);
maxw = IntWidth(spincontrol->max.i);
break;
}
// Choose the wider of the two values. Add one so that there is always
// space for the cursor when editing.
if (minw > maxw)
{
return minw;
}
else
{
return maxw;
}
}
static void TXT_SpinControlSizeCalc(TXT_UNCAST_ARG(spincontrol))
{
TXT_CAST_ARG(txt_spincontrol_t, spincontrol);
spincontrol->widget.w = SpinControlWidth(spincontrol) + 5;
spincontrol->widget.h = 1;
}
static void SetBuffer(txt_spincontrol_t *spincontrol)
{
char format[25];
switch (spincontrol->type)
{
case TXT_SPINCONTROL_INT:
TXT_snprintf(spincontrol->buffer, spincontrol->buffer_len,
"%i", spincontrol->value->i);
break;
case TXT_SPINCONTROL_FLOAT:
FloatFormatString(spincontrol->step.f, format, sizeof(format));
TXT_snprintf(spincontrol->buffer, spincontrol->buffer_len,
format, spincontrol->value->f);
break;
}
}
static void TXT_SpinControlDrawer(TXT_UNCAST_ARG(spincontrol))
{
TXT_CAST_ARG(txt_spincontrol_t, spincontrol);
unsigned int i;
unsigned int padding;
txt_saved_colors_t colors;
int focused;
focused = spincontrol->widget.focused;
TXT_SaveColors(&colors);
TXT_FGColor(TXT_COLOR_BRIGHT_CYAN);
TXT_DrawString("\x1b ");
TXT_RestoreColors(&colors);
// Choose background color
if (focused && spincontrol->editing)
{
TXT_BGColor(TXT_COLOR_BLACK, 0);
}
else
{
TXT_SetWidgetBG(spincontrol);
}
if (!spincontrol->editing)
{
SetBuffer(spincontrol);
}
i = 0;
padding = spincontrol->widget.w - strlen(spincontrol->buffer) - 4;
while (i < padding)
{
TXT_DrawString(" ");
++i;
}
TXT_DrawString(spincontrol->buffer);
i += strlen(spincontrol->buffer);
while (i < spincontrol->widget.w - 4)
{
TXT_DrawString(" ");
++i;
}
TXT_RestoreColors(&colors);
TXT_FGColor(TXT_COLOR_BRIGHT_CYAN);
TXT_DrawString(" \x1a");
}
static void TXT_SpinControlDestructor(TXT_UNCAST_ARG(spincontrol))
{
TXT_CAST_ARG(txt_spincontrol_t, spincontrol);
free(spincontrol->buffer);
}
static void AddCharacter(txt_spincontrol_t *spincontrol, int key)
{
if (strlen(spincontrol->buffer) < SpinControlWidth(spincontrol))
{
spincontrol->buffer[strlen(spincontrol->buffer) + 1] = '\0';
spincontrol->buffer[strlen(spincontrol->buffer)] = key;
}
}
static void Backspace(txt_spincontrol_t *spincontrol)
{
if (strlen(spincontrol->buffer) > 0)
{
spincontrol->buffer[strlen(spincontrol->buffer) - 1] = '\0';
}
}
static void EnforceLimits(txt_spincontrol_t *spincontrol)
{
switch (spincontrol->type)
{
case TXT_SPINCONTROL_INT:
if (spincontrol->value->i > spincontrol->max.i)
spincontrol->value->i = spincontrol->max.i;
else if (spincontrol->value->i < spincontrol->min.i)
spincontrol->value->i = spincontrol->min.i;
break;
case TXT_SPINCONTROL_FLOAT:
if (spincontrol->value->f > spincontrol->max.f)
spincontrol->value->f = spincontrol->max.f;
else if (spincontrol->value->f < spincontrol->min.f)
spincontrol->value->f = spincontrol->min.f;
break;
}
}
static void FinishEditing(txt_spincontrol_t *spincontrol)
{
switch (spincontrol->type)
{
case TXT_SPINCONTROL_INT:
spincontrol->value->i = atoi(spincontrol->buffer);
break;
case TXT_SPINCONTROL_FLOAT:
spincontrol->value->f = (float) atof(spincontrol->buffer);
break;
}
spincontrol->editing = 0;
EnforceLimits(spincontrol);
}
static int TXT_SpinControlKeyPress(TXT_UNCAST_ARG(spincontrol), int key)
{
TXT_CAST_ARG(txt_spincontrol_t, spincontrol);
// Enter to enter edit mode
if (spincontrol->editing)
{
if (key == KEY_ENTER)
{
FinishEditing(spincontrol);
return 1;
}
if (key == KEY_ESCAPE)
{
// Abort without saving value
spincontrol->editing = 0;
return 1;
}
if (isdigit(key) || key == '-' || key == '.')
{
AddCharacter(spincontrol, key);
return 1;
}
if (key == KEY_BACKSPACE)
{
Backspace(spincontrol);
return 1;
}
}
else
{
// Non-editing mode
if (key == KEY_ENTER)
{
spincontrol->editing = 1;
TXT_StringCopy(spincontrol->buffer, "", spincontrol->buffer_len);
return 1;
}
if (key == KEY_LEFTARROW)
{
switch (spincontrol->type)
{
case TXT_SPINCONTROL_INT:
spincontrol->value->i -= spincontrol->step.i;
break;
case TXT_SPINCONTROL_FLOAT:
spincontrol->value->f -= spincontrol->step.f;
break;
}
EnforceLimits(spincontrol);
return 1;
}
if (key == KEY_RIGHTARROW)
{
switch (spincontrol->type)
{
case TXT_SPINCONTROL_INT:
spincontrol->value->i += spincontrol->step.i;
break;
case TXT_SPINCONTROL_FLOAT:
spincontrol->value->f += spincontrol->step.f;
break;
}
EnforceLimits(spincontrol);
return 1;
}
}
return 0;
}
static void TXT_SpinControlMousePress(TXT_UNCAST_ARG(spincontrol),
int x, int y, int b)
{
TXT_CAST_ARG(txt_spincontrol_t, spincontrol);
unsigned int rel_x;
rel_x = x - spincontrol->widget.x;
if (rel_x < 2)
{
TXT_SpinControlKeyPress(spincontrol, KEY_LEFTARROW);
}
else if (rel_x >= spincontrol->widget.w - 2)
{
TXT_SpinControlKeyPress(spincontrol, KEY_RIGHTARROW);
}
}
static void TXT_SpinControlFocused(TXT_UNCAST_ARG(spincontrol), int focused)
{
TXT_CAST_ARG(txt_spincontrol_t, spincontrol);
FinishEditing(spincontrol);
}
txt_widget_class_t txt_spincontrol_class =
{
TXT_AlwaysSelectable,
TXT_SpinControlSizeCalc,
TXT_SpinControlDrawer,
TXT_SpinControlKeyPress,
TXT_SpinControlDestructor,
TXT_SpinControlMousePress,
NULL,
TXT_SpinControlFocused,
};
static txt_spincontrol_t *TXT_BaseSpinControl(void)
{
txt_spincontrol_t *spincontrol;
spincontrol = malloc(sizeof(txt_spincontrol_t));
TXT_InitWidget(spincontrol, &txt_spincontrol_class);
spincontrol->buffer_len = 25;
spincontrol->buffer = malloc(spincontrol->buffer_len);
TXT_StringCopy(spincontrol->buffer, "", spincontrol->buffer_len);
spincontrol->editing = 0;
return spincontrol;
}
txt_spincontrol_t *TXT_NewSpinControl(int *value, int min, int max)
{
txt_spincontrol_t *spincontrol;
spincontrol = TXT_BaseSpinControl();
spincontrol->type = TXT_SPINCONTROL_INT;
spincontrol->value = (void *) value;
spincontrol->min.i = min;
spincontrol->max.i = max;
spincontrol->step.i = 1;
return spincontrol;
}
txt_spincontrol_t *TXT_NewFloatSpinControl(float *value, float min, float max)
{
txt_spincontrol_t *spincontrol;
spincontrol = TXT_BaseSpinControl();
spincontrol->type = TXT_SPINCONTROL_FLOAT;
spincontrol->value = (void *) value;
spincontrol->min.f = min;
spincontrol->max.f = max;
spincontrol->step.f = 0.1f;
return spincontrol;
}