mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-12-24 10:40:46 +00:00
- added ZDoom's menu system as a base to build a new menu on.
# Conflicts: # source/CMakeLists.txt # source/build/src/engine.cpp
This commit is contained in:
parent
4be63d55d3
commit
81ddf22d5d
28 changed files with 8013 additions and 43 deletions
|
@ -624,6 +624,7 @@ file( GLOB HEADER_FILES
|
||||||
common/filesystem/*.h
|
common/filesystem/*.h
|
||||||
common/music/*.h
|
common/music/*.h
|
||||||
common/dobject/*.h
|
common/dobject/*.h
|
||||||
|
common/menu/*.h
|
||||||
|
|
||||||
build/src/*.h
|
build/src/*.h
|
||||||
thirdparty/include/*.h
|
thirdparty/include/*.h
|
||||||
|
@ -791,6 +792,7 @@ set (PCH_SOURCES
|
||||||
common/utility/m_png.cpp
|
common/utility/m_png.cpp
|
||||||
common/utility/memarena.cpp
|
common/utility/memarena.cpp
|
||||||
common/utility/sc_man.cpp
|
common/utility/sc_man.cpp
|
||||||
|
common/utility/stringtable.cpp
|
||||||
common/utility/stats.cpp
|
common/utility/stats.cpp
|
||||||
|
|
||||||
common/filesystem/filesystem.cpp
|
common/filesystem/filesystem.cpp
|
||||||
|
@ -831,6 +833,16 @@ set (PCH_SOURCES
|
||||||
common/dobject/dobject.cpp
|
common/dobject/dobject.cpp
|
||||||
common/dobject/dobjtype.cpp
|
common/dobject/dobjtype.cpp
|
||||||
|
|
||||||
|
common/menu/joystickmenu.cpp
|
||||||
|
common/menu/listmenu.cpp
|
||||||
|
common/menu/loadsavemenu.cpp
|
||||||
|
common/menu/menu.cpp
|
||||||
|
common/menu/menudef.cpp
|
||||||
|
common/menu/menuinput.cpp
|
||||||
|
common/menu/messagebox.cpp
|
||||||
|
common/menu/optionmenu.cpp
|
||||||
|
common/menu/readthis.cpp
|
||||||
|
|
||||||
common/dobject/zzautozend.cpp #must be last
|
common/dobject/zzautozend.cpp #must be last
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -895,6 +907,7 @@ include_directories(
|
||||||
common/filesystem
|
common/filesystem
|
||||||
common/music
|
common/music
|
||||||
common/dobject
|
common/dobject
|
||||||
|
common/menu
|
||||||
platform
|
platform
|
||||||
|
|
||||||
${CMAKE_BINARY_DIR}/libraries/gdtoa
|
${CMAKE_BINARY_DIR}/libraries/gdtoa
|
||||||
|
@ -1004,6 +1017,7 @@ source_group("Code\\Fonts" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/comm
|
||||||
source_group("Code\\File System" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/filesystem/.+")
|
source_group("Code\\File System" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/filesystem/.+")
|
||||||
source_group("Code\\Music" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/music/.+")
|
source_group("Code\\Music" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/music/.+")
|
||||||
source_group("Code\\DObject" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/dobject/.+")
|
source_group("Code\\DObject" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/dobject/.+")
|
||||||
|
source_group("Code\\Menu" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/menu/.+")
|
||||||
source_group("Utility\\Audiolib" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/audiolib/.+")
|
source_group("Utility\\Audiolib" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/audiolib/.+")
|
||||||
source_group("Utility\\Audiolib Headers" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/audiolib/include/.+")
|
source_group("Utility\\Audiolib Headers" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/audiolib/include/.+")
|
||||||
source_group("Utility\\Audiolib Sources" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/audiolib/src/.+")
|
source_group("Utility\\Audiolib Sources" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/audiolib/src/.+")
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "gamecvars.h"
|
#include "gamecvars.h"
|
||||||
#include "c_console.h"
|
#include "c_console.h"
|
||||||
#include "v_2ddrawer.h"
|
#include "v_2ddrawer.h"
|
||||||
|
#include "v_draw.h"
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
#include "stats.h"
|
#include "stats.h"
|
||||||
|
|
||||||
|
@ -9984,6 +9985,7 @@ int32_t videoSetGameMode(char davidoption, int32_t daupscaledxdim, int32_t daups
|
||||||
}
|
}
|
||||||
xdim = daupscaledxdim/scalefactor;
|
xdim = daupscaledxdim/scalefactor;
|
||||||
ydim = daupscaledydim/scalefactor;
|
ydim = daupscaledydim/scalefactor;
|
||||||
|
V_UpdateModeSize(xdim, ydim);
|
||||||
|
|
||||||
#ifdef USE_OPENGL
|
#ifdef USE_OPENGL
|
||||||
fxdim = (float) xdim;
|
fxdim = (float) xdim;
|
||||||
|
@ -12066,42 +12068,6 @@ void setfirstwall(int16_t sectnum, int16_t newfirstwall)
|
||||||
Xfree(tmpwall);
|
Xfree(tmpwall);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// qsetmodeany
|
|
||||||
//
|
|
||||||
void videoSet2dMode(int32_t daxdim, int32_t daydim)
|
|
||||||
{
|
|
||||||
if (daxdim < 640) daxdim = 640;
|
|
||||||
if (daydim < 480) daydim = 480;
|
|
||||||
|
|
||||||
if (qsetmode != ((daxdim<<16)|(daydim&0xffff)))
|
|
||||||
{
|
|
||||||
g_lastpalettesum = 0;
|
|
||||||
if (videoSetMode(daxdim, daydim, 8, fullscreen) < 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
xdim = xres;
|
|
||||||
ydim = yres;
|
|
||||||
|
|
||||||
#ifdef USE_OPENGL
|
|
||||||
fxdim = (float) xdim;
|
|
||||||
fydim = (float) ydim;
|
|
||||||
|
|
||||||
rendmode = REND_CLASSIC;
|
|
||||||
#endif
|
|
||||||
videoAllocateBuffers();
|
|
||||||
|
|
||||||
ydim16 = ydim - STATUS2DSIZ2;
|
|
||||||
halfxdim16 = xdim >> 1;
|
|
||||||
midydim16 = ydim16 >> 1; // scale(200,ydim,480);
|
|
||||||
|
|
||||||
videoBeginDrawing(); //{{{
|
|
||||||
Bmemset((char *)frameplace, 0, ydim*bytesperline);
|
|
||||||
videoEndDrawing(); //}}}
|
|
||||||
}
|
|
||||||
|
|
||||||
qsetmode = ((daxdim<<16)|(daydim&0xffff));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int32_t printext_checkypos(int32_t ypos, int32_t *yminptr, int32_t *ymaxptr)
|
static int32_t printext_checkypos(int32_t ypos, int32_t *yminptr, int32_t *ymaxptr)
|
||||||
{
|
{
|
||||||
|
|
|
@ -2060,6 +2060,18 @@ int32_t handleevents(void)
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void I_SetMouseCapture()
|
||||||
|
{
|
||||||
|
// Clear out any mouse movement.
|
||||||
|
SDL_GetRelativeMouseState(NULL, NULL);
|
||||||
|
SDL_SetRelativeMouseMode(SDL_TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void I_ReleaseMouseCapture()
|
||||||
|
{
|
||||||
|
SDL_SetRelativeMouseMode(SDL_FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
auto vsnprintfptr = vsnprintf; // This is an inline in Visual Studio but we need an address for it to satisfy the MinGW compiled libraries.
|
auto vsnprintfptr = vsnprintf; // This is an inline in Visual Studio but we need an address for it to satisfy the MinGW compiled libraries.
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
@ -107,6 +107,36 @@ int CleanWidth, CleanHeight;
|
||||||
int CleanXfac_1, CleanYfac_1, CleanWidth_1, CleanHeight_1;
|
int CleanXfac_1, CleanYfac_1, CleanWidth_1, CleanHeight_1;
|
||||||
|
|
||||||
|
|
||||||
|
void V_UpdateModeSize(int width, int height)
|
||||||
|
{
|
||||||
|
// This calculates the menu scale.
|
||||||
|
// The optimal scale will always be to fit a virtual 640 pixel wide display onto the screen.
|
||||||
|
// Exceptions are made for a few ranges where the available virtual width is > 480.
|
||||||
|
|
||||||
|
// This reference size is being used so that on 800x450 (small 16:9) a scale of 2 gets used.
|
||||||
|
|
||||||
|
CleanXfac = std::max(std::min(screen->GetWidth() / 400, screen->GetHeight() / 240), 1);
|
||||||
|
if (CleanXfac >= 4) CleanXfac--; // Otherwise we do not have enough space for the episode/skill menus in some languages.
|
||||||
|
CleanYfac = CleanXfac;
|
||||||
|
CleanWidth = screen->GetWidth() / CleanXfac;
|
||||||
|
CleanHeight = screen->GetHeight() / CleanYfac;
|
||||||
|
|
||||||
|
int w = screen->GetWidth();
|
||||||
|
int factor;
|
||||||
|
if (w < 640) factor = 1;
|
||||||
|
else if (w >= 1024 && w < 1280) factor = 2;
|
||||||
|
else if (w >= 1600 && w < 1920) factor = 3;
|
||||||
|
else factor = w / 640;
|
||||||
|
|
||||||
|
if (w < 1360) factor = 1;
|
||||||
|
else if (w < 1920) factor = 2;
|
||||||
|
else factor = int(factor * 0.7);
|
||||||
|
|
||||||
|
CleanYfac_1 = CleanXfac_1 = factor;// MAX(1, int(factor * 0.7));
|
||||||
|
CleanWidth_1 = width / CleanXfac_1;
|
||||||
|
CleanHeight_1 = height / CleanYfac_1;
|
||||||
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
// Draw parameter parsing
|
// Draw parameter parsing
|
||||||
|
|
|
@ -42,6 +42,7 @@ double AspectPspriteOffset(float aspect);
|
||||||
int AspectMultiplier(float aspect);
|
int AspectMultiplier(float aspect);
|
||||||
bool AspectTallerThanWide(float aspect);
|
bool AspectTallerThanWide(float aspect);
|
||||||
void ScaleWithAspect(int& w, int& h, int Width, int Height);
|
void ScaleWithAspect(int& w, int& h, int Width, int Height);
|
||||||
|
void V_UpdateModeSize(int width, int height);
|
||||||
|
|
||||||
void DrawTexture(F2DDrawer *drawer, FTexture* img, double x, double y, int tags_first, ...);
|
void DrawTexture(F2DDrawer *drawer, FTexture* img, double x, double y, int tags_first, ...);
|
||||||
void DrawChar (F2DDrawer* drawer, FFont *font, int normalcolor, double x, double y, int character, int tag_first, ...);
|
void DrawChar (F2DDrawer* drawer, FFont *font, int normalcolor, double x, double y, int character, int tag_first, ...);
|
||||||
|
|
|
@ -140,6 +140,16 @@ public:
|
||||||
Class = inClass;
|
Class = inClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void* operator new(size_t p)
|
||||||
|
{
|
||||||
|
return malloc(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator delete (void* mem)
|
||||||
|
{
|
||||||
|
free(mem);
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// This form of placement new and delete is for use *only* by PClass's
|
// This form of placement new and delete is for use *only* by PClass's
|
||||||
// CreateNew() method. Do not use them for some other purpose.
|
// CreateNew() method. Do not use them for some other purpose.
|
||||||
|
|
|
@ -42,8 +42,7 @@ struct PClass
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find a type, given its name.
|
// Find a type, given its name.
|
||||||
static const PClass *FindClass (const char *name) { return FindClass (FName (name, true)); }
|
static const PClass* FindClass(const char* name) { FName nm(name, true); return nm == NAME_None ? nullptr : FindClass(nm); }
|
||||||
static const PClass *FindClass (const FString &name) { return FindClass (FName (name, true)); }
|
|
||||||
static const PClass *FindClass (ENamedName name) { return FindClass (FName (name)); }
|
static const PClass *FindClass (ENamedName name) { return FindClass (FName (name)); }
|
||||||
static const PClass *FindClass (FName name);
|
static const PClass *FindClass (FName name);
|
||||||
};
|
};
|
||||||
|
|
|
@ -720,12 +720,10 @@ void V_InitFonts()
|
||||||
NewSmallFont = CreateHexLumpFont2("NewSmallFont", "demolition/newconsolefont.hex");
|
NewSmallFont = CreateHexLumpFont2("NewSmallFont", "demolition/newconsolefont.hex");
|
||||||
CurrentConsoleFont = NewConsoleFont;
|
CurrentConsoleFont = NewConsoleFont;
|
||||||
|
|
||||||
/*
|
ConFont = V_GetFont("ConsoleFont", "confont"); // The con font is needed for the slider graphics
|
||||||
ConFont = V_GetFont("ConsoleFont", "CONFONT");
|
|
||||||
{
|
{
|
||||||
ConFont = SmallFont;
|
ConFont = SmallFont;
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void V_ClearFonts()
|
void V_ClearFonts()
|
||||||
|
|
430
source/common/menu/joystickmenu.cpp
Normal file
430
source/common/menu/joystickmenu.cpp
Normal file
|
@ -0,0 +1,430 @@
|
||||||
|
/*
|
||||||
|
** joystickmenu.cpp
|
||||||
|
** The joystick configuration menus
|
||||||
|
**
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
** Copyright 2010 Christoph Oelckers
|
||||||
|
** All rights reserved.
|
||||||
|
**
|
||||||
|
** Redistribution and use in source and binary forms, with or without
|
||||||
|
** modification, are permitted provided that the following conditions
|
||||||
|
** are met:
|
||||||
|
**
|
||||||
|
** 1. Redistributions of source code must retain the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer.
|
||||||
|
** 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer in the
|
||||||
|
** documentation and/or other materials provided with the distribution.
|
||||||
|
** 3. The name of the author may not be used to endorse or promote products
|
||||||
|
** derived from this software without specific prior written permission.
|
||||||
|
**
|
||||||
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <float.h>
|
||||||
|
|
||||||
|
#include "menu.h"
|
||||||
|
#include "c_dispatch.h"
|
||||||
|
#include "filesystem.h"
|
||||||
|
#include "sc_man.h"
|
||||||
|
#include "v_font.h"
|
||||||
|
#include "c_bind.h"
|
||||||
|
#include "d_event.h"
|
||||||
|
#include "d_gui.h"
|
||||||
|
|
||||||
|
#define NO_IMP
|
||||||
|
#include "optionmenuitems.h"
|
||||||
|
|
||||||
|
#if 0 // This requires the entire ZDoom backend to work.
|
||||||
|
|
||||||
|
static TArray<IJoystickConfig *> Joysticks;
|
||||||
|
IJoystickConfig *SELECTED_JOYSTICK;
|
||||||
|
|
||||||
|
FOptionMenuDescriptor *UpdateJoystickConfigMenu(IJoystickConfig *joy);
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
class FOptionMenuSliderJoySensitivity : public FOptionMenuSliderBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FOptionMenuSliderJoySensitivity(const char *label, double min, double max, double step, int showval)
|
||||||
|
: FOptionMenuSliderBase(label, min, max, step, showval)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
double GetSliderValue()
|
||||||
|
{
|
||||||
|
return SELECTED_JOYSTICK->GetSensitivity();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetSliderValue(double val)
|
||||||
|
{
|
||||||
|
SELECTED_JOYSTICK->SetSensitivity(float(val));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
class FOptionMenuSliderJoyScale : public FOptionMenuSliderBase
|
||||||
|
{
|
||||||
|
int mAxis;
|
||||||
|
int mNeg;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FOptionMenuSliderJoyScale(const char *label, int axis, double min, double max, double step, int showval)
|
||||||
|
: FOptionMenuSliderBase(label, min, max, step, showval)
|
||||||
|
{
|
||||||
|
mAxis = axis;
|
||||||
|
mNeg = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
double GetSliderValue()
|
||||||
|
{
|
||||||
|
double d = SELECTED_JOYSTICK->GetAxisScale(mAxis);
|
||||||
|
mNeg = d < 0? -1:1;
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetSliderValue(double val)
|
||||||
|
{
|
||||||
|
SELECTED_JOYSTICK->SetAxisScale(mAxis, float(val * mNeg));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
class FOptionMenuSliderJoyDeadZone : public FOptionMenuSliderBase
|
||||||
|
{
|
||||||
|
int mAxis;
|
||||||
|
int mNeg;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FOptionMenuSliderJoyDeadZone(const char *label, int axis, double min, double max, double step, int showval)
|
||||||
|
: FOptionMenuSliderBase(label, min, max, step, showval)
|
||||||
|
{
|
||||||
|
mAxis = axis;
|
||||||
|
mNeg = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
double GetSliderValue()
|
||||||
|
{
|
||||||
|
double d = SELECTED_JOYSTICK->GetAxisDeadZone(mAxis);
|
||||||
|
mNeg = d < 0? -1:1;
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetSliderValue(double val)
|
||||||
|
{
|
||||||
|
SELECTED_JOYSTICK->SetAxisDeadZone(mAxis, float(val * mNeg));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
class FOptionMenuItemJoyMap : public FOptionMenuItemOptionBase
|
||||||
|
{
|
||||||
|
int mAxis;
|
||||||
|
public:
|
||||||
|
|
||||||
|
FOptionMenuItemJoyMap(const char *label, int axis, const char *values, int center)
|
||||||
|
: FOptionMenuItemOptionBase(label, "none", values, NULL, center)
|
||||||
|
{
|
||||||
|
mAxis = axis;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetSelection()
|
||||||
|
{
|
||||||
|
double f = SELECTED_JOYSTICK->GetAxisMap(mAxis);
|
||||||
|
FOptionValues **opt = OptionValues.CheckKey(mValues);
|
||||||
|
if (opt != NULL && *opt != NULL)
|
||||||
|
{
|
||||||
|
// Map from joystick axis to menu selection.
|
||||||
|
for(unsigned i = 0; i < (*opt)->mValues.Size(); i++)
|
||||||
|
{
|
||||||
|
if (fabs(f - (*opt)->mValues[i].Value) < FLT_EPSILON)
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetSelection(int selection)
|
||||||
|
{
|
||||||
|
FOptionValues **opt = OptionValues.CheckKey(mValues);
|
||||||
|
// Map from menu selection to joystick axis.
|
||||||
|
if (opt == NULL || *opt == NULL || (unsigned)selection >= (*opt)->mValues.Size())
|
||||||
|
{
|
||||||
|
selection = JOYAXIS_None;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
selection = (int)(*opt)->mValues[selection].Value;
|
||||||
|
}
|
||||||
|
SELECTED_JOYSTICK->SetAxisMap(mAxis, (EJoyAxis)selection);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
class FOptionMenuItemInverter : public FOptionMenuItemOptionBase
|
||||||
|
{
|
||||||
|
int mAxis;
|
||||||
|
public:
|
||||||
|
|
||||||
|
FOptionMenuItemInverter(const char *label, int axis, int center)
|
||||||
|
: FOptionMenuItemOptionBase(label, "none", "YesNo", NULL, center)
|
||||||
|
{
|
||||||
|
mAxis = axis;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetSelection()
|
||||||
|
{
|
||||||
|
float f = SELECTED_JOYSTICK->GetAxisScale(mAxis);
|
||||||
|
return f > 0? 0:1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetSelection(int Selection)
|
||||||
|
{
|
||||||
|
float f = fabs(SELECTED_JOYSTICK->GetAxisScale(mAxis));
|
||||||
|
if (Selection) f*=-1;
|
||||||
|
SELECTED_JOYSTICK->SetAxisScale(mAxis, f);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DJoystickConfigMenu : public DOptionMenu
|
||||||
|
{
|
||||||
|
DECLARE_CLASS(DJoystickConfigMenu, DOptionMenu)
|
||||||
|
};
|
||||||
|
|
||||||
|
IMPLEMENT_CLASS(DJoystickConfigMenu)
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// Executes a CCMD, action is a CCMD name
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
class FOptionMenuItemJoyConfigMenu : public FOptionMenuItemSubmenu
|
||||||
|
{
|
||||||
|
IJoystickConfig *mJoy;
|
||||||
|
public:
|
||||||
|
FOptionMenuItemJoyConfigMenu(const char *label, IJoystickConfig *joy)
|
||||||
|
: FOptionMenuItemSubmenu(label, "JoystickConfigMenu")
|
||||||
|
{
|
||||||
|
mJoy = joy;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Activate()
|
||||||
|
{
|
||||||
|
UpdateJoystickConfigMenu(mJoy);
|
||||||
|
return FOptionMenuItemSubmenu::Activate();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*=======================================
|
||||||
|
*
|
||||||
|
* Joystick Menu
|
||||||
|
*
|
||||||
|
*=======================================*/
|
||||||
|
|
||||||
|
FOptionMenuDescriptor *UpdateJoystickConfigMenu(IJoystickConfig *joy)
|
||||||
|
{
|
||||||
|
FMenuDescriptor **desc = MenuDescriptors.CheckKey(NAME_JoystickConfigMenu);
|
||||||
|
if (desc != NULL && (*desc)->mType == MDESC_OptionsMenu)
|
||||||
|
{
|
||||||
|
FOptionMenuDescriptor *opt = (FOptionMenuDescriptor *)*desc;
|
||||||
|
FOptionMenuItem *it;
|
||||||
|
for(unsigned i=0;i<opt->mItems.Size();i++)
|
||||||
|
{
|
||||||
|
delete opt->mItems[i];
|
||||||
|
opt->mItems.Clear();
|
||||||
|
}
|
||||||
|
if (joy == NULL)
|
||||||
|
{
|
||||||
|
opt->mTitle = "Configure Controller";
|
||||||
|
it = new FOptionMenuItemStaticText("Invalid controller specified for menu", false);
|
||||||
|
opt->mItems.Push(it);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
opt->mTitle.Format("Configure %s", joy->GetName().GetChars());
|
||||||
|
|
||||||
|
SELECTED_JOYSTICK = joy;
|
||||||
|
|
||||||
|
it = new FOptionMenuSliderJoySensitivity("Overall sensitivity", 0, 2, 0.1, 3);
|
||||||
|
opt->mItems.Push(it);
|
||||||
|
it = new FOptionMenuItemStaticText(" ", false);
|
||||||
|
opt->mItems.Push(it);
|
||||||
|
|
||||||
|
if (joy->GetNumAxes() > 0)
|
||||||
|
{
|
||||||
|
it = new FOptionMenuItemStaticText("Axis Configuration", true);
|
||||||
|
opt->mItems.Push(it);
|
||||||
|
|
||||||
|
for (int i = 0; i < joy->GetNumAxes(); ++i)
|
||||||
|
{
|
||||||
|
it = new FOptionMenuItemStaticText(" ", false);
|
||||||
|
opt->mItems.Push(it);
|
||||||
|
|
||||||
|
it = new FOptionMenuItemJoyMap(joy->GetAxisName(i), i, "JoyAxisMapNames", false);
|
||||||
|
opt->mItems.Push(it);
|
||||||
|
it = new FOptionMenuSliderJoyScale("Overall sensitivity", i, 0, 4, 0.1, 3);
|
||||||
|
opt->mItems.Push(it);
|
||||||
|
it = new FOptionMenuItemInverter("Invert", i, false);
|
||||||
|
opt->mItems.Push(it);
|
||||||
|
it = new FOptionMenuSliderJoyDeadZone("Dead Zone", i, 0, 0.9, 0.05, 3);
|
||||||
|
opt->mItems.Push(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
it = new FOptionMenuItemStaticText("No configurable axes", false);
|
||||||
|
opt->mItems.Push(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
opt->mScrollPos = 0;
|
||||||
|
opt->mSelectedItem = -1;
|
||||||
|
opt->mIndent = 0;
|
||||||
|
opt->mPosition = -25;
|
||||||
|
opt->CalcIndent();
|
||||||
|
return opt;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void UpdateJoystickMenu(IJoystickConfig *selected)
|
||||||
|
{
|
||||||
|
FMenuDescriptor **desc = MenuDescriptors.CheckKey(NAME_JoystickOptions);
|
||||||
|
if (desc != NULL && (*desc)->mType == MDESC_OptionsMenu)
|
||||||
|
{
|
||||||
|
FOptionMenuDescriptor *opt = (FOptionMenuDescriptor *)*desc;
|
||||||
|
FOptionMenuItem *it;
|
||||||
|
for(unsigned i=0;i<opt->mItems.Size();i++)
|
||||||
|
{
|
||||||
|
delete opt->mItems[i];
|
||||||
|
}
|
||||||
|
opt->mItems.Clear();
|
||||||
|
|
||||||
|
int i;
|
||||||
|
int itemnum = -1;
|
||||||
|
|
||||||
|
I_GetJoysticks(Joysticks);
|
||||||
|
if ((unsigned)itemnum >= Joysticks.Size())
|
||||||
|
{
|
||||||
|
itemnum = Joysticks.Size() - 1;
|
||||||
|
}
|
||||||
|
if (selected != NULL)
|
||||||
|
{
|
||||||
|
for (i = 0; (unsigned)i < Joysticks.Size(); ++i)
|
||||||
|
{
|
||||||
|
if (Joysticks[i] == selected)
|
||||||
|
{
|
||||||
|
itemnum = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Todo: Block joystick for changing this one.
|
||||||
|
it = new FOptionMenuItemOption("Enable controller support", "use_joystick", "YesNo", NULL, false);
|
||||||
|
opt->mItems.Push(it);
|
||||||
|
#ifdef _WIN32
|
||||||
|
it = new FOptionMenuItemOption("Enable DirectInput controllers", "joy_dinput", "YesNo", NULL, false);
|
||||||
|
opt->mItems.Push(it);
|
||||||
|
it = new FOptionMenuItemOption("Enable XInput controllers", "joy_xinput", "YesNo", NULL, false);
|
||||||
|
opt->mItems.Push(it);
|
||||||
|
it = new FOptionMenuItemOption("Enable raw PlayStation 2 adapters", "joy_ps2raw", "YesNo", NULL, false);
|
||||||
|
opt->mItems.Push(it);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
it = new FOptionMenuItemStaticText(" ", false);
|
||||||
|
opt->mItems.Push(it);
|
||||||
|
|
||||||
|
if (Joysticks.Size() == 0)
|
||||||
|
{
|
||||||
|
it = new FOptionMenuItemStaticText("No controllers detected", false);
|
||||||
|
opt->mItems.Push(it);
|
||||||
|
if (!use_joystick)
|
||||||
|
{
|
||||||
|
it = new FOptionMenuItemStaticText("Controller support must be", false);
|
||||||
|
opt->mItems.Push(it);
|
||||||
|
it = new FOptionMenuItemStaticText("enabled to detect any", false);
|
||||||
|
opt->mItems.Push(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
it = new FOptionMenuItemStaticText("Configure controllers:", false);
|
||||||
|
opt->mItems.Push(it);
|
||||||
|
|
||||||
|
for (int i = 0; i < (int)Joysticks.Size(); ++i)
|
||||||
|
{
|
||||||
|
it = new FOptionMenuItemJoyConfigMenu(Joysticks[i]->GetName(), Joysticks[i]);
|
||||||
|
opt->mItems.Push(it);
|
||||||
|
if (i == itemnum) opt->mSelectedItem = opt->mItems.Size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (opt->mSelectedItem >= (int)opt->mItems.Size())
|
||||||
|
{
|
||||||
|
opt->mSelectedItem = opt->mItems.Size() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
opt->CalcIndent();
|
||||||
|
|
||||||
|
// If the joystick config menu is open, close it if the device it's
|
||||||
|
// open for is gone.
|
||||||
|
for (i = 0; (unsigned)i < Joysticks.Size(); ++i)
|
||||||
|
{
|
||||||
|
if (Joysticks[i] == SELECTED_JOYSTICK)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i == (int)Joysticks.Size())
|
||||||
|
{
|
||||||
|
SELECTED_JOYSTICK = NULL;
|
||||||
|
if (DMenu::CurrentMenu != NULL && DMenu::CurrentMenu->IsKindOf(RUNTIME_CLASS(DJoystickConfigMenu)))
|
||||||
|
{
|
||||||
|
DMenu::CurrentMenu->Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
569
source/common/menu/listmenu.cpp
Normal file
569
source/common/menu/listmenu.cpp
Normal file
|
@ -0,0 +1,569 @@
|
||||||
|
/*
|
||||||
|
** listmenu.cpp
|
||||||
|
** A simple menu consisting of a list of items
|
||||||
|
**
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
** Copyright 2010 Christoph Oelckers
|
||||||
|
** All rights reserved.
|
||||||
|
**
|
||||||
|
** Redistribution and use in source and binary forms, with or without
|
||||||
|
** modification, are permitted provided that the following conditions
|
||||||
|
** are met:
|
||||||
|
**
|
||||||
|
** 1. Redistributions of source code must retain the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer.
|
||||||
|
** 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer in the
|
||||||
|
** documentation and/or other materials provided with the distribution.
|
||||||
|
** 3. The name of the author may not be used to endorse or promote products
|
||||||
|
** derived from this software without specific prior written permission.
|
||||||
|
**
|
||||||
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "v_font.h"
|
||||||
|
#include "cmdlib.h"
|
||||||
|
#include "gstrings.h"
|
||||||
|
#include "d_gui.h"
|
||||||
|
#include "d_event.h"
|
||||||
|
#include "menu.h"
|
||||||
|
#include "v_draw.h"
|
||||||
|
|
||||||
|
IMPLEMENT_CLASS(DListMenu)
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
DListMenu::DListMenu(DMenu *parent, FListMenuDescriptor *desc)
|
||||||
|
: DMenu(parent)
|
||||||
|
{
|
||||||
|
mDesc = NULL;
|
||||||
|
if (desc != NULL) Init(parent, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void DListMenu::Init(DMenu *parent, FListMenuDescriptor *desc)
|
||||||
|
{
|
||||||
|
mParentMenu = parent;
|
||||||
|
mDesc = desc;
|
||||||
|
if (desc->mCenter)
|
||||||
|
{
|
||||||
|
int center = 160;
|
||||||
|
for(unsigned i=0;i<mDesc->mItems.Size(); i++)
|
||||||
|
{
|
||||||
|
int xpos = mDesc->mItems[i]->GetX();
|
||||||
|
int width = mDesc->mItems[i]->GetWidth();
|
||||||
|
int curx = mDesc->mSelectOfsX;
|
||||||
|
|
||||||
|
if (width > 0 && mDesc->mItems[i]->Selectable())
|
||||||
|
{
|
||||||
|
int left = 160 - (width - curx) / 2 - curx;
|
||||||
|
if (left < center) center = left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(unsigned i=0;i<mDesc->mItems.Size(); i++)
|
||||||
|
{
|
||||||
|
int width = mDesc->mItems[i]->GetWidth();
|
||||||
|
|
||||||
|
if (width > 0)
|
||||||
|
{
|
||||||
|
mDesc->mItems[i]->SetX(center);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
FListMenuItem *DListMenu::GetItem(FName name)
|
||||||
|
{
|
||||||
|
for(unsigned i=0;i<mDesc->mItems.Size(); i++)
|
||||||
|
{
|
||||||
|
FName nm = mDesc->mItems[i]->GetAction(NULL);
|
||||||
|
if (nm == name) return mDesc->mItems[i];
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
bool DListMenu::Responder (event_t *ev)
|
||||||
|
{
|
||||||
|
if (ev->type == EV_GUI_Event)
|
||||||
|
{
|
||||||
|
if (ev->subtype == EV_GUI_KeyDown)
|
||||||
|
{
|
||||||
|
int ch = tolower (ev->data1);
|
||||||
|
|
||||||
|
for(unsigned i = mDesc->mSelectedItem + 1; i < mDesc->mItems.Size(); i++)
|
||||||
|
{
|
||||||
|
if (mDesc->mItems[i]->CheckHotkey(ch))
|
||||||
|
{
|
||||||
|
mDesc->mSelectedItem = i;
|
||||||
|
//S_Sound(CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(int i = 0; i < mDesc->mSelectedItem; i++)
|
||||||
|
{
|
||||||
|
if (mDesc->mItems[i]->CheckHotkey(ch))
|
||||||
|
{
|
||||||
|
mDesc->mSelectedItem = i;
|
||||||
|
//S_Sound(CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Super::Responder(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
bool DListMenu::MenuEvent (int mkey, bool fromcontroller)
|
||||||
|
{
|
||||||
|
int startedAt = mDesc->mSelectedItem;
|
||||||
|
|
||||||
|
switch (mkey)
|
||||||
|
{
|
||||||
|
case MKEY_Up:
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (--mDesc->mSelectedItem < 0) mDesc->mSelectedItem = mDesc->mItems.Size()-1;
|
||||||
|
}
|
||||||
|
while (!mDesc->mItems[mDesc->mSelectedItem]->Selectable() && mDesc->mSelectedItem != startedAt);
|
||||||
|
//S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MKEY_Down:
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (++mDesc->mSelectedItem >= (int)mDesc->mItems.Size()) mDesc->mSelectedItem = 0;
|
||||||
|
}
|
||||||
|
while (!mDesc->mItems[mDesc->mSelectedItem]->Selectable() && mDesc->mSelectedItem != startedAt);
|
||||||
|
//S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MKEY_Enter:
|
||||||
|
if (mDesc->mSelectedItem >= 0 && mDesc->mItems[mDesc->mSelectedItem]->Activate())
|
||||||
|
{
|
||||||
|
//S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return Super::MenuEvent(mkey, fromcontroller);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
bool DListMenu::MouseEvent(int type, int x, int y)
|
||||||
|
{
|
||||||
|
int sel = -1;
|
||||||
|
|
||||||
|
// convert x/y from screen to virtual coordinates, according to CleanX/Yfac use in DrawTexture
|
||||||
|
x = ((x - (screen->GetWidth() / 2)) / CleanXfac) + 160;
|
||||||
|
y = ((y - (screen->GetHeight() / 2)) / CleanYfac) + 100;
|
||||||
|
|
||||||
|
if (mFocusControl != NULL)
|
||||||
|
{
|
||||||
|
mFocusControl->MouseEvent(type, x, y);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((mDesc->mWLeft <= 0 || x > mDesc->mWLeft) &&
|
||||||
|
(mDesc->mWRight <= 0 || x < mDesc->mWRight))
|
||||||
|
{
|
||||||
|
for(unsigned i=0;i<mDesc->mItems.Size(); i++)
|
||||||
|
{
|
||||||
|
if (mDesc->mItems[i]->CheckCoordinate(x, y))
|
||||||
|
{
|
||||||
|
if ((int)i != mDesc->mSelectedItem)
|
||||||
|
{
|
||||||
|
//S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
|
||||||
|
}
|
||||||
|
mDesc->mSelectedItem = i;
|
||||||
|
mDesc->mItems[i]->MouseEvent(type, x, y);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mDesc->mSelectedItem = -1;
|
||||||
|
return Super::MouseEvent(type, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void DListMenu::Ticker ()
|
||||||
|
{
|
||||||
|
Super::Ticker();
|
||||||
|
for(unsigned i=0;i<mDesc->mItems.Size(); i++)
|
||||||
|
{
|
||||||
|
mDesc->mItems[i]->Ticker();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void DListMenu::Drawer ()
|
||||||
|
{
|
||||||
|
for(unsigned i=0;i<mDesc->mItems.Size(); i++)
|
||||||
|
{
|
||||||
|
if (mDesc->mItems[i]->mEnabled) mDesc->mItems[i]->Drawer(mDesc->mSelectedItem == (int)i);
|
||||||
|
}
|
||||||
|
if (mDesc->mSelectedItem >= 0 && mDesc->mSelectedItem < (int)mDesc->mItems.Size())
|
||||||
|
mDesc->mItems[mDesc->mSelectedItem]->DrawSelector(mDesc->mSelectOfsX, mDesc->mSelectOfsY, mDesc->mSelector);
|
||||||
|
Super::Drawer();
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// base class for menu items
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
FListMenuItem::~FListMenuItem()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FListMenuItem::CheckCoordinate(int x, int y)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FListMenuItem::Ticker()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void FListMenuItem::Drawer(bool selected)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FListMenuItem::Selectable()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FListMenuItem::DrawSelector(int xofs, int yofs, FTexture *tex)
|
||||||
|
{
|
||||||
|
if (!tex)
|
||||||
|
{
|
||||||
|
if ((DMenu::MenuTime%8) < 6)
|
||||||
|
{
|
||||||
|
DrawText(&twod, ConFont, OptionSettings.mFontColorSelection,
|
||||||
|
(mXpos + xofs - 160) * CleanXfac + screen->GetWidth() / 2,
|
||||||
|
(mYpos + yofs - 100) * CleanYfac + screen->GetHeight() / 2,
|
||||||
|
"\xd",
|
||||||
|
DTA_CellX, 8 * CleanXfac,
|
||||||
|
DTA_CellY, 8 * CleanYfac,
|
||||||
|
TAG_DONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DrawTexture (&twod, tex, mXpos + xofs, mYpos + yofs, DTA_Clean, true, TAG_DONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FListMenuItem::Activate()
|
||||||
|
{
|
||||||
|
return false; // cannot be activated
|
||||||
|
}
|
||||||
|
|
||||||
|
FName FListMenuItem::GetAction(int *pparam)
|
||||||
|
{
|
||||||
|
return mAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FListMenuItem::SetString(int i, const char *s)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FListMenuItem::GetString(int i, char *s, int len)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FListMenuItem::SetValue(int i, int value)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FListMenuItem::GetValue(int i, int *pvalue)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FListMenuItem::Enable(bool on)
|
||||||
|
{
|
||||||
|
mEnabled = on;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FListMenuItem::MenuEvent(int mkey, bool fromcontroller)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FListMenuItem::MouseEvent(int type, int x, int y)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FListMenuItem::CheckHotkey(int c)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FListMenuItem::GetWidth()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// static patch
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
FListMenuItemStaticPatch::FListMenuItemStaticPatch(int x, int y, FTexture *patch, bool centered)
|
||||||
|
: FListMenuItem(x, y)
|
||||||
|
{
|
||||||
|
mTexture = patch;
|
||||||
|
mCentered = centered;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FListMenuItemStaticPatch::Drawer(bool selected)
|
||||||
|
{
|
||||||
|
if (!mTexture)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int x = mXpos;
|
||||||
|
FTexture *tex = mTexture;
|
||||||
|
if (mYpos >= 0)
|
||||||
|
{
|
||||||
|
if (mCentered) x -= tex->GetWidth()/2;
|
||||||
|
DrawTexture (&twod, tex, x, mYpos, DTA_Clean, true, TAG_DONE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int x = (mXpos - 160) * CleanXfac + (screen->GetWidth()>>1);
|
||||||
|
if (mCentered) x -= (tex->GetWidth()*CleanXfac)/2;
|
||||||
|
DrawTexture (&twod, tex, x, -mYpos*CleanYfac, DTA_CleanNoMove, true, TAG_DONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// static text
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
FListMenuItemStaticText::FListMenuItemStaticText(int x, int y, const char *text, FFont *font, EColorRange color, bool centered)
|
||||||
|
: FListMenuItem(x, y)
|
||||||
|
{
|
||||||
|
mText = text;
|
||||||
|
mFont = font;
|
||||||
|
mColor = color;
|
||||||
|
mCentered = centered;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FListMenuItemStaticText::Drawer(bool selected)
|
||||||
|
{
|
||||||
|
const char *text = mText;
|
||||||
|
if (text != NULL)
|
||||||
|
{
|
||||||
|
if (*text == '$') text = GStrings(text+1);
|
||||||
|
if (mYpos >= 0)
|
||||||
|
{
|
||||||
|
int x = mXpos;
|
||||||
|
if (mCentered) x -= mFont->StringWidth(text)/2;
|
||||||
|
DrawText(&twod, mFont, mColor, x, mYpos, text, DTA_Clean, true, TAG_DONE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int x = (mXpos - 160) * CleanXfac + (screen->GetWidth()>>1);
|
||||||
|
if (mCentered) x -= (mFont->StringWidth(text)*CleanXfac)/2;
|
||||||
|
DrawText (&twod, mFont, mColor, x, -mYpos*CleanYfac, text, DTA_CleanNoMove, true, TAG_DONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FListMenuItemStaticText::~FListMenuItemStaticText()
|
||||||
|
{
|
||||||
|
if (mText != NULL) delete [] mText;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// base class for selectable items
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
FListMenuItemSelectable::FListMenuItemSelectable(int x, int y, int height, FName action, int param)
|
||||||
|
: FListMenuItem(x, y, action)
|
||||||
|
{
|
||||||
|
mHeight = height;
|
||||||
|
mParam = param;
|
||||||
|
mHotkey = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FListMenuItemSelectable::CheckCoordinate(int x, int y)
|
||||||
|
{
|
||||||
|
return mEnabled && y >= mYpos && y < mYpos + mHeight; // no x check here
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FListMenuItemSelectable::Selectable()
|
||||||
|
{
|
||||||
|
return mEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FListMenuItemSelectable::Activate()
|
||||||
|
{
|
||||||
|
M_SetMenu(mAction, mParam);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
FName FListMenuItemSelectable::GetAction(int *pparam)
|
||||||
|
{
|
||||||
|
if (pparam != NULL) *pparam = mParam;
|
||||||
|
return mAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FListMenuItemSelectable::CheckHotkey(int c)
|
||||||
|
{
|
||||||
|
return c == tolower(mHotkey);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FListMenuItemSelectable::MouseEvent(int type, int x, int y)
|
||||||
|
{
|
||||||
|
if (type == DMenu::MOUSE_Release)
|
||||||
|
{
|
||||||
|
if (NULL != DMenu::CurrentMenu && DMenu::CurrentMenu->MenuEvent(MKEY_Enter, true))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// text item
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
FListMenuItemText::FListMenuItemText(int x, int y, int height, int hotkey, const char *text, FFont *font, EColorRange color, EColorRange color2, FName child, int param)
|
||||||
|
: FListMenuItemSelectable(x, y, height, child, param)
|
||||||
|
{
|
||||||
|
mText = text;
|
||||||
|
mFont = font;
|
||||||
|
mColor = color;
|
||||||
|
mColorSelected = color2;
|
||||||
|
mHotkey = hotkey;
|
||||||
|
}
|
||||||
|
|
||||||
|
FListMenuItemText::~FListMenuItemText()
|
||||||
|
{
|
||||||
|
if (mText != NULL)
|
||||||
|
{
|
||||||
|
delete [] mText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FListMenuItemText::Drawer(bool selected)
|
||||||
|
{
|
||||||
|
const char *text = mText;
|
||||||
|
if (text != NULL)
|
||||||
|
{
|
||||||
|
if (*text == '$') text = GStrings(text+1);
|
||||||
|
DrawText(&twod, mFont, selected ? mColorSelected : mColor, mXpos, mYpos, text, DTA_Clean, true, TAG_DONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int FListMenuItemText::GetWidth()
|
||||||
|
{
|
||||||
|
const char *text = mText;
|
||||||
|
if (text != NULL)
|
||||||
|
{
|
||||||
|
if (*text == '$') text = GStrings(text+1);
|
||||||
|
return mFont->StringWidth(text);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// patch item
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
FListMenuItemPatch::FListMenuItemPatch(int x, int y, int height, int hotkey, FTexture *patch, FName child, int param)
|
||||||
|
: FListMenuItemSelectable(x, y, height, child, param)
|
||||||
|
{
|
||||||
|
mHotkey = hotkey;
|
||||||
|
mTexture = patch;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FListMenuItemPatch::Drawer(bool selected)
|
||||||
|
{
|
||||||
|
DrawTexture (&twod, mTexture, mXpos, mYpos, DTA_Clean, true, TAG_DONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int FListMenuItemPatch::GetWidth()
|
||||||
|
{
|
||||||
|
return mTexture
|
||||||
|
? mTexture->GetWidth()
|
||||||
|
: 0;
|
||||||
|
}
|
||||||
|
|
958
source/common/menu/loadsavemenu.cpp
Normal file
958
source/common/menu/loadsavemenu.cpp
Normal file
|
@ -0,0 +1,958 @@
|
||||||
|
/*
|
||||||
|
** loadsavemenu.cpp
|
||||||
|
** The load game and save game menus
|
||||||
|
**
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
** Copyright 2001-2010 Randy Heit
|
||||||
|
** Copyright 2010 Christoph Oelckers
|
||||||
|
** All rights reserved.
|
||||||
|
**
|
||||||
|
** Redistribution and use in source and binary forms, with or without
|
||||||
|
** modification, are permitted provided that the following conditions
|
||||||
|
** are met:
|
||||||
|
**
|
||||||
|
** 1. Redistributions of source code must retain the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer.
|
||||||
|
** 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer in the
|
||||||
|
** documentation and/or other materials provided with the distribution.
|
||||||
|
** 3. The name of the author may not be used to endorse or promote products
|
||||||
|
** derived from this software without specific prior written permission.
|
||||||
|
**
|
||||||
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "menu/menu.h"
|
||||||
|
#include "version.h"
|
||||||
|
#include "m_png.h"
|
||||||
|
#include "filesystem.h"
|
||||||
|
#include "v_text.h"
|
||||||
|
#include "d_event.h"
|
||||||
|
#include "gstrings.h"
|
||||||
|
#include "d_gui.h"
|
||||||
|
#include "v_draw.h"
|
||||||
|
#include "../../platform/win32/i_findfile.h" // This is a temporary direct path. Needs to be fixed when stuff gets cleaned up.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class DLoadSaveMenu : public DListMenu
|
||||||
|
{
|
||||||
|
DECLARE_CLASS(DLoadSaveMenu, DListMenu)
|
||||||
|
friend void ClearSaveGames();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static TArray<FSaveGameNode*> SaveGames;
|
||||||
|
static int LastSaved;
|
||||||
|
static int LastAccessed;
|
||||||
|
|
||||||
|
int Selected;
|
||||||
|
int TopItem;
|
||||||
|
|
||||||
|
|
||||||
|
int savepicLeft;
|
||||||
|
int savepicTop;
|
||||||
|
int savepicWidth;
|
||||||
|
int savepicHeight;
|
||||||
|
|
||||||
|
int rowHeight;
|
||||||
|
int listboxLeft;
|
||||||
|
int listboxTop;
|
||||||
|
int listboxWidth;
|
||||||
|
|
||||||
|
int listboxRows;
|
||||||
|
int listboxHeight;
|
||||||
|
int listboxRight;
|
||||||
|
int listboxBottom;
|
||||||
|
|
||||||
|
int commentLeft;
|
||||||
|
int commentTop;
|
||||||
|
int commentWidth;
|
||||||
|
int commentHeight;
|
||||||
|
int commentRight;
|
||||||
|
int commentBottom;
|
||||||
|
|
||||||
|
|
||||||
|
static int InsertSaveNode (FSaveGameNode *node);
|
||||||
|
static void ReadSaveStrings ();
|
||||||
|
|
||||||
|
|
||||||
|
FTexture *SavePic;
|
||||||
|
TArray<FBrokenLines> SaveComment;
|
||||||
|
bool mEntering;
|
||||||
|
FString savegamestring;
|
||||||
|
|
||||||
|
DLoadSaveMenu(DMenu *parent = NULL, FListMenuDescriptor *desc = NULL);
|
||||||
|
void Destroy();
|
||||||
|
|
||||||
|
int RemoveSaveSlot (int index);
|
||||||
|
void UnloadSaveData ();
|
||||||
|
void ClearSaveStuff ();
|
||||||
|
void ExtractSaveData (int index);
|
||||||
|
void Drawer ();
|
||||||
|
bool MenuEvent (int mkey, bool fromcontroller);
|
||||||
|
bool MouseEvent(int type, int x, int y);
|
||||||
|
bool Responder(event_t *ev);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static void NotifyNewSave (const char *file, const char *title, bool okForQuicksave);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
IMPLEMENT_CLASS(DLoadSaveMenu)
|
||||||
|
|
||||||
|
TArray<FSaveGameNode*> DLoadSaveMenu::SaveGames;
|
||||||
|
int DLoadSaveMenu::LastSaved = -1;
|
||||||
|
int DLoadSaveMenu::LastAccessed = -1;
|
||||||
|
|
||||||
|
FSaveGameNode *quickSaveSlot;
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// Save data maintenance (stored statically)
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void ClearSaveGames()
|
||||||
|
{
|
||||||
|
for(unsigned i=0;i<DLoadSaveMenu::SaveGames.Size(); i++)
|
||||||
|
{
|
||||||
|
if(!DLoadSaveMenu::SaveGames[i]->bNoDelete)
|
||||||
|
delete DLoadSaveMenu::SaveGames[i];
|
||||||
|
}
|
||||||
|
DLoadSaveMenu::SaveGames.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// Save data maintenance (stored statically)
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
int DLoadSaveMenu::RemoveSaveSlot (int index)
|
||||||
|
{
|
||||||
|
FSaveGameNode *file = SaveGames[index];
|
||||||
|
|
||||||
|
if (quickSaveSlot == SaveGames[index])
|
||||||
|
{
|
||||||
|
quickSaveSlot = NULL;
|
||||||
|
}
|
||||||
|
if (Selected == index)
|
||||||
|
{
|
||||||
|
Selected = -1;
|
||||||
|
}
|
||||||
|
if (!file->bNoDelete) delete file;
|
||||||
|
SaveGames.Delete(index);
|
||||||
|
if ((unsigned)index >= SaveGames.Size()) index--;
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
int DLoadSaveMenu::InsertSaveNode (FSaveGameNode *node)
|
||||||
|
{
|
||||||
|
if (SaveGames.Size() == 0)
|
||||||
|
{
|
||||||
|
return SaveGames.Push(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node->bOldVersion)
|
||||||
|
{ // Add node at bottom of list
|
||||||
|
return SaveGames.Push(node);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ // Add node at top of list
|
||||||
|
unsigned int i;
|
||||||
|
for(i = 0; i < SaveGames.Size(); i++)
|
||||||
|
{
|
||||||
|
if (SaveGames[i]->bOldVersion ||
|
||||||
|
stricmp (node->Title, SaveGames[i]->Title) <= 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SaveGames.Insert(i, node);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// M_ReadSaveStrings
|
||||||
|
//
|
||||||
|
// Find savegames and read their titles
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void DLoadSaveMenu::ReadSaveStrings ()
|
||||||
|
{
|
||||||
|
if (SaveGames.Size() == 0)
|
||||||
|
{
|
||||||
|
void *filefirst;
|
||||||
|
findstate_t c_file;
|
||||||
|
FString filter;
|
||||||
|
|
||||||
|
LastSaved = LastAccessed = -1;
|
||||||
|
quickSaveSlot = NULL;
|
||||||
|
filter = "";// G_BuildSaveName("*.zds", -1);
|
||||||
|
filefirst = I_FindFirst (filter.GetChars(), &c_file);
|
||||||
|
if (filefirst != ((void *)(-1)))
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// I_FindName only returns the file's name and not its full path
|
||||||
|
FString filepath = "";// G_BuildSaveName(I_FindName(&c_file), -1);
|
||||||
|
FILE *file = fopen (filepath, "rb");
|
||||||
|
|
||||||
|
if (file != NULL)
|
||||||
|
{
|
||||||
|
//PNGHandle *png;
|
||||||
|
//char sig[16];
|
||||||
|
FString title;
|
||||||
|
bool oldVer = true;
|
||||||
|
bool addIt = false;
|
||||||
|
bool missing = false;
|
||||||
|
|
||||||
|
// ZDoom 1.23 betas 21-33 have the savesig first.
|
||||||
|
// Earlier versions have the savesig second.
|
||||||
|
// Later versions have the savegame encapsulated inside a PNG.
|
||||||
|
//
|
||||||
|
// Old savegame versions are always added to the menu so
|
||||||
|
// the user can easily delete them if desired.
|
||||||
|
|
||||||
|
// Todo: Identify savegames here.
|
||||||
|
}
|
||||||
|
} while (I_FindNext (filefirst, &c_file) == 0);
|
||||||
|
I_FindClose (filefirst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void DLoadSaveMenu::NotifyNewSave (const char *file, const char *title, bool okForQuicksave)
|
||||||
|
{
|
||||||
|
FSaveGameNode *node;
|
||||||
|
|
||||||
|
if (file == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ReadSaveStrings ();
|
||||||
|
|
||||||
|
// See if the file is already in our list
|
||||||
|
for (unsigned i=0; i<SaveGames.Size(); i++)
|
||||||
|
{
|
||||||
|
FSaveGameNode *node = SaveGames[i];
|
||||||
|
#ifdef __unix__
|
||||||
|
if (node->Filename.Compare (file) == 0)
|
||||||
|
#else
|
||||||
|
if (node->Filename.CompareNoCase (file) == 0)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
node->Title = title;
|
||||||
|
node->bOldVersion = false;
|
||||||
|
node->bMissingWads = false;
|
||||||
|
if (okForQuicksave)
|
||||||
|
{
|
||||||
|
if (quickSaveSlot == NULL) quickSaveSlot = node;
|
||||||
|
LastAccessed = LastSaved = i;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node = new FSaveGameNode;
|
||||||
|
node->Title = title;
|
||||||
|
node->Filename = file;
|
||||||
|
node->bOldVersion = false;
|
||||||
|
node->bMissingWads = false;
|
||||||
|
int index = InsertSaveNode (node);
|
||||||
|
|
||||||
|
if (okForQuicksave)
|
||||||
|
{
|
||||||
|
if (quickSaveSlot == NULL) quickSaveSlot = node;
|
||||||
|
LastAccessed = LastSaved = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void M_NotifyNewSave (const char *file, const char *title, bool okForQuicksave)
|
||||||
|
{
|
||||||
|
DLoadSaveMenu::NotifyNewSave(file, title, okForQuicksave);
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// End of static savegame maintenance code
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
DLoadSaveMenu::DLoadSaveMenu(DMenu *parent, FListMenuDescriptor *desc)
|
||||||
|
: DListMenu(parent, desc)
|
||||||
|
{
|
||||||
|
ReadSaveStrings();
|
||||||
|
|
||||||
|
savepicLeft = 10;
|
||||||
|
savepicTop = 54*CleanYfac;
|
||||||
|
savepicWidth = 216*screen->GetWidth()/640;
|
||||||
|
savepicHeight = 135*screen->GetHeight()/400;
|
||||||
|
|
||||||
|
rowHeight = (SmallFont->GetHeight() + 1) * CleanYfac;
|
||||||
|
listboxLeft = savepicLeft + savepicWidth + 14;
|
||||||
|
listboxTop = savepicTop;
|
||||||
|
listboxWidth = screen->GetWidth() - listboxLeft - 10;
|
||||||
|
int listboxHeight1 = screen->GetHeight() - listboxTop - 10;
|
||||||
|
listboxRows = (listboxHeight1 - 1) / rowHeight;
|
||||||
|
listboxHeight = listboxRows * rowHeight + 1;
|
||||||
|
listboxRight = listboxLeft + listboxWidth;
|
||||||
|
listboxBottom = listboxTop + listboxHeight;
|
||||||
|
|
||||||
|
commentLeft = savepicLeft;
|
||||||
|
commentTop = savepicTop + savepicHeight + 16;
|
||||||
|
commentWidth = savepicWidth;
|
||||||
|
commentHeight = (51+(screen->GetHeight()>200?10:0))*CleanYfac;
|
||||||
|
commentRight = commentLeft + commentWidth;
|
||||||
|
commentBottom = commentTop + commentHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DLoadSaveMenu::Destroy()
|
||||||
|
{
|
||||||
|
ClearSaveStuff ();
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void DLoadSaveMenu::UnloadSaveData ()
|
||||||
|
{
|
||||||
|
if (SavePic != NULL)
|
||||||
|
{
|
||||||
|
delete SavePic;
|
||||||
|
}
|
||||||
|
SaveComment.Clear();
|
||||||
|
|
||||||
|
SavePic = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void DLoadSaveMenu::ClearSaveStuff ()
|
||||||
|
{
|
||||||
|
UnloadSaveData();
|
||||||
|
if (quickSaveSlot == (FSaveGameNode*)1)
|
||||||
|
{
|
||||||
|
quickSaveSlot = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void DLoadSaveMenu::ExtractSaveData (int index)
|
||||||
|
{
|
||||||
|
FILE *file;
|
||||||
|
//PNGHandle *png;
|
||||||
|
FSaveGameNode *node;
|
||||||
|
|
||||||
|
UnloadSaveData ();
|
||||||
|
|
||||||
|
if ((unsigned)index < SaveGames.Size() &&
|
||||||
|
(node = SaveGames[index]) &&
|
||||||
|
!node->Filename.IsEmpty() &&
|
||||||
|
!node->bOldVersion &&
|
||||||
|
(file = fopen (node->Filename.GetChars(), "rb")) != NULL)
|
||||||
|
{
|
||||||
|
// Todo.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void DLoadSaveMenu::Drawer ()
|
||||||
|
{
|
||||||
|
Super::Drawer();
|
||||||
|
|
||||||
|
FSaveGameNode *node;
|
||||||
|
int i;
|
||||||
|
unsigned j;
|
||||||
|
bool didSeeSelected = false;
|
||||||
|
|
||||||
|
// Draw picture area
|
||||||
|
/*
|
||||||
|
if (gameaction == ga_loadgame || gameaction == ga_loadgamehidecon || gameaction == ga_savegame)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
//V_DrawFrame (savepicLeft, savepicTop, savepicWidth, savepicHeight);
|
||||||
|
if (SavePic != NULL)
|
||||||
|
{
|
||||||
|
DrawTexture(&twod, SavePic, savepicLeft, savepicTop,
|
||||||
|
DTA_DestWidth, savepicWidth,
|
||||||
|
DTA_DestHeight, savepicHeight,
|
||||||
|
DTA_Masked, false,
|
||||||
|
TAG_DONE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
twod.AddColorOnlyQuad(savepicLeft, savepicTop, savepicLeft+savepicWidth, savepicTop+savepicHeight, 0xff000000);
|
||||||
|
|
||||||
|
if (SaveGames.Size() > 0)
|
||||||
|
{
|
||||||
|
const char *text =
|
||||||
|
(Selected == -1 || !SaveGames[Selected]->bOldVersion)
|
||||||
|
? GStrings("MNU_NOPICTURE") : GStrings("MNU_DIFFVERSION");
|
||||||
|
const int textlen = SmallFont->StringWidth (text)*CleanXfac;
|
||||||
|
|
||||||
|
DrawText (&twod, SmallFont, CR_GOLD, savepicLeft+(savepicWidth-textlen)/2,
|
||||||
|
savepicTop+(savepicHeight-rowHeight)/2, text,
|
||||||
|
DTA_CleanNoMove, true, TAG_DONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw comment area
|
||||||
|
//V_DrawFrame (commentLeft, commentTop, commentWidth, commentHeight);
|
||||||
|
twod.AddColorOnlyQuad(commentLeft, commentTop, commentRight, commentBottom, 0xff000000);
|
||||||
|
if (SaveComment.Size())
|
||||||
|
{
|
||||||
|
// I'm not sure why SaveComment would go NULL in this loop, but I got
|
||||||
|
// a crash report where it was NULL when i reached 1, so now I check
|
||||||
|
// for that.
|
||||||
|
for (i = 0; i < SaveComment.Size() && SaveComment[i].Width >= 0 && i < 6; ++i)
|
||||||
|
{
|
||||||
|
DrawText (&twod, SmallFont, CR_GOLD, commentLeft, commentTop
|
||||||
|
+ SmallFont->GetHeight()*i*CleanYfac, SaveComment[i].Text,
|
||||||
|
DTA_CleanNoMove, true, TAG_DONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw file area
|
||||||
|
//V_DrawFrame (listboxLeft, listboxTop, listboxWidth, listboxHeight);
|
||||||
|
twod.AddColorOnlyQuad(listboxLeft, listboxTop, listboxRight, listboxBottom, 0xff000000);
|
||||||
|
|
||||||
|
if (SaveGames.Size() == 0)
|
||||||
|
{
|
||||||
|
const char * text = GStrings("MNU_NOFILES");
|
||||||
|
const int textlen = SmallFont->StringWidth (text)*CleanXfac;
|
||||||
|
|
||||||
|
DrawText (&twod, SmallFont, CR_GOLD, listboxLeft+(listboxWidth-textlen)/2,
|
||||||
|
listboxTop+(listboxHeight-rowHeight)/2, text,
|
||||||
|
DTA_CleanNoMove, true, TAG_DONE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0, j = TopItem; i < listboxRows && j < SaveGames.Size(); i++,j++)
|
||||||
|
{
|
||||||
|
int color;
|
||||||
|
node = SaveGames[j];
|
||||||
|
if (node->bOldVersion)
|
||||||
|
{
|
||||||
|
color = CR_BLUE;
|
||||||
|
}
|
||||||
|
else if (node->bMissingWads)
|
||||||
|
{
|
||||||
|
color = CR_ORANGE;
|
||||||
|
}
|
||||||
|
else if ((int)j == Selected)
|
||||||
|
{
|
||||||
|
color = CR_WHITE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
color = CR_TAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((int)j == Selected)
|
||||||
|
{
|
||||||
|
twod.AddColorOnlyQuad(listboxLeft, listboxTop+rowHeight*i, listboxRight, listboxTop+rowHeight*(i+1), mEntering ? PalEntry(255,255,0,0) : PalEntry(255,0,0,255));
|
||||||
|
didSeeSelected = true;
|
||||||
|
if (!mEntering)
|
||||||
|
{
|
||||||
|
DrawText(&twod, SmallFont, color,
|
||||||
|
listboxLeft+1, listboxTop+rowHeight*i+CleanYfac, node->Title,
|
||||||
|
DTA_CleanNoMove, true, TAG_DONE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DrawText(&twod, SmallFont, CR_WHITE,
|
||||||
|
listboxLeft+1, listboxTop+rowHeight*i+CleanYfac, savegamestring,
|
||||||
|
DTA_CleanNoMove, true, TAG_DONE);
|
||||||
|
|
||||||
|
char curs[2] = { SmallFont->GetCursor(), 0 };
|
||||||
|
DrawText(&twod, SmallFont, CR_WHITE,
|
||||||
|
listboxLeft+1+SmallFont->StringWidth (savegamestring)*CleanXfac,
|
||||||
|
listboxTop+rowHeight*i+CleanYfac,
|
||||||
|
curs,
|
||||||
|
DTA_CleanNoMove, true, TAG_DONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DrawText(&twod, SmallFont, color,
|
||||||
|
listboxLeft+1, listboxTop+rowHeight*i+CleanYfac, node->Title,
|
||||||
|
DTA_CleanNoMove, true, TAG_DONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
bool DLoadSaveMenu::MenuEvent (int mkey, bool fromcontroller)
|
||||||
|
{
|
||||||
|
switch (mkey)
|
||||||
|
{
|
||||||
|
case MKEY_Up:
|
||||||
|
if (SaveGames.Size() > 1)
|
||||||
|
{
|
||||||
|
if (Selected == -1) Selected = TopItem;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (--Selected < 0) Selected = SaveGames.Size()-1;
|
||||||
|
if (Selected < TopItem) TopItem = Selected;
|
||||||
|
else if (Selected >= TopItem + listboxRows) TopItem = std::max(0, Selected - listboxRows + 1);
|
||||||
|
}
|
||||||
|
UnloadSaveData ();
|
||||||
|
ExtractSaveData (Selected);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MKEY_Down:
|
||||||
|
if (SaveGames.Size() > 1)
|
||||||
|
{
|
||||||
|
if (Selected == -1) Selected = TopItem;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (unsigned(++Selected) >= SaveGames.Size()) Selected = 0;
|
||||||
|
if (Selected < TopItem) TopItem = Selected;
|
||||||
|
else if (Selected >= TopItem + listboxRows) TopItem = std::max(0, Selected - listboxRows + 1);
|
||||||
|
}
|
||||||
|
UnloadSaveData ();
|
||||||
|
ExtractSaveData (Selected);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MKEY_PageDown:
|
||||||
|
if (SaveGames.Size() > 1)
|
||||||
|
{
|
||||||
|
if (TopItem >= (int)SaveGames.Size() - listboxRows)
|
||||||
|
{
|
||||||
|
TopItem = 0;
|
||||||
|
if (Selected != -1) Selected = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TopItem = std::min<int>(TopItem + listboxRows, SaveGames.Size() - listboxRows);
|
||||||
|
if (TopItem > Selected && Selected != -1) Selected = TopItem;
|
||||||
|
}
|
||||||
|
UnloadSaveData ();
|
||||||
|
ExtractSaveData (Selected);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MKEY_PageUp:
|
||||||
|
if (SaveGames.Size() > 1)
|
||||||
|
{
|
||||||
|
if (TopItem == 0)
|
||||||
|
{
|
||||||
|
TopItem = SaveGames.Size() - listboxRows;
|
||||||
|
if (Selected != -1) Selected = TopItem;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TopItem = std::max(TopItem - listboxRows, 0);
|
||||||
|
if (Selected >= TopItem + listboxRows) Selected = TopItem;
|
||||||
|
}
|
||||||
|
UnloadSaveData ();
|
||||||
|
ExtractSaveData (Selected);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MKEY_Enter:
|
||||||
|
return false; // This event will be handled by the subclasses
|
||||||
|
|
||||||
|
case MKEY_MBYes:
|
||||||
|
{
|
||||||
|
if ((unsigned)Selected < SaveGames.Size())
|
||||||
|
{
|
||||||
|
int listindex = SaveGames[0]->bNoDelete? Selected-1 : Selected;
|
||||||
|
remove (SaveGames[Selected]->Filename.GetChars());
|
||||||
|
UnloadSaveData ();
|
||||||
|
Selected = RemoveSaveSlot (Selected);
|
||||||
|
ExtractSaveData (Selected);
|
||||||
|
|
||||||
|
if (LastSaved == listindex) LastSaved = -1;
|
||||||
|
else if (LastSaved > listindex) LastSaved--;
|
||||||
|
if (LastAccessed == listindex) LastAccessed = -1;
|
||||||
|
else if (LastAccessed > listindex) LastAccessed--;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return Super::MenuEvent(mkey, fromcontroller);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
bool DLoadSaveMenu::MouseEvent(int type, int x, int y)
|
||||||
|
{
|
||||||
|
if (x >= listboxLeft && x < listboxLeft + listboxWidth &&
|
||||||
|
y >= listboxTop && y < listboxTop + listboxHeight)
|
||||||
|
{
|
||||||
|
int lineno = (y - listboxTop) / rowHeight;
|
||||||
|
|
||||||
|
if (TopItem + lineno < (int)SaveGames.Size())
|
||||||
|
{
|
||||||
|
Selected = TopItem + lineno;
|
||||||
|
UnloadSaveData ();
|
||||||
|
ExtractSaveData (Selected);
|
||||||
|
if (type == MOUSE_Release)
|
||||||
|
{
|
||||||
|
if (MenuEvent(MKEY_Enter, true))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else Selected = -1;
|
||||||
|
}
|
||||||
|
else Selected = -1;
|
||||||
|
|
||||||
|
return Super::MouseEvent(type, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
bool DLoadSaveMenu::Responder (event_t *ev)
|
||||||
|
{
|
||||||
|
if (ev->type == EV_GUI_Event)
|
||||||
|
{
|
||||||
|
if (ev->subtype == EV_GUI_KeyDown)
|
||||||
|
{
|
||||||
|
if ((unsigned)Selected < SaveGames.Size())
|
||||||
|
{
|
||||||
|
switch (ev->data1)
|
||||||
|
{
|
||||||
|
case GK_F1:
|
||||||
|
if (!SaveGames[Selected]->Filename.IsEmpty())
|
||||||
|
{
|
||||||
|
FStringf workbuf("File on disk:\n%s", SaveGames[Selected]->Filename.GetChars());
|
||||||
|
SaveComment = V_BreakLines (SmallFont, 216*screen->GetWidth()/640/CleanXfac, workbuf);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case GK_DEL:
|
||||||
|
case '\b':
|
||||||
|
{
|
||||||
|
FString EndString;
|
||||||
|
EndString.Format("%s" TEXTCOLOR_WHITE "%s" TEXTCOLOR_NORMAL "?\n\n%s",
|
||||||
|
GStrings("MNU_DELETESG"), SaveGames[Selected]->Title, GStrings("PRESSYN"));
|
||||||
|
M_StartMessage (EndString, 0);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ev->subtype == EV_GUI_WheelUp)
|
||||||
|
{
|
||||||
|
if (TopItem > 0) TopItem--;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (ev->subtype == EV_GUI_WheelDown)
|
||||||
|
{
|
||||||
|
if (TopItem < (int)SaveGames.Size() - listboxRows) TopItem++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Super::Responder(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
class DSaveMenu : public DLoadSaveMenu
|
||||||
|
{
|
||||||
|
DECLARE_CLASS(DSaveMenu, DLoadSaveMenu)
|
||||||
|
|
||||||
|
FSaveGameNode NewSaveNode;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
DSaveMenu(DMenu *parent = NULL, FListMenuDescriptor *desc = NULL);
|
||||||
|
void Destroy();
|
||||||
|
void DoSave (FSaveGameNode *node);
|
||||||
|
bool Responder (event_t *ev);
|
||||||
|
bool MenuEvent (int mkey, bool fromcontroller);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
IMPLEMENT_CLASS(DSaveMenu)
|
||||||
|
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
DSaveMenu::DSaveMenu(DMenu *parent, FListMenuDescriptor *desc)
|
||||||
|
: DLoadSaveMenu(parent, desc)
|
||||||
|
{
|
||||||
|
NewSaveNode.Title = GStrings["NEWSAVE"];
|
||||||
|
NewSaveNode.bNoDelete = true;
|
||||||
|
SaveGames.Insert(0, &NewSaveNode);
|
||||||
|
TopItem = 0;
|
||||||
|
if (LastSaved == -1)
|
||||||
|
{
|
||||||
|
Selected = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Selected = LastSaved + 1;
|
||||||
|
}
|
||||||
|
ExtractSaveData (Selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void DSaveMenu::Destroy()
|
||||||
|
{
|
||||||
|
if (SaveGames[0] == &NewSaveNode)
|
||||||
|
{
|
||||||
|
SaveGames.Delete(0);
|
||||||
|
if (Selected == 0) Selected = -1;
|
||||||
|
else Selected--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void DSaveMenu::DoSave (FSaveGameNode *node)
|
||||||
|
{
|
||||||
|
if (node != &NewSaveNode)
|
||||||
|
{
|
||||||
|
//G_SaveGame (node->Filename.GetChars(), savegamestring);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Find an unused filename and save as that
|
||||||
|
FString filename;
|
||||||
|
int i;
|
||||||
|
FILE *test;
|
||||||
|
|
||||||
|
for (i = 0;; ++i)
|
||||||
|
{
|
||||||
|
filename = "";// G_BuildSaveName("save", i);
|
||||||
|
test = fopen (filename, "rb");
|
||||||
|
if (test == NULL)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fclose (test);
|
||||||
|
}
|
||||||
|
//G_SaveGame (filename, savegamestring);
|
||||||
|
}
|
||||||
|
M_ClearMenus();
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
bool DSaveMenu::MenuEvent (int mkey, bool fromcontroller)
|
||||||
|
{
|
||||||
|
if (Super::MenuEvent(mkey, fromcontroller))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (Selected == -1)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mkey == MKEY_Enter)
|
||||||
|
{
|
||||||
|
if (Selected != 0)
|
||||||
|
{
|
||||||
|
savegamestring = SaveGames[Selected]->Title;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
savegamestring = "";
|
||||||
|
}
|
||||||
|
DMenu *input = new DTextEnterMenu(this, savegamestring, 1, fromcontroller);
|
||||||
|
M_ActivateMenu(input);
|
||||||
|
mEntering = true;
|
||||||
|
}
|
||||||
|
else if (mkey == MKEY_Input)
|
||||||
|
{
|
||||||
|
mEntering = false;
|
||||||
|
DoSave(SaveGames[Selected]);
|
||||||
|
}
|
||||||
|
else if (mkey == MKEY_Abort)
|
||||||
|
{
|
||||||
|
mEntering = false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
bool DSaveMenu::Responder (event_t *ev)
|
||||||
|
{
|
||||||
|
if (ev->subtype == EV_GUI_KeyDown)
|
||||||
|
{
|
||||||
|
if (Selected != -1)
|
||||||
|
{
|
||||||
|
switch (ev->data1)
|
||||||
|
{
|
||||||
|
case GK_DEL:
|
||||||
|
case '\b':
|
||||||
|
// cannot delete 'new save game' item
|
||||||
|
if (Selected == 0) return true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'N':
|
||||||
|
Selected = TopItem = 0;
|
||||||
|
UnloadSaveData ();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Super::Responder(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
class DLoadMenu : public DLoadSaveMenu
|
||||||
|
{
|
||||||
|
DECLARE_CLASS(DLoadMenu, DLoadSaveMenu)
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
DLoadMenu(DMenu *parent = NULL, FListMenuDescriptor *desc = NULL);
|
||||||
|
|
||||||
|
bool MenuEvent (int mkey, bool fromcontroller);
|
||||||
|
};
|
||||||
|
|
||||||
|
IMPLEMENT_CLASS(DLoadMenu)
|
||||||
|
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
DLoadMenu::DLoadMenu(DMenu *parent, FListMenuDescriptor *desc)
|
||||||
|
: DLoadSaveMenu(parent, desc)
|
||||||
|
{
|
||||||
|
TopItem = 0;
|
||||||
|
if (LastAccessed != -1)
|
||||||
|
{
|
||||||
|
Selected = LastAccessed;
|
||||||
|
}
|
||||||
|
ExtractSaveData (Selected);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
bool DLoadMenu::MenuEvent (int mkey, bool fromcontroller)
|
||||||
|
{
|
||||||
|
if (Super::MenuEvent(mkey, fromcontroller))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (Selected == -1 || SaveGames.Size() == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mkey == MKEY_Enter)
|
||||||
|
{
|
||||||
|
//G_LoadGame (SaveGames[Selected]->Filename.GetChars(), true);
|
||||||
|
if (quickSaveSlot == (FSaveGameNode*)1)
|
||||||
|
{
|
||||||
|
quickSaveSlot = SaveGames[Selected];
|
||||||
|
}
|
||||||
|
M_ClearMenus();
|
||||||
|
LastAccessed = Selected;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
812
source/common/menu/menu.cpp
Normal file
812
source/common/menu/menu.cpp
Normal file
|
@ -0,0 +1,812 @@
|
||||||
|
/*
|
||||||
|
** menu.cpp
|
||||||
|
** Menu base class and global interface
|
||||||
|
**
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
** Copyright 2010 Christoph Oelckers
|
||||||
|
** All rights reserved.
|
||||||
|
**
|
||||||
|
** Redistribution and use in source and binary forms, with or without
|
||||||
|
** modification, are permitted provided that the following conditions
|
||||||
|
** are met:
|
||||||
|
**
|
||||||
|
** 1. Redistributions of source code must retain the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer.
|
||||||
|
** 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer in the
|
||||||
|
** documentation and/or other materials provided with the distribution.
|
||||||
|
** 3. The name of the author may not be used to endorse or promote products
|
||||||
|
** derived from this software without specific prior written permission.
|
||||||
|
**
|
||||||
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "c_dispatch.h"
|
||||||
|
#include "d_gui.h"
|
||||||
|
#include "c_console.h"
|
||||||
|
#include "c_bind.h"
|
||||||
|
#include "c_cvars.h"
|
||||||
|
#include "d_event.h"
|
||||||
|
//#include "i_input.h"
|
||||||
|
#include "gameconfigfile.h"
|
||||||
|
#include "gstrings.h"
|
||||||
|
#include "menu.h"
|
||||||
|
#include "textures.h"
|
||||||
|
#include "c_buttons.h"
|
||||||
|
#include "v_2ddrawer.h"
|
||||||
|
#include "printf.h"
|
||||||
|
#include "v_draw.h"
|
||||||
|
#include "gamecontrol.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// Todo: Move these elsewhere
|
||||||
|
//
|
||||||
|
CVAR (Float, mouse_sensitivity, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||||
|
EXTERN_CVAR (Bool, show_messages)
|
||||||
|
|
||||||
|
|
||||||
|
CVAR (Float, snd_menuvolume, 0.6f, CVAR_ARCHIVE)
|
||||||
|
CVAR(Int, m_use_mouse, 1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||||
|
CVAR(Int, m_show_backbutton, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||||
|
|
||||||
|
DMenu *DMenu::CurrentMenu;
|
||||||
|
int DMenu::MenuTime;
|
||||||
|
|
||||||
|
FGameStartup GameStartupInfo;
|
||||||
|
EMenuState menuactive;
|
||||||
|
bool M_DemoNoPlay;
|
||||||
|
FButtonStatus MenuButtons[NUM_MKEYS];
|
||||||
|
int MenuButtonTickers[NUM_MKEYS];
|
||||||
|
bool MenuButtonOrigin[NUM_MKEYS];
|
||||||
|
int BackbuttonTime;
|
||||||
|
float BackbuttonAlpha;
|
||||||
|
static bool MenuEnabled = true;
|
||||||
|
|
||||||
|
|
||||||
|
#define KEY_REPEAT_DELAY (MENU_TICRATE*5/12)
|
||||||
|
#define KEY_REPEAT_RATE (3)
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// DMenu base class
|
||||||
|
//
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
IMPLEMENT_CLASS (DMenu)
|
||||||
|
|
||||||
|
DMenu::DMenu(DMenu *parent)
|
||||||
|
{
|
||||||
|
mParentMenu = parent;
|
||||||
|
mMouseCapture = false;
|
||||||
|
mBackbuttonSelected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DMenu::Responder (event_t *ev)
|
||||||
|
{
|
||||||
|
bool res = false;
|
||||||
|
if (ev->type == EV_GUI_Event)
|
||||||
|
{
|
||||||
|
if (ev->subtype == EV_GUI_LButtonDown)
|
||||||
|
{
|
||||||
|
res = MouseEventBack(MOUSE_Click, ev->data1, ev->data2);
|
||||||
|
// make the menu's mouse handler believe that the current coordinate is outside the valid range
|
||||||
|
if (res) ev->data2 = -1;
|
||||||
|
res |= MouseEvent(MOUSE_Click, ev->data1, ev->data2);
|
||||||
|
if (res)
|
||||||
|
{
|
||||||
|
SetCapture();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (ev->subtype == EV_GUI_MouseMove)
|
||||||
|
{
|
||||||
|
BackbuttonTime = BACKBUTTON_TIME;
|
||||||
|
if (mMouseCapture || m_use_mouse == 1)
|
||||||
|
{
|
||||||
|
res = MouseEventBack(MOUSE_Move, ev->data1, ev->data2);
|
||||||
|
if (res) ev->data2 = -1;
|
||||||
|
res |= MouseEvent(MOUSE_Move, ev->data1, ev->data2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ev->subtype == EV_GUI_LButtonUp)
|
||||||
|
{
|
||||||
|
if (mMouseCapture)
|
||||||
|
{
|
||||||
|
ReleaseCapture();
|
||||||
|
res = MouseEventBack(MOUSE_Release, ev->data1, ev->data2);
|
||||||
|
if (res) ev->data2 = -1;
|
||||||
|
res |= MouseEvent(MOUSE_Release, ev->data1, ev->data2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
bool DMenu::MenuEvent (int mkey, bool fromcontroller)
|
||||||
|
{
|
||||||
|
switch (mkey)
|
||||||
|
{
|
||||||
|
case MKEY_Back:
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
//S_Sound (CHAN_VOICE | CHAN_UI, DMenu::CurrentMenu != NULL? "menu/backup" : "menu/clear", snd_menuvolume, ATTN_NONE);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void DMenu::Close ()
|
||||||
|
{
|
||||||
|
assert(DMenu::CurrentMenu == this);
|
||||||
|
DMenu::CurrentMenu = mParentMenu;
|
||||||
|
Destroy();
|
||||||
|
if (DMenu::CurrentMenu == NULL)
|
||||||
|
{
|
||||||
|
M_ClearMenus ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
bool DMenu::MouseEvent(int type, int x, int y)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
bool DMenu::MouseEventBack(int type, int x, int y)
|
||||||
|
{
|
||||||
|
if (m_show_backbutton >= 0)
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
FTexture *tex = TexMan(gameinfo.mBackButton);
|
||||||
|
if (tex != NULL)
|
||||||
|
{
|
||||||
|
if (m_show_backbutton&1) x -= screen->GetWidth() - tex->GetScaledWidth() * CleanXfac;
|
||||||
|
if (m_show_backbutton&2) y -= screen->GetHeight() - tex->GetScaledHeight() * CleanYfac;
|
||||||
|
mBackbuttonSelected = ( x >= 0 && x < tex->GetScaledWidth() * CleanXfac &&
|
||||||
|
y >= 0 && y < tex->GetScaledHeight() * CleanYfac);
|
||||||
|
if (mBackbuttonSelected && type == MOUSE_Release)
|
||||||
|
{
|
||||||
|
if (m_use_mouse == 2) mBackbuttonSelected = false;
|
||||||
|
MenuEvent(MKEY_Back, true);
|
||||||
|
}
|
||||||
|
return mBackbuttonSelected;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void DMenu::SetCapture()
|
||||||
|
{
|
||||||
|
if (!mMouseCapture)
|
||||||
|
{
|
||||||
|
mMouseCapture = true;
|
||||||
|
I_SetMouseCapture();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DMenu::ReleaseCapture()
|
||||||
|
{
|
||||||
|
if (mMouseCapture)
|
||||||
|
{
|
||||||
|
mMouseCapture = false;
|
||||||
|
I_ReleaseMouseCapture();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void DMenu::Ticker ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void DMenu::Drawer ()
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
if (this == DMenu::CurrentMenu && BackbuttonAlpha > 0 && m_show_backbutton >= 0 && m_use_mouse)
|
||||||
|
{
|
||||||
|
FTexture *tex = TexMan(gameinfo.mBackButton);
|
||||||
|
int w = tex->GetScaledWidth() * CleanXfac;
|
||||||
|
int h = tex->GetScaledHeight() * CleanYfac;
|
||||||
|
int x = (!(m_show_backbutton&1))? 0:screen->GetWidth() - w;
|
||||||
|
int y = (!(m_show_backbutton&2))? 0:screen->GetHeight() - h;
|
||||||
|
if (mBackbuttonSelected && (mMouseCapture || m_use_mouse == 1))
|
||||||
|
{
|
||||||
|
screen->DrawTexture(tex, x, y, DTA_CleanNoMove, true, DTA_ColorOverlay, MAKEARGB(40, 255,255,255), TAG_DONE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
screen->DrawTexture(tex, x, y, DTA_CleanNoMove, true, DTA_Alpha, BackbuttonAlpha, TAG_DONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DMenu::DimAllowed()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DMenu::TranslateKeyboardEvents()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void M_StartControlPanel (bool makeSound)
|
||||||
|
{
|
||||||
|
// intro might call this repeatedly
|
||||||
|
if (DMenu::CurrentMenu != NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
buttonMap.ResetButtonStates ();
|
||||||
|
for (int i = 0; i < NUM_MKEYS; ++i)
|
||||||
|
{
|
||||||
|
MenuButtons[i].ReleaseKey(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
C_HideConsole (); // [RH] Make sure console goes bye bye.
|
||||||
|
menuactive = MENU_On;
|
||||||
|
// Pause sound effects before we play the menu switch sound.
|
||||||
|
// That way, it won't be paused.
|
||||||
|
//P_CheckTickerPaused ();
|
||||||
|
|
||||||
|
if (makeSound)
|
||||||
|
{
|
||||||
|
//S_Sound (CHAN_VOICE | CHAN_UI, "menu/activate", snd_menuvolume, ATTN_NONE);
|
||||||
|
}
|
||||||
|
BackbuttonTime = 0;
|
||||||
|
BackbuttonAlpha = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void M_ActivateMenu(DMenu *menu)
|
||||||
|
{
|
||||||
|
if (menuactive == MENU_Off) menuactive = MENU_On;
|
||||||
|
if (DMenu::CurrentMenu != NULL) DMenu::CurrentMenu->ReleaseCapture();
|
||||||
|
DMenu::CurrentMenu = menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void M_SetMenu(FName menu, int param)
|
||||||
|
{
|
||||||
|
// some menus need some special treatment (needs to be adjusted for the various frontends.
|
||||||
|
#if 0
|
||||||
|
switch (menu)
|
||||||
|
{
|
||||||
|
case NAME_Episodemenu:
|
||||||
|
// sent from the player class menu
|
||||||
|
GameStartupInfo.Skill = -1;
|
||||||
|
GameStartupInfo.Episode = -1;
|
||||||
|
GameStartupInfo.PlayerClass =
|
||||||
|
param == -1000? NULL :
|
||||||
|
param == -1? "Random" : GetPrintableDisplayName(PlayerClasses[param].Type);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAME_Skillmenu:
|
||||||
|
// sent from the episode menu
|
||||||
|
|
||||||
|
if ((gameinfo.flags & GI_SHAREWARE) && param > 0)
|
||||||
|
{
|
||||||
|
// Only Doom and Heretic have multi-episode shareware versions.
|
||||||
|
M_StartMessage(GStrings("SWSTRING"), 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameStartupInfo.Episode = param;
|
||||||
|
M_StartupSkillMenu(&GameStartupInfo); // needs player class name from class menu (later)
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAME_StartgameConfirm:
|
||||||
|
{
|
||||||
|
// sent from the skill menu for a skill that needs to be confirmed
|
||||||
|
GameStartupInfo.Skill = param;
|
||||||
|
|
||||||
|
const char *msg = AllSkills[param].MustConfirmText;
|
||||||
|
if (*msg==0) msg = GStrings("NIGHTMARE");
|
||||||
|
M_StartMessage (msg, 0, NAME_StartgameConfirmed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case NAME_Startgame:
|
||||||
|
// sent either from skill menu or confirmation screen. Skill gets only set if sent from skill menu
|
||||||
|
// Now we can finally start the game. Ugh...
|
||||||
|
GameStartupInfo.Skill = param;
|
||||||
|
case NAME_StartgameConfirmed:
|
||||||
|
|
||||||
|
G_DeferedInitNew (&GameStartupInfo);
|
||||||
|
if (gamestate == GS_FULLCONSOLE)
|
||||||
|
{
|
||||||
|
gamestate = GS_HIDECONSOLE;
|
||||||
|
gameaction = ga_newgame;
|
||||||
|
}
|
||||||
|
M_ClearMenus ();
|
||||||
|
return;
|
||||||
|
|
||||||
|
case NAME_Savegamemenu:
|
||||||
|
if (!usergame || (players[consoleplayer].health <= 0 && !multiplayer) || gamestate != GS_LEVEL)
|
||||||
|
{
|
||||||
|
// cannot save outside the game.
|
||||||
|
M_StartMessage (GStrings("SAVEDEAD"), 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// End of special checks
|
||||||
|
|
||||||
|
FMenuDescriptor **desc = MenuDescriptors.CheckKey(menu);
|
||||||
|
if (desc != NULL)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
if ((*desc)->mNetgameMessage.IsNotEmpty() && netgame && !demoplayback)
|
||||||
|
{
|
||||||
|
M_StartMessage((*desc)->mNetgameMessage, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ((*desc)->mType == MDESC_ListMenu)
|
||||||
|
{
|
||||||
|
FListMenuDescriptor *ld = static_cast<FListMenuDescriptor*>(*desc);
|
||||||
|
if (ld->mAutoselect >= 0 && ld->mAutoselect < (int)ld->mItems.Size())
|
||||||
|
{
|
||||||
|
// recursively activate the autoselected item without ever creating this menu.
|
||||||
|
ld->mItems[ld->mAutoselect]->Activate();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const PClass *cls = ld->mClass == NULL? RUNTIME_CLASS(DListMenu) : ld->mClass;
|
||||||
|
|
||||||
|
DListMenu *newmenu = (DListMenu *)cls->CreateNew();
|
||||||
|
newmenu->Init(DMenu::CurrentMenu, ld);
|
||||||
|
M_ActivateMenu(newmenu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ((*desc)->mType == MDESC_OptionsMenu)
|
||||||
|
{
|
||||||
|
FOptionMenuDescriptor *ld = static_cast<FOptionMenuDescriptor*>(*desc);
|
||||||
|
const PClass *cls = ld->mClass == NULL? RUNTIME_CLASS(DOptionMenu) : ld->mClass;
|
||||||
|
|
||||||
|
DOptionMenu *newmenu = (DOptionMenu *)cls->CreateNew();
|
||||||
|
newmenu->Init(DMenu::CurrentMenu, ld);
|
||||||
|
M_ActivateMenu(newmenu);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const PClass *menuclass = PClass::FindClass(menu);
|
||||||
|
if (menuclass != NULL)
|
||||||
|
{
|
||||||
|
if (menuclass->IsDescendantOf(RUNTIME_CLASS(DMenu)))
|
||||||
|
{
|
||||||
|
DMenu *newmenu = (DMenu*)menuclass->CreateNew();
|
||||||
|
newmenu->mParentMenu = DMenu::CurrentMenu;
|
||||||
|
M_ActivateMenu(newmenu);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Printf("Attempting to open menu of unknown type '%s'\n", menu.GetChars());
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
bool M_Responder (event_t *ev)
|
||||||
|
{
|
||||||
|
int ch = 0;
|
||||||
|
bool keyup = false;
|
||||||
|
int mkey = NUM_MKEYS;
|
||||||
|
bool fromcontroller = true;
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (chatmodeon)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (DMenu::CurrentMenu != NULL && menuactive != MENU_Off)
|
||||||
|
{
|
||||||
|
// There are a few input sources we are interested in:
|
||||||
|
//
|
||||||
|
// EV_KeyDown / EV_KeyUp : joysticks/gamepads/controllers
|
||||||
|
// EV_GUI_KeyDown / EV_GUI_KeyUp : the keyboard
|
||||||
|
// EV_GUI_Char : printable characters, which we want in string input mode
|
||||||
|
//
|
||||||
|
// This code previously listened for EV_GUI_KeyRepeat to handle repeating
|
||||||
|
// in the menus, but that doesn't work with gamepads, so now we combine
|
||||||
|
// the multiple inputs into buttons and handle the repetition manually.
|
||||||
|
if (ev->type == EV_GUI_Event)
|
||||||
|
{
|
||||||
|
fromcontroller = false;
|
||||||
|
if (ev->subtype == EV_GUI_KeyRepeat)
|
||||||
|
{
|
||||||
|
// We do our own key repeat handling but still want to eat the
|
||||||
|
// OS's repeated keys.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (ev->subtype == EV_GUI_BackButtonDown || ev->subtype == EV_GUI_BackButtonUp)
|
||||||
|
{
|
||||||
|
mkey = MKEY_Back;
|
||||||
|
keyup = ev->subtype == EV_GUI_BackButtonUp;
|
||||||
|
}
|
||||||
|
else if (ev->subtype != EV_GUI_KeyDown && ev->subtype != EV_GUI_KeyUp)
|
||||||
|
{
|
||||||
|
// do we want mouse input?
|
||||||
|
if (ev->subtype >= EV_GUI_FirstMouseEvent && ev->subtype <= EV_GUI_LastMouseEvent)
|
||||||
|
{
|
||||||
|
if (!m_use_mouse)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// pass everything else on to the current menu
|
||||||
|
return DMenu::CurrentMenu->Responder(ev);
|
||||||
|
}
|
||||||
|
else if (DMenu::CurrentMenu->TranslateKeyboardEvents())
|
||||||
|
{
|
||||||
|
ch = ev->data1;
|
||||||
|
keyup = ev->subtype == EV_GUI_KeyUp;
|
||||||
|
switch (ch)
|
||||||
|
{
|
||||||
|
case GK_BACK: mkey = MKEY_Back; break;
|
||||||
|
case GK_ESCAPE: mkey = MKEY_Back; break;
|
||||||
|
case GK_RETURN: mkey = MKEY_Enter; break;
|
||||||
|
case GK_UP: mkey = MKEY_Up; break;
|
||||||
|
case GK_DOWN: mkey = MKEY_Down; break;
|
||||||
|
case GK_LEFT: mkey = MKEY_Left; break;
|
||||||
|
case GK_RIGHT: mkey = MKEY_Right; break;
|
||||||
|
case GK_BACKSPACE: mkey = MKEY_Clear; break;
|
||||||
|
case GK_PGUP: mkey = MKEY_PageUp; break;
|
||||||
|
case GK_PGDN: mkey = MKEY_PageDown; break;
|
||||||
|
default:
|
||||||
|
if (!keyup)
|
||||||
|
{
|
||||||
|
return DMenu::CurrentMenu->Responder(ev);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (menuactive != MENU_WaitKey && (ev->type == EV_KeyDown || ev->type == EV_KeyUp))
|
||||||
|
{
|
||||||
|
keyup = ev->type == EV_KeyUp;
|
||||||
|
|
||||||
|
ch = ev->data1;
|
||||||
|
switch (ch)
|
||||||
|
{
|
||||||
|
case KEY_JOY1:
|
||||||
|
case KEY_PAD_A:
|
||||||
|
mkey = MKEY_Enter;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEY_JOY2:
|
||||||
|
case KEY_PAD_B:
|
||||||
|
mkey = MKEY_Back;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEY_JOY3:
|
||||||
|
case KEY_PAD_X:
|
||||||
|
mkey = MKEY_Clear;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEY_JOY5:
|
||||||
|
case KEY_PAD_LSHOULDER:
|
||||||
|
mkey = MKEY_PageUp;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEY_JOY6:
|
||||||
|
case KEY_PAD_RSHOULDER:
|
||||||
|
mkey = MKEY_PageDown;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEY_PAD_DPAD_UP:
|
||||||
|
case KEY_PAD_LTHUMB_UP:
|
||||||
|
case KEY_JOYAXIS1MINUS:
|
||||||
|
case KEY_JOYPOV1_UP:
|
||||||
|
mkey = MKEY_Up;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEY_PAD_DPAD_DOWN:
|
||||||
|
case KEY_PAD_LTHUMB_DOWN:
|
||||||
|
case KEY_JOYAXIS1PLUS:
|
||||||
|
case KEY_JOYPOV1_DOWN:
|
||||||
|
mkey = MKEY_Down;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEY_PAD_DPAD_LEFT:
|
||||||
|
case KEY_PAD_LTHUMB_LEFT:
|
||||||
|
case KEY_JOYAXIS2MINUS:
|
||||||
|
case KEY_JOYPOV1_LEFT:
|
||||||
|
mkey = MKEY_Left;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEY_PAD_DPAD_RIGHT:
|
||||||
|
case KEY_PAD_LTHUMB_RIGHT:
|
||||||
|
case KEY_JOYAXIS2PLUS:
|
||||||
|
case KEY_JOYPOV1_RIGHT:
|
||||||
|
mkey = MKEY_Right;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mkey != NUM_MKEYS)
|
||||||
|
{
|
||||||
|
if (keyup)
|
||||||
|
{
|
||||||
|
MenuButtons[mkey].ReleaseKey(ch);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MenuButtons[mkey].PressKey(ch);
|
||||||
|
MenuButtonOrigin[mkey] = fromcontroller;
|
||||||
|
if (mkey <= MKEY_PageDown)
|
||||||
|
{
|
||||||
|
MenuButtonTickers[mkey] = KEY_REPEAT_DELAY;
|
||||||
|
}
|
||||||
|
DMenu::CurrentMenu->MenuEvent(mkey, fromcontroller);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return DMenu::CurrentMenu->Responder(ev) || !keyup;
|
||||||
|
}
|
||||||
|
else if (MenuEnabled)
|
||||||
|
{
|
||||||
|
if (ev->type == EV_KeyDown)
|
||||||
|
{
|
||||||
|
// Pop-up menu?
|
||||||
|
if (ev->data1 == KEY_ESCAPE)
|
||||||
|
{
|
||||||
|
M_StartControlPanel(true);
|
||||||
|
M_SetMenu(NAME_Mainmenu, -1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (ev->type == EV_GUI_Event && ev->subtype == EV_GUI_LButtonDown &&
|
||||||
|
ConsoleState != c_down && m_use_mouse)
|
||||||
|
{
|
||||||
|
M_StartControlPanel(true);
|
||||||
|
M_SetMenu(NAME_Mainmenu, -1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void M_Ticker (void)
|
||||||
|
{
|
||||||
|
DMenu::MenuTime++;
|
||||||
|
if (DMenu::CurrentMenu != NULL && menuactive != MENU_Off)
|
||||||
|
{
|
||||||
|
DMenu::CurrentMenu->Ticker();
|
||||||
|
|
||||||
|
for (int i = 0; i < NUM_MKEYS; ++i)
|
||||||
|
{
|
||||||
|
if (MenuButtons[i].bDown)
|
||||||
|
{
|
||||||
|
if (MenuButtonTickers[i] > 0 && --MenuButtonTickers[i] <= 0)
|
||||||
|
{
|
||||||
|
MenuButtonTickers[i] = KEY_REPEAT_RATE;
|
||||||
|
DMenu::CurrentMenu->MenuEvent(i, MenuButtonOrigin[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (BackbuttonTime > 0)
|
||||||
|
{
|
||||||
|
if (BackbuttonAlpha < 1.0f) BackbuttonAlpha += 0.1f;
|
||||||
|
BackbuttonTime--;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (BackbuttonAlpha > 0) BackbuttonAlpha -= 0.1f;
|
||||||
|
if (BackbuttonAlpha < 0) BackbuttonAlpha = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void M_Drawer (void)
|
||||||
|
{
|
||||||
|
PalEntry fade = 0;// x70000000;
|
||||||
|
#if 0
|
||||||
|
player_t *player = &players[consoleplayer];
|
||||||
|
AActor *camera = player->camera;
|
||||||
|
|
||||||
|
if (!screen->Accel2D && camera != NULL && (gamestate == GS_LEVEL || gamestate == GS_TITLELEVEL))
|
||||||
|
{
|
||||||
|
if (camera->player != NULL)
|
||||||
|
{
|
||||||
|
player = camera->player;
|
||||||
|
}
|
||||||
|
fade = PalEntry (BYTE(player->BlendA*255), BYTE(player->BlendR*255), BYTE(player->BlendG*255), BYTE(player->BlendB*255));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
if (DMenu::CurrentMenu != NULL && menuactive != MENU_Off && fade)
|
||||||
|
{
|
||||||
|
if (DMenu::CurrentMenu->DimAllowed()) twod.AddColorOnlyQuad(0, 0, screen->GetWidth(), screen->GetHeight(), fade);
|
||||||
|
DMenu::CurrentMenu->Drawer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void M_ClearMenus ()
|
||||||
|
{
|
||||||
|
M_DemoNoPlay = false;
|
||||||
|
if (DMenu::CurrentMenu != NULL)
|
||||||
|
{
|
||||||
|
DMenu::CurrentMenu->Destroy();
|
||||||
|
DMenu::CurrentMenu = NULL;
|
||||||
|
}
|
||||||
|
menuactive = MENU_Off;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void M_Init (void)
|
||||||
|
{
|
||||||
|
M_ParseMenuDefs();
|
||||||
|
M_CreateMenus();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void M_EnableMenu (bool on)
|
||||||
|
{
|
||||||
|
MenuEnabled = on;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// [RH] Most menus can now be accessed directly
|
||||||
|
// through console commands.
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
CCMD (openmenu)
|
||||||
|
{
|
||||||
|
if (argv.argc() < 2)
|
||||||
|
{
|
||||||
|
Printf("Usage: openmenu \"menu_name\"");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
M_StartControlPanel (true);
|
||||||
|
M_SetMenu(argv[1], -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
CCMD (closemenu)
|
||||||
|
{
|
||||||
|
M_ClearMenus();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Toggle messages on/off
|
||||||
|
//
|
||||||
|
CCMD (togglemessages)
|
||||||
|
{
|
||||||
|
if (show_messages)
|
||||||
|
{
|
||||||
|
Printf (128, "%s\n", GStrings("MSGOFF"));
|
||||||
|
show_messages = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Printf (128, "%s\n", GStrings("MSGON"));
|
||||||
|
show_messages = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EXTERN_CVAR (Int, screenblocks)
|
||||||
|
|
||||||
|
CCMD(menuconsole)
|
||||||
|
{
|
||||||
|
M_ClearMenus();
|
||||||
|
C_ToggleConsole();
|
||||||
|
}
|
||||||
|
|
||||||
|
CCMD(reset2defaults)
|
||||||
|
{
|
||||||
|
C_SetDefaultBindings ();
|
||||||
|
C_SetCVarsToDefaults ();
|
||||||
|
}
|
||||||
|
|
||||||
|
CCMD(reset2saved)
|
||||||
|
{
|
||||||
|
GameConfig->DoGlobalSetup ();
|
||||||
|
GameConfig->DoGameSetup (currentGame);
|
||||||
|
}
|
615
source/common/menu/menu.h
Normal file
615
source/common/menu/menu.h
Normal file
|
@ -0,0 +1,615 @@
|
||||||
|
#ifndef __M_MENU_MENU_H__
|
||||||
|
#define __M_MENU_MENU_H__
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "dobject.h"
|
||||||
|
#include "c_cvars.h"
|
||||||
|
#include "v_font.h"
|
||||||
|
#include "version.h"
|
||||||
|
#include "textures.h"
|
||||||
|
|
||||||
|
EXTERN_CVAR(Float, snd_menuvolume)
|
||||||
|
EXTERN_CVAR(Int, m_use_mouse);
|
||||||
|
|
||||||
|
|
||||||
|
const int MENU_TICRATE = 30;
|
||||||
|
|
||||||
|
|
||||||
|
enum EMenuState : int
|
||||||
|
{
|
||||||
|
MENU_Off, // Menu is closed
|
||||||
|
MENU_On, // Menu is opened
|
||||||
|
MENU_WaitKey, // Menu is opened and waiting for a key in the controls menu
|
||||||
|
MENU_OnNoPause, // Menu is opened but does not pause the game
|
||||||
|
};
|
||||||
|
|
||||||
|
struct event_t;
|
||||||
|
class FTexture;
|
||||||
|
class FFont;
|
||||||
|
enum EColorRange;
|
||||||
|
class FPlayerClass;
|
||||||
|
class FKeyBindings;
|
||||||
|
|
||||||
|
enum EMenuKey
|
||||||
|
{
|
||||||
|
MKEY_Up,
|
||||||
|
MKEY_Down,
|
||||||
|
MKEY_Left,
|
||||||
|
MKEY_Right,
|
||||||
|
MKEY_PageUp,
|
||||||
|
MKEY_PageDown,
|
||||||
|
//----------------- Keys past here do not repeat.
|
||||||
|
MKEY_Enter,
|
||||||
|
MKEY_Back, // Back to previous menu
|
||||||
|
MKEY_Clear, // Clear keybinding/flip player sprite preview
|
||||||
|
NUM_MKEYS,
|
||||||
|
|
||||||
|
// These are not buttons but events sent from other menus
|
||||||
|
|
||||||
|
MKEY_Input, // Sent when input is confirmed
|
||||||
|
MKEY_Abort, // Input aborted
|
||||||
|
MKEY_MBYes,
|
||||||
|
MKEY_MBNo,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct FGameStartup
|
||||||
|
{
|
||||||
|
const char *PlayerClass;
|
||||||
|
int Episode;
|
||||||
|
int Skill;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern FGameStartup GameStartupInfo;
|
||||||
|
|
||||||
|
struct FSaveGameNode
|
||||||
|
{
|
||||||
|
FString Title;
|
||||||
|
FString Filename;
|
||||||
|
bool bOldVersion;
|
||||||
|
bool bMissingWads;
|
||||||
|
bool bNoDelete;
|
||||||
|
|
||||||
|
FSaveGameNode() { bNoDelete = false; }
|
||||||
|
};
|
||||||
|
|
||||||
|
extern EMenuState menuactive;
|
||||||
|
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// menu descriptor. This is created from the menu definition lump
|
||||||
|
// Items must be inserted in the order they are cycled through with the cursor
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
enum EMenuDescriptorType
|
||||||
|
{
|
||||||
|
MDESC_ListMenu,
|
||||||
|
MDESC_OptionsMenu,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FMenuDescriptor
|
||||||
|
{
|
||||||
|
FName mMenuName;
|
||||||
|
FString mNetgameMessage;
|
||||||
|
int mType;
|
||||||
|
const PClass *mClass;
|
||||||
|
|
||||||
|
virtual ~FMenuDescriptor() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class FListMenuItem;
|
||||||
|
class FOptionMenuItem;
|
||||||
|
|
||||||
|
struct FListMenuDescriptor : public FMenuDescriptor
|
||||||
|
{
|
||||||
|
TDeletingArray<FListMenuItem *> mItems;
|
||||||
|
int mSelectedItem;
|
||||||
|
int mSelectOfsX;
|
||||||
|
int mSelectOfsY;
|
||||||
|
FTexture *mSelector;
|
||||||
|
int mDisplayTop;
|
||||||
|
int mXpos, mYpos;
|
||||||
|
int mWLeft, mWRight;
|
||||||
|
int mLinespacing; // needs to be stored for dynamically created menus
|
||||||
|
int mAutoselect; // this can only be set by internal menu creation functions
|
||||||
|
FFont *mFont;
|
||||||
|
EColorRange mFontColor;
|
||||||
|
EColorRange mFontColor2;
|
||||||
|
FMenuDescriptor *mRedirect; // used to redirect overlong skill and episode menus to option menu based alternatives
|
||||||
|
bool mCenter;
|
||||||
|
|
||||||
|
void Reset()
|
||||||
|
{
|
||||||
|
// Reset the default settings (ignore all other values in the struct)
|
||||||
|
mSelectOfsX = 0;
|
||||||
|
mSelectOfsY = 0;
|
||||||
|
mSelector = nullptr;
|
||||||
|
mDisplayTop = 0;
|
||||||
|
mXpos = 0;
|
||||||
|
mYpos = 0;
|
||||||
|
mLinespacing = 0;
|
||||||
|
mNetgameMessage = "";
|
||||||
|
mFont = NULL;
|
||||||
|
mFontColor = CR_UNTRANSLATED;
|
||||||
|
mFontColor2 = CR_UNTRANSLATED;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FOptionMenuSettings
|
||||||
|
{
|
||||||
|
EColorRange mTitleColor;
|
||||||
|
EColorRange mFontColor;
|
||||||
|
EColorRange mFontColorValue;
|
||||||
|
EColorRange mFontColorMore;
|
||||||
|
EColorRange mFontColorHeader;
|
||||||
|
EColorRange mFontColorHighlight;
|
||||||
|
EColorRange mFontColorSelection;
|
||||||
|
int mLinespacing;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FOptionMenuDescriptor : public FMenuDescriptor
|
||||||
|
{
|
||||||
|
TDeletingArray<FOptionMenuItem *> mItems;
|
||||||
|
FString mTitle;
|
||||||
|
int mSelectedItem;
|
||||||
|
int mDrawTop;
|
||||||
|
int mScrollTop;
|
||||||
|
int mScrollPos;
|
||||||
|
int mIndent;
|
||||||
|
int mPosition;
|
||||||
|
bool mDontDim;
|
||||||
|
|
||||||
|
void CalcIndent();
|
||||||
|
FOptionMenuItem *GetItem(FName name);
|
||||||
|
void Reset()
|
||||||
|
{
|
||||||
|
// Reset the default settings (ignore all other values in the struct)
|
||||||
|
mPosition = 0;
|
||||||
|
mScrollTop = 0;
|
||||||
|
mIndent = 0;
|
||||||
|
mDontDim = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef TMap<FName, FMenuDescriptor *> MenuDescriptorList;
|
||||||
|
|
||||||
|
extern FOptionMenuSettings OptionSettings;
|
||||||
|
extern MenuDescriptorList MenuDescriptors;
|
||||||
|
|
||||||
|
#define CURSORSPACE (14 * CleanXfac_1)
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
struct FMenuRect
|
||||||
|
{
|
||||||
|
int x, y;
|
||||||
|
int width, height;
|
||||||
|
|
||||||
|
void set(int _x, int _y, int _w, int _h)
|
||||||
|
{
|
||||||
|
x = _x;
|
||||||
|
y = _y;
|
||||||
|
width = _w;
|
||||||
|
height = _h;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool inside(int _x, int _y)
|
||||||
|
{
|
||||||
|
return _x >= x && _x < x+width && _y >= y && _y < y+height;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class DMenu : public DObject
|
||||||
|
{
|
||||||
|
DECLARE_CLASS (DMenu, DObject)
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool mMouseCapture;
|
||||||
|
bool mBackbuttonSelected;
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
MOUSE_Click,
|
||||||
|
MOUSE_Move,
|
||||||
|
MOUSE_Release
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
BACKBUTTON_TIME = 4*MENU_TICRATE
|
||||||
|
};
|
||||||
|
|
||||||
|
static DMenu *CurrentMenu;
|
||||||
|
static int MenuTime;
|
||||||
|
|
||||||
|
DMenu *mParentMenu;
|
||||||
|
|
||||||
|
DMenu(DMenu *parent = NULL);
|
||||||
|
virtual bool Responder (event_t *ev);
|
||||||
|
virtual bool MenuEvent (int mkey, bool fromcontroller);
|
||||||
|
virtual void Ticker ();
|
||||||
|
virtual void Drawer ();
|
||||||
|
virtual bool DimAllowed ();
|
||||||
|
virtual bool TranslateKeyboardEvents();
|
||||||
|
virtual void Close();
|
||||||
|
virtual bool MouseEvent(int type, int x, int y);
|
||||||
|
bool MouseEventBack(int type, int x, int y);
|
||||||
|
void SetCapture();
|
||||||
|
void ReleaseCapture();
|
||||||
|
bool HasCapture()
|
||||||
|
{
|
||||||
|
return mMouseCapture;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// base class for menu items
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
class FListMenuItem
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
int mXpos, mYpos;
|
||||||
|
FName mAction;
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool mEnabled;
|
||||||
|
|
||||||
|
FListMenuItem(int xpos = 0, int ypos = 0, FName action = NAME_None)
|
||||||
|
{
|
||||||
|
mXpos = xpos;
|
||||||
|
mYpos = ypos;
|
||||||
|
mAction = action;
|
||||||
|
mEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~FListMenuItem();
|
||||||
|
|
||||||
|
virtual bool CheckCoordinate(int x, int y);
|
||||||
|
virtual void Ticker();
|
||||||
|
virtual void Drawer(bool selected);
|
||||||
|
virtual bool Selectable();
|
||||||
|
virtual bool Activate();
|
||||||
|
virtual FName GetAction(int *pparam);
|
||||||
|
virtual bool SetString(int i, const char *s);
|
||||||
|
virtual bool GetString(int i, char *s, int len);
|
||||||
|
virtual bool SetValue(int i, int value);
|
||||||
|
virtual bool GetValue(int i, int *pvalue);
|
||||||
|
virtual void Enable(bool on);
|
||||||
|
virtual bool MenuEvent (int mkey, bool fromcontroller);
|
||||||
|
virtual bool MouseEvent(int type, int x, int y);
|
||||||
|
virtual bool CheckHotkey(int c);
|
||||||
|
virtual int GetWidth();
|
||||||
|
void DrawSelector(int xofs, int yofs, FTexture *tex);
|
||||||
|
void OffsetPositionY(int ydelta) { mYpos += ydelta; }
|
||||||
|
int GetY() { return mYpos; }
|
||||||
|
int GetX() { return mXpos; }
|
||||||
|
void SetX(int x) { mXpos = x; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class FListMenuItemStaticPatch : public FListMenuItem
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
FTexture *mTexture;
|
||||||
|
bool mCentered;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FListMenuItemStaticPatch(int x, int y, FTexture *patch, bool centered);
|
||||||
|
void Drawer(bool selected);
|
||||||
|
};
|
||||||
|
|
||||||
|
class FListMenuItemStaticText : public FListMenuItem
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
const char *mText;
|
||||||
|
FFont *mFont;
|
||||||
|
EColorRange mColor;
|
||||||
|
bool mCentered;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FListMenuItemStaticText(int x, int y, const char *text, FFont *font, EColorRange color, bool centered);
|
||||||
|
~FListMenuItemStaticText();
|
||||||
|
void Drawer(bool selected);
|
||||||
|
};
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// the player sprite window
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
#if 0
|
||||||
|
class FListMenuItemPlayerDisplay : public FListMenuItem
|
||||||
|
{
|
||||||
|
FListMenuDescriptor *mOwner;
|
||||||
|
FTexture *mBackdrop;
|
||||||
|
FRemapTable mRemap;
|
||||||
|
FPlayerClass *mPlayerClass;
|
||||||
|
int mPlayerTics;
|
||||||
|
bool mNoportrait;
|
||||||
|
uint8_t mRotation;
|
||||||
|
uint8_t mMode; // 0: automatic (used by class selection), 1: manual (used by player setup)
|
||||||
|
uint8_t mTranslate;
|
||||||
|
int mSkin;
|
||||||
|
int mRandomClass;
|
||||||
|
int mRandomTimer;
|
||||||
|
int mClassNum;
|
||||||
|
|
||||||
|
void SetPlayerClass(int classnum, bool force = false);
|
||||||
|
bool UpdatePlayerClass();
|
||||||
|
void UpdateRandomClass();
|
||||||
|
void UpdateTranslation();
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
PDF_ROTATION = 0x10001,
|
||||||
|
PDF_SKIN = 0x10002,
|
||||||
|
PDF_CLASS = 0x10003,
|
||||||
|
PDF_MODE = 0x10004,
|
||||||
|
PDF_TRANSLATE = 0x10005,
|
||||||
|
};
|
||||||
|
|
||||||
|
FListMenuItemPlayerDisplay(FListMenuDescriptor *menu, int x, int y, PalEntry c1, PalEntry c2, bool np, FName action);
|
||||||
|
~FListMenuItemPlayerDisplay();
|
||||||
|
virtual void Ticker();
|
||||||
|
virtual void Drawer(bool selected);
|
||||||
|
bool SetValue(int i, int value);
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// selectable items
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
class FListMenuItemSelectable : public FListMenuItem
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
int mHotkey;
|
||||||
|
int mHeight;
|
||||||
|
int mParam;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FListMenuItemSelectable(int x, int y, int height, FName childmenu, int mParam = -1);
|
||||||
|
bool CheckCoordinate(int x, int y);
|
||||||
|
bool Selectable();
|
||||||
|
bool CheckHotkey(int c);
|
||||||
|
bool Activate();
|
||||||
|
bool MouseEvent(int type, int x, int y);
|
||||||
|
FName GetAction(int *pparam);
|
||||||
|
};
|
||||||
|
|
||||||
|
class FListMenuItemText : public FListMenuItemSelectable
|
||||||
|
{
|
||||||
|
const char *mText;
|
||||||
|
FFont *mFont;
|
||||||
|
EColorRange mColor;
|
||||||
|
EColorRange mColorSelected;
|
||||||
|
public:
|
||||||
|
FListMenuItemText(int x, int y, int height, int hotkey, const char *text, FFont *font, EColorRange color, EColorRange color2, FName child, int param = 0);
|
||||||
|
~FListMenuItemText();
|
||||||
|
void Drawer(bool selected);
|
||||||
|
int GetWidth();
|
||||||
|
};
|
||||||
|
|
||||||
|
class FListMenuItemPatch : public FListMenuItemSelectable
|
||||||
|
{
|
||||||
|
FTexture* mTexture;
|
||||||
|
public:
|
||||||
|
FListMenuItemPatch(int x, int y, int height, int hotkey, FTexture* patch, FName child, int param = 0);
|
||||||
|
void Drawer(bool selected);
|
||||||
|
int GetWidth();
|
||||||
|
};
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// list menu class runs a menu described by a FListMenuDescriptor
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
class DListMenu : public DMenu
|
||||||
|
{
|
||||||
|
DECLARE_CLASS(DListMenu, DMenu)
|
||||||
|
|
||||||
|
protected:
|
||||||
|
FListMenuDescriptor *mDesc;
|
||||||
|
FListMenuItem *mFocusControl;
|
||||||
|
|
||||||
|
public:
|
||||||
|
DListMenu(DMenu *parent = NULL, FListMenuDescriptor *desc = NULL);
|
||||||
|
virtual void Init(DMenu *parent = NULL, FListMenuDescriptor *desc = NULL);
|
||||||
|
FListMenuItem *GetItem(FName name);
|
||||||
|
bool Responder (event_t *ev);
|
||||||
|
bool MenuEvent (int mkey, bool fromcontroller);
|
||||||
|
bool MouseEvent(int type, int x, int y);
|
||||||
|
void Ticker ();
|
||||||
|
void Drawer ();
|
||||||
|
void SetFocus(FListMenuItem *fc)
|
||||||
|
{
|
||||||
|
mFocusControl = fc;
|
||||||
|
}
|
||||||
|
bool CheckFocus(FListMenuItem *fc)
|
||||||
|
{
|
||||||
|
return mFocusControl == fc;
|
||||||
|
}
|
||||||
|
void ReleaseFocus()
|
||||||
|
{
|
||||||
|
mFocusControl = NULL;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// base class for menu items
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
class FOptionMenuItem : public FListMenuItem
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
FString mLabel;
|
||||||
|
bool mCentered;
|
||||||
|
|
||||||
|
void drawLabel(int indent, int y, EColorRange color, bool grayed = false);
|
||||||
|
public:
|
||||||
|
|
||||||
|
FOptionMenuItem(const char *text, FName action = NAME_None, bool center = false)
|
||||||
|
: FListMenuItem(0, 0, action)
|
||||||
|
{
|
||||||
|
mLabel = text;
|
||||||
|
mCentered = center;
|
||||||
|
}
|
||||||
|
|
||||||
|
~FOptionMenuItem();
|
||||||
|
virtual int Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected);
|
||||||
|
virtual bool Selectable();
|
||||||
|
virtual int GetIndent();
|
||||||
|
virtual bool MouseEvent(int type, int x, int y);
|
||||||
|
};
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
struct FOptionValues
|
||||||
|
{
|
||||||
|
struct Pair
|
||||||
|
{
|
||||||
|
double Value;
|
||||||
|
FString TextValue;
|
||||||
|
FString Text;
|
||||||
|
};
|
||||||
|
|
||||||
|
TArray<Pair> mValues;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef TMap< FName, FOptionValues* > FOptionMap;
|
||||||
|
|
||||||
|
extern FOptionMap OptionValues;
|
||||||
|
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// Option menu class runs a menu described by a FOptionMenuDescriptor
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
class DOptionMenu : public DMenu
|
||||||
|
{
|
||||||
|
DECLARE_CLASS(DOptionMenu, DMenu)
|
||||||
|
|
||||||
|
bool CanScrollUp;
|
||||||
|
bool CanScrollDown;
|
||||||
|
int VisBottom;
|
||||||
|
FOptionMenuItem *mFocusControl;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
FOptionMenuDescriptor *mDesc;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FOptionMenuItem *GetItem(FName name);
|
||||||
|
DOptionMenu(DMenu *parent = NULL, FOptionMenuDescriptor *desc = NULL);
|
||||||
|
virtual void Init(DMenu *parent = NULL, FOptionMenuDescriptor *desc = NULL);
|
||||||
|
int FirstSelectable();
|
||||||
|
bool Responder (event_t *ev);
|
||||||
|
bool MenuEvent (int mkey, bool fromcontroller);
|
||||||
|
bool MouseEvent(int type, int x, int y);
|
||||||
|
void Ticker ();
|
||||||
|
void Drawer ();
|
||||||
|
const FOptionMenuDescriptor *GetDescriptor() const { return mDesc; }
|
||||||
|
void SetFocus(FOptionMenuItem *fc)
|
||||||
|
{
|
||||||
|
mFocusControl = fc;
|
||||||
|
}
|
||||||
|
bool CheckFocus(FOptionMenuItem *fc)
|
||||||
|
{
|
||||||
|
return mFocusControl == fc;
|
||||||
|
}
|
||||||
|
void ReleaseFocus()
|
||||||
|
{
|
||||||
|
mFocusControl = NULL;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// Input some text
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
class DTextEnterMenu : public DMenu
|
||||||
|
{
|
||||||
|
DECLARE_ABSTRACT_CLASS(DTextEnterMenu, DMenu)
|
||||||
|
|
||||||
|
TArray<char> mEnterString;
|
||||||
|
FString* mOutString;
|
||||||
|
unsigned int mEnterSize;
|
||||||
|
unsigned int mEnterPos;
|
||||||
|
int mSizeMode; // 1: size is length in chars. 2: also check string width
|
||||||
|
bool mInputGridOkay;
|
||||||
|
|
||||||
|
int InputGridX;
|
||||||
|
int InputGridY;
|
||||||
|
|
||||||
|
// [TP]
|
||||||
|
bool AllowColors;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// [TP] Added allowcolors
|
||||||
|
DTextEnterMenu(DMenu *parent, FString &textbuffer, int sizemode, bool showgrid, bool allowcolors = false);
|
||||||
|
|
||||||
|
void Drawer ();
|
||||||
|
bool MenuEvent (int mkey, bool fromcontroller);
|
||||||
|
bool Responder(event_t *ev);
|
||||||
|
bool TranslateKeyboardEvents();
|
||||||
|
bool MouseEvent(int type, int x, int y);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct event_t;
|
||||||
|
void M_EnableMenu (bool on) ;
|
||||||
|
bool M_Responder (event_t *ev);
|
||||||
|
void M_Ticker (void);
|
||||||
|
void M_Drawer (void);
|
||||||
|
void M_Init (void);
|
||||||
|
void M_CreateMenus();
|
||||||
|
void M_ActivateMenu(DMenu *menu);
|
||||||
|
void M_ClearMenus ();
|
||||||
|
void M_ParseMenuDefs();
|
||||||
|
void M_StartupSkillMenu(FGameStartup *gs);
|
||||||
|
int M_GetDefaultSkill();
|
||||||
|
void M_StartControlPanel (bool makeSound);
|
||||||
|
void M_SetMenu(FName menu, int param = -1);
|
||||||
|
void M_NotifyNewSave (const char *file, const char *title, bool okForQuicksave);
|
||||||
|
void M_StartMessage(const char *message, int messagemode, FName action = NAME_None);
|
||||||
|
|
||||||
|
void I_SetMouseCapture();
|
||||||
|
void I_ReleaseMouseCapture();
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
1321
source/common/menu/menudef.cpp
Normal file
1321
source/common/menu/menudef.cpp
Normal file
File diff suppressed because it is too large
Load diff
367
source/common/menu/menuinput.cpp
Normal file
367
source/common/menu/menuinput.cpp
Normal file
|
@ -0,0 +1,367 @@
|
||||||
|
/*
|
||||||
|
** menuinput.cpp
|
||||||
|
** The string input code
|
||||||
|
**
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
** Copyright 2001-2010 Randy Heit
|
||||||
|
** Copyright 2010 Christoph Oelckers
|
||||||
|
** All rights reserved.
|
||||||
|
**
|
||||||
|
** Redistribution and use in source and binary forms, with or without
|
||||||
|
** modification, are permitted provided that the following conditions
|
||||||
|
** are met:
|
||||||
|
**
|
||||||
|
** 1. Redistributions of source code must retain the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer.
|
||||||
|
** 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer in the
|
||||||
|
** documentation and/or other materials provided with the distribution.
|
||||||
|
** 3. The name of the author may not be used to endorse or promote products
|
||||||
|
** derived from this software without specific prior written permission.
|
||||||
|
**
|
||||||
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "menu/menu.h"
|
||||||
|
#include "c_cvars.h"
|
||||||
|
#include "d_event.h"
|
||||||
|
#include "d_gui.h"
|
||||||
|
#include "v_font.h"
|
||||||
|
#include "v_text.h"
|
||||||
|
#include "v_draw.h"
|
||||||
|
|
||||||
|
IMPLEMENT_ABSTRACT_CLASS(DTextEnterMenu)
|
||||||
|
|
||||||
|
#define INPUTGRID_WIDTH 13
|
||||||
|
#define INPUTGRID_HEIGHT 5
|
||||||
|
|
||||||
|
// Heretic and Hexen do not, by default, come with glyphs for all of these
|
||||||
|
// characters. Oh well. Doom and Strife do.
|
||||||
|
static const char InputGridChars[INPUTGRID_WIDTH * INPUTGRID_HEIGHT] =
|
||||||
|
"ABCDEFGHIJKLM"
|
||||||
|
"NOPQRSTUVWXYZ"
|
||||||
|
"0123456789+-="
|
||||||
|
".,!?@'\":;[]()"
|
||||||
|
"<>^#$%&*/_ \b";
|
||||||
|
|
||||||
|
|
||||||
|
CVAR(Bool, m_showinputgrid, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
// [TP] Added allowcolors
|
||||||
|
DTextEnterMenu::DTextEnterMenu(DMenu *parent, FString &textbuffer, int sizemode, bool showgrid, bool allowcolors)
|
||||||
|
: DMenu(parent)
|
||||||
|
{
|
||||||
|
mOutString = &textbuffer;
|
||||||
|
mEnterSize = 32; // this needs to calculate the size based on screen space (or scroll)
|
||||||
|
mEnterString.Resize(mEnterSize + 1);
|
||||||
|
mEnterPos = (unsigned)textbuffer.Len();
|
||||||
|
mSizeMode = sizemode;
|
||||||
|
mInputGridOkay = showgrid || m_showinputgrid;
|
||||||
|
if (mEnterPos > 0)
|
||||||
|
{
|
||||||
|
InputGridX = INPUTGRID_WIDTH - 1;
|
||||||
|
InputGridY = INPUTGRID_HEIGHT - 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If we are naming a new save, don't start the cursor on "end".
|
||||||
|
InputGridX = 0;
|
||||||
|
InputGridY = 0;
|
||||||
|
}
|
||||||
|
AllowColors = allowcolors; // [TP]
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
bool DTextEnterMenu::TranslateKeyboardEvents()
|
||||||
|
{
|
||||||
|
return mInputGridOkay;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
bool DTextEnterMenu::Responder(event_t *ev)
|
||||||
|
{
|
||||||
|
if (ev->type == EV_GUI_Event)
|
||||||
|
{
|
||||||
|
// Save game and player name string input
|
||||||
|
if (ev->subtype == EV_GUI_Char)
|
||||||
|
{
|
||||||
|
mInputGridOkay = false;
|
||||||
|
if (mEnterPos < mEnterSize &&
|
||||||
|
(mSizeMode == 2/*entering player name*/ || (size_t)SmallFont->StringWidth(mEnterString) < (mEnterSize-1)*8))
|
||||||
|
{
|
||||||
|
mEnterString[mEnterPos] = (char)ev->data1;
|
||||||
|
mEnterString[++mEnterPos] = 0;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
char ch = (char)ev->data1;
|
||||||
|
if ((ev->subtype == EV_GUI_KeyDown || ev->subtype == EV_GUI_KeyRepeat) && ch == '\b')
|
||||||
|
{
|
||||||
|
if (mEnterPos > 0)
|
||||||
|
{
|
||||||
|
mEnterPos--;
|
||||||
|
mEnterString[mEnterPos] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ev->subtype == EV_GUI_KeyDown)
|
||||||
|
{
|
||||||
|
if (ch == GK_ESCAPE)
|
||||||
|
{
|
||||||
|
DMenu *parent = mParentMenu;
|
||||||
|
Close();
|
||||||
|
parent->MenuEvent(MKEY_Abort, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (ch == '\r')
|
||||||
|
{
|
||||||
|
if (mEnterString[0])
|
||||||
|
{
|
||||||
|
DMenu *parent = mParentMenu;
|
||||||
|
Close();
|
||||||
|
parent->MenuEvent(MKEY_Input, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ev->subtype == EV_GUI_KeyDown || ev->subtype == EV_GUI_KeyRepeat)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Super::Responder(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
bool DTextEnterMenu::MouseEvent(int type, int x, int y)
|
||||||
|
{
|
||||||
|
const int cell_width = 18 * CleanXfac;
|
||||||
|
const int cell_height = 12 * CleanYfac;
|
||||||
|
const int screen_y = screen->GetHeight() - INPUTGRID_HEIGHT * cell_height;
|
||||||
|
const int screen_x = (screen->GetWidth() - INPUTGRID_WIDTH * cell_width) / 2;
|
||||||
|
|
||||||
|
if (x >= screen_x && x < screen_x + INPUTGRID_WIDTH * cell_width && y >= screen_y)
|
||||||
|
{
|
||||||
|
InputGridX = (x - screen_x) / cell_width;
|
||||||
|
InputGridY = (y - screen_y) / cell_height;
|
||||||
|
if (type == DMenu::MOUSE_Release)
|
||||||
|
{
|
||||||
|
if (MenuEvent(MKEY_Enter, true))
|
||||||
|
{
|
||||||
|
//S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE);
|
||||||
|
if (m_use_mouse == 2) InputGridX = InputGridY = -1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
InputGridX = InputGridY = -1;
|
||||||
|
}
|
||||||
|
return Super::MouseEvent(type, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
bool DTextEnterMenu::MenuEvent (int key, bool fromcontroller)
|
||||||
|
{
|
||||||
|
if (key == MKEY_Back)
|
||||||
|
{
|
||||||
|
mParentMenu->MenuEvent(MKEY_Abort, false);
|
||||||
|
return Super::MenuEvent(key, fromcontroller);
|
||||||
|
}
|
||||||
|
if (fromcontroller)
|
||||||
|
{
|
||||||
|
mInputGridOkay = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mInputGridOkay)
|
||||||
|
{
|
||||||
|
int ch;
|
||||||
|
|
||||||
|
if (InputGridX == -1 || InputGridY == -1)
|
||||||
|
{
|
||||||
|
InputGridX = InputGridY = 0;
|
||||||
|
}
|
||||||
|
switch (key)
|
||||||
|
{
|
||||||
|
case MKEY_Down:
|
||||||
|
InputGridY = (InputGridY + 1) % INPUTGRID_HEIGHT;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MKEY_Up:
|
||||||
|
InputGridY = (InputGridY + INPUTGRID_HEIGHT - 1) % INPUTGRID_HEIGHT;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MKEY_Right:
|
||||||
|
InputGridX = (InputGridX + 1) % INPUTGRID_WIDTH;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MKEY_Left:
|
||||||
|
InputGridX = (InputGridX + INPUTGRID_WIDTH - 1) % INPUTGRID_WIDTH;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MKEY_Clear:
|
||||||
|
if (mEnterPos > 0)
|
||||||
|
{
|
||||||
|
mEnterString[--mEnterPos] = 0;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MKEY_Enter:
|
||||||
|
assert(unsigned(InputGridX) < INPUTGRID_WIDTH && unsigned(InputGridY) < INPUTGRID_HEIGHT);
|
||||||
|
if (mInputGridOkay)
|
||||||
|
{
|
||||||
|
ch = InputGridChars[InputGridX + InputGridY * INPUTGRID_WIDTH];
|
||||||
|
if (ch == 0) // end
|
||||||
|
{
|
||||||
|
if (mEnterString[0] != '\0')
|
||||||
|
{
|
||||||
|
DMenu *parent = mParentMenu;
|
||||||
|
Close();
|
||||||
|
parent->MenuEvent(MKEY_Input, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ch == '\b') // bs
|
||||||
|
{
|
||||||
|
if (mEnterPos > 0)
|
||||||
|
{
|
||||||
|
mEnterString[--mEnterPos] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (mEnterPos < mEnterSize &&
|
||||||
|
(mSizeMode == 2/*entering player name*/ || (size_t)SmallFont->StringWidth(mEnterString) < (mEnterSize-1)*8))
|
||||||
|
{
|
||||||
|
mEnterString[mEnterPos] = ch;
|
||||||
|
mEnterString[++mEnterPos] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break; // Keep GCC quiet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void DTextEnterMenu::Drawer ()
|
||||||
|
{
|
||||||
|
mParentMenu->Drawer();
|
||||||
|
if (mInputGridOkay)
|
||||||
|
{
|
||||||
|
const int cell_width = 18 * CleanXfac;
|
||||||
|
const int cell_height = 12 * CleanYfac;
|
||||||
|
const int top_padding = cell_height / 2 - SmallFont->GetHeight() * CleanYfac / 2;
|
||||||
|
|
||||||
|
// Darken the background behind the character grid.
|
||||||
|
// Unless we frame it with a border, I think it looks better to extend the
|
||||||
|
// background across the full width of the screen.
|
||||||
|
twod.AddColorOnlyQuad(0 /*screen->GetWidth()/2 - 13 * cell_width / 2*/,
|
||||||
|
screen->GetHeight() - INPUTGRID_HEIGHT * cell_height,
|
||||||
|
screen->GetWidth() /*13 * cell_width*/,
|
||||||
|
INPUTGRID_HEIGHT * cell_height, 0xff000000);
|
||||||
|
|
||||||
|
if (InputGridX >= 0 && InputGridY >= 0)
|
||||||
|
{
|
||||||
|
// Highlight the background behind the selected character.
|
||||||
|
twod.AddColorOnlyQuad(
|
||||||
|
InputGridX * cell_width - INPUTGRID_WIDTH * cell_width / 2 + screen->GetWidth() / 2,
|
||||||
|
InputGridY * cell_height - INPUTGRID_HEIGHT * cell_height + screen->GetHeight(),
|
||||||
|
cell_width, cell_height, PalEntry(255, 255, 248, 220));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int y = 0; y < INPUTGRID_HEIGHT; ++y)
|
||||||
|
{
|
||||||
|
const int yy = y * cell_height - INPUTGRID_HEIGHT * cell_height + screen->GetHeight();
|
||||||
|
for (int x = 0; x < INPUTGRID_WIDTH; ++x)
|
||||||
|
{
|
||||||
|
int width;
|
||||||
|
const int xx = x * cell_width - INPUTGRID_WIDTH * cell_width / 2 + screen->GetWidth() / 2;
|
||||||
|
const int ch = InputGridChars[y * INPUTGRID_WIDTH + x];
|
||||||
|
FTexture *pic = SmallFont->GetChar(ch, CR_DARKGRAY, &width);
|
||||||
|
EColorRange color;
|
||||||
|
int remap;
|
||||||
|
|
||||||
|
// The highlighted character is yellow; the rest are dark gray.
|
||||||
|
color = (x == InputGridX && y == InputGridY) ? CR_YELLOW : CR_DARKGRAY;
|
||||||
|
remap = SmallFont->GetColorTranslation(color);
|
||||||
|
|
||||||
|
if (pic != NULL)
|
||||||
|
{
|
||||||
|
// Draw a normal character.
|
||||||
|
DrawTexture(&twod, pic, xx + cell_width/2 - width*CleanXfac/2, yy + top_padding,
|
||||||
|
DTA_TranslationIndex, remap,
|
||||||
|
DTA_CleanNoMove, true,
|
||||||
|
TAG_DONE);
|
||||||
|
}
|
||||||
|
else if (ch == ' ')
|
||||||
|
{
|
||||||
|
// Draw the space as a box outline. We also draw it 50% wider than it really is.
|
||||||
|
const int x1 = xx + cell_width/2 - width * CleanXfac * 3 / 4;
|
||||||
|
const int x2 = x1 + width * 3 * CleanXfac / 2;
|
||||||
|
const int y1 = yy + top_padding;
|
||||||
|
const int y2 = y1 + SmallFont->GetHeight() * CleanYfac;
|
||||||
|
auto palcolor = PalEntry(255, 160, 160, 160);
|
||||||
|
twod.AddColorOnlyQuad(x1, y1, x2, y1+CleanYfac, palcolor); // top
|
||||||
|
twod.AddColorOnlyQuad(x1, y2, x2, y2+CleanYfac, palcolor); // bottom
|
||||||
|
twod.AddColorOnlyQuad(x1, y1+CleanYfac, x1+CleanXfac, y2, palcolor); // left
|
||||||
|
twod.AddColorOnlyQuad(x2-CleanXfac, y1+CleanYfac, x2, y2, palcolor); // right
|
||||||
|
}
|
||||||
|
else if (ch == '\b' || ch == 0)
|
||||||
|
{
|
||||||
|
// Draw the backspace and end "characters".
|
||||||
|
const char *const str = ch == '\b' ? "BS" : "ED";
|
||||||
|
DrawText(&twod, SmallFont, color,
|
||||||
|
xx + cell_width/2 - SmallFont->StringWidth(str)*CleanXfac/2,
|
||||||
|
yy + top_padding, str, DTA_CleanNoMove, true, TAG_DONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Super::Drawer();
|
||||||
|
}
|
361
source/common/menu/messagebox.cpp
Normal file
361
source/common/menu/messagebox.cpp
Normal file
|
@ -0,0 +1,361 @@
|
||||||
|
/*
|
||||||
|
** messagebox.cpp
|
||||||
|
** Confirmation, notification screns
|
||||||
|
**
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
** Copyright 2010 Christoph Oelckers
|
||||||
|
** All rights reserved.
|
||||||
|
**
|
||||||
|
** Redistribution and use in source and binary forms, with or without
|
||||||
|
** modification, are permitted provided that the following conditions
|
||||||
|
** are met:
|
||||||
|
**
|
||||||
|
** 1. Redistributions of source code must retain the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer.
|
||||||
|
** 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer in the
|
||||||
|
** documentation and/or other materials provided with the distribution.
|
||||||
|
** 3. The name of the author may not be used to endorse or promote products
|
||||||
|
** derived from this software without specific prior written permission.
|
||||||
|
**
|
||||||
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "menu/menu.h"
|
||||||
|
#include "d_event.h"
|
||||||
|
#include "d_gui.h"
|
||||||
|
#include "v_text.h"
|
||||||
|
#include "v_draw.h"
|
||||||
|
#include "gstrings.h"
|
||||||
|
#include "c_dispatch.h"
|
||||||
|
#include "v_2ddrawer.h"
|
||||||
|
|
||||||
|
|
||||||
|
extern FSaveGameNode *quickSaveSlot;
|
||||||
|
|
||||||
|
class DMessageBoxMenu : public DMenu
|
||||||
|
{
|
||||||
|
DECLARE_CLASS(DMessageBoxMenu, DMenu)
|
||||||
|
|
||||||
|
TArray<FBrokenLines> mMessage;
|
||||||
|
int mMessageMode;
|
||||||
|
int messageSelection;
|
||||||
|
int mMouseLeft, mMouseRight, mMouseY;
|
||||||
|
FName mAction;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
DMessageBoxMenu(DMenu *parent = NULL, const char *message = NULL, int messagemode = 0, bool playsound = false, FName action = NAME_None);
|
||||||
|
void Destroy();
|
||||||
|
void Init(DMenu *parent, const char *message, int messagemode, bool playsound = false);
|
||||||
|
void Drawer();
|
||||||
|
bool Responder(event_t *ev);
|
||||||
|
bool MenuEvent(int mkey, bool fromcontroller);
|
||||||
|
bool MouseEvent(int type, int x, int y);
|
||||||
|
void CloseSound();
|
||||||
|
virtual void HandleResult(bool res);
|
||||||
|
};
|
||||||
|
|
||||||
|
IMPLEMENT_CLASS(DMessageBoxMenu)
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
DMessageBoxMenu::DMessageBoxMenu(DMenu *parent, const char *message, int messagemode, bool playsound, FName action)
|
||||||
|
: DMenu(parent)
|
||||||
|
{
|
||||||
|
mAction = action;
|
||||||
|
messageSelection = 0;
|
||||||
|
mMouseLeft = 140;
|
||||||
|
mMouseY = INT_MIN;
|
||||||
|
int mr1 = 170 + SmallFont->StringWidth(GStrings["TXT_YES"]);
|
||||||
|
int mr2 = 170 + SmallFont->StringWidth(GStrings["TXT_NO"]);
|
||||||
|
mMouseRight = std::max(mr1, mr2);
|
||||||
|
|
||||||
|
Init(parent, message, messagemode, playsound);
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void DMessageBoxMenu::Init(DMenu *parent, const char *message, int messagemode, bool playsound)
|
||||||
|
{
|
||||||
|
mParentMenu = parent;
|
||||||
|
if (message != NULL)
|
||||||
|
{
|
||||||
|
if (*message == '$') message = GStrings(message+1);
|
||||||
|
mMessage = V_BreakLines(SmallFont, 300, message);
|
||||||
|
}
|
||||||
|
mMessageMode = messagemode;
|
||||||
|
if (playsound)
|
||||||
|
{
|
||||||
|
//S_StopSound (CHAN_VOICE);
|
||||||
|
//S_Sound (CHAN_VOICE | CHAN_UI, "menu/prompt", snd_menuvolume, ATTN_NONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void DMessageBoxMenu::Destroy()
|
||||||
|
{
|
||||||
|
mMessage.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void DMessageBoxMenu::CloseSound()
|
||||||
|
{
|
||||||
|
//S_Sound (CHAN_VOICE | CHAN_UI, DMenu::CurrentMenu != NULL? "menu/backup" : "menu/dismiss", snd_menuvolume, ATTN_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void DMessageBoxMenu::HandleResult(bool res)
|
||||||
|
{
|
||||||
|
if (mParentMenu != NULL)
|
||||||
|
{
|
||||||
|
if (mMessageMode == 0)
|
||||||
|
{
|
||||||
|
if (mAction == NAME_None)
|
||||||
|
{
|
||||||
|
mParentMenu->MenuEvent(res? MKEY_MBYes : MKEY_MBNo, false);
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
if (res) M_SetMenu(mAction, -1);
|
||||||
|
}
|
||||||
|
CloseSound();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void DMessageBoxMenu::Drawer ()
|
||||||
|
{
|
||||||
|
int y;
|
||||||
|
PalEntry fade = 0;
|
||||||
|
|
||||||
|
int fontheight = SmallFont->GetHeight();
|
||||||
|
//V_SetBorderNeedRefresh();
|
||||||
|
//ST_SetNeedRefresh();
|
||||||
|
|
||||||
|
y = 100;
|
||||||
|
|
||||||
|
if (mMessage.Size())
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < mMessage.Size(); i++)
|
||||||
|
y -= SmallFont->GetHeight () / 2;
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < mMessage.Size(); i++)
|
||||||
|
{
|
||||||
|
DrawText(&twod, SmallFont, CR_UNTRANSLATED, 160 - mMessage[i].Width/2, y, mMessage[i].Text,
|
||||||
|
DTA_Clean, true, TAG_DONE);
|
||||||
|
y += fontheight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mMessageMode == 0)
|
||||||
|
{
|
||||||
|
y += fontheight;
|
||||||
|
mMouseY = y;
|
||||||
|
DrawText(&twod, SmallFont,
|
||||||
|
messageSelection == 0? OptionSettings.mFontColorSelection : OptionSettings.mFontColor,
|
||||||
|
160, y, GStrings["TXT_YES"], DTA_Clean, true, TAG_DONE);
|
||||||
|
DrawText(&twod, SmallFont,
|
||||||
|
messageSelection == 1? OptionSettings.mFontColorSelection : OptionSettings.mFontColor,
|
||||||
|
160, y + fontheight + 1, GStrings["TXT_NO"], DTA_Clean, true, TAG_DONE);
|
||||||
|
|
||||||
|
if (messageSelection >= 0)
|
||||||
|
{
|
||||||
|
if ((DMenu::MenuTime%8) < 6)
|
||||||
|
{
|
||||||
|
DrawText(&twod, ConFont, OptionSettings.mFontColorSelection,
|
||||||
|
(150 - 160) * CleanXfac + screen->GetWidth() / 2,
|
||||||
|
(y + (fontheight + 1) * messageSelection - 100 + fontheight/2 - 5) * CleanYfac + screen->GetHeight() / 2,
|
||||||
|
"\xd",
|
||||||
|
DTA_CellX, 8 * CleanXfac,
|
||||||
|
DTA_CellY, 8 * CleanYfac,
|
||||||
|
TAG_DONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
bool DMessageBoxMenu::Responder(event_t *ev)
|
||||||
|
{
|
||||||
|
if (ev->type == EV_GUI_Event && ev->subtype == EV_GUI_KeyDown)
|
||||||
|
{
|
||||||
|
if (mMessageMode == 0)
|
||||||
|
{
|
||||||
|
int ch = tolower(ev->data1);
|
||||||
|
if (ch == 'n' || ch == ' ')
|
||||||
|
{
|
||||||
|
HandleResult(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (ch == 'y')
|
||||||
|
{
|
||||||
|
HandleResult(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (ev->type == EV_KeyDown)
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return Super::Responder(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
bool DMessageBoxMenu::MenuEvent(int mkey, bool fromcontroller)
|
||||||
|
{
|
||||||
|
if (mMessageMode == 0)
|
||||||
|
{
|
||||||
|
if (mkey == MKEY_Up || mkey == MKEY_Down)
|
||||||
|
{
|
||||||
|
//S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
|
||||||
|
messageSelection = !messageSelection;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (mkey == MKEY_Enter)
|
||||||
|
{
|
||||||
|
// 0 is yes, 1 is no
|
||||||
|
HandleResult(!messageSelection);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (mkey == MKEY_Back)
|
||||||
|
{
|
||||||
|
HandleResult(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
CloseSound();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
bool DMessageBoxMenu::MouseEvent(int type, int x, int y)
|
||||||
|
{
|
||||||
|
if (mMessageMode == 1)
|
||||||
|
{
|
||||||
|
if (type == MOUSE_Click)
|
||||||
|
{
|
||||||
|
return MenuEvent(MKEY_Enter, true);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int sel = -1;
|
||||||
|
int fh = SmallFont->GetHeight() + 1;
|
||||||
|
|
||||||
|
// convert x/y from screen to virtual coordinates, according to CleanX/Yfac use in DrawTexture
|
||||||
|
x = ((x - (screen->GetWidth() / 2)) / CleanXfac) + 160;
|
||||||
|
y = ((y - (screen->GetHeight() / 2)) / CleanYfac) + 100;
|
||||||
|
|
||||||
|
if (x >= mMouseLeft && x <= mMouseRight && y >= mMouseY && y < mMouseY + 2 * fh)
|
||||||
|
{
|
||||||
|
sel = y >= mMouseY + fh;
|
||||||
|
}
|
||||||
|
if (sel != -1 && sel != messageSelection)
|
||||||
|
{
|
||||||
|
//S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
|
||||||
|
}
|
||||||
|
messageSelection = sel;
|
||||||
|
if (type == MOUSE_Release)
|
||||||
|
{
|
||||||
|
return MenuEvent(MKEY_Enter, true);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void M_StartMessage(const char *message, int messagemode, FName action)
|
||||||
|
{
|
||||||
|
if (DMenu::CurrentMenu == NULL)
|
||||||
|
{
|
||||||
|
// only play a sound if no menu was active before
|
||||||
|
M_StartControlPanel(menuactive == MENU_Off);
|
||||||
|
}
|
||||||
|
DMenu *newmenu = new DMessageBoxMenu(DMenu::CurrentMenu, message, messagemode, false, action);
|
||||||
|
newmenu->mParentMenu = DMenu::CurrentMenu;
|
||||||
|
M_ActivateMenu(newmenu);
|
||||||
|
}
|
||||||
|
|
553
source/common/menu/optionmenu.cpp
Normal file
553
source/common/menu/optionmenu.cpp
Normal file
|
@ -0,0 +1,553 @@
|
||||||
|
/*
|
||||||
|
** optionmenu.cpp
|
||||||
|
** Handler class for the option menus and associated items
|
||||||
|
**
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
** Copyright 2010 Christoph Oelckers
|
||||||
|
** All rights reserved.
|
||||||
|
**
|
||||||
|
** Redistribution and use in source and binary forms, with or without
|
||||||
|
** modification, are permitted provided that the following conditions
|
||||||
|
** are met:
|
||||||
|
**
|
||||||
|
** 1. Redistributions of source code must retain the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer.
|
||||||
|
** 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer in the
|
||||||
|
** documentation and/or other materials provided with the distribution.
|
||||||
|
** 3. The name of the author may not be used to endorse or promote products
|
||||||
|
** derived from this software without specific prior written permission.
|
||||||
|
**
|
||||||
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "v_font.h"
|
||||||
|
#include "cmdlib.h"
|
||||||
|
#include "gstrings.h"
|
||||||
|
#include "d_gui.h"
|
||||||
|
#include "d_event.h"
|
||||||
|
#include "c_dispatch.h"
|
||||||
|
#include "c_console.h"
|
||||||
|
#include "c_cvars.h"
|
||||||
|
#include "c_bind.h"
|
||||||
|
#include "gameconfigfile.h"
|
||||||
|
#include "menu/menu.h"
|
||||||
|
#include "v_draw.h"
|
||||||
|
#include "v_2ddrawer.h"
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// Draws a string in the console font, scaled to the 8x8 cells
|
||||||
|
// used by the default console font.
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void M_DrawConText (int color, int x, int y, const char *str)
|
||||||
|
{
|
||||||
|
DrawText (&twod, ConFont, color, x, y, str,
|
||||||
|
DTA_CellX, 8 * CleanXfac_1,
|
||||||
|
DTA_CellY, 8 * CleanYfac_1,
|
||||||
|
TAG_DONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
IMPLEMENT_CLASS(DOptionMenu)
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
DOptionMenu::DOptionMenu(DMenu *parent, FOptionMenuDescriptor *desc)
|
||||||
|
: DMenu(parent)
|
||||||
|
{
|
||||||
|
CanScrollUp = false;
|
||||||
|
CanScrollDown = false;
|
||||||
|
VisBottom = 0;
|
||||||
|
mFocusControl = NULL;
|
||||||
|
Init(parent, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void DOptionMenu::Init(DMenu *parent, FOptionMenuDescriptor *desc)
|
||||||
|
{
|
||||||
|
mParentMenu = parent;
|
||||||
|
mDesc = desc;
|
||||||
|
if (mDesc != NULL && mDesc->mSelectedItem == -1) mDesc->mSelectedItem = FirstSelectable();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
int DOptionMenu::FirstSelectable()
|
||||||
|
{
|
||||||
|
if (mDesc != NULL)
|
||||||
|
{
|
||||||
|
// Go down to the first selectable item
|
||||||
|
int i = -1;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
while (i < (int)mDesc->mItems.Size() && !mDesc->mItems[i]->Selectable());
|
||||||
|
if (i>=0 && i < (int)mDesc->mItems.Size()) return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
FOptionMenuItem *DOptionMenu::GetItem(FName name)
|
||||||
|
{
|
||||||
|
for(unsigned i=0;i<mDesc->mItems.Size(); i++)
|
||||||
|
{
|
||||||
|
FName nm = mDesc->mItems[i]->GetAction(NULL);
|
||||||
|
if (nm == name) return mDesc->mItems[i];
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
bool DOptionMenu::Responder (event_t *ev)
|
||||||
|
{
|
||||||
|
if (ev->type == EV_GUI_Event)
|
||||||
|
{
|
||||||
|
if (ev->subtype == EV_GUI_WheelUp)
|
||||||
|
{
|
||||||
|
int scrollamt = std::min(2, mDesc->mScrollPos);
|
||||||
|
mDesc->mScrollPos -= scrollamt;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (ev->subtype == EV_GUI_WheelDown)
|
||||||
|
{
|
||||||
|
if (CanScrollDown)
|
||||||
|
{
|
||||||
|
if (VisBottom < (int)(mDesc->mItems.Size()-2))
|
||||||
|
{
|
||||||
|
mDesc->mScrollPos += 2;
|
||||||
|
VisBottom += 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mDesc->mScrollPos++;
|
||||||
|
VisBottom++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Super::Responder(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
bool DOptionMenu::MenuEvent (int mkey, bool fromcontroller)
|
||||||
|
{
|
||||||
|
int startedAt = mDesc->mSelectedItem;
|
||||||
|
|
||||||
|
switch (mkey)
|
||||||
|
{
|
||||||
|
case MKEY_Up:
|
||||||
|
if (mDesc->mSelectedItem == -1)
|
||||||
|
{
|
||||||
|
mDesc->mSelectedItem = FirstSelectable();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
do
|
||||||
|
{
|
||||||
|
--mDesc->mSelectedItem;
|
||||||
|
|
||||||
|
if (mDesc->mScrollPos > 0 &&
|
||||||
|
mDesc->mSelectedItem <= mDesc->mScrollTop + mDesc->mScrollPos)
|
||||||
|
{
|
||||||
|
mDesc->mScrollPos = std::max(mDesc->mSelectedItem - mDesc->mScrollTop - 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mDesc->mSelectedItem < 0)
|
||||||
|
{
|
||||||
|
// Figure out how many lines of text fit on the menu
|
||||||
|
int y = mDesc->mPosition;
|
||||||
|
|
||||||
|
if (y <= 0)
|
||||||
|
{
|
||||||
|
if (BigFont && mDesc->mTitle.IsNotEmpty())
|
||||||
|
{
|
||||||
|
y = -y + BigFont->GetHeight();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
y = -y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
y *= CleanYfac_1;
|
||||||
|
int rowheight = OptionSettings.mLinespacing * CleanYfac_1;
|
||||||
|
int maxitems = (screen->GetHeight() - rowheight - y) / rowheight + 1;
|
||||||
|
|
||||||
|
mDesc->mScrollPos = std::max(0, (int)mDesc->mItems.Size() - maxitems + mDesc->mScrollTop);
|
||||||
|
mDesc->mSelectedItem = mDesc->mItems.Size()-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (!mDesc->mItems[mDesc->mSelectedItem]->Selectable() && mDesc->mSelectedItem != startedAt);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MKEY_Down:
|
||||||
|
if (mDesc->mSelectedItem == -1)
|
||||||
|
{
|
||||||
|
mDesc->mSelectedItem = FirstSelectable();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
do
|
||||||
|
{
|
||||||
|
++mDesc->mSelectedItem;
|
||||||
|
|
||||||
|
if (CanScrollDown && mDesc->mSelectedItem == VisBottom)
|
||||||
|
{
|
||||||
|
mDesc->mScrollPos++;
|
||||||
|
VisBottom++;
|
||||||
|
}
|
||||||
|
if (mDesc->mSelectedItem >= (int)mDesc->mItems.Size())
|
||||||
|
{
|
||||||
|
if (startedAt == -1)
|
||||||
|
{
|
||||||
|
mDesc->mSelectedItem = -1;
|
||||||
|
mDesc->mScrollPos = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mDesc->mSelectedItem = 0;
|
||||||
|
mDesc->mScrollPos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (!mDesc->mItems[mDesc->mSelectedItem]->Selectable() && mDesc->mSelectedItem != startedAt);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MKEY_PageUp:
|
||||||
|
if (mDesc->mScrollPos > 0)
|
||||||
|
{
|
||||||
|
mDesc->mScrollPos -= VisBottom - mDesc->mScrollPos - mDesc->mScrollTop;
|
||||||
|
if (mDesc->mScrollPos < 0)
|
||||||
|
{
|
||||||
|
mDesc->mScrollPos = 0;
|
||||||
|
}
|
||||||
|
if (mDesc->mSelectedItem != -1)
|
||||||
|
{
|
||||||
|
mDesc->mSelectedItem = mDesc->mScrollTop + mDesc->mScrollPos + 1;
|
||||||
|
while (!mDesc->mItems[mDesc->mSelectedItem]->Selectable())
|
||||||
|
{
|
||||||
|
if (++mDesc->mSelectedItem >= (int)mDesc->mItems.Size())
|
||||||
|
{
|
||||||
|
mDesc->mSelectedItem = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mDesc->mScrollPos > mDesc->mSelectedItem)
|
||||||
|
{
|
||||||
|
mDesc->mScrollPos = mDesc->mSelectedItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MKEY_PageDown:
|
||||||
|
if (CanScrollDown)
|
||||||
|
{
|
||||||
|
int pagesize = VisBottom - mDesc->mScrollPos - mDesc->mScrollTop;
|
||||||
|
mDesc->mScrollPos += pagesize;
|
||||||
|
if (mDesc->mScrollPos + mDesc->mScrollTop + pagesize > (int)mDesc->mItems.Size())
|
||||||
|
{
|
||||||
|
mDesc->mScrollPos = mDesc->mItems.Size() - mDesc->mScrollTop - pagesize;
|
||||||
|
}
|
||||||
|
if (mDesc->mSelectedItem != -1)
|
||||||
|
{
|
||||||
|
mDesc->mSelectedItem = mDesc->mScrollTop + mDesc->mScrollPos;
|
||||||
|
while (!mDesc->mItems[mDesc->mSelectedItem]->Selectable())
|
||||||
|
{
|
||||||
|
if (++mDesc->mSelectedItem >= (int)mDesc->mItems.Size())
|
||||||
|
{
|
||||||
|
mDesc->mSelectedItem = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mDesc->mScrollPos > mDesc->mSelectedItem)
|
||||||
|
{
|
||||||
|
mDesc->mScrollPos = mDesc->mSelectedItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MKEY_Enter:
|
||||||
|
if (mDesc->mSelectedItem >= 0 && mDesc->mItems[mDesc->mSelectedItem]->Activate())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// fall through to default
|
||||||
|
default:
|
||||||
|
if (mDesc->mSelectedItem >= 0 &&
|
||||||
|
mDesc->mItems[mDesc->mSelectedItem]->MenuEvent(mkey, fromcontroller)) return true;
|
||||||
|
return Super::MenuEvent(mkey, fromcontroller);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mDesc->mSelectedItem != startedAt)
|
||||||
|
{
|
||||||
|
//S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
bool DOptionMenu::MouseEvent(int type, int x, int y)
|
||||||
|
{
|
||||||
|
y = (y / CleanYfac_1) - mDesc->mDrawTop;
|
||||||
|
|
||||||
|
if (mFocusControl)
|
||||||
|
{
|
||||||
|
mFocusControl->MouseEvent(type, x, y);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int yline = (y / OptionSettings.mLinespacing);
|
||||||
|
if (yline >= mDesc->mScrollTop)
|
||||||
|
{
|
||||||
|
yline += mDesc->mScrollPos;
|
||||||
|
}
|
||||||
|
if ((unsigned)yline < mDesc->mItems.Size() && mDesc->mItems[yline]->Selectable())
|
||||||
|
{
|
||||||
|
if (yline != mDesc->mSelectedItem)
|
||||||
|
{
|
||||||
|
mDesc->mSelectedItem = yline;
|
||||||
|
//S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
|
||||||
|
}
|
||||||
|
mDesc->mItems[yline]->MouseEvent(type, x, y);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mDesc->mSelectedItem = -1;
|
||||||
|
return Super::MouseEvent(type, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void DOptionMenu::Ticker ()
|
||||||
|
{
|
||||||
|
Super::Ticker();
|
||||||
|
for(unsigned i=0;i<mDesc->mItems.Size(); i++)
|
||||||
|
{
|
||||||
|
mDesc->mItems[i]->Ticker();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void DOptionMenu::Drawer ()
|
||||||
|
{
|
||||||
|
int y = mDesc->mPosition;
|
||||||
|
|
||||||
|
if (y <= 0)
|
||||||
|
{
|
||||||
|
if (BigFont && mDesc->mTitle.IsNotEmpty())
|
||||||
|
{
|
||||||
|
const char *tt = mDesc->mTitle;
|
||||||
|
if (*tt == '$') tt = GStrings(tt+1);
|
||||||
|
DrawText (&twod, BigFont, OptionSettings.mTitleColor,
|
||||||
|
(screen->GetWidth() - BigFont->StringWidth(tt) * CleanXfac_1) / 2, 10*CleanYfac_1,
|
||||||
|
tt, DTA_CleanNoMove_1, true, TAG_DONE);
|
||||||
|
y = -y + BigFont->GetHeight();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
y = -y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mDesc->mDrawTop = y;
|
||||||
|
int fontheight = OptionSettings.mLinespacing * CleanYfac_1;
|
||||||
|
y *= CleanYfac_1;
|
||||||
|
|
||||||
|
int indent = mDesc->mIndent;
|
||||||
|
if (indent > 280)
|
||||||
|
{ // kludge for the compatibility options with their extremely long labels
|
||||||
|
if (indent + 40 <= CleanWidth_1)
|
||||||
|
{
|
||||||
|
indent = (screen->GetWidth() - ((indent + 40) * CleanXfac_1)) / 2 + indent * CleanXfac_1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
indent = screen->GetWidth() - 40 * CleanXfac_1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
indent = (indent - 160) * CleanXfac_1 + screen->GetWidth() / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ytop = y + mDesc->mScrollTop * 8 * CleanYfac_1;
|
||||||
|
int lastrow = screen->GetHeight() - SmallFont->GetHeight() * CleanYfac_1;
|
||||||
|
|
||||||
|
unsigned i;
|
||||||
|
for (i = 0; i < mDesc->mItems.Size() && y <= lastrow; i++, y += fontheight)
|
||||||
|
{
|
||||||
|
// Don't scroll the uppermost items
|
||||||
|
if ((int)i == mDesc->mScrollTop)
|
||||||
|
{
|
||||||
|
i += mDesc->mScrollPos;
|
||||||
|
if (i >= mDesc->mItems.Size()) break; // skipped beyond end of menu
|
||||||
|
}
|
||||||
|
bool isSelected = mDesc->mSelectedItem == (int)i;
|
||||||
|
int cur_indent = mDesc->mItems[i]->Draw(mDesc, y, indent, isSelected);
|
||||||
|
if (cur_indent >= 0 && isSelected && mDesc->mItems[i]->Selectable())
|
||||||
|
{
|
||||||
|
if (((DMenu::MenuTime%8) < 6) || DMenu::CurrentMenu != this)
|
||||||
|
{
|
||||||
|
M_DrawConText(OptionSettings.mFontColorSelection, cur_indent + 3 * CleanXfac_1, y+fontheight-9*CleanYfac_1, "\xd");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CanScrollUp = (mDesc->mScrollPos > 0);
|
||||||
|
CanScrollDown = (i < mDesc->mItems.Size());
|
||||||
|
VisBottom = i - 1;
|
||||||
|
|
||||||
|
if (CanScrollUp)
|
||||||
|
{
|
||||||
|
M_DrawConText(CR_ORANGE, 3 * CleanXfac_1, ytop, "\x1a");
|
||||||
|
}
|
||||||
|
if (CanScrollDown)
|
||||||
|
{
|
||||||
|
M_DrawConText(CR_ORANGE, 3 * CleanXfac_1, y - 8*CleanYfac_1, "\x1b");
|
||||||
|
}
|
||||||
|
Super::Drawer();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// base class for menu items
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
FOptionMenuItem::~FOptionMenuItem()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int FOptionMenuItem::Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected)
|
||||||
|
{
|
||||||
|
return indent;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FOptionMenuItem::Selectable()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FOptionMenuItem::MouseEvent(int type, int x, int y)
|
||||||
|
{
|
||||||
|
if (Selectable() && type == DMenu::MOUSE_Release)
|
||||||
|
{
|
||||||
|
return DMenu::CurrentMenu->MenuEvent(MKEY_Enter, true);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FOptionMenuItem::GetIndent()
|
||||||
|
{
|
||||||
|
if (mCentered)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const char *label = mLabel;
|
||||||
|
if (*label == '$') label = GStrings(label+1);
|
||||||
|
return SmallFont->StringWidth(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FOptionMenuItem::drawLabel(int indent, int y, EColorRange color, bool grayed)
|
||||||
|
{
|
||||||
|
const char *label = mLabel;
|
||||||
|
if (*label == '$') label = GStrings(label+1);
|
||||||
|
|
||||||
|
int overlay = grayed? MAKEARGB(96,48,0,0) : 0;
|
||||||
|
|
||||||
|
int x;
|
||||||
|
int w = SmallFont->StringWidth(label) * CleanXfac_1;
|
||||||
|
if (!mCentered) x = indent - w;
|
||||||
|
else x = (screen->GetWidth() - w) / 2;
|
||||||
|
DrawText (&twod, SmallFont, color, x, y, label, DTA_CleanNoMove_1, true, DTA_ColorOverlay, overlay, TAG_DONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void FOptionMenuDescriptor::CalcIndent()
|
||||||
|
{
|
||||||
|
// calculate the menu indent
|
||||||
|
int widest = 0, thiswidth;
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < mItems.Size(); i++)
|
||||||
|
{
|
||||||
|
thiswidth = mItems[i]->GetIndent();
|
||||||
|
if (thiswidth > widest) widest = thiswidth;
|
||||||
|
}
|
||||||
|
mIndent = widest + 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
FOptionMenuItem *FOptionMenuDescriptor::GetItem(FName name)
|
||||||
|
{
|
||||||
|
for(unsigned i=0;i<mItems.Size(); i++)
|
||||||
|
{
|
||||||
|
FName nm = mItems[i]->GetAction(NULL);
|
||||||
|
if (nm == name) return mItems[i];
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
961
source/common/menu/optionmenuitems.h
Normal file
961
source/common/menu/optionmenuitems.h
Normal file
|
@ -0,0 +1,961 @@
|
||||||
|
/*
|
||||||
|
** optionmenuitems.h
|
||||||
|
** Control items for option menus
|
||||||
|
**
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
** Copyright 2010 Christoph Oelckers
|
||||||
|
** All rights reserved.
|
||||||
|
**
|
||||||
|
** Redistribution and use in source and binary forms, with or without
|
||||||
|
** modification, are permitted provided that the following conditions
|
||||||
|
** are met:
|
||||||
|
**
|
||||||
|
** 1. Redistributions of source code must retain the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer.
|
||||||
|
** 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer in the
|
||||||
|
** documentation and/or other materials provided with the distribution.
|
||||||
|
** 3. The name of the author may not be used to endorse or promote products
|
||||||
|
** derived from this software without specific prior written permission.
|
||||||
|
**
|
||||||
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
#include "v_text.h"
|
||||||
|
#include "v_draw.h"
|
||||||
|
#include "gstrings.h"
|
||||||
|
|
||||||
|
|
||||||
|
void M_DrawConText (int color, int x, int y, const char *str);
|
||||||
|
void M_SetVideoMode();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// opens a submenu, action is a submenu name
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
class FOptionMenuItemSubmenu : public FOptionMenuItem
|
||||||
|
{
|
||||||
|
int mParam;
|
||||||
|
public:
|
||||||
|
FOptionMenuItemSubmenu(const char *label, const char *menu, int param = 0)
|
||||||
|
: FOptionMenuItem(label, menu)
|
||||||
|
{
|
||||||
|
mParam = param;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected)
|
||||||
|
{
|
||||||
|
drawLabel(indent, y, selected? OptionSettings.mFontColorSelection : OptionSettings.mFontColorMore);
|
||||||
|
return indent;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Activate()
|
||||||
|
{
|
||||||
|
//S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE);
|
||||||
|
M_SetMenu(mAction, mParam);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// Executes a CCMD, action is a CCMD name
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
class FOptionMenuItemCommand : public FOptionMenuItemSubmenu
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FOptionMenuItemCommand(const char *label, const char *menu)
|
||||||
|
: FOptionMenuItemSubmenu(label, menu)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Activate()
|
||||||
|
{
|
||||||
|
//S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE);
|
||||||
|
C_DoCommand(mAction);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// Executes a CCMD after confirmation, action is a CCMD name
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
class FOptionMenuItemSafeCommand : public FOptionMenuItemCommand
|
||||||
|
{
|
||||||
|
// action is a CCMD
|
||||||
|
public:
|
||||||
|
FOptionMenuItemSafeCommand(const char *label, const char *menu)
|
||||||
|
: FOptionMenuItemCommand(label, menu)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MenuEvent (int mkey, bool fromcontroller)
|
||||||
|
{
|
||||||
|
if (mkey == MKEY_MBYes)
|
||||||
|
{
|
||||||
|
C_DoCommand(mAction);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return FOptionMenuItemCommand::MenuEvent(mkey, fromcontroller);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Activate()
|
||||||
|
{
|
||||||
|
M_StartMessage("Do you really want to do this?", 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// Base class for option lists
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
class FOptionMenuItemOptionBase : public FOptionMenuItem
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
// action is a CVAR
|
||||||
|
FName mValues; // Entry in OptionValues table
|
||||||
|
FBaseCVar *mGrayCheck;
|
||||||
|
int mCenter;
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
OP_VALUES = 0x11001
|
||||||
|
};
|
||||||
|
|
||||||
|
FOptionMenuItemOptionBase(const char *label, const char *menu, const char *values, const char *graycheck, int center)
|
||||||
|
: FOptionMenuItem(label, menu)
|
||||||
|
{
|
||||||
|
mValues = values;
|
||||||
|
mGrayCheck = (FBoolCVar*)FindCVar(graycheck, NULL);
|
||||||
|
mCenter = center;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SetString(int i, const char *newtext)
|
||||||
|
{
|
||||||
|
if (i == OP_VALUES)
|
||||||
|
{
|
||||||
|
FOptionValues **opt = OptionValues.CheckKey(newtext);
|
||||||
|
mValues = newtext;
|
||||||
|
if (opt != NULL && *opt != NULL)
|
||||||
|
{
|
||||||
|
int s = GetSelection();
|
||||||
|
if (s >= (int)(*opt)->mValues.Size()) s = 0;
|
||||||
|
SetSelection(s); // readjust the CVAR if its value is outside the range now
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
virtual int GetSelection() = 0;
|
||||||
|
virtual void SetSelection(int Selection) = 0;
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
int Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected)
|
||||||
|
{
|
||||||
|
bool grayed = mGrayCheck != NULL && !(mGrayCheck->GetGenericRep(CVAR_Bool).Bool);
|
||||||
|
|
||||||
|
if (mCenter)
|
||||||
|
{
|
||||||
|
indent = (screen->GetWidth() / 2);
|
||||||
|
}
|
||||||
|
drawLabel(indent, y, selected? OptionSettings.mFontColorSelection : OptionSettings.mFontColor, grayed);
|
||||||
|
|
||||||
|
int overlay = grayed? MAKEARGB(96,48,0,0) : 0;
|
||||||
|
const char *text;
|
||||||
|
int Selection = GetSelection();
|
||||||
|
FOptionValues **opt = OptionValues.CheckKey(mValues);
|
||||||
|
if (Selection < 0 || opt == NULL || *opt == NULL)
|
||||||
|
{
|
||||||
|
text = "Unknown";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
text = (*opt)->mValues[Selection].Text;
|
||||||
|
}
|
||||||
|
if (*text == '$') text = GStrings(text + 1);
|
||||||
|
DrawText(&twod,SmallFont, OptionSettings.mFontColorValue, indent + CURSORSPACE, y,
|
||||||
|
text, DTA_CleanNoMove_1, true, DTA_ColorOverlay, overlay, TAG_DONE);
|
||||||
|
return indent;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
bool MenuEvent (int mkey, bool fromcontroller)
|
||||||
|
{
|
||||||
|
FOptionValues **opt = OptionValues.CheckKey(mValues);
|
||||||
|
if (opt != NULL && *opt != NULL && (*opt)->mValues.Size() > 0)
|
||||||
|
{
|
||||||
|
int Selection = GetSelection();
|
||||||
|
if (mkey == MKEY_Left)
|
||||||
|
{
|
||||||
|
if (Selection == -1) Selection = 0;
|
||||||
|
else if (--Selection < 0) Selection = (*opt)->mValues.Size()-1;
|
||||||
|
}
|
||||||
|
else if (mkey == MKEY_Right || mkey == MKEY_Enter)
|
||||||
|
{
|
||||||
|
if (++Selection >= (int)(*opt)->mValues.Size()) Selection = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return FOptionMenuItem::MenuEvent(mkey, fromcontroller);
|
||||||
|
}
|
||||||
|
SetSelection(Selection);
|
||||||
|
//S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Selectable()
|
||||||
|
{
|
||||||
|
return !(mGrayCheck != NULL && !(mGrayCheck->GetGenericRep(CVAR_Bool).Bool));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// Change a CVAR, action is the CVAR name
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
class FOptionMenuItemOption : public FOptionMenuItemOptionBase
|
||||||
|
{
|
||||||
|
// action is a CVAR
|
||||||
|
FBaseCVar *mCVar;
|
||||||
|
public:
|
||||||
|
|
||||||
|
FOptionMenuItemOption(const char *label, const char *menu, const char *values, const char *graycheck, int center)
|
||||||
|
: FOptionMenuItemOptionBase(label, menu, values, graycheck, center)
|
||||||
|
{
|
||||||
|
mCVar = FindCVar(mAction, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
int GetSelection()
|
||||||
|
{
|
||||||
|
int Selection = -1;
|
||||||
|
FOptionValues **opt = OptionValues.CheckKey(mValues);
|
||||||
|
if (opt != NULL && *opt != NULL && mCVar != NULL && (*opt)->mValues.Size() > 0)
|
||||||
|
{
|
||||||
|
if ((*opt)->mValues[0].TextValue.IsEmpty())
|
||||||
|
{
|
||||||
|
UCVarValue cv = mCVar->GetGenericRep(CVAR_Float);
|
||||||
|
for(unsigned i = 0; i < (*opt)->mValues.Size(); i++)
|
||||||
|
{
|
||||||
|
if (fabs(cv.Float - (*opt)->mValues[i].Value) < FLT_EPSILON)
|
||||||
|
{
|
||||||
|
Selection = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UCVarValue cv = mCVar->GetGenericRep(CVAR_String);
|
||||||
|
for(unsigned i = 0; i < (*opt)->mValues.Size(); i++)
|
||||||
|
{
|
||||||
|
if ((*opt)->mValues[i].TextValue.CompareNoCase(cv.String) == 0)
|
||||||
|
{
|
||||||
|
Selection = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Selection;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetSelection(int Selection)
|
||||||
|
{
|
||||||
|
UCVarValue value;
|
||||||
|
FOptionValues **opt = OptionValues.CheckKey(mValues);
|
||||||
|
if (opt != NULL && *opt != NULL && mCVar != NULL && (*opt)->mValues.Size() > 0)
|
||||||
|
{
|
||||||
|
if ((*opt)->mValues[0].TextValue.IsEmpty())
|
||||||
|
{
|
||||||
|
value.Float = (float)(*opt)->mValues[Selection].Value;
|
||||||
|
mCVar->SetGenericRep (value, CVAR_Float);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value.String = (*opt)->mValues[Selection].TextValue.LockBuffer();
|
||||||
|
mCVar->SetGenericRep (value, CVAR_String);
|
||||||
|
(*opt)->mValues[Selection].TextValue.UnlockBuffer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// This class is used to capture the key to be used as the new key binding
|
||||||
|
// for a control item
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
class DEnterKey : public DMenu
|
||||||
|
{
|
||||||
|
DECLARE_CLASS(DEnterKey, DMenu)
|
||||||
|
|
||||||
|
int *pKey;
|
||||||
|
|
||||||
|
public:
|
||||||
|
DEnterKey(DMenu *parent, int *keyptr)
|
||||||
|
: DMenu(parent)
|
||||||
|
{
|
||||||
|
pKey = keyptr;
|
||||||
|
SetMenuMessage(1);
|
||||||
|
menuactive = MENU_WaitKey; // There should be a better way to disable GUI capture...
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TranslateKeyboardEvents()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetMenuMessage(int which)
|
||||||
|
{
|
||||||
|
if (mParentMenu->IsKindOf(RUNTIME_CLASS(DOptionMenu)))
|
||||||
|
{
|
||||||
|
DOptionMenu *m = static_cast<DOptionMenu*>(mParentMenu);
|
||||||
|
FListMenuItem *it = m->GetItem(NAME_Controlmessage);
|
||||||
|
if (it != NULL)
|
||||||
|
{
|
||||||
|
it->SetValue(0, which);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Responder(event_t *ev)
|
||||||
|
{
|
||||||
|
if (ev->type == EV_KeyDown)
|
||||||
|
{
|
||||||
|
*pKey = ev->data1;
|
||||||
|
menuactive = MENU_On;
|
||||||
|
SetMenuMessage(0);
|
||||||
|
Close();
|
||||||
|
mParentMenu->MenuEvent((ev->data1 == KEY_ESCAPE)? MKEY_Abort : MKEY_Input, 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Drawer()
|
||||||
|
{
|
||||||
|
mParentMenu->Drawer();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef NO_IMP
|
||||||
|
IMPLEMENT_ABSTRACT_CLASS(DEnterKey)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// // Edit a key binding, Action is the CCMD to bind
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
class FOptionMenuItemControl : public FOptionMenuItem
|
||||||
|
{
|
||||||
|
FKeyBindings *mBindings;
|
||||||
|
int mInput;
|
||||||
|
bool mWaiting;
|
||||||
|
public:
|
||||||
|
|
||||||
|
FOptionMenuItemControl(const char *label, const char *menu, FKeyBindings *bindings)
|
||||||
|
: FOptionMenuItem(label, menu)
|
||||||
|
{
|
||||||
|
mBindings = bindings;
|
||||||
|
mWaiting = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
int Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected)
|
||||||
|
{
|
||||||
|
drawLabel(indent, y, mWaiting? OptionSettings.mFontColorHighlight:
|
||||||
|
(selected? OptionSettings.mFontColorSelection : OptionSettings.mFontColor));
|
||||||
|
|
||||||
|
auto keys = mBindings->GetKeysForCommand(mAction);
|
||||||
|
auto description = C_NameKeys(keys.Data(), keys.Size());
|
||||||
|
if (description.IsNotEmpty())
|
||||||
|
{
|
||||||
|
M_DrawConText(CR_WHITE, indent + CURSORSPACE, y + (OptionSettings.mLinespacing-8)*CleanYfac_1, description);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DrawText(&twod,SmallFont, CR_BLACK, indent + CURSORSPACE, y + (OptionSettings.mLinespacing-8)*CleanYfac_1, "---",
|
||||||
|
DTA_CleanNoMove_1, true, TAG_DONE);
|
||||||
|
}
|
||||||
|
return indent;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
bool MenuEvent(int mkey, bool fromcontroller)
|
||||||
|
{
|
||||||
|
if (mkey == MKEY_Input)
|
||||||
|
{
|
||||||
|
mWaiting = false;
|
||||||
|
mBindings->SetBind(mInput, mAction);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (mkey == MKEY_Clear)
|
||||||
|
{
|
||||||
|
mBindings->UnbindACommand(mAction);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (mkey == MKEY_Abort)
|
||||||
|
{
|
||||||
|
mWaiting = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Activate()
|
||||||
|
{
|
||||||
|
//S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE);
|
||||||
|
mWaiting = true;
|
||||||
|
DMenu *input = new DEnterKey(DMenu::CurrentMenu, &mInput);
|
||||||
|
M_ActivateMenu(input);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
class FOptionMenuItemStaticText : public FOptionMenuItem
|
||||||
|
{
|
||||||
|
EColorRange mColor;
|
||||||
|
public:
|
||||||
|
FOptionMenuItemStaticText(const char *label, bool header)
|
||||||
|
: FOptionMenuItem(label, NAME_None, true)
|
||||||
|
{
|
||||||
|
mColor = header? OptionSettings.mFontColorHeader : OptionSettings.mFontColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected)
|
||||||
|
{
|
||||||
|
drawLabel(indent, y, mColor);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Selectable()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
class FOptionMenuItemStaticTextSwitchable : public FOptionMenuItem
|
||||||
|
{
|
||||||
|
EColorRange mColor;
|
||||||
|
FString mAltText;
|
||||||
|
int mCurrent;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FOptionMenuItemStaticTextSwitchable(const char *label, const char *label2, FName action, bool header)
|
||||||
|
: FOptionMenuItem(label, action, true)
|
||||||
|
{
|
||||||
|
mColor = header? OptionSettings.mFontColorHeader : OptionSettings.mFontColor;
|
||||||
|
mAltText = label2;
|
||||||
|
mCurrent = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected)
|
||||||
|
{
|
||||||
|
const char *txt = mCurrent? mAltText.GetChars() : mLabel.GetChars();
|
||||||
|
if (*txt == '$') txt = GStrings(txt + 1);
|
||||||
|
int w = SmallFont->StringWidth(txt) * CleanXfac_1;
|
||||||
|
int x = (screen->GetWidth() - w) / 2;
|
||||||
|
DrawText(&twod,SmallFont, mColor, x, y, txt, DTA_CleanNoMove_1, true, TAG_DONE);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SetValue(int i, int val)
|
||||||
|
{
|
||||||
|
if (i == 0)
|
||||||
|
{
|
||||||
|
mCurrent = val;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SetString(int i, const char *newtext)
|
||||||
|
{
|
||||||
|
if (i == 0)
|
||||||
|
{
|
||||||
|
mAltText = newtext;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Selectable()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
class FOptionMenuSliderBase : public FOptionMenuItem
|
||||||
|
{
|
||||||
|
// action is a CVAR
|
||||||
|
double mMin, mMax, mStep;
|
||||||
|
int mShowValue;
|
||||||
|
int mDrawX;
|
||||||
|
int mSliderShort;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FOptionMenuSliderBase(const char *label, double min, double max, double step, int showval)
|
||||||
|
: FOptionMenuItem(label, NAME_None)
|
||||||
|
{
|
||||||
|
mMin = min;
|
||||||
|
mMax = max;
|
||||||
|
mStep = step;
|
||||||
|
mShowValue = showval;
|
||||||
|
mDrawX = 0;
|
||||||
|
mSliderShort = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual double GetSliderValue() = 0;
|
||||||
|
virtual void SetSliderValue(double val) = 0;
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// Draw a slider. Set fracdigits negative to not display the current value numerically.
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void DrawSlider (int x, int y, double min, double max, double cur, int fracdigits, int indent)
|
||||||
|
{
|
||||||
|
char textbuf[16];
|
||||||
|
double range;
|
||||||
|
int maxlen = 0;
|
||||||
|
int right = x + (12*8 + 4) * CleanXfac_1;
|
||||||
|
int cy = y + (OptionSettings.mLinespacing-8)*CleanYfac_1;
|
||||||
|
|
||||||
|
range = max - min;
|
||||||
|
double ccur = clamp(cur, min, max) - min;
|
||||||
|
|
||||||
|
if (fracdigits >= 0)
|
||||||
|
{
|
||||||
|
snprintf(textbuf, countof(textbuf), "%.*f", fracdigits, max);
|
||||||
|
maxlen = SmallFont->StringWidth(textbuf) * CleanXfac_1;
|
||||||
|
}
|
||||||
|
|
||||||
|
mSliderShort = right + maxlen > screen->GetWidth();
|
||||||
|
|
||||||
|
if (!mSliderShort)
|
||||||
|
{
|
||||||
|
M_DrawConText(CR_WHITE, x, cy, "\x10\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x12");
|
||||||
|
M_DrawConText(CR_ORANGE, x + int((5 + ((ccur * 78) / range)) * CleanXfac_1), cy, "\x13");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// On 320x200 we need a shorter slider
|
||||||
|
M_DrawConText(CR_WHITE, x, cy, "\x10\x11\x11\x11\x11\x11\x12");
|
||||||
|
M_DrawConText(CR_ORANGE, x + int((5 + ((ccur * 38) / range)) * CleanXfac_1), cy, "\x13");
|
||||||
|
right -= 5*8*CleanXfac_1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fracdigits >= 0 && right + maxlen <= screen->GetWidth())
|
||||||
|
{
|
||||||
|
snprintf(textbuf, countof(textbuf), "%.*f", fracdigits, cur);
|
||||||
|
DrawText(&twod,SmallFont, CR_DARKGRAY, right, y, textbuf, DTA_CleanNoMove_1, true, TAG_DONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
int Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected)
|
||||||
|
{
|
||||||
|
drawLabel(indent, y, selected? OptionSettings.mFontColorSelection : OptionSettings.mFontColor);
|
||||||
|
mDrawX = indent + CURSORSPACE;
|
||||||
|
DrawSlider (mDrawX, y, mMin, mMax, GetSliderValue(), mShowValue, indent);
|
||||||
|
return indent;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
bool MenuEvent (int mkey, bool fromcontroller)
|
||||||
|
{
|
||||||
|
double value = GetSliderValue();
|
||||||
|
|
||||||
|
if (mkey == MKEY_Left)
|
||||||
|
{
|
||||||
|
value -= mStep;
|
||||||
|
}
|
||||||
|
else if (mkey == MKEY_Right)
|
||||||
|
{
|
||||||
|
value += mStep;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return FOptionMenuItem::MenuEvent(mkey, fromcontroller);
|
||||||
|
}
|
||||||
|
SetSliderValue(clamp(value, mMin, mMax));
|
||||||
|
//S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MouseEvent(int type, int x, int y)
|
||||||
|
{
|
||||||
|
DOptionMenu *lm = static_cast<DOptionMenu*>(DMenu::CurrentMenu);
|
||||||
|
if (type != DMenu::MOUSE_Click)
|
||||||
|
{
|
||||||
|
if (!lm->CheckFocus(this)) return false;
|
||||||
|
}
|
||||||
|
if (type == DMenu::MOUSE_Release)
|
||||||
|
{
|
||||||
|
lm->ReleaseFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
int slide_left = mDrawX+8*CleanXfac_1;
|
||||||
|
int slide_right = slide_left + (10*8*CleanXfac_1 >> mSliderShort); // 12 char cells with 8 pixels each.
|
||||||
|
|
||||||
|
if (type == DMenu::MOUSE_Click)
|
||||||
|
{
|
||||||
|
if (x < slide_left || x >= slide_right) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
x = clamp(x, slide_left, slide_right);
|
||||||
|
double v = mMin + ((x - slide_left) * (mMax - mMin)) / (slide_right - slide_left);
|
||||||
|
if (v != GetSliderValue())
|
||||||
|
{
|
||||||
|
SetSliderValue(v);
|
||||||
|
////S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE);
|
||||||
|
}
|
||||||
|
if (type == DMenu::MOUSE_Click)
|
||||||
|
{
|
||||||
|
lm->SetFocus(this);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
class FOptionMenuSliderCVar : public FOptionMenuSliderBase
|
||||||
|
{
|
||||||
|
FBaseCVar *mCVar;
|
||||||
|
public:
|
||||||
|
FOptionMenuSliderCVar(const char *label, const char *menu, double min, double max, double step, int showval)
|
||||||
|
: FOptionMenuSliderBase(label, min, max, step, showval)
|
||||||
|
{
|
||||||
|
mCVar = FindCVar(menu, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
double GetSliderValue()
|
||||||
|
{
|
||||||
|
if (mCVar != NULL)
|
||||||
|
{
|
||||||
|
return mCVar->GetGenericRep(CVAR_Float).Float;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetSliderValue(double val)
|
||||||
|
{
|
||||||
|
if (mCVar != NULL)
|
||||||
|
{
|
||||||
|
UCVarValue value;
|
||||||
|
value.Float = (float)val;
|
||||||
|
mCVar->SetGenericRep(value, CVAR_Float);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
class FOptionMenuSliderVar : public FOptionMenuSliderBase
|
||||||
|
{
|
||||||
|
float *mPVal;
|
||||||
|
public:
|
||||||
|
|
||||||
|
FOptionMenuSliderVar(const char *label, float *pVal, double min, double max, double step, int showval)
|
||||||
|
: FOptionMenuSliderBase(label, min, max, step, showval)
|
||||||
|
{
|
||||||
|
mPVal = pVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
double GetSliderValue()
|
||||||
|
{
|
||||||
|
return *mPVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetSliderValue(double val)
|
||||||
|
{
|
||||||
|
*mPVal = (float)val;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// [TP] FOptionMenuFieldBase
|
||||||
|
//
|
||||||
|
// Base class for input fields
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
class FOptionMenuFieldBase : public FOptionMenuItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FOptionMenuFieldBase ( const char* label, const char* menu, const char* graycheck ) :
|
||||||
|
FOptionMenuItem ( label, menu ),
|
||||||
|
mCVar ( FindCVar( mAction, NULL )),
|
||||||
|
mGrayCheck (( graycheck && strlen( graycheck )) ? FindCVar( graycheck, NULL ) : NULL ) {}
|
||||||
|
|
||||||
|
const char* GetCVarString()
|
||||||
|
{
|
||||||
|
if ( mCVar == NULL )
|
||||||
|
return "";
|
||||||
|
|
||||||
|
return mCVar->GetGenericRep( CVAR_String ).String;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual FString Represent()
|
||||||
|
{
|
||||||
|
return GetCVarString();
|
||||||
|
}
|
||||||
|
|
||||||
|
int Draw ( FOptionMenuDescriptor*, int y, int indent, bool selected )
|
||||||
|
{
|
||||||
|
bool grayed = mGrayCheck != NULL && !( mGrayCheck->GetGenericRep( CVAR_Bool ).Bool );
|
||||||
|
drawLabel( indent, y, selected ? OptionSettings.mFontColorSelection : OptionSettings.mFontColor, grayed );
|
||||||
|
int overlay = grayed? MAKEARGB( 96, 48, 0, 0 ) : 0;
|
||||||
|
|
||||||
|
DrawText(&twod, SmallFont, OptionSettings.mFontColorValue, indent + CURSORSPACE, y,
|
||||||
|
Represent().GetChars(), DTA_CleanNoMove_1, true, DTA_ColorOverlay, overlay, TAG_DONE );
|
||||||
|
return indent;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetString ( int i, char* s, int len )
|
||||||
|
{
|
||||||
|
if ( i == 0 )
|
||||||
|
{
|
||||||
|
strncpy( s, GetCVarString(), len );
|
||||||
|
s[len - 1] = '\0';
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SetString ( int i, const char* s )
|
||||||
|
{
|
||||||
|
if ( i == 0 )
|
||||||
|
{
|
||||||
|
if ( mCVar )
|
||||||
|
{
|
||||||
|
UCVarValue vval;
|
||||||
|
vval.String = s;
|
||||||
|
mCVar->SetGenericRep( vval, CVAR_String );
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Action is a CVar in this class and derivatives.
|
||||||
|
FBaseCVar* mCVar;
|
||||||
|
FBaseCVar* mGrayCheck;
|
||||||
|
};
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// [TP] FOptionMenuTextField
|
||||||
|
//
|
||||||
|
// A text input field widget, for use with string CVars.
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
class FOptionMenuTextField : public FOptionMenuFieldBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FOptionMenuTextField ( const char *label, const char* menu, const char* graycheck ) :
|
||||||
|
FOptionMenuFieldBase ( label, menu, graycheck ),
|
||||||
|
mEntering ( false ) {}
|
||||||
|
|
||||||
|
FString Represent()
|
||||||
|
{
|
||||||
|
FString text = mEntering ? mEditName : GetCVarString();
|
||||||
|
|
||||||
|
if ( mEntering )
|
||||||
|
text += '_';
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Draw(FOptionMenuDescriptor*desc, int y, int indent, bool selected)
|
||||||
|
{
|
||||||
|
if (mEntering)
|
||||||
|
{
|
||||||
|
// reposition the text so that the cursor is visible when in entering mode.
|
||||||
|
FString text = Represent();
|
||||||
|
int tlen = SmallFont->StringWidth(text) * CleanXfac_1;
|
||||||
|
int newindent = screen->GetWidth() - tlen - CURSORSPACE;
|
||||||
|
if (newindent < indent) indent = newindent;
|
||||||
|
}
|
||||||
|
return FOptionMenuFieldBase::Draw(desc, y, indent, selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MenuEvent ( int mkey, bool fromcontroller )
|
||||||
|
{
|
||||||
|
if ( mkey == MKEY_Enter )
|
||||||
|
{
|
||||||
|
//S_Sound( CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE );
|
||||||
|
strcpy( mEditName, GetCVarString() );
|
||||||
|
mEntering = true;
|
||||||
|
//DMenu* input = new DTextEnterMenu ( DMenu::CurrentMenu, mEditName, sizeof mEditName, 2, fromcontroller );
|
||||||
|
//M_ActivateMenu( input );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if ( mkey == MKEY_Input )
|
||||||
|
{
|
||||||
|
if ( mCVar )
|
||||||
|
{
|
||||||
|
UCVarValue vval;
|
||||||
|
vval.String = mEditName;
|
||||||
|
mCVar->SetGenericRep( vval, CVAR_String );
|
||||||
|
}
|
||||||
|
|
||||||
|
mEntering = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if ( mkey == MKEY_Abort )
|
||||||
|
{
|
||||||
|
mEntering = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FOptionMenuItem::MenuEvent( mkey, fromcontroller );
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool mEntering;
|
||||||
|
char mEditName[128];
|
||||||
|
};
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// [TP] FOptionMenuNumberField
|
||||||
|
//
|
||||||
|
// A numeric input field widget, for use with number CVars where sliders are inappropriate (i.e.
|
||||||
|
// where the user is interested in the exact value specifically)
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
class FOptionMenuNumberField : public FOptionMenuFieldBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FOptionMenuNumberField ( const char *label, const char* menu, float minimum, float maximum,
|
||||||
|
float step, const char* graycheck )
|
||||||
|
: FOptionMenuFieldBase ( label, menu, graycheck ),
|
||||||
|
mMinimum ( minimum ),
|
||||||
|
mMaximum ( maximum ),
|
||||||
|
mStep ( step )
|
||||||
|
{
|
||||||
|
if ( mMaximum <= mMinimum )
|
||||||
|
std::swap( mMinimum, mMaximum );
|
||||||
|
|
||||||
|
if ( mStep <= 0 )
|
||||||
|
mStep = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MenuEvent ( int mkey, bool fromcontroller )
|
||||||
|
{
|
||||||
|
if ( mCVar )
|
||||||
|
{
|
||||||
|
float value = mCVar->GetGenericRep( CVAR_Float ).Float;
|
||||||
|
|
||||||
|
if ( mkey == MKEY_Left )
|
||||||
|
{
|
||||||
|
value -= mStep;
|
||||||
|
|
||||||
|
if ( value < mMinimum )
|
||||||
|
value = mMaximum;
|
||||||
|
}
|
||||||
|
else if ( mkey == MKEY_Right || mkey == MKEY_Enter )
|
||||||
|
{
|
||||||
|
value += mStep;
|
||||||
|
|
||||||
|
if ( value > mMaximum )
|
||||||
|
value = mMinimum;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return FOptionMenuItem::MenuEvent( mkey, fromcontroller );
|
||||||
|
|
||||||
|
UCVarValue vval;
|
||||||
|
vval.Float = value;
|
||||||
|
mCVar->SetGenericRep( vval, CVAR_Float );
|
||||||
|
//S_Sound( CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE );
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
float mMinimum;
|
||||||
|
float mMaximum;
|
||||||
|
float mStep;
|
||||||
|
};
|
149
source/common/menu/readthis.cpp
Normal file
149
source/common/menu/readthis.cpp
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
/*
|
||||||
|
** readthis.cpp
|
||||||
|
** Help screens
|
||||||
|
**
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
** Copyright 2001-2010 Randy Heit
|
||||||
|
** Copyright 2010 Christoph Oelckers
|
||||||
|
** All rights reserved.
|
||||||
|
**
|
||||||
|
** Redistribution and use in source and binary forms, with or without
|
||||||
|
** modification, are permitted provided that the following conditions
|
||||||
|
** are met:
|
||||||
|
**
|
||||||
|
** 1. Redistributions of source code must retain the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer.
|
||||||
|
** 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer in the
|
||||||
|
** documentation and/or other materials provided with the distribution.
|
||||||
|
** 3. The name of the author may not be used to endorse or promote products
|
||||||
|
** derived from this software without specific prior written permission.
|
||||||
|
**
|
||||||
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "menu/menu.h"
|
||||||
|
#include "v_draw.h"
|
||||||
|
#include "textures/textures.h"
|
||||||
|
|
||||||
|
#if 0 // This is probably useless. To be replaced with what Build games use to draw their help screens.
|
||||||
|
|
||||||
|
class DReadThisMenu : public DMenu
|
||||||
|
{
|
||||||
|
DECLARE_CLASS(DReadThisMenu, DMenu)
|
||||||
|
int mScreen;
|
||||||
|
int mInfoTic;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
DReadThisMenu(DMenu *parent = NULL);
|
||||||
|
void Drawer();
|
||||||
|
bool MenuEvent(int mkey, bool fromcontroller);
|
||||||
|
bool DimAllowed () { return false; }
|
||||||
|
bool MouseEvent(int type, int x, int y);
|
||||||
|
};
|
||||||
|
|
||||||
|
IMPLEMENT_CLASS(DReadThisMenu)
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// Read This Menus
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
DReadThisMenu::DReadThisMenu(DMenu *parent)
|
||||||
|
: DMenu(parent)
|
||||||
|
{
|
||||||
|
mScreen = 1;
|
||||||
|
mInfoTic = gametic;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
void DReadThisMenu::Drawer()
|
||||||
|
{
|
||||||
|
FTexture *tex = NULL, *prevpic = NULL;
|
||||||
|
fixed_t alpha;
|
||||||
|
|
||||||
|
// Did the mapper choose a custom help page via MAPINFO?
|
||||||
|
if ((level.info != NULL) && level.info->F1Pic.Len() != 0)
|
||||||
|
{
|
||||||
|
tex = TexMan.FindTexture(level.info->F1Pic);
|
||||||
|
mScreen = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tex == NULL)
|
||||||
|
{
|
||||||
|
tex = TexMan[gameinfo.infoPages[mScreen-1].GetChars()];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mScreen > 1)
|
||||||
|
{
|
||||||
|
prevpic = TexMan[gameinfo.infoPages[mScreen-2].GetChars()];
|
||||||
|
}
|
||||||
|
|
||||||
|
screen->Dim(0, 1.0, 0,0, SCREENWIDTH, SCREENHEIGHT);
|
||||||
|
alpha = MIN<fixed_t> (Scale (gametic - mInfoTic, OPAQUE, TICRATE/3), OPAQUE);
|
||||||
|
if (alpha < OPAQUE && prevpic != NULL)
|
||||||
|
{
|
||||||
|
screen->DrawTexture (prevpic, 0, 0, DTA_Fullscreen, true, TAG_DONE);
|
||||||
|
}
|
||||||
|
screen->DrawTexture (tex, 0, 0, DTA_Fullscreen, true, DTA_Alpha, alpha, TAG_DONE);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
bool DReadThisMenu::MenuEvent(int mkey, bool fromcontroller)
|
||||||
|
{
|
||||||
|
if (mkey == MKEY_Enter)
|
||||||
|
{
|
||||||
|
S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE);
|
||||||
|
mScreen++;
|
||||||
|
mInfoTic = gametic;
|
||||||
|
if ((level.info != NULL && level.info->F1Pic.Len() != 0) || mScreen > int(gameinfo.infoPages.Size()))
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else return Super::MenuEvent(mkey, fromcontroller);
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
bool DReadThisMenu::MouseEvent(int type, int x, int y)
|
||||||
|
{
|
||||||
|
if (type == MOUSE_Click)
|
||||||
|
{
|
||||||
|
return MenuEvent(MKEY_Enter, true);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
48
source/common/utility/gstrings.h
Normal file
48
source/common/utility/gstrings.h
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
** gstrings.h
|
||||||
|
**
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
** Copyright 1998-2006 Randy Heit
|
||||||
|
** All rights reserved.
|
||||||
|
**
|
||||||
|
** Redistribution and use in source and binary forms, with or without
|
||||||
|
** modification, are permitted provided that the following conditions
|
||||||
|
** are met:
|
||||||
|
**
|
||||||
|
** 1. Redistributions of source code must retain the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer.
|
||||||
|
** 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer in the
|
||||||
|
** documentation and/or other materials provided with the distribution.
|
||||||
|
** 3. The name of the author may not be used to endorse or promote products
|
||||||
|
** derived from this software without specific prior written permission.
|
||||||
|
**
|
||||||
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __GSTRINGS_H__
|
||||||
|
#define __GSTRINGS_H__
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma once
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "stringtable.h"
|
||||||
|
|
||||||
|
extern FStringTable GStrings;
|
||||||
|
|
||||||
|
extern const char *endmsg[];
|
||||||
|
|
||||||
|
|
||||||
|
#endif //__GSTRINGS_H__
|
|
@ -34,6 +34,8 @@
|
||||||
#ifndef NAME_H
|
#ifndef NAME_H
|
||||||
#define NAME_H
|
#define NAME_H
|
||||||
|
|
||||||
|
#include "tarray.h"
|
||||||
|
|
||||||
enum ENamedName
|
enum ENamedName
|
||||||
{
|
{
|
||||||
#define xx(n) NAME_##n,
|
#define xx(n) NAME_##n,
|
||||||
|
@ -50,8 +52,6 @@ public:
|
||||||
FName (const char *text) { Index = NameData.FindName (text, false); }
|
FName (const char *text) { Index = NameData.FindName (text, false); }
|
||||||
FName (const char *text, bool noCreate) { Index = NameData.FindName (text, noCreate); }
|
FName (const char *text, bool noCreate) { Index = NameData.FindName (text, noCreate); }
|
||||||
FName (const char *text, size_t textlen, bool noCreate) { Index = NameData.FindName (text, textlen, noCreate); }
|
FName (const char *text, size_t textlen, bool noCreate) { Index = NameData.FindName (text, textlen, noCreate); }
|
||||||
FName (const FString &text);
|
|
||||||
FName (const FString &text, bool noCreate);
|
|
||||||
FName (const FName &other) = default;
|
FName (const FName &other) = default;
|
||||||
FName (ENamedName index) { Index = index; }
|
FName (ENamedName index) { Index = index; }
|
||||||
// ~FName () {} // Names can be added but never removed.
|
// ~FName () {} // Names can be added but never removed.
|
||||||
|
@ -122,4 +122,13 @@ protected:
|
||||||
static NameManager NameData;
|
static NameManager NameData;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template<> struct THashTraits<FName>
|
||||||
|
{
|
||||||
|
hash_t Hash(FName key)
|
||||||
|
{
|
||||||
|
return key.GetIndex();
|
||||||
|
}
|
||||||
|
int Compare(FName left, FName right) { return left != right; }
|
||||||
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -8,3 +8,5 @@ xx(SEQ)
|
||||||
xx(SFX)
|
xx(SFX)
|
||||||
xx(RAW)
|
xx(RAW)
|
||||||
xx(MAP)
|
xx(MAP)
|
||||||
|
xx(Mainmenu)
|
||||||
|
xx(Controlmessage)
|
666
source/common/utility/stringtable.cpp
Normal file
666
source/common/utility/stringtable.cpp
Normal file
|
@ -0,0 +1,666 @@
|
||||||
|
/*
|
||||||
|
** stringtable.cpp
|
||||||
|
** Implements the FStringTable class
|
||||||
|
**
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
** Copyright 1998-2006 Randy Heit
|
||||||
|
** All rights reserved.
|
||||||
|
**
|
||||||
|
** Redistribution and use in source and binary forms, with or without
|
||||||
|
** modification, are permitted provided that the following conditions
|
||||||
|
** are met:
|
||||||
|
**
|
||||||
|
** 1. Redistributions of source code must retain the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer.
|
||||||
|
** 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer in the
|
||||||
|
** documentation and/or other materials provided with the distribution.
|
||||||
|
** 3. The name of the author may not be used to endorse or promote products
|
||||||
|
** derived from this software without specific prior written permission.
|
||||||
|
**
|
||||||
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "stringtable.h"
|
||||||
|
#include "cmdlib.h"
|
||||||
|
#include "filesystem.h"
|
||||||
|
#include "sc_man.h"
|
||||||
|
#include "c_dispatch.h"
|
||||||
|
#include "v_text.h"
|
||||||
|
#include "c_cvars.h"
|
||||||
|
#include "printf.h"
|
||||||
|
|
||||||
|
EXTERN_CVAR(String, language)
|
||||||
|
CUSTOM_CVAR(Int, cl_gender, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||||
|
{
|
||||||
|
if (self < 0 || self > 3) self = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FStringTable::LoadStrings ()
|
||||||
|
{
|
||||||
|
int lastlump, lump;
|
||||||
|
|
||||||
|
lastlump = 0;
|
||||||
|
while ((lump = fileSystem.Iterate("Language/lmacros", &lastlump, ELookupMode::NoExtension)) != -1)
|
||||||
|
{
|
||||||
|
readMacros(lump);
|
||||||
|
}
|
||||||
|
|
||||||
|
lastlump = 0;
|
||||||
|
while ((lump = fileSystem.Iterate ("Language/language", &lastlump, ELookupMode::NoExtension)) != -1)
|
||||||
|
{
|
||||||
|
auto lumpdata = fileSystem.GetFileData(lump);
|
||||||
|
|
||||||
|
if (!ParseLanguageCSV(lump, lumpdata))
|
||||||
|
LoadLanguage (lump, lumpdata);
|
||||||
|
}
|
||||||
|
UpdateLanguage();
|
||||||
|
allMacros.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// This was tailored to parse CSV as exported by Google Docs.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
|
||||||
|
TArray<TArray<FString>> FStringTable::parseCSV(const TArray<uint8_t> &buffer)
|
||||||
|
{
|
||||||
|
const size_t bufLength = buffer.Size();
|
||||||
|
TArray<TArray<FString>> data;
|
||||||
|
TArray<FString> row;
|
||||||
|
TArray<char> cell;
|
||||||
|
bool quoted = false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
auto myisspace = [](int ch) { return ch == '\t' || ch == '\r' || ch == '\n' || ch == ' '; };
|
||||||
|
while (*vcopy && myisspace((unsigned char)*vcopy)) vcopy++; // skip over leaading whitespace;
|
||||||
|
auto vend = vcopy + strlen(vcopy);
|
||||||
|
while (vend > vcopy && myisspace((unsigned char)vend[-1])) *--vend = 0; // skip over trailing whitespace
|
||||||
|
*/
|
||||||
|
|
||||||
|
for (size_t i = 0; i < bufLength; ++i)
|
||||||
|
{
|
||||||
|
if (buffer[i] == '"')
|
||||||
|
{
|
||||||
|
// Double quotes inside a quoted string count as an escaped quotation mark.
|
||||||
|
if (quoted && i < bufLength - 1 && buffer[i + 1] == '"')
|
||||||
|
{
|
||||||
|
cell.Push('"');
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
else if (cell.Size() == 0 || quoted)
|
||||||
|
{
|
||||||
|
quoted = !quoted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (buffer[i] == ',')
|
||||||
|
{
|
||||||
|
if (!quoted)
|
||||||
|
{
|
||||||
|
cell.Push(0);
|
||||||
|
ProcessEscapes(cell.Data());
|
||||||
|
row.Push(cell.Data());
|
||||||
|
cell.Clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cell.Push(buffer[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (buffer[i] == '\r')
|
||||||
|
{
|
||||||
|
// Ignore all CR's.
|
||||||
|
}
|
||||||
|
else if (buffer[i] == '\n' && !quoted)
|
||||||
|
{
|
||||||
|
cell.Push(0);
|
||||||
|
ProcessEscapes(cell.Data());
|
||||||
|
row.Push(cell.Data());
|
||||||
|
data.Push(std::move(row));
|
||||||
|
cell.Clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cell.Push(buffer[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle last line without linebreak
|
||||||
|
if (cell.Size() > 0 || row.Size() > 0)
|
||||||
|
{
|
||||||
|
cell.Push(0);
|
||||||
|
ProcessEscapes(cell.Data());
|
||||||
|
row.Push(cell.Data());
|
||||||
|
data.Push(std::move(row));
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
bool FStringTable::readMacros(int lumpnum)
|
||||||
|
{
|
||||||
|
auto lumpdata = fileSystem.GetFileData(lumpnum);
|
||||||
|
auto data = parseCSV(lumpdata);
|
||||||
|
|
||||||
|
for (unsigned i = 1; i < data.Size(); i++)
|
||||||
|
{
|
||||||
|
auto macroname = data[i][0];
|
||||||
|
auto language = data[i][1];
|
||||||
|
if (macroname.IsEmpty() || language.IsEmpty()) continue;
|
||||||
|
FStringf combined_name("%s/%s", language.GetChars(), macroname.GetChars());
|
||||||
|
FName name = combined_name.GetChars();
|
||||||
|
|
||||||
|
StringMacro macro;
|
||||||
|
|
||||||
|
for (int k = 0; k < 4; k++)
|
||||||
|
{
|
||||||
|
macro.Replacements[k] = data[i][k+2];
|
||||||
|
}
|
||||||
|
allMacros.Insert(name, macro);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
bool FStringTable::ParseLanguageCSV(int lumpnum, const TArray<uint8_t> &buffer)
|
||||||
|
{
|
||||||
|
if (memcmp(buffer.Data(), "default,", 8)) return false;
|
||||||
|
auto data = parseCSV(buffer);
|
||||||
|
|
||||||
|
int labelcol = -1;
|
||||||
|
int filtercol = -1;
|
||||||
|
TArray<std::pair<int, unsigned>> langrows;
|
||||||
|
bool hasDefaultEntry = false;
|
||||||
|
|
||||||
|
if (data.Size() > 0)
|
||||||
|
{
|
||||||
|
for (unsigned column = 0; column < data[0].Size(); column++)
|
||||||
|
{
|
||||||
|
auto &entry = data[0][column];
|
||||||
|
if (entry.CompareNoCase("filter") == 0)
|
||||||
|
{
|
||||||
|
filtercol = column;
|
||||||
|
}
|
||||||
|
else if (entry.CompareNoCase("identifier") == 0)
|
||||||
|
{
|
||||||
|
labelcol = column;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto languages = entry.Split(" ", FString::TOK_SKIPEMPTY);
|
||||||
|
for (auto &lang : languages)
|
||||||
|
{
|
||||||
|
if (lang.CompareNoCase("default") == 0)
|
||||||
|
{
|
||||||
|
langrows.Push(std::make_pair(column, default_table));
|
||||||
|
hasDefaultEntry = true;
|
||||||
|
}
|
||||||
|
else if (lang.Len() < 4)
|
||||||
|
{
|
||||||
|
lang.ToLower();
|
||||||
|
langrows.Push(std::make_pair(column, MAKE_ID(lang[0], lang[1], lang[2], 0)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned i = 1; i < data.Size(); i++)
|
||||||
|
{
|
||||||
|
auto &row = data[i];
|
||||||
|
#if 0
|
||||||
|
if (filtercol > -1)
|
||||||
|
{
|
||||||
|
auto filterstr = row[filtercol];
|
||||||
|
auto filter = filterstr.Split(" ", FString::TOK_SKIPEMPTY);
|
||||||
|
if (filter.Size() > 0 && filter.FindEx([](const auto &str) { return str.CompareNoCase(GameNames[gameinfo.gametype]) == 0; }) == filter.Size())
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FName strName = row[labelcol].GetChars();
|
||||||
|
if (hasDefaultEntry)
|
||||||
|
{
|
||||||
|
DeleteForLabel(lumpnum, strName);
|
||||||
|
}
|
||||||
|
for (auto &langentry : langrows)
|
||||||
|
{
|
||||||
|
auto str = row[langentry.first];
|
||||||
|
if (str.Len() > 0)
|
||||||
|
{
|
||||||
|
InsertString(lumpnum, langentry.second, strName, str);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DeleteString(langentry.second, strName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FStringTable::LoadLanguage (int lumpnum, const TArray<uint8_t> &buffer)
|
||||||
|
{
|
||||||
|
bool errordone = false;
|
||||||
|
TArray<uint32_t> activeMaps;
|
||||||
|
FScanner sc;
|
||||||
|
bool hasDefaultEntry = false;
|
||||||
|
|
||||||
|
sc.OpenMem("LANGUAGE", (const char*)buffer.Data(), buffer.Size());
|
||||||
|
sc.SetCMode (true);
|
||||||
|
while (sc.GetString ())
|
||||||
|
{
|
||||||
|
if (sc.Compare ("["))
|
||||||
|
{ // Process language identifiers
|
||||||
|
activeMaps.Clear();
|
||||||
|
sc.MustGetString ();
|
||||||
|
do
|
||||||
|
{
|
||||||
|
size_t len = sc.StringLen;
|
||||||
|
if (len != 2 && len != 3)
|
||||||
|
{
|
||||||
|
if (len == 1 && sc.String[0] == '~')
|
||||||
|
{
|
||||||
|
// deprecated and ignored
|
||||||
|
sc.ScriptMessage("Deprecated option '~' found in language list");
|
||||||
|
sc.MustGetString ();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (len == 1 && sc.String[0] == '*')
|
||||||
|
{
|
||||||
|
activeMaps.Clear();
|
||||||
|
activeMaps.Push(global_table);
|
||||||
|
}
|
||||||
|
else if (len == 7 && stricmp (sc.String, "default") == 0)
|
||||||
|
{
|
||||||
|
activeMaps.Clear();
|
||||||
|
activeMaps.Push(default_table);
|
||||||
|
hasDefaultEntry = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sc.ScriptError ("The language code must be 2 or 3 characters long.\n'%s' is %lu characters long.",
|
||||||
|
sc.String, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (activeMaps.Size() != 1 || (activeMaps[0] != default_table && activeMaps[0] != global_table))
|
||||||
|
activeMaps.Push(MAKE_ID(tolower(sc.String[0]), tolower(sc.String[1]), tolower(sc.String[2]), 0));
|
||||||
|
}
|
||||||
|
sc.MustGetString ();
|
||||||
|
} while (!sc.Compare ("]"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ // Process string definitions.
|
||||||
|
if (activeMaps.Size() == 0)
|
||||||
|
{
|
||||||
|
// LANGUAGE lump is bad. We need to check if this is an old binary
|
||||||
|
// lump and if so just skip it to allow old WADs to run which contain
|
||||||
|
// such a lump.
|
||||||
|
if (!sc.isText())
|
||||||
|
{
|
||||||
|
if (!errordone) Printf("Skipping binary 'LANGUAGE' lump.\n");
|
||||||
|
errordone = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sc.ScriptError ("Found a string without a language specified.");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool skip = false;
|
||||||
|
#if 0 // I don't think this is needed.
|
||||||
|
if (sc.Compare("$"))
|
||||||
|
{
|
||||||
|
sc.MustGetStringName("ifgame");
|
||||||
|
sc.MustGetStringName("(");
|
||||||
|
sc.MustGetString();
|
||||||
|
if (sc.Compare("strifeteaser"))
|
||||||
|
{
|
||||||
|
skip |= (gameinfo.gametype != GAME_Strife) || !(gameinfo.flags & GI_SHAREWARE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
skip |= !sc.Compare(GameTypeName());
|
||||||
|
}
|
||||||
|
sc.MustGetStringName(")");
|
||||||
|
sc.MustGetString();
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FName strName (sc.String);
|
||||||
|
sc.MustGetStringName ("=");
|
||||||
|
sc.MustGetString ();
|
||||||
|
FString strText (sc.String, ProcessEscapes (sc.String));
|
||||||
|
sc.MustGetString ();
|
||||||
|
while (!sc.Compare (";"))
|
||||||
|
{
|
||||||
|
ProcessEscapes (sc.String);
|
||||||
|
strText += sc.String;
|
||||||
|
sc.MustGetString ();
|
||||||
|
}
|
||||||
|
if (!skip)
|
||||||
|
{
|
||||||
|
if (hasDefaultEntry)
|
||||||
|
{
|
||||||
|
DeleteForLabel(lumpnum, strName);
|
||||||
|
}
|
||||||
|
// Insert the string into all relevant tables.
|
||||||
|
for (auto map : activeMaps)
|
||||||
|
{
|
||||||
|
InsertString(lumpnum, map, strName, strText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FStringTable::DeleteString(int langid, FName label)
|
||||||
|
{
|
||||||
|
allStrings[langid].Remove(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// This deletes all older entries for a given label. This gets called
|
||||||
|
// when a string in the default table gets updated.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FStringTable::DeleteForLabel(int lumpnum, FName label)
|
||||||
|
{
|
||||||
|
decltype(allStrings)::Iterator it(allStrings);
|
||||||
|
decltype(allStrings)::Pair *pair;
|
||||||
|
auto filenum = fileSystem.GetFileContainer(lumpnum);
|
||||||
|
|
||||||
|
while (it.NextPair(pair))
|
||||||
|
{
|
||||||
|
auto entry = pair->Value.CheckKey(label);
|
||||||
|
if (entry && entry->filenum < filenum)
|
||||||
|
{
|
||||||
|
pair->Value.Remove(label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FStringTable::InsertString(int lumpnum, int langid, FName label, const FString &string)
|
||||||
|
{
|
||||||
|
const char *strlangid = (const char *)&langid;
|
||||||
|
TableElement te = { fileSystem.GetFileContainer(lumpnum), { string, string, string, string } };
|
||||||
|
long index;
|
||||||
|
while ((index = te.strings[0].IndexOf("@[")) >= 0)
|
||||||
|
{
|
||||||
|
auto endindex = te.strings[0].IndexOf(']', index);
|
||||||
|
if (endindex == -1)
|
||||||
|
{
|
||||||
|
Printf("Bad macro in %s : %s\n", strlangid, label.GetChars());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
FString macroname(te.strings[0].GetChars() + index + 2, endindex - index - 2);
|
||||||
|
FStringf lookupstr("%s/%s", strlangid, macroname.GetChars());
|
||||||
|
FStringf replacee("@[%s]", macroname.GetChars());
|
||||||
|
FName lookupname(lookupstr.GetChars(), true);
|
||||||
|
auto replace = allMacros.CheckKey(lookupname);
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
const char *replacement = replace? replace->Replacements[i].GetChars() : "";
|
||||||
|
te.strings[i].Substitute(replacee, replacement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
allStrings[langid].Insert(label, te);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FStringTable::UpdateLanguage()
|
||||||
|
{
|
||||||
|
size_t langlen = strlen(language);
|
||||||
|
|
||||||
|
int LanguageID = (langlen < 2 || langlen > 3) ?
|
||||||
|
MAKE_ID('e', 'n', 'u', '\0') :
|
||||||
|
MAKE_ID(language[0], language[1], language[2], '\0');
|
||||||
|
|
||||||
|
currentLanguageSet.Clear();
|
||||||
|
|
||||||
|
auto checkone = [&](uint32_t lang_id)
|
||||||
|
{
|
||||||
|
auto list = allStrings.CheckKey(lang_id);
|
||||||
|
if (list && currentLanguageSet.FindEx([&](const auto &element) { return element.first == lang_id; }) == currentLanguageSet.Size())
|
||||||
|
currentLanguageSet.Push(std::make_pair(lang_id, list));
|
||||||
|
};
|
||||||
|
|
||||||
|
checkone(global_table);
|
||||||
|
checkone(LanguageID);
|
||||||
|
checkone(LanguageID & MAKE_ID(0xff, 0xff, 0, 0));
|
||||||
|
checkone(default_table);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Replace \ escape sequences in a string with the escaped characters.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
size_t FStringTable::ProcessEscapes (char *iptr)
|
||||||
|
{
|
||||||
|
char *sptr = iptr, *optr = iptr, c;
|
||||||
|
|
||||||
|
while ((c = *iptr++) != '\0')
|
||||||
|
{
|
||||||
|
if (c == '\\')
|
||||||
|
{
|
||||||
|
c = *iptr++;
|
||||||
|
if (c == 'n')
|
||||||
|
c = '\n';
|
||||||
|
else if (c == 'c')
|
||||||
|
c = TEXTCOLOR_ESCAPE;
|
||||||
|
else if (c == 'r')
|
||||||
|
c = '\r';
|
||||||
|
else if (c == 't')
|
||||||
|
c = '\t';
|
||||||
|
else if (c == '\n')
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
*optr++ = c;
|
||||||
|
}
|
||||||
|
*optr = '\0';
|
||||||
|
return optr - sptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Checks if the given key exists in any one of the default string tables that are valid for all languages.
|
||||||
|
// To replace IWAD content this condition must be true.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
bool FStringTable::exists(const char *name)
|
||||||
|
{
|
||||||
|
if (name == nullptr || *name == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
FName nm(name, true);
|
||||||
|
if (nm != NAME_None)
|
||||||
|
{
|
||||||
|
uint32_t defaultStrings[] = { default_table, global_table };
|
||||||
|
|
||||||
|
for (auto mapid : defaultStrings)
|
||||||
|
{
|
||||||
|
auto map = allStrings.CheckKey(mapid);
|
||||||
|
if (map)
|
||||||
|
{
|
||||||
|
auto item = map->CheckKey(nm);
|
||||||
|
if (item) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Finds a string by name and returns its value
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
const char *FStringTable::GetString(const char *name, uint32_t *langtable, int gender) const
|
||||||
|
{
|
||||||
|
if (name == nullptr || *name == 0)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (gender == -1) gender = cl_gender;
|
||||||
|
if (gender < 0 || gender > 3) gender = 0;
|
||||||
|
FName nm(name, true);
|
||||||
|
if (nm != NAME_None)
|
||||||
|
{
|
||||||
|
for (auto map : currentLanguageSet)
|
||||||
|
{
|
||||||
|
auto item = map.second->CheckKey(nm);
|
||||||
|
if (item)
|
||||||
|
{
|
||||||
|
if (langtable) *langtable = map.first;
|
||||||
|
return item->strings[gender].GetChars();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Finds a string by name in a given language
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
const char *FStringTable::GetLanguageString(const char *name, uint32_t langtable, int gender) const
|
||||||
|
{
|
||||||
|
if (name == nullptr || *name == 0)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (gender == -1) gender = cl_gender;
|
||||||
|
if (gender < 0 || gender > 3) gender = 0;
|
||||||
|
FName nm(name, true);
|
||||||
|
if (nm != NAME_None)
|
||||||
|
{
|
||||||
|
auto map = allStrings.CheckKey(langtable);
|
||||||
|
if (map == nullptr) return nullptr;
|
||||||
|
auto item = map->CheckKey(nm);
|
||||||
|
if (item)
|
||||||
|
{
|
||||||
|
return item->strings[gender].GetChars();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FStringTable::MatchDefaultString(const char *name, const char *content) const
|
||||||
|
{
|
||||||
|
// This only compares the first line to avoid problems with bad linefeeds. For the few cases where this feature is needed it is sufficient.
|
||||||
|
auto c = GetLanguageString(name, FStringTable::default_table);
|
||||||
|
if (!c) return false;
|
||||||
|
|
||||||
|
// Check a secondary key, in case the text comparison cannot be done due to needed orthographic fixes (see Harmony's exit text)
|
||||||
|
FStringf checkkey("%s_CHECK", name);
|
||||||
|
auto cc = GetLanguageString(checkkey, FStringTable::default_table);
|
||||||
|
if (cc) c = cc;
|
||||||
|
|
||||||
|
return (c && !strnicmp(c, content, strcspn(content, "\n\r\t")));
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Finds a string by name and returns its value. If the string does
|
||||||
|
// not exist, returns the passed name instead.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
const char *FStringTable::operator() (const char *name) const
|
||||||
|
{
|
||||||
|
const char *str = operator[] (name);
|
||||||
|
return str ? str : name;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Find a string with the same exact text. Returns its name.
|
||||||
|
// This does not need to check genders, it is only used by
|
||||||
|
// Dehacked on the English table for finding stock strings.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
const char *StringMap::MatchString (const char *string) const
|
||||||
|
{
|
||||||
|
StringMap::ConstIterator it(*this);
|
||||||
|
StringMap::ConstPair *pair;
|
||||||
|
|
||||||
|
while (it.NextPair(pair))
|
||||||
|
{
|
||||||
|
if (pair->Value.strings[0].CompareNoCase(string) == 0)
|
||||||
|
{
|
||||||
|
return pair->Key.GetChars();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
FStringTable GStrings;
|
||||||
|
CVAR(String, language, "en", CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
116
source/common/utility/stringtable.h
Normal file
116
source/common/utility/stringtable.h
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
/*
|
||||||
|
** stringtable.h
|
||||||
|
**
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
** Copyright 1998-2006 Randy Heit
|
||||||
|
** All rights reserved.
|
||||||
|
**
|
||||||
|
** Redistribution and use in source and binary forms, with or without
|
||||||
|
** modification, are permitted provided that the following conditions
|
||||||
|
** are met:
|
||||||
|
**
|
||||||
|
** 1. Redistributions of source code must retain the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer.
|
||||||
|
** 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer in the
|
||||||
|
** documentation and/or other materials provided with the distribution.
|
||||||
|
** 3. The name of the author may not be used to endorse or promote products
|
||||||
|
** derived from this software without specific prior written permission.
|
||||||
|
**
|
||||||
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
**
|
||||||
|
**
|
||||||
|
** FStringTable
|
||||||
|
**
|
||||||
|
** This class manages a list of localizable strings stored in a wad file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __STRINGTABLE_H__
|
||||||
|
#define __STRINGTABLE_H__
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma once
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "basics.h"
|
||||||
|
#include "zstring.h"
|
||||||
|
#include "tarray.h"
|
||||||
|
#include "name.h"
|
||||||
|
|
||||||
|
struct TableElement
|
||||||
|
{
|
||||||
|
int filenum;
|
||||||
|
FString strings[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
// This public interface is for Dehacked
|
||||||
|
class StringMap : public TMap<FName, TableElement>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
const char *MatchString(const char *string) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct StringMacro
|
||||||
|
{
|
||||||
|
FString Replacements[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class FStringTable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum : uint32_t
|
||||||
|
{
|
||||||
|
default_table = MAKE_ID('*', '*', 0, 0),
|
||||||
|
global_table = MAKE_ID('*', 0, 0, 0),
|
||||||
|
};
|
||||||
|
|
||||||
|
using LangMap = TMap<uint32_t, StringMap>;
|
||||||
|
using StringMacroMap = TMap<FName, StringMacro>;
|
||||||
|
|
||||||
|
void LoadStrings ();
|
||||||
|
void UpdateLanguage();
|
||||||
|
|
||||||
|
const char *GetLanguageString(const char *name, uint32_t langtable, int gender = -1) const;
|
||||||
|
bool MatchDefaultString(const char *name, const char *content) const;
|
||||||
|
const char *GetString(const char *name, uint32_t *langtable, int gender = -1) const;
|
||||||
|
const char *operator() (const char *name) const; // Never returns NULL
|
||||||
|
const char *operator[] (const char *name) const
|
||||||
|
{
|
||||||
|
return GetString(name, nullptr);
|
||||||
|
}
|
||||||
|
bool exists(const char *name);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
StringMacroMap allMacros;
|
||||||
|
LangMap allStrings;
|
||||||
|
TArray<std::pair<uint32_t, StringMap*>> currentLanguageSet;
|
||||||
|
|
||||||
|
void LoadLanguage (int lumpnum, const TArray<uint8_t> &buffer);
|
||||||
|
TArray<TArray<FString>> parseCSV(const TArray<uint8_t> &buffer);
|
||||||
|
bool ParseLanguageCSV(int lumpnum, const TArray<uint8_t> &buffer);
|
||||||
|
|
||||||
|
bool LoadLanguageFromSpreadsheet(int lumpnum, const TArray<uint8_t> &buffer);
|
||||||
|
bool readMacros(int lumpnum);
|
||||||
|
void InsertString(int lumpnum, int langid, FName label, const FString &string);
|
||||||
|
void DeleteString(int langid, FName label);
|
||||||
|
void DeleteForLabel(int lumpnum, FName label);
|
||||||
|
|
||||||
|
static size_t ProcessEscapes (char *str);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //__STRINGTABLE_H__
|
BIN
wadsrc/static/fonts/consolefont/0000.png
Normal file
BIN
wadsrc/static/fonts/consolefont/0000.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.7 KiB |
BIN
wadsrc/static/fonts/consolefont/0100.png
Normal file
BIN
wadsrc/static/fonts/consolefont/0100.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
BIN
wadsrc/static/fonts/consolefont/0400.png
Normal file
BIN
wadsrc/static/fonts/consolefont/0400.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
3
wadsrc/static/fonts/consolefont/font.inf
Normal file
3
wadsrc/static/fonts/consolefont/font.inf
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
TranslationType Console
|
||||||
|
CellSize 8, 8 // This implies font sheets
|
||||||
|
|
Loading…
Reference in a new issue