Added Enigma game

This commit is contained in:
pelya
2010-10-13 17:30:44 +03:00
parent 8bd2d39dfe
commit bf7d3f22c6
308 changed files with 92986 additions and 39 deletions

View File

@@ -0,0 +1,114 @@
/*
* Copyright (C) 2006 Ronald Lamprecht
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "gui/ErrorMenu.hh"
#include "enigma.hh"
#include "video.hh"
using namespace ecl;
using namespace std;
namespace enigma { namespace gui {
/* -------------------- HelpMenu -------------------- */
ErrorMenu::ErrorMenu(std::string message, std::string quitTitle) :
text (message), rejectQuit (false), laterQuit (false),
quit (new gui::StaticTextButton(quitTitle, this)) {
const video::VMInfo *vminfo = video::GetInfo();
add(quit, Rect(vminfo->width-170, vminfo->height-60, 150, 40));
}
ErrorMenu::ErrorMenu(std::string message, std::string quitTitle, std::string rejectTitle) :
text (message), rejectQuit (false), laterQuit (false),
quit (new gui::StaticTextButton(quitTitle, this)),
reject (new gui::StaticTextButton(rejectTitle, this)) {
const video::VMInfo *vminfo = video::GetInfo();
add(quit, Rect(vminfo->width-170, vminfo->height-60, 150, 40));
add(reject, Rect(vminfo->width-340, vminfo->height-60, 150, 40));
}
ErrorMenu::ErrorMenu(std::string message, std::string quitTitle, std::string rejectTitle,
std::string laterTitle) :
text (message), rejectQuit (false), laterQuit (false),
quit (new gui::StaticTextButton(quitTitle, this)),
reject (new gui::StaticTextButton(rejectTitle, this)),
later (new gui::StaticTextButton(laterTitle, this)) {
const video::VMInfo *vminfo = video::GetInfo();
add(quit, Rect(vminfo->width-170, vminfo->height-60, 150, 40));
add(later, Rect(vminfo->width-340, vminfo->height-60, 150, 40));
add(reject, Rect(vminfo->width-510, vminfo->height-60, 150, 40));
}
bool ErrorMenu::isRejectQuit() {
return rejectQuit;
}
bool ErrorMenu::isLaterQuit() {
return laterQuit;
}
bool ErrorMenu::on_event (const SDL_Event &e)
{
if (e.type == SDL_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_RIGHT) {
Menu::quit();
return true;
}
return false;
}
void ErrorMenu::on_action (gui::Widget *w) {
if (w == quit) {
Menu::quit();
} else if (w == reject) {
rejectQuit = true;
Menu::quit();
} else if (w == later) {
laterQuit = true;
Menu::quit();
}
}
void ErrorMenu::draw_background (ecl::GC &gc) {
blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg"));
Font *f = enigma::GetFont("menufont");
vector<string> lines;
ecl::split_copy (text, '\n', back_inserter(lines));
int x = 60;
int y = 60;
int yskip = 25;
const video::VMInfo *vminfo = video::GetInfo();
int width = vminfo->width - 120;
for (unsigned i=0; i<lines.size(); ) {
std::string::size_type breakPos = breakString(f, lines[i],
" ", width);
f->render(gc, x, y, lines[i].substr(0,breakPos).c_str());
y += yskip;
if (breakPos != lines[i].size()) {
// process rest of line
lines[i] = lines[i].substr(breakPos);
} else {
// process next line
i++;
}
}
}
}} // namespace enigma::gui

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2006 Ronald Lamprecht
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef GUI_ERRORMENU_HH_INCLUDED
#define GUI_ERRORMENU_HH_INCLUDED
#include "gui/Menu.hh"
#include "ecl.hh"
namespace enigma { namespace gui {
class ErrorMenu : public Menu {
public:
ErrorMenu (std::string message, std::string quitTitle);
ErrorMenu (std::string message, std::string quitTitle, std::string rejectTitle);
ErrorMenu (std::string message, std::string quitTitle, std::string rejectTitle,
std::string laterTitle);
bool isRejectQuit();
bool isLaterQuit();
private:
bool on_event (const SDL_Event &e);
void on_action (gui::Widget *w);
void draw_background (ecl::GC &gc);
std::string text;
gui::Widget *quit;
gui::Widget *reject;
gui::Widget *later;
bool rejectQuit;
bool laterQuit;
};
}} // namespace enigma::gui
#endif // GUI_ERRORMENU_HH_INCLUDED

View File

@@ -0,0 +1,177 @@
/*
* Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "gui/GameMenu.hh"
#include "gui/OptionsMenu.hh"
#include "gui/LevelInspector.hh"
#include "client.hh"
#include "display.hh"
#include "ecl.hh"
#include "main.hh"
#include "nls.hh"
#include "server.hh"
#include "video.hh"
#include "lev/Index.hh"
#include "lev/Proxy.hh"
#include <cassert>
using namespace ecl;
using namespace std;
namespace enigma { namespace gui {
/* -------------------- GameMenu -------------------- */
GameMenu::GameMenu (int zoomxpos_, int zoomypos_)
: zoomed(0),
zoomxpos(zoomxpos_),
zoomypos(zoomypos_)
{
resume = new gui::StaticTextButton(N_("Resume Level"), this);
restart = new gui::StaticTextButton(N_("Restart Level"), this);
options = new gui::StaticTextButton(N_("Options"), this);
info = new gui::StaticTextButton(N_("Level Info"), this);
abort = new gui::StaticTextButton(N_("Abort Level"), this);
add(resume, Rect(0,0,180,40));
add(restart, Rect(0,45,180,40));
add(options, Rect(0,90,180,40));
add(info, Rect(0,135,180,40));
add(abort, Rect(0,180,180,40));
center();
}
GameMenu::~GameMenu() {
delete(zoomed);
}
void GameMenu::draw_background(ecl::GC &gc)
{
const video::VMInfo *vminfo = video::GetInfo();
if (!zoomed) {
Rect game_area = display::GetGameArea();
int part_width = game_area.w/3;
int part_height = (part_width*vminfo->height)/vminfo->width;
if (part_height > game_area.h) {
part_height = game_area.h/3;
part_width = (part_height*vminfo->width)/vminfo->height;
assert(part_width <= game_area.w);
}
// randomly choose ball offset
int x, y;
for (int trials = 5; trials; --trials) {
x = IntegerRand(0, 5);
y = IntegerRand(0, 3);
// try to avoid menu-ball overlap:
if (x<2 || x>3 || y<1 || y>2 || (trials == 1)) {
int ax = zoomxpos-game_area.x;
int ay = zoomypos-game_area.y;
// upper left corner of part
x = ax/32-1-x;
y = ay/32-1-y;
// ensure part is inside game_area
x = max(0, min(x, (game_area.w-part_width)/32-1));
y = max(0, min(y, (game_area.h-part_height)/32-1));
// adjust to game fields
x = x*32+24;
y = y*32+16;
break;
}
}
// Be sure to redraw everything, or actors may appear on top
// of the stones (actors are drawn in one pass and only
// clipped to the screen boundary).
display::RedrawAll(video::GetScreen());
// get the selected part from screen
// SDL_Surface *back = video::GetScreen()->get_surface();
Rect src_area(game_area.x+x, game_area.y+y, part_width, part_height);
Surface *src = Grab(video::GetScreen()->get_surface(), src_area);
// zoom multiple times for softer image
// const double stepsize = 0.3;
// for (double zoom = 0.4; zoom < 0.9; zoom += stepsize) {
// int sx = round_down<int>(zoom * vminfo->width);
// int sy = round_down<int>(zoom * vminfo->height);
// Surface *tmp = src->zoom(sx, sy);
// delete src;
// src = tmp;
// }
zoomed = src->zoom(vminfo->width, vminfo->height);
delete src;
}
ecl::blit(gc, 0,0, zoomed);
}
bool GameMenu::on_event (const SDL_Event &e)
{
if (e.type == SDL_MOUSEBUTTONDOWN
&& e.button.button == SDL_BUTTON_RIGHT)
{
Menu::quit();
return true;
}
return false;
}
void GameMenu::on_action(gui::Widget *w) {
lev::Index *ind = lev::Index::getCurrentIndex();
if (w == resume) {
Menu::quit();
}
else if (w == abort) {
client::Msg_Command("abort");
Menu::quit();
}
else if (w == restart) {
if (w->lastModifierKeys() & KMOD_CTRL && w->lastModifierKeys() & KMOD_SHIFT) {
// force a reload from file
lev::Proxy * curProxy = lev::Proxy::loadedLevel();
if (curProxy != NULL)
curProxy->release();
}
client::Stop ();
server::Msg_LoadLevel(ind->getCurrent(), false);
Menu::quit();
}
else if (w == options) {
enigma::gui::ShowOptionsMenu (0);
invalidate_all();
// Menu::quit();
}
else if (w == info) {
LevelInspector m(ind->getCurrent());
m.manage();
invalidate_all();
// Menu::quit();
}
}
}} // namespace enigma::gui

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef GUI_GAMEMENU_HH_INCLUDED
#define GUI_GAMEMENU_HH_INCLUDED
#include "gui/Menu.hh"
namespace enigma { namespace gui {
/* -------------------- GameMenu -------------------- */
class GameMenu : public Menu {
public:
GameMenu(int zoomxpos_, int zoomypos_);
virtual ~GameMenu();
private:
bool on_event (const SDL_Event &e);
void on_action(gui::Widget *w);
void draw_background(ecl::GC &gc);
gui::Widget *resume, *restart, *options, *info, *abort;
ecl::Surface *zoomed;
int zoomxpos, zoomypos; // position to be zoomed
};
}} // namespace enigma::gui
#endif

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 2003,2004 Daniel Heck, Ralf Westram
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "gui/HelpMenu.hh"
#include "enigma.hh"
#include "video.hh"
#include "nls.hh"
using namespace ecl;
using namespace std;
namespace enigma { namespace gui {
/* -------------------- HelpMenu -------------------- */
HelpMenu::HelpMenu (const char **helptext_, int xoffset_) :
helptext (helptext_),
ok (new gui::StaticTextButton(N_("Ok"), this)),
cfg (xoffset_)
{
const video::VMInfo &vminfo = *video::GetInfo();
add(ok, Rect(vminfo.width-170,vminfo.height-60,150,40));
}
bool HelpMenu::on_event (const SDL_Event &e)
{
if (e.type == SDL_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_RIGHT)
{
Menu::quit();
return true;
}
return false;
}
void HelpMenu::on_action (gui::Widget *w)
{
if (w == ok)
Menu::quit();
}
void HelpMenu::draw_background (ecl::GC &gc)
{
const video::VMInfo &vminfo = *video::GetInfo();
blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg"));
Font *f = enigma::GetFont(cfg.fontname.c_str());
int y = cfg.y0 + (vminfo.height - 480)/2;
int x = (vminfo.width-640)/2;
for (int i = 0; helptext[i]; i += 2)
{
f->render (gc, cfg.x0 + x, y, _(helptext[i])); // translate
f->render (gc, cfg.x1 + x, y, _(helptext[i+1])); // translate
y += cfg.yskip;
}
}
/* -------------------- Functions -------------------- */
void displayHelp(const char **helptext, int xoffset)
{
FX_Fade (video::FADEOUT);
HelpMenu menu(helptext, xoffset);
menu.draw_all();
FX_Fade (video::FADEIN);
menu.manage();
}
}} // namespace enigma::gui

View File

@@ -0,0 +1,60 @@
/*
* Copyright (C) 2002,2003 Daniel Heck, Ralf Westram
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef ENIGMA_HELP_HH
#define ENIGMA_HELP_HH
#include "gui/Menu.hh"
#include "ecl.hh"
namespace enigma { namespace gui {
struct HelpMenuConfig {
int x0, x1; // x coordinates of first and second column
int y0; // y coordinate
int yskip;
std::string fontname;
HelpMenuConfig (int xoffset) {
x0 = 40;
x1 = x0 + xoffset;
y0 = 40;
yskip = 30;
fontname = "menufont";
}
};
class HelpMenu : public Menu {
public:
HelpMenu (const char **helptext_, int xoffset);
private:
bool on_event (const SDL_Event &e);
void on_action (gui::Widget *w);
void draw_background (ecl::GC &gc);
const char **helptext;
gui::Widget *ok;
HelpMenuConfig cfg;
};
void displayHelp (const char **helptext, int xoffset);
}} // namespace enigma::gui
#endif // ENIGMA_HELP_HH

View File

@@ -0,0 +1,84 @@
/*
* Copyright (C) 2006 Ronald Lamprecht
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "gui/InfoMenu.hh"
#include "ecl.hh"
#include "enigma.hh"
#include "video.hh"
#include "nls.hh"
using namespace ecl;
using namespace std;
namespace enigma { namespace gui {
InfoMenu::InfoMenu(const char **infotext, int pages) : info (infotext),
curPage (0), numPages (pages) {
const video::VMInfo &vminfo = *video::GetInfo();
but_ok = new StaticTextButton(N_("Ok"), this);
pgup = new ImageButton("ic-up", "ic-up1", this);
pgdown = new ImageButton("ic-down", "ic-down1", this);
add(but_ok, Rect(vminfo.width-120, vminfo.height-60, 100, 40));
add(pgup, Rect(vminfo.width-30, vminfo.height/2, 20, 50));
add(pgdown, Rect(vminfo.width-30, vminfo.height/2 +70, 20, 50));
}
void InfoMenu::draw_background(ecl::GC &gc) {
const video::VMInfo &vminfo = *video::GetInfo();
blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg"));
Font *f = enigma::GetFont("menufont");
int row = 0;
for (int p=0; p<curPage; p++) {
while (info[row])
row++;
// select first row of next page
row++;
}
for (int i = 0; info[row]; row++, i++) {
const char *t = _(info[row]);
f->render (gc, 40 + (vminfo.width-640)/2,
20 + (vminfo.height-480)/2 + i*f->get_height(), t);
}
}
void InfoMenu::on_action (gui::Widget *w) {
if (w == but_ok) {
Menu::quit();
} else if (w == pgup) {
if (curPage > 0) {
curPage--;
invalidate_all();
}
} else if (w == pgdown) {
if (curPage < numPages - 1) {
curPage++;
invalidate_all();
}
}
}
void displayInfo(const char **infotext, int pages) {
FX_Fade (video::FADEOUT);
InfoMenu menu(infotext, pages);
menu.draw_all();
FX_Fade (video::FADEIN);
menu.manage();
}
}} // namespace enigma::gui

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C) 2006 Ronald Lamprecht
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef GUI_INFOMENU_HH_INCLUDED
#define GUI_INFOMENU_HH_INCLUDED
#include "gui/Menu.hh"
namespace enigma { namespace gui {
class InfoMenu : public gui::Menu {
public:
InfoMenu(const char **infotext, int pages);
void on_action(Widget *w);
void draw_background(ecl::GC &gc);
private:
const char **info;
int curPage;
int numPages;
Widget *but_ok;
Widget *pgup;
Widget *pgdown;
};
void displayInfo(const char **helptext, int pages);
}} // namespace enigma::gui
#endif

View File

@@ -0,0 +1,231 @@
/*
* Copyright (C) 2006 Ronald Lamprecht
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "gui/LPGroupConfig.hh"
#include "gui/LevelPackConfig.hh"
#include "ecl.hh"
#include "errors.hh"
#include "nls.hh"
#include "video.hh"
#include "lev/Index.hh"
#include "main.hh"
using namespace ecl;
using namespace std;
namespace enigma { namespace gui {
LPGroupConfig::LPGroupConfig(std::string groupName) : oldGroupName (groupName) {
const video::VMInfo &vminfo = *video::GetInfo();
groups = lev::Index::getGroupNames();
position = -1;
for (int i = 0; i < groups.size(); i++) {
if (groups[i] == groupName) {
position = i;
break;
}
}
oldPosition = position;
if (position < 0) {
groups.push_back(groupName);
position = groups.size() - 1;
}
VList * titleVList = new VList;
titleVList->set_spacing(12);
titleVList->set_alignment(HALIGN_LEFT, VALIGN_CENTER);
titleVList->set_default_size(160, 35);
Label * groupLabel = new Label(N_("Group:"), HALIGN_RIGHT);
titleVList->add_back(groupLabel);
VList * groupsVList = new VList;
groupsVList->set_spacing(12);
groupsVList->set_alignment(HALIGN_LEFT, VALIGN_CENTER);
groupsVList->set_default_size(160, 35);
Label * positionLabel = new Label(N_("Position:"), HALIGN_LEFT);
pre2Group = new UntranslatedLabel("");
pre1Group = new UntranslatedLabel("");
tf_groupName = new TextField(groupName);
post1Group = new UntranslatedLabel("");
post2Group = new UntranslatedLabel("");
Label * dummyLabel = new Label("");
groupsVList->add_back(positionLabel);
groupsVList->add_back(pre2Group);
groupsVList->add_back(pre1Group);
groupsVList->add_back(tf_groupName);
groupsVList->add_back(post1Group);
groupsVList->add_back(post2Group);
groupsVList->add_back(dummyLabel);
VList * scrollVList = new VList;
scrollVList->set_spacing(12);
scrollVList->set_alignment(HALIGN_LEFT, VALIGN_CENTER);
scrollVList->set_default_size(40, 35);
scrollUp = new ImageButton("ic-up", "ic-up1", this);
scrollDown = new ImageButton("ic-down", "ic-down1", this);
scrollVList->add_back(scrollUp);
scrollVList->add_back(scrollDown);
this->add(titleVList, Rect(vminfo.width/2 - 290, 0, 160, vminfo.height-100));
this->add(groupsVList, Rect(vminfo.width/2 - 80, 0, 160, vminfo.height-100));
this->add(scrollVList, Rect(vminfo.width/2 + 130, 0, 40, vminfo.height-100));
errorLabel = new Label("", HALIGN_CENTER);
this->add(errorLabel, Rect(10, vminfo.height-100, vminfo.width-20, 35));
// Create buttons - positioning identical to Levelmenu
but_newPack = new StaticTextButton(N_("New Pack"), this);
but_delete = new StaticTextButton(N_("Delete Group"), this);
but_ignore = new StaticTextButton(N_("Undo"), this);
but_ok = new StaticTextButton(N_("Ok"), this);
HList * commandHList = new HList;
commandHList->set_spacing(10);
commandHList->set_alignment(HALIGN_CENTER, VALIGN_TOP);
commandHList->set_default_size(140, 35);
commandHList->add_back(but_newPack);
commandHList->add_back(but_delete);
commandHList->add_back(but_ignore);
commandHList->add_back(but_ok);
this->add(commandHList, Rect(10, vminfo.height-50, vminfo.width-20, 35));
updateGroupList();
}
void LPGroupConfig::updateGroupList() {
pre2Group->set_text((position > 1) ? groups[position - 2] : "");
pre1Group->set_text((position > 0) ? groups[position - 1] : "");
post1Group->set_text((position < groups.size() - 1) ? groups[position + 1] : "");
post2Group->set_text((position < groups.size() - 2) ? groups[position + 2] : "");
}
bool LPGroupConfig::doChanges() {
// rename first for consistency
std::string newName = tf_groupName->getText();
std::string::size_type lastChar = newName.find_last_not_of(" ");
if (lastChar == std::string::npos) {
// the name is effectively an empty string
errorLabel->set_text(N_("Error: empty group name not allowed - press \"Undo\" to exit without modifications"));
return false;
}
// strip off trailing and leading spaces
newName = newName.substr(0 , lastChar + 1);
newName = newName.substr(newName.find_first_not_of(" "));
// check if new group name is unique
for (int i = 0; i < groups.size(); i++) {
if (i != position && groups[i] == newName) {
errorLabel->set_text(N_("Error: group name is a duplicate of an existing group"));
return false;
}
}
if (newName == INDEX_EVERY_GROUP) {
errorLabel->set_text(N_("Error: \"Every group\" is a reserved group name"));
return false;
}
if (newName.size() > 2 && newName[0] == '['
&& newName[newName.size() -1] == ']') {
errorLabel->set_text(N_("Error: group name must not be enclosed in square brackets"));
return false;
}
if (oldGroupName.empty()) {
// menu called without an existing group
lev::Index::insertGroup(newName, position);
} else {
// menu called with an existing group
if (newName != oldGroupName)
lev::Index::renameGroup(oldGroupName, newName);
if (oldPosition >= 0 && position != oldPosition) {
// move the group to the new position
lev::Index::moveGroup(newName, position);
}
}
return true;
}
void LPGroupConfig::on_action(Widget *w) {
if (w == but_ok) {
if (doChanges())
Menu::quit();
else
invalidate_all();
} else if (w == but_ignore) {
Menu::quit();
} else if (w == scrollUp) {
if (position > 0) {
std::string tmp = groups[position];
groups[position] = groups[position - 1];
groups[position - 1] = tmp;
position--;
updateGroupList();
invalidate_all();
}
} else if (w == scrollDown) {
if (position < groups.size() - 1) {
std::string tmp = groups[position];
groups[position] = groups[position + 1];
groups[position + 1] = tmp;
position++;
updateGroupList();
invalidate_all();
}
} else if (w == but_delete) {
std::vector<lev::Index *> * indices = lev::Index::getGroup(oldGroupName);
if (indices != NULL) {
// reassign remaining indices from back to front to keep the
// group vector valid
for (int i = indices->size() - 1; i >= 0; i--) {
if ((*indices)[i]->getGroupName() != INDEX_EVERY_GROUP) {
LevelPackConfig m((*indices)[i]->getName(), oldGroupName, true);
if (!m.manage() || m.isUndoQuit()) {
errorLabel->set_text(N_("Error: group not empty"));
invalidate_all();
return;
}
}
}
lev::Index::deleteGroup(oldGroupName);
}
Menu::quit();
} else if (w == but_newPack) {
if (doChanges()) {
LevelPackConfig m("");
m.manage();
Menu::quit();
} else {
invalidate_all();
}
}
}
void LPGroupConfig::draw_background(ecl::GC &gc) {
video::SetCaption(("Enigma - Level Pack Group Configuration"));
blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg"));
}
}} // namespace enigma::gui

View File

@@ -0,0 +1,56 @@
/*
* Copyright (C) 2006 Ronald Lamprecht
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef GUI_LPGROUPCONFIG_HH_INCLUDED
#define GUI_LPGROUPCONFIG_HH_INCLUDED
#include "gui/Menu.hh"
#include "gui/TextField.hh"
namespace enigma { namespace gui {
class LPGroupConfig : public gui::Menu {
public:
LPGroupConfig (std::string groupName);
void on_action(Widget *w);
void draw_background(ecl::GC &gc);
private:
void updateGroupList();
bool doChanges();
TextField *tf_groupName;
Label *pre2Group;
Label *pre1Group;
Label *post1Group;
Label *post2Group;
Widget *scrollUp;
Widget *scrollDown;
Label *errorLabel;
Widget *but_newPack;
Widget *but_delete;
Widget *but_ignore;
Widget *but_ok;
std::vector<std::string> groups;
int position; // new position of group that the user selected
int oldPosition; // position of group when entering menu, -1 for new group
std::string oldGroupName;
};
}} // namespace enigma::gui
#endif

View File

@@ -0,0 +1,712 @@
/*
* Copyright (C) 2006, 2007 Ronald Lamprecht
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "errors.hh"
#include "gui/LevelInspector.hh"
#include "main.hh"
#include "enigma.hh"
#include "nls.hh"
#include "video.hh"
#include "ecl_util.hh"
#include "gui/MonospacedLabel.hh"
#include "gui/LevelPreviewCache.hh"
#include "gui/ScreenshotViewer.hh"
#include "lev/RatingManager.hh"
#include "lev/ScoreManager.hh"
#include "StateManager.hh"
#include <vector>
using namespace ecl;
using namespace enigma;
namespace enigma { namespace gui {
class IntelligenceButton : public ValueButton {
int get_value() const {
return theRatingMgr->getIntelligence(theLevel);
}
void set_value(int value) {
theRatingMgr->setIntelligence(theLevel, value);
}
string get_text(int value) const {
return strf("%d", value);
}
public:
IntelligenceButton(lev::Proxy *aLevel) : ValueButton(0, 5),
theLevel (aLevel) {
theRatingMgr = lev::RatingManager::instance();
init();
}
private:
lev::RatingManager *theRatingMgr;
lev::Proxy *theLevel;
};
class DexterityButton : public ValueButton {
int get_value() const {
return theRatingMgr->getDexterity(theLevel);
}
void set_value(int value) {
theRatingMgr->setDexterity(theLevel, value);
}
string get_text(int value) const {
return strf("%d", value);
}
public:
DexterityButton(lev::Proxy *aLevel) : ValueButton(0, 5),
theLevel (aLevel) {
theRatingMgr = lev::RatingManager::instance();
init();
}
private:
lev::RatingManager *theRatingMgr;
lev::Proxy *theLevel;
};
class PatienceButton : public ValueButton {
int get_value() const {
return theRatingMgr->getPatience(theLevel);
}
void set_value(int value) {
theRatingMgr->setPatience(theLevel, value);
}
string get_text(int value) const {
return strf("%d", value);
}
public:
PatienceButton(lev::Proxy *aLevel) : ValueButton(0, 5),
theLevel (aLevel) {
theRatingMgr = lev::RatingManager::instance();
init();
}
private:
lev::RatingManager *theRatingMgr;
lev::Proxy *theLevel;
};
class KnowledgeButton : public ValueButton {
int get_value() const {
return theRatingMgr->getKnowledge(theLevel);
}
void set_value(int value) {
theRatingMgr->setKnowledge(theLevel, value);
}
string get_text(int value) const {
return strf("%d", value);
}
public:
KnowledgeButton(lev::Proxy *aLevel) : ValueButton(0, 6),
theLevel (aLevel) {
theRatingMgr = lev::RatingManager::instance();
init();
}
private:
lev::RatingManager *theRatingMgr;
lev::Proxy *theLevel;
};
class SpeedButton : public ValueButton {
int get_value() const {
return theRatingMgr->getSpeed(theLevel);
}
void set_value(int value) {
theRatingMgr->setSpeed(theLevel, value);
}
string get_text(int value) const {
return strf("%d", value);
}
public:
SpeedButton(lev::Proxy *aLevel) : ValueButton(0, 5),
theLevel (aLevel) {
theRatingMgr = lev::RatingManager::instance();
init();
}
private:
lev::RatingManager *theRatingMgr;
lev::Proxy *theLevel;
};
class RatingButton : public ValueButton {
int get_value() const {
return theScoreMgr->getRating(theLevel);
}
void set_value(int value) {
theScoreMgr->setRating(theLevel, value);
if (get_parent() != NULL) {
get_parent()->invalidate_all();
}
}
string get_text(int value) const {
return (value == -1) ? "-" : ecl::strf("%d", value);
}
public:
RatingButton(lev::Proxy *aLevel) : ValueButton(-1, 10),
theLevel (aLevel) {
theScoreMgr = lev::ScoreManager::instance();
init();
}
private:
lev::ScoreManager *theScoreMgr;
lev::Proxy *theLevel;
};
LevelInspector::LevelInspector(lev::Proxy *aLevel, bool showDeveloperInfo):
levelProxy(aLevel), isDeveloperMode(showDeveloperInfo), annotation (new TextField()),
back (new StaticTextButton(N_("Ok"), this)),
screenshot (new StaticTextButton(N_("Screenshot"), this))
{
bool didGenerate; // dummy
previewImage = LevelPreviewCache::instance()->getPreview(aLevel, true, didGenerate);
const video::VMInfo *vminfo = video::GetInfo();
vspacing = vminfo->height < 500 ? 2 :(vminfo->height < 650 ? 3 : 4);
vspacing2 = vminfo->height < 500 ? 16 :(vminfo->height < 650 ? 14 : 16);
vmargin = vminfo->height < 500 ? 10 :(vminfo->height < 650 ? 20 : 30);
hmargin = vminfo->width < 660 ? 10 : (vminfo->width < 900 ? 20 : 30);
bool highres = vminfo->height > 650 ? true : false;
bool lowres = vminfo->height < 600 ? true : false;
add(back, Rect(vminfo->width-130-2*hmargin,vminfo->height-50,130,35));
add(screenshot, Rect(vminfo->width-260-3*hmargin,vminfo->height-50,130,35));
try {
aLevel->loadMetadata(true);
}
catch (XLevelLoading &err) {
std::vector<string> lines;
std::string errmsg = _("Server Error: could not load level '")
+ aLevel->getNormLevelPath() + "'\n"
+ err.what();
ecl::split_copy (errmsg, '\n', back_inserter(lines));
int x = 60;
int y = 60;
int yskip = 25;
for (unsigned i=0; i<lines.size(); ++i) {
add(new Label(lines[i], HALIGN_LEFT), Rect(x, y, vminfo->width-80,yskip));
y += yskip;
}
return;
}
std::string tmp, tmp2;
lev::RatingManager *theRatingMgr = lev::RatingManager::instance();
lev::ScoreManager *theScoreMgr = lev::ScoreManager::instance();
withEasy = aLevel->hasEasymode();
ratingInherited = theScoreMgr->isRatingInherited(aLevel);
ecl::Font *menufont = enigma::GetFont("menufont");
levelPathString =
(levelProxy->getNormPathType() == lev::Proxy::pt_resource) ?
levelProxy->getAbsLevelPath() : levelProxy->getNormLevelPath();
// substitute all backslashes by slashes
for (std::string::size_type slpos = levelPathString.find('\\');
slpos != std::string::npos; slpos = levelPathString.find('\\'))
levelPathString.replace(slpos, 1, 1, '/');
BuildVList authorT(this, Rect(hmargin,vmargin,110,25), vspacing);
authorT.add(new Label(N_("Level: "), HALIGN_RIGHT));
authorT.add(new Label((""), HALIGN_RIGHT)); // subtitle
authorT.add(new Label(N_("Author: "), HALIGN_RIGHT));
authorT.add(new Label(N_("Contact: "), HALIGN_RIGHT));
authorT.add(new Label(N_("Homepage: "), HALIGN_RIGHT));
BuildVList author(this, Rect(hmargin+110+10,vmargin,
vminfo->width-(hmargin+110+10)-10-(vminfo->thumbw+10)-hmargin,25), vspacing);
tmp = levelProxy->getTitle();
tmp2 = levelProxy->getLocalizedString("title");
if (tmp != tmp2)
tmp = tmp + " -- " + tmp2;
author.add(new Label( tmp, HALIGN_LEFT));
tmp = levelProxy->getLocalizedString("subtitle");
if (tmp == "subtitle")
tmp = "";
author.add(new Label(tmp, HALIGN_LEFT)); // subtitle
author.add(new Label(levelProxy->getAuthor(), HALIGN_LEFT));
BuildVList address(this, Rect(hmargin+110+10,vmargin+3*(25+vspacing),
vminfo->width-(hmargin+110+10)-hmargin,25), vspacing);
address.add(new Label(levelProxy->getContact(), HALIGN_LEFT));
address.add(new Label(levelProxy->getHomepage(), HALIGN_LEFT));
BuildVList ratingPubT(this, Rect(hmargin+65,vmargin+5*25+4*vspacing+vspacing2, 130,25), 2);
ratingPubT.add(new Label(N_("Public Ratings"), HALIGN_CENTER));
BuildVList ratingPubST(this, Rect(hmargin,vmargin+6*25+5*vspacing+vspacing2, 130,25), 2);
ratingPubST.add(new Label(N_("Intelligence: "), HALIGN_RIGHT));
ratingPubST.add(new Label(N_("Dexterity: "), HALIGN_RIGHT));
ratingPubST.add(new Label(N_("Patience: "), HALIGN_RIGHT));
ratingPubST.add(new Label(N_("Knowledge: "), HALIGN_RIGHT));
ratingPubST.add(new Label(N_("Speed: "), HALIGN_RIGHT));
if (!lowres) ratingPubST.add(new Label(N_("Difficulty: "), HALIGN_RIGHT));
BuildVList ratingPub(this, Rect(hmargin+130+15,vmargin+6*25+5*vspacing+vspacing2, 30,25), 2);
if (WizardMode) {
ratingPub.add(new IntelligenceButton(aLevel));
ratingPub.add(new DexterityButton(aLevel));
ratingPub.add(new PatienceButton(aLevel));
ratingPub.add(new KnowledgeButton(aLevel));
ratingPub.add(new SpeedButton(aLevel));
if (!lowres) ratingPub.add(new MonospacedLabel(ratingToString(theRatingMgr->getDifficulty(aLevel)).c_str(),'8', " 0123456789", HALIGN_CENTER));
} else {
ratingPub.add(new MonospacedLabel(ratingToString(theRatingMgr->getIntelligence(aLevel)).c_str(),'8', " 0123456789", HALIGN_CENTER));
ratingPub.add(new MonospacedLabel(ratingToString(theRatingMgr->getDexterity(aLevel)).c_str(),'8', " 0123456789", HALIGN_CENTER));
ratingPub.add(new MonospacedLabel(ratingToString(theRatingMgr->getPatience(aLevel)).c_str(),'8', " 0123456789", HALIGN_CENTER));
ratingPub.add(new MonospacedLabel(ratingToString(theRatingMgr->getKnowledge(aLevel)).c_str(),'8', " 0123456789", HALIGN_CENTER));
ratingPub.add(new MonospacedLabel(ratingToString(theRatingMgr->getSpeed(aLevel)).c_str(),'8', " 0123456789", HALIGN_CENTER));
if (!lowres) ratingPub.add(new MonospacedLabel(ratingToString(theRatingMgr->getDifficulty(aLevel)).c_str(),'8', " 0123456789", HALIGN_CENTER));
}
BuildVList scoresT(this, Rect(vminfo->width/2-100-20,vmargin+5*25+4*vspacing+vspacing2,100,25), 2);
scoresT.add(new Label(N_("Scores"), HALIGN_RIGHT));
BuildVList scoresST(this, Rect(vminfo->width/2-100-20,vmargin+6*25+5*vspacing+vspacing2,100,25), 2);
scoresST.add(new Label(N_("You: "), HALIGN_RIGHT));
scoresST.add(new Label(N_("World: "), HALIGN_RIGHT));
// TRANSLATORS: PAR = professional average rate - an expression used by golfers
scoresST.add(new Label(N_("PAR: "), HALIGN_RIGHT));
scoresST.add(new Label(N_("Author: "), HALIGN_RIGHT));
scoresST.add(new Label(N_("Solved %: "), HALIGN_RIGHT));
if (!lowres) scoresST.add(new Label(N_("Solved #: "), HALIGN_RIGHT));
BuildVList scores(this, Rect(vminfo->width/2-15+(withEasy?0:20),
vmargin+6*25+5*vspacing+vspacing2,(withEasy?117:54),25), 2);
scores.add(new MonospacedLabel(scoreToString(theScoreMgr->getBestUserScore(aLevel, DIFFICULTY_EASY),
theScoreMgr->getBestUserScore(aLevel, DIFFICULTY_HARD),aLevel,true).c_str(),'8', " 0123456789", HALIGN_CENTER));
scores.add(new MonospacedLabel(scoreToString(theRatingMgr->getBestScoreEasy(aLevel),
theRatingMgr->getBestScoreDifficult(aLevel),aLevel,true).c_str(),'8', " 0123456789", HALIGN_CENTER));
scores.add(new MonospacedLabel(scoreToString(theRatingMgr->getParScoreEasy(aLevel),
theRatingMgr->getParScoreDifficult(aLevel),aLevel,true).c_str(),'8', " 0123456789", HALIGN_CENTER));
scores.add(new MonospacedLabel(scoreToString(aLevel->getEasyScore(),
aLevel->getDifficultScore(),aLevel,true).c_str(),'8', " 0123456789", HALIGN_CENTER));
scores.add(new MonospacedLabel(withEasy ? (theRatingMgr->getPcSolvedEasy(aLevel) +
" /" + theRatingMgr->getPcSolvedDifficult(aLevel) + " ").c_str() :
(theRatingMgr->getPcSolvedDifficult(aLevel) + " ").c_str(),
'8', " 0123456789", HALIGN_CENTER));
if (!lowres) scores.add(new MonospacedLabel(withEasy ? (ecl::strf("%5d", theRatingMgr->getNumSolvedEasy(aLevel)) +
" /" + ecl::strf("%5d", theRatingMgr->getNumSolvedDifficult(aLevel)) + " ").c_str() :
(ecl::strf("%5d", theRatingMgr->getNumSolvedDifficult(aLevel)) + " ").c_str(),
'8', " 0123456789", HALIGN_CENTER));
BuildVList versionT(this, Rect(vminfo->width-100/2-90-2*hmargin,vmargin+5*25+4*vspacing+vspacing2,100,25), 2);
versionT.add(new Label(N_("Version"), HALIGN_CENTER));
BuildVList versionST(this, Rect(vminfo->width-110-90-2*hmargin,vmargin+6*25+5*vspacing+vspacing2,110,25), 2);
if (!lowres || aLevel->getLevelStatus() == lev::STATUS_RELEASED)
versionST.add(new Label(N_("Score: "), HALIGN_RIGHT));
else
versionST.add(new Label(N_("Status: "), HALIGN_RIGHT));
versionST.add(new Label(N_("Release: "), HALIGN_RIGHT));
versionST.add(new Label(N_("Revision: "), HALIGN_RIGHT));
if (!lowres)
versionST.add(new Label(N_("Status: "), HALIGN_RIGHT));
versionST.add(new Label(N_("Control: "), HALIGN_RIGHT));
versionST.add(new Label(N_("Target: "), HALIGN_RIGHT));
BuildVList version(this, Rect(vminfo->width-80-2*hmargin,vmargin+6*25+5*vspacing+vspacing2,80+2*hmargin,25), 2);
if (!lowres || aLevel->getLevelStatus() == lev::STATUS_RELEASED)
version.add(new MonospacedLabel(ecl::strf("%6d", aLevel->getScoreVersion()).c_str(),
'8', " 0123456789", HALIGN_LEFT));
else if (aLevel->getLevelStatus() == lev::STATUS_STABLE)
version.add(new Label(N_("stable"), HALIGN_LEFT));
else if (aLevel->getLevelStatus() == lev::STATUS_TEST)
version.add(new Label(N_("test"), HALIGN_LEFT));
else if (aLevel->getLevelStatus() == lev::STATUS_EXPERIMENTAL)
version.add(new Label(N_("experimental"), HALIGN_LEFT));
else
version.add(new Label(N_("unknown"), HALIGN_LEFT));
version.add(new MonospacedLabel(ecl::strf("%6d", aLevel->getReleaseVersion()).c_str(),
'8', " 0123456789", HALIGN_LEFT));
version.add(new MonospacedLabel(ecl::strf("%6d", aLevel->getRevisionNumber()).c_str(),
'8', " 0123456789", HALIGN_LEFT));
if (!lowres)
if (aLevel->getLevelStatus() == lev::STATUS_RELEASED)
version.add(new Label(N_("released"), HALIGN_LEFT));
else if (aLevel->getLevelStatus() == lev::STATUS_STABLE)
version.add(new Label(N_("stable"), HALIGN_LEFT));
else if (aLevel->getLevelStatus() == lev::STATUS_TEST)
version.add(new Label(N_("test"), HALIGN_LEFT));
else if (aLevel->getLevelStatus() == lev::STATUS_EXPERIMENTAL)
version.add(new Label(N_("experimental"), HALIGN_LEFT));
else
version.add(new Label(N_("unknown"), HALIGN_LEFT));
switch (aLevel->getControl()) {
case lev::force:
version.add(new Label(N_("force"), HALIGN_LEFT));
break;
case lev::balance:
version.add(new Label(N_("balance"), HALIGN_LEFT));
break;
case lev::key:
version.add(new Label(N_("key"), HALIGN_LEFT));
break;
default:
version.add(new Label(N_("unknown"), HALIGN_LEFT));
break;
}
#if 0
// fake gettext to register the following strings for I18N
_("time")
_("pushes")
_("moves")
#endif
version.add(new Label(aLevel->getScoreTarget().c_str(), HALIGN_LEFT));
int bestScoreHolderLines = 0;
int creditsLines = 0;
int dedicationLines = 0;
int levelPathLines = 0;
int annotationLines = 0;
int compatibilityLines = 0;
int idLines = 0;
int vnext = vmargin+ (lowres?11:12)*25+(lowres?9:10)*vspacing+2*vspacing2;
int textwidth = vminfo->width-3*hmargin-110-10;
dispatchBottomLines(bestScoreHolderLines, creditsLines, dedicationLines,
levelPathLines, annotationLines, compatibilityLines, idLines,
(vminfo->height-vnext-vmargin-25-vspacing2)/27, textwidth);
if (bestScoreHolderLines == 1) {
add(new Label(N_("World Record Holders: "), HALIGN_RIGHT),Rect(hmargin,vnext,200,25));
std::string holders;
if (withEasy) {
holders = theRatingMgr->getBestScoreEasyHolder(aLevel);
if (holders.empty())
holders = " - ";
holders += " / ";
}
if (theRatingMgr->getBestScoreDifficultHolder(aLevel).empty())
holders += " -";
else
holders += theRatingMgr->getBestScoreDifficultHolder(aLevel);
Label *wrLabel = new Label(holders, HALIGN_LEFT);
add(wrLabel, Rect(hmargin+200+10,vnext,textwidth-90,25));
if (!wrLabel->text_fits()) {
int cutEasy = 0;
int cutDiff = 0;
std::string diffHolders = theRatingMgr->getBestScoreDifficultHolder(aLevel);
if (withEasy) {
std::string easyHolders = theRatingMgr->getBestScoreEasyHolder(aLevel);
bool hasEasyHolders = !easyHolders.empty();
bool hasDiffHolders = !diffHolders.empty();
int limit = 10;
do {
std::string cutHolders;
wrLabel->set_text(easyHolders);
if (!wrLabel->text_fits(0.48)) {
cutHolders = theRatingMgr->getBestScoreEasyHolder(aLevel, ++cutEasy);
if (cutHolders.empty())
cutEasy--;
else
easyHolders = cutHolders;
}
wrLabel->set_text(diffHolders);
if (!wrLabel->text_fits(0.48)) {
cutHolders = theRatingMgr->getBestScoreDifficultHolder(aLevel, ++cutDiff);
if (cutHolders.empty())
cutDiff--;
else
diffHolders = cutHolders;
}
holders = (hasEasyHolders ? easyHolders : std::string(" - "))
+ " / " + (hasDiffHolders ? diffHolders : std::string(" -"));
wrLabel->set_text(holders);
limit--;
} while (!wrLabel->text_fits() && limit > 0);
} else {
std::string cutHolders;
do {
cutHolders = theRatingMgr->getBestScoreDifficultHolder(aLevel, ++cutDiff);
wrLabel->set_text(cutHolders);
} while (!wrLabel->text_fits());
if (cutHolders.empty()) {
// we did cut off to many holders, take last attempt even if it was too long
wrLabel->set_text(theRatingMgr->getBestScoreDifficultHolder(aLevel, --cutDiff));
}
}
}
vnext += 25 + vspacing;
}
if (creditsLines >= 1) {
add(new Label(N_("Credits: "), HALIGN_RIGHT),Rect(hmargin,vnext,110,25));
std::string creditsString = levelProxy->getCredits(true);
for (int i = 0; i< creditsLines; i++) {
std::string::size_type breakPos = breakString(menufont, creditsString,
" ", textwidth);
add(new Label(creditsString.substr(0,breakPos), HALIGN_LEFT), Rect(hmargin+110+10,vnext,textwidth,25));
creditsString = creditsString.substr(breakPos);
vnext += (25 + vspacing);
}
}
if (dedicationLines >= 1) {
add(new Label(N_("Dedication: "), HALIGN_RIGHT),Rect(hmargin,vnext,110,25));
std::string dedicationString = levelProxy->getDedication(true);
for (int i = 0; i< dedicationLines; i++) {
std::string::size_type breakPos = breakString( menufont, dedicationString,
" ", textwidth);
add(new Label(dedicationString.substr(0,breakPos), HALIGN_LEFT), Rect(hmargin+110+10,vnext,textwidth,25));
dedicationString = dedicationString.substr(breakPos);
vnext += (25 + vspacing);
}
}
if (levelPathLines >= 1) {
add(new Label(N_("Level Path: "), HALIGN_RIGHT),Rect(hmargin,vnext,110,25));
std::string workString = levelPathString;
for (int i = 0; i< levelPathLines - 1; i++) {
std::string::size_type breakPos = breakString(menufont, workString,
"/", textwidth);
add(new Label(workString.substr(0,breakPos), HALIGN_LEFT), Rect(hmargin+110+10,vnext,textwidth,25));
workString = workString.substr(breakPos);
vnext += (25 + vspacing);
}
// show as much as possible from last line
if (menufont->get_width(workString.c_str()) > textwidth) {
// show the filename at the end - skip leading parts if necessary
add(new Label(workString, HALIGN_RIGHT), Rect(hmargin+110+10,vnext,textwidth,25));
} else {
// display up to the last character
add(new Label(workString, HALIGN_LEFT), Rect(hmargin+110+10,vnext,textwidth,25));
}
vnext += (25 + vspacing);
}
if (idLines >= 1) {
add(new Label(N_("Id: "), HALIGN_RIGHT),Rect(hmargin,vnext,110,25));
add(new Label(levelProxy->getId(), HALIGN_LEFT),Rect(hmargin+110+10, vnext, textwidth, 25));
vnext += (25 + vspacing)*idLines;
}
if (compatibilityLines >= 1) {
add(new Label(N_("Compatibility: "), HALIGN_RIGHT),Rect(hmargin,vnext,110,25));
std::string compString = ecl::strf("Enigma v%.2f / ", levelProxy->getEnigmaCompatibility()) +
GetGameTypeName(levelProxy->getEngineCompatibility());
add(new Label(compString , HALIGN_LEFT),Rect(hmargin+110+10, vnext, textwidth, 25));
vnext += (25 + vspacing)*compatibilityLines;
}
annotation->set_text(app.state->getAnnotation(levelProxy->getId())); // field needs to initialized for saves
if (annotationLines >= 1) {
add(new Label(N_("Annotation: "), HALIGN_RIGHT),Rect(hmargin,vnext,110,25));
add(annotation, Rect(hmargin+110+10, vnext, textwidth, 25));
vnext += (25 + vspacing)*annotationLines;
}
vnext += vspacing2 - vspacing;
add(new Label(N_("Rating: "), HALIGN_RIGHT),Rect(hmargin,vnext,110,25));
add(new RatingButton(aLevel),Rect(hmargin+110+10,vnext,40,25));
add(new Label(N_("Average: "), HALIGN_RIGHT),Rect(hmargin+110+10+40+20,vnext,105,25));
add(new Label(theRatingMgr->getAverageRating(aLevel), HALIGN_RIGHT),Rect(hmargin+110+10+40+18+105+6,vnext,31,25));
}
LevelInspector::~LevelInspector () {
}
bool LevelInspector::isEndDeveloperMode() {
return isDeveloperMode;
}
bool LevelInspector::on_event (const SDL_Event &e) {
bool handled = false;
if (e.type == SDL_KEYDOWN) {
handled=true;
switch (e.key.keysym.sym) {
case SDLK_F2:
if (!isDeveloperMode) {
if (!annotation->getText().empty() ||
!app.state->getAnnotation(levelProxy->getId()).empty()) {
app.state->setAnnotation(levelProxy->getId(), annotation->getText());
}
LevelInspector m(levelProxy, true);
m.manage();
if (m.isEndDeveloperMode()) {
// reinit user input fields
annotation->set_text(app.state->getAnnotation(levelProxy->getId()));
invalidate_all();
} else {
Menu::quit();
}
} else {
if (!annotation->getText().empty() ||
!app.state->getAnnotation(levelProxy->getId()).empty()) {
app.state->setAnnotation(levelProxy->getId(), annotation->getText());
}
Menu::quit();
}
break;
default: handled=false; break;
}
}
return handled;
}
void LevelInspector::on_action(gui::Widget *w) {
if (w == back) {
// save annotation - but avoid to save unnecessary empty annotations
if (!annotation->getText().empty() ||
!app.state->getAnnotation(levelProxy->getId()).empty()) {
app.state->setAnnotation(levelProxy->getId(), annotation->getText());
}
isDeveloperMode = false;
Menu::quit();
} else if (w == screenshot) {
ScreenshotViewer m(levelProxy);
m.manage();
invalidate_all();
}
}
void LevelInspector::draw_background(ecl::GC &gc) {
const video::VMInfo *vminfo = video::GetInfo();
video::SetCaption((std::string("Enigma - Level ") +
(isDeveloperMode ? "Developer " : "") + "Inspector").c_str());
blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg"));
blit(gc, vminfo->width-vminfo->thumbw-10-hmargin, vmargin, previewImage);
Surface *img_hard = enigma::GetImage("completed");
if (withEasy) {
Surface *img_easy = enigma::GetImage("completed-easy");
blit (gc, vminfo->width/2-4, vmargin+5*25+4*vspacing+vspacing2, img_easy);
blit (gc, vminfo->width/2-4+63, vmargin+5*25+4*vspacing+vspacing2, img_hard);
} else {
blit (gc, vminfo->width/2-4+20, vmargin+5*25+4*vspacing+vspacing2, img_hard);
}
Surface *img_changed = enigma::GetImage("changed");
ratingInherited = lev::ScoreManager::instance()->isRatingInherited(levelProxy);
if (ratingInherited) {
int numLines = vminfo->height < 500 ? 14 :(vminfo->height < 650 ? 18 : 19);
blit (gc, hmargin+110+10+40, vmargin + numLines*25 +
(numLines-3)*vspacing + 3*vspacing2, img_changed);
}
}
void LevelInspector::tick(double dtime) {
}
std::string LevelInspector::ratingToString(int value) {
if (value == 0) {
// no rating available
return " ";
} else {
return ecl::strf("%3d", value);
}
}
std::string LevelInspector::scoreToString(int easy, int difficult,
lev::Proxy *aLevel, bool constLengthForCenteredClipping) {
if (withEasy) {
if (!constLengthForCenteredClipping)
return scoreToString(easy, aLevel) + " / " +
scoreToString(difficult, aLevel);
else
if (aLevel->getScoreUnit() == lev::duration)
//
return (easy >= 0 ? "- " : ": ") +
scoreToString(easy, aLevel) + " / " +
scoreToString(difficult, aLevel) +
(difficult >= 0 ? " -" : " :") ;
else
return (easy >= 0 ? "- " : " ") +
scoreToString(easy, aLevel) + " / " +
scoreToString(difficult, aLevel) +
(difficult >= 0 ? " -" : " ") ;
} else {
return scoreToString(difficult, aLevel);
}
}
std::string LevelInspector::scoreToString(int score, lev::Proxy *aLevel) {
if (aLevel->getScoreUnit() == lev::duration)
if (score >= 0 && score <= (99*60+59))
return ecl::strf("%2d:%02d", score/60, score%60);
else
return " - ";
else
if (score >= 0 && score <= 9999)
return ecl::strf("%4d", score);
else
return " -";
}
void LevelInspector::dispatchBottomLines(int &bestScoreHolderLines,
int &creditsLines, int &dedicationLines, int &levelPathLines,
int &annotationLines, int &compatibilityLines, int &idLines, int numLines, int width) {
enum botType {holder, credits, dedication, path, annotation, compatibility, id};
const int sequenceSize = 13;
botType sequence1[sequenceSize] = {credits, dedication, annotation, path,
holder, annotation, path, compatibility, credits, dedication,
annotation, credits, annotation};
botType sequence2[sequenceSize] = {id, path, compatibility, holder, path,
annotation, annotation, credits, dedication,
credits, dedication, annotation, annotation};
botType *sequence = isDeveloperMode ? sequence2 : sequence1;
int j = 0;
std::string creditsString = levelProxy->getCredits(true);
std::string dedicationString = levelProxy->getDedication(true);
std::string pathWorkString = levelPathString;
ecl::Font *menufont = enigma::GetFont("menufont");
for (int i = 0; i<numLines; i++) {
bool assigned = false;
do {
switch (sequence[j++]) {
case holder:
bestScoreHolderLines++;
assigned = true;
break;
case credits:
if (!(creditsString.empty())) {
creditsLines++;
creditsString = creditsString.substr(breakString(menufont,
creditsString, " ", width));
assigned = true;
}
break;
case dedication:
if (!(dedicationString.empty())) {
dedicationLines++;
dedicationString = dedicationString.substr(breakString(menufont,
dedicationString, " ", width));
assigned = true;
}
break;
case path:
if (!(pathWorkString.empty())) {
levelPathLines++;
pathWorkString = pathWorkString.substr(breakString(menufont,
pathWorkString, "/", width));
assigned = true;
}
break;
case annotation:
annotationLines++;
assigned = true;
break;
case compatibility:
compatibilityLines++;
assigned = true;
break;
case id:
idLines++;
assigned = true;
break;
}
} while (!assigned && j < sequenceSize);
if (j == sequenceSize)
return;
}
}
}} // namespace enigma::lev

View File

@@ -0,0 +1,68 @@
/*
* Copyright (C) 2006, 2007 Ronald Lamprecht
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef GUI_LEVELINSPECTOR_HH_INCLUDED
#define GUI_LEVELINSPECTOR_HH_INCLUDED
#include "ecl.hh"
#include "gui/Menu.hh"
#include "gui/TextField.hh"
#include "lev/Proxy.hh"
namespace enigma { namespace gui {
class LevelInspector : public gui::Menu {
public:
LevelInspector (lev::Proxy *aLevel, bool showDeveloperInfo = false);
~LevelInspector ();
bool isEndDeveloperMode();
private:
// ActionListener interface.
bool on_event (const SDL_Event &e);
void on_action(gui::Widget *w);
// Menu interface.
void draw_background(ecl::GC &gc);
void tick(double dtime);
//
std::string ratingToString(int value);
std::string scoreToString(int easy, int difficult, lev::Proxy *aLevel,
bool constLengthForCenteredClipping);
std::string scoreToString(int score, lev::Proxy *aLevel);
void dispatchBottomLines(int &bestScoreHolderLines,
int &creditsLines, int &dedicationLines, int &levelPathLines,
int &annotationLines, int &compatibilityLines, int &idLines, int numLines, int width);
// Variables.
gui::Widget *back;
gui::Widget *screenshot;
gui::TextField *annotation;
ecl::Surface *previewImage;
lev::Proxy *levelProxy;
std::string levelPathString;
int vspacing;
int vspacing2;
int vmargin;
int hmargin;
bool withEasy;
bool ratingInherited;
bool isDeveloperMode;
};
}} // namespace enigma::gui
#endif

View File

@@ -0,0 +1,499 @@
/*
* Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "gui/LevelMenu.hh"
#include "gui/HelpMenu.hh"
#include "gui/LevelPackMenu.hh"
#include "ecl.hh"
#include "game.hh"
#include "main.hh"
#include "nls.hh"
#include "options.hh"
#include "server.hh"
#include "sound.hh"
#include "StateManager.hh"
#include "video.hh"
#include "lev/Index.hh"
using namespace std;
using namespace ecl;
namespace enigma { namespace gui {
/* -------------------- Level Menu -------------------- */
struct LevelMenuConfig {
int buttonw, ibuttonw, buttonh;
int lbuttonw, lbuttonh;
ecl::Rect previewarea;
int thumbsy; // y coordinate of thumbnail window
int leftborder;
LevelMenuConfig (const ecl::Rect &screen)
: buttonw (140), ibuttonw (90), buttonh (35),
lbuttonw (140), lbuttonh (100),
previewarea (10, 60, screen.w-50, screen.h-130),
thumbsy (60),
leftborder (10)
{}
};
LevelMenu::LevelMenu()
: but_advancemode (new AdvanceModeButton),
but_next (new ImageButton("ic-next", "ic-next1", this)),
but_back (new StaticTextButton(N_("Main Menu"), this)),
but_difficulty (new DifficultyButton),
but_levelpack (new StaticTextButton(N_("Level Pack"), this)),
lbl_lpinfo (new Label("")),
lbl_statistics (new Label("")),
lbl_levelname (new Label("", HALIGN_LEFT)),
lbl_levelinfo (new Label("", HALIGN_LEFT)),
shown_text_ttl(-1.0), main_quit (false)
{
HList *hl, *hll, *hlr ;
const video::VMInfo &vminfo = *video::GetInfo();
// Levelmenu configuration
const int Y2 = 10; // y position for information area
const int Y3 = vminfo.height-50; // y position for bottom button row
LevelMenuConfig c (Rect (0, 0, vminfo.width, vminfo.height));
but_difficulty->set_listener (this);
// Create buttons
hll = new HList;
hll->set_spacing (10);
hll->set_alignment (HALIGN_CENTER, VALIGN_TOP);
hll->set_default_size (c.ibuttonw, c.buttonh);
hll->add_back (but_advancemode);
hll->add_back (but_next);
hll->add_back (but_difficulty);
hlr = new HList;
hlr->set_spacing (10);
hlr->set_alignment (HALIGN_CENTER, VALIGN_TOP);
hlr->set_default_size (c.buttonw, c.buttonh);
hlr->add_back (but_levelpack);
hlr->add_back (but_back);
hl = new HList;
hl->set_spacing (10);
hl->set_alignment (HALIGN_CENTER, VALIGN_TOP);
hl->set_default_size (2*c.buttonw + 10, c.buttonh);
hl->add_back (hll);
hl->add_back (hlr);
this->add (hl, Rect(c.leftborder, Y3, vminfo.width-20, c.buttonh));
// Add navigation buttons
pgup = new ImageButton("ic-up", "ic-up1", this);
pgdown = new ImageButton("ic-down", "ic-down1", this);
start = new ImageButton("ic-top", "ic-top1", this);
end = new ImageButton("ic-bottom", "ic-bottom1", this);
Rect r(vminfo.width-30, c.thumbsy, 20, 50);
r.y = c.thumbsy;
add (pgup, r);
r.y += 60;
add (pgdown, r);
r.y = c.thumbsy + 240;
add (start, r);
r.y += 60;
add (end, r);
// Information area
hl = new HList;
hl->add_back (lbl_levelname, List::EXPAND);
hl->add_back (lbl_lpinfo, List::TIGHT);
this->add (hl, Rect (5, Y2, vminfo.width - 10, 28));
hl_info_stat = new HList;
hl_info_stat->add_back (lbl_levelinfo, List::EXPAND); //Rect (c.leftborder, Y2+20,305, 28));
hl_info_stat->add_back (lbl_statistics, List::TIGHT);
this->add (hl_info_stat, Rect (5, Y2+20, vminfo.width - 10, 28));
// Prepare level selection widget
levelwidget = new LevelWidget();
levelwidget->set_listener(this);
levelwidget->realize (c.previewarea);
levelwidget->set_area (c.previewarea);
this->add (levelwidget);
updateIndex();
}
void LevelMenu::tick(double dtime)
{
levelwidget->tick(dtime);
static double timeaccu = 0.0;
// info texts disappear after some time
if (shown_text_ttl>0.0) {
shown_text_ttl -= dtime;
if (shown_text_ttl <= 0.0)
shown_text = "";
}
timeaccu += dtime;
if (timeaccu > 0.1) {
update_info();
timeaccu = 0.0;
}
}
static const char *helptext_levelmenu[] = {
N_("Escape:"), N_("Skip to main menu"),
"F1:", N_("Show this help"),
"F5:", 0, // see below
N_("Arrows:"), N_("Select level"),
N_("Return:"), N_("Play selected level"),
N_("Back/Space:"), N_("Previous/next levelpack"),
"u", N_("Mark current level as Unsolved"),
// "s", N_("Mark current level as Solved"),
N_("Alt+Return:"), N_("Switch between fullscreen and window"),
N_("Left click:"), N_("Play selected level"),
N_("Right or control click:"), N_("Inspect selected level"),
0
};
bool LevelMenu::on_event (const SDL_Event &e)
{
// Pass all events to the level widget first
bool handled=levelwidget->on_event(e);
if (!handled) {
if (e.type == SDL_KEYDOWN) {
handled=true;
switch (e.key.keysym.sym) {
case SDLK_SPACE: next_levelpack(); break;
case SDLK_BACKSPACE: previous_levelpack(); break;
case SDLK_F1:
if (app.state->getInt("NextLevelMode") == lev::NEXT_LEVEL_NOT_BEST)
helptext_levelmenu[5] = N_("Select next level for world record hunt");
else
helptext_levelmenu[5] = N_("Select next unsolved level");
displayHelp(helptext_levelmenu, 200);
draw_all();
break;
case SDLK_F5:
next_unsolved();
break;
case SDLK_u: {
lev::ScoreManager::instance()->markUnsolved(lev::Index::getCurrentProxy(),
app.state->getInt("Difficulty"));
invalidate_all();
break;
}
case SDLK_s:
lev::ScoreManager::instance()->markSolved(lev::Index::getCurrentProxy(),
app.state->getInt("Difficulty"));
invalidate_all();
break;
default: handled=false; break;
}
}
else
handled = Menu::on_event (e);
}
return handled;
}
void LevelMenu::on_action(Widget *w)
{
if (w==levelwidget) {
lev::Index *ind = lev::Index::getCurrentIndex();
int ilevel = ind->getCurrentPosition();
if (w->lastModifierKeys() & KMOD_CTRL && w->lastModifierKeys() & KMOD_SHIFT) {
// force a reload from file
lev::Proxy * curProxy = lev::Proxy::loadedLevel();
if (curProxy != NULL)
curProxy->release();
}
if ((unsigned)ilevel < ind->size()) {
if (ind->mayPlayLevel(ilevel+1)) {
game::StartGame();
ilevel = ind->getCurrentPosition();
invalidate_all();
ind->setCurrentPosition(ilevel);
levelwidget->syncFromIndexMgr();
}
else
show_text(_("You are not allowed to play this level yet."));
}
} else if (w == but_back) {
main_quit = true;
Menu::quit();
} else if (w == pgup) {
levelwidget->page_up();
} else if (w == pgdown) {
levelwidget->page_down();
} else if (w == start) {
levelwidget->start();
} else if (w == end) {
levelwidget->end();
} else if (w == but_next) {
next_unsolved();
} else if (w == but_levelpack) {
main_quit = false;
Menu::quit();
} else if (w == but_difficulty) {
but_difficulty->on_action(w);
invalidate_all();
}
}
void LevelMenu::update_info() {
// Note: all format strings have to be translated directly
// as the formatted strings can no longer be translated.
// The instant language change is guaranteed by the frequent
// call of is method!
lev::Index *ind = lev::Index::getCurrentIndex();
int size = ind->size();
lev::ScoreManager *scm = lev::ScoreManager::instance();
lev::Proxy *curProxy = ind->getCurrent();
int difficulty = app.state->getInt("Difficulty");
lbl_lpinfo->set_text(ecl::strf(_("%s: %d levels"),
ind->getName().c_str(), size));
if (size == 0) {
// empty level pack
lbl_statistics->set_text ("-");
lbl_levelname->set_text ("-");
lbl_levelinfo->set_text ("-");
}
else {
int iselected = ind->getCurrentPosition();
// Display levelpack statistics (percentage of solved levels)
if (app.state->getInt("NextLevelMode") == lev::NEXT_LEVEL_NOT_BEST) {
int pct = 100* scm->countBestScore(ind, difficulty)/ size;
lbl_statistics->set_text(ecl::strf(_("%d%% best"), pct));
}
else if (app.state->getInt("NextLevelMode") == lev::NEXT_LEVEL_OVER_PAR) {
int pct = 100* scm->countParScore(ind, difficulty)/ size;
double hcp = scm->calcHCP(ind, difficulty);
lbl_statistics->set_text(ecl::strf(_("%d%% par, hcp %.1f"), pct, hcp));
}
else {
int pct = 100* scm->countSolved(ind, difficulty) / size;
lbl_statistics->set_text(ecl::strf(_("%d%% solved"), pct));
}
// Display level name
if (enigma::WizardMode) {
// add level path info - we just can display the normalized path
// as we did not yet locate the absolute path - the user can
// use the inspector to check the absolute path!
lbl_levelname->set_text(ecl::strf("#%d: %s (%s)",
ind->getCurrentLevel(), curProxy->getTitle().c_str(),
curProxy->getNormLevelPath().c_str()));
} else {
lbl_levelname->set_text(ecl::strf("#%d: %s",
ind->getCurrentLevel(), curProxy->getTitle().c_str()));
}
// Display best time
if (shown_text.length()) {
lbl_levelinfo->set_text(shown_text);
}
else {
// TODO prepare for scores that are not time based!
char txt[200];
lev::RatingManager *ratingMgr = lev::RatingManager::instance();
int wr_time = ratingMgr->getBestScore(curProxy, difficulty);
int par_time = ratingMgr->getParScore(curProxy, difficulty);
bool is_par = scm->parScoreReached(curProxy, difficulty);
int best_user_time = scm->getBestUserScore(curProxy, difficulty);
string wr_name = ratingMgr->getBestScoreHolder(curProxy, difficulty);
bool wr_name_displayed = false;
string your_time;
string wr_text;
if (best_user_time>0) {
your_time = strf(_("Your time: %d:%02d"),
best_user_time/60, best_user_time%60);
if (wr_time>0) {
int below = wr_time - best_user_time;
if (below == 0)
wr_text = _("That's world record.");
else if (below>0)
wr_text = strf(_("That's %d:%02d below world record."),
below/60, below%60);
}
}
if (wr_text.length() == 0 && wr_time>0) {
if (wr_name.length()) {
wr_name_displayed = true;
} else
if (is_par || par_time < 0)
wr_text = strf(_("World record: %d:%02d"), wr_time/60, wr_time%60);
else
wr_text = strf(_("Par: %d:%02d World record: %d:%02d"),
par_time/60, par_time%60, wr_time/60, wr_time%60);
}
if (!your_time.empty())
your_time += " ";
int wr_cut = 0;
do {
if (wr_name_displayed) {
std::string tmp = ratingMgr->getBestScoreHolder(curProxy, difficulty, wr_cut++);
if (!tmp.empty())
wr_name = tmp;
if (is_par || par_time < 0)
wr_text = strf(_("World record by %s: %d:%02d"),
wr_name.c_str(), wr_time/60, wr_time%60);
else
wr_text = strf(_("Par: %d:%02d World record by %s: %d:%02d"),
par_time/60, par_time%60, wr_name.c_str(), wr_time/60, wr_time%60);
}
lbl_levelinfo->set_text(your_time + wr_text);
} while (!hl_info_stat->fits() && wr_name_displayed && (wr_cut < 20));
}
}
}
void LevelMenu::updateIndex()
{
levelwidget->syncFromIndexMgr();
update_info();
}
void LevelMenu::draw_background(ecl::GC &gc)
{
video::SetCaption(("Enigma - Level Menu"));
sound::PlayMusic (options::GetString("MenuMusicFile"));
blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg"));
}
void LevelMenu::next_unsolved()
{
lev::Index *ind = lev::Index::getCurrentIndex();
if (ind->advanceLevel(lev::ADVANCE_NEXT_MODE)) {
levelwidget->syncFromIndexMgr();
} else
show_text(_("No further unsolved level available!"));
}
void LevelMenu::next_levelpack()
{
lev::Index::setCurrentIndex(lev::Index::nextGroupIndex()->getName());
updateIndex();
}
void LevelMenu::previous_levelpack() {
lev::Index::setCurrentIndex(lev::Index::previousGroupIndex()->getName());
updateIndex();
}
void LevelMenu::show_text(const string& text) {
shown_text = text;
shown_text_ttl = 2.0; // show for two seconds
}
bool LevelMenu::isMainQuit() {
return main_quit;
}
/* -------------------- DifficultyButton -------------------- */
DifficultyButton::DifficultyButton() : ImageButton("ic-easymode","ic-easymode",this) {
update();
}
void DifficultyButton::update() {
if (app.state->getInt("Difficulty") == DIFFICULTY_EASY)
ImageButton::set_images("ic-easymode","ic-normalmode");
else
ImageButton::set_images("ic-normalmode","ic-easymode");
}
void DifficultyButton::on_action(Widget *)
{
int newdifficulty = (DIFFICULTY_EASY+DIFFICULTY_HARD) - app.state->getInt("Difficulty");
app.state->setProperty("Difficulty", newdifficulty); update();
invalidate();
}
void DifficultyButton::draw(ecl::GC &gc, const ecl::Rect &r) {
update();
ImageButton::draw(gc, r);
}
/* -------------------- AdvanceModeButton -------------------- */
AdvanceModeButton::AdvanceModeButton() : ImageButton("","",this) {
update();
}
void AdvanceModeButton::update() {
switch (app.state->getInt("NextLevelMode")) {
case lev::NEXT_LEVEL_UNSOLVED :
ImageButton::set_images("ic-unsolved", "par");
break;
case lev::NEXT_LEVEL_OVER_PAR :
ImageButton::set_images("par", "ic-worldrecord");
break;
case lev::NEXT_LEVEL_NOT_BEST :
ImageButton::set_images("ic-worldrecord", "ic-strictlynext");
break;
case lev::NEXT_LEVEL_STRICTLY : // use as default, too
default:
ImageButton::set_images("ic-strictlynext","ic-unsolved");
break;
}
}
void AdvanceModeButton::on_action(Widget *)
{
switch (app.state->getInt("NextLevelMode")) {
case lev::NEXT_LEVEL_STRICTLY :
app.state->setProperty("NextLevelMode", lev::NEXT_LEVEL_UNSOLVED);
break;
case lev::NEXT_LEVEL_UNSOLVED :
app.state->setProperty("NextLevelMode", lev::NEXT_LEVEL_OVER_PAR);
break;
case lev::NEXT_LEVEL_OVER_PAR :
app.state->setProperty("NextLevelMode", lev::NEXT_LEVEL_NOT_BEST);
break;
case lev::NEXT_LEVEL_NOT_BEST :
default:
app.state->setProperty("NextLevelMode", lev::NEXT_LEVEL_STRICTLY);
}
update();
invalidate();
}
}} // namespace enigma::gui

View File

@@ -0,0 +1,99 @@
/*
* Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef GUI_LEVELMENU_HH_INCLUDED
#define GUI_LEVELMENU_HH_INCLUDED
#include "gui/Menu.hh"
#include "gui/LevelWidget.hh"
#include "lev/Proxy.hh"
#include "lev/Index.hh"
namespace enigma { namespace gui {
/* -------------------- LevelMenu -------------------- */
class LevelMenu : public Menu {
public:
LevelMenu();
// Rotate through levels and packs
void next_levelpack();
void previous_levelpack();
void show_text(const string& text);
bool isMainQuit();
private:
void update_info();
void next_unsolved();
void updateIndex();
// Menu interface.
void tick (double time);
void draw_background(ecl::GC &gc);
// Widget interface.
bool on_event (const SDL_Event &e);
// ActionListener interface.
void on_action(Widget *w);
// Variables.
Widget *pgup, *pgdown, *start, *end;
Widget *but_advancemode; // Next unsolved level button
Widget *but_next;
Widget *but_back; // "Back" button
Widget *but_difficulty; // "Difficulty" button
TextButton *but_levelpack; // "Levelpack" button
HList *hl_info_stat;
Label *lbl_lpinfo; // Levelpack information
Label *lbl_statistics; // percentage solved
Label *lbl_levelname;
Label *lbl_levelinfo;
LevelWidget *levelwidget;
string shown_text; // info text (disappears automatically)
double shown_text_ttl; // rest duration for shown_text
bool main_quit;
};
/* -------------------- Buttons -------------------- */
class DifficultyButton : public ImageButton {
// ActionListener interface.
void on_action(Widget *);
public:
DifficultyButton();
virtual void draw(ecl::GC &gc, const ecl::Rect &r);
private:
void update();
};
class AdvanceModeButton : public ImageButton {
// ActionListener interface.
void on_action(Widget *);
public:
AdvanceModeButton();
private:
void update();
};
}} // namespace enigma::gui
#endif

View File

@@ -0,0 +1,391 @@
/*
* Copyright (C) 2006 Ronald Lamprecht
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "gui/LevelPackComposer.hh"
#include "gui/HelpMenu.hh"
#include "ecl.hh"
#include "enigma.hh"
#include "errors.hh"
#include "nls.hh"
#include "sound.hh"
#include "video.hh"
#include "lev/Index.hh"
#include "main.hh"
#include <cstdio>
using namespace ecl;
using namespace std;
namespace enigma { namespace gui {
lev::PersistentIndex * LevelPackComposer::clipboard = NULL;
static const char *helptext[] = {
N_("Shift click:"), N_("Add to clipboard"),
N_("Shift delete:"), N_("Clear clipboard"),
N_("F8:"), N_("Insert clipboard as reference"),
N_("F9:"), N_("Insert clipboard as copy"),
// N_("F10:"), N_("Move clipboard levels"),
N_("Alt left arrow:"), N_("Exchange level with predecessor"),
N_("Alt right arrow:"), N_("Exchange level with successor"),
N_("Delete:"), N_("Delete level"),
N_("F5:"), N_("Update index from levels"),
0
};
#if 0
// fake gettext to register the following strings for I18N
_("F10")
_("Move clipboard levels")
#endif
LevelPackComposer::LevelPackComposer(bool enableEdit) :
isEditable (enableEdit), isModified (false) {
if (clipboard == NULL) {
std::vector<std::string> dummy;
clipboard = new lev::PersistentIndex(" ", false); // mark as incomplete
}
curIndex = dynamic_cast<lev::PersistentIndex *>(lev::Index::getCurrentIndex());
const video::VMInfo &vminfo = *video::GetInfo();
// Add navigation buttons
pgup = new ImageButton("ic-up", "ic-up1", this);
pgdown = new ImageButton("ic-down", "ic-down1", this);
start = new ImageButton("ic-top", "ic-top1", this);
end = new ImageButton("ic-bottom", "ic-bottom1", this);
Rect r(vminfo.width-30, 60, 20, 50);
r.y = 60;
add (pgup, r);
r.y += 60;
add (pgdown, r);
r.y = 60 + 240;
add (start, r);
r.y += 60;
add (end, r);
// Prepare level selection widget
levelwidget = new LevelWidget(false, isEditable);
levelwidget->set_listener(this);
ecl::Rect previewarea(10, 60, vminfo.width-50, vminfo.height-130);
levelwidget->realize (previewarea);
levelwidget->set_area (previewarea);
this->add(levelwidget);
// Information area
lbl_lpinfo = new Label();
lbl_clipinfo = new Label();
lbl_levelname = new Label();
lbl_clipcontent = new Label();
HList *hl = new HList;
hl->set_spacing(10);
hl->set_alignment(HALIGN_CENTER, VALIGN_TOP);
hl->set_default_size(vminfo.width/2 - 10, 28);
hl->add_back (lbl_lpinfo);
hl->add_back (lbl_clipinfo);
this->add (hl, Rect (5, 10, vminfo.width - 10, 28));
hl = new HList;
hl->set_spacing(10);
hl->set_alignment(HALIGN_CENTER, VALIGN_TOP);
clipContentWidth = vminfo.width/2 - 10;
hl->set_default_size(clipContentWidth, 28);
hl->add_back (lbl_levelname);
hl->add_back (lbl_clipcontent);
this->add (hl, Rect (5, 10+20, vminfo.width - 10, 28));
// Create buttons - positioning identical to Levelmenu
but_ignore = new StaticTextButton(N_("Undo"), this);
but_back = new StaticTextButton(N_("Ok"), this);
HList * commandHList = new HList;
commandHList->set_spacing(10);
commandHList->set_alignment(HALIGN_CENTER, VALIGN_TOP);
commandHList->set_default_size(140, 35);
commandHList->add_back(new Label());
commandHList->add_back(new Label());
commandHList->add_back(but_ignore);
commandHList->add_back(but_back);
this->add(commandHList, Rect(10, vminfo.height-50, vminfo.width-20, 35));
}
void LevelPackComposer::tick(double dtime)
{
levelwidget->tick(dtime);
static double timeaccu = 0.0;
// // info texts disappear after some time
// if (shown_text_ttl>0.0) {
// shown_text_ttl -= dtime;
// if (shown_text_ttl <= 0.0)
// shown_text = "";
// }
timeaccu += dtime;
if (timeaccu > 0.1) {
update_info();
timeaccu = 0.0;
}
}
bool LevelPackComposer::on_event (const SDL_Event &e) {
// Pass all events to the level widget first
bool handled=levelwidget->on_event(e);
if (!handled) {
if (e.type == SDL_KEYDOWN) {
switch (e.key.keysym.sym) {
case SDLK_DELETE:
if (SDL_GetModState() & KMOD_SHIFT) {
// delete clipboard
clipboard->clear();
handled=true;
} else {
// delete level
if (isEditable) {
int pos = curIndex->getCurrentPosition();
lev::Proxy * curProxy = curIndex->getCurrent();
if (curProxy == NULL) {
// levelpack is empty
handled=true;
break;
}
if (curIndex->isSource(curProxy) &&
backups.find(curProxy->getNormLevelPath()) == backups.end()) {
// mark as deletion candidate - the final check
// if we delete it really occurs on save
deletions.insert(curProxy->getNormLevelPath());
}
curIndex->erase(pos);
if (pos >= curIndex->size() && pos > 0)
curIndex->setCurrentPosition(pos-1);
levelwidget->syncFromIndexMgr();
isModified = true;
invalidate_all();
handled=true;
}
}
break;
case SDLK_F8:
if (isEditable) {
int pos = (curIndex->size() == 0) ? 0 : curIndex->getCurrentPosition() + 1;
for (int i = 0; i < clipboard->size(); i++) {
lev::Variation var = clipboard->getVariation(i);
curIndex->insertProxy(pos++, clipboard->getProxy(i), true,
var.ctrl, var.unit, var.target, var.extensions);
isModified = true;
}
invalidate_all();
handled=true;
}
break;
case SDLK_F9:
if (isEditable && !curIndex->isCross()) {
int pos = (curIndex->size() == 0) ? 0 : curIndex->getCurrentPosition() + 1;
for (int i = 0; i < clipboard->size(); i++) {
lev::Variation var = clipboard->getVariation(i);
lev::Proxy *levelCopy = clipboard->getProxy(i)->copy(app.userPath + "/levels",
curIndex->getPackPath(), true);
if (levelCopy == NULL) {
// insert a crossreference
curIndex->insertProxy(pos++, clipboard->getProxy(i), true,
var.ctrl, var.unit, var.target, var.extensions);
} else {
// insert reference to our copy
curIndex->insertProxy(pos++, levelCopy, true,
var.ctrl, var.unit, var.target, var.extensions);
backups.insert(levelCopy->getNormLevelPath());
deletions.erase(levelCopy->getNormLevelPath());
}
isModified = true;
}
invalidate_all();
handled=true;
}
break;
case SDLK_LEFT:
if (isEditable && (SDL_GetModState() & KMOD_ALT)) {
int pos = curIndex->getCurrentPosition();
if (pos > 0) {
curIndex->exchange(pos, pos-1);
levelwidget->syncFromIndexMgr();
isModified = true;
}
invalidate_all();
handled=true;
}
break;
case SDLK_RIGHT:
if (isEditable && (SDL_GetModState() & KMOD_ALT)) {
int pos = curIndex->getCurrentPosition();
if (pos < curIndex->size() - 1) {
curIndex->exchange(pos, pos+1);
levelwidget->syncFromIndexMgr();
isModified = true;
}
invalidate_all();
handled=true;
}
break;
case SDLK_F5:
if (isEditable) {
curIndex->updateFromProxies();
isModified = true;
invalidate_all();
handled=true;
}
break;
case SDLK_F1:
displayHelp(helptext, 200);
invalidate_all();
handled=true;
break;
default:
break;
}
}
else
handled = Menu::on_event (e);
}
return handled;
}
void LevelPackComposer::on_action(Widget *w) {
if (w==levelwidget) {
lev::Index *ind = lev::Index::getCurrentIndex();
int ilevel = ind->getCurrentPosition();
if (w->lastModifierKeys() & KMOD_SHIFT) {
lev::Variation var;
lev::Proxy * curProxy = lev::Index::getCurrentProxy();
if (curProxy->getNormPathType() != lev::Proxy::pt_absolute) {
// all but absolute commandline proxies may be put on the clipboard
if (curIndex != NULL)
var = curIndex->getVariation(curIndex->getCurrentPosition());
clipboard->appendProxy(curProxy, var.ctrl,
var.unit, var.target, var.extensions);
sound::EmitSoundEvent ("menuok");
} else {
sound::EmitSoundEvent ("menustop");
}
}
} else if (w == but_back) {
if (isModified) {
// save index
curIndex->save(true);
// delete levelfiles
std::set<std::string>::iterator it;
for (it = deletions.begin(); it != deletions.end(); it++) {
if (!curIndex->hasNormLevelPath(*it)) {
// delete plain files on user path - ignore system and zip levels
std::string path = app.userPath + "/levels/" + (*it);
std::remove((path + ".xml").c_str());
std::remove((path + ".lua").c_str());
}
}
// delete backups
std::string base = app.userPath + "/levels/";
for (it = backups.begin(); it != backups.end(); it++) {
std::remove((base + *it + ".xml~").c_str());
std::remove((base + *it + ".lua~").c_str());
}
}
Menu::quit();
} else if (w == but_ignore) {
if (isModified) {
// we need to reload the index
curIndex->loadDoc();
// restore backups
std::string base = app.userPath + "/levels/";
std::set<std::string>::iterator it;
for (it = backups.begin(); it != backups.end(); it++) {
std::remove((base + *it + ".xml").c_str());
std::rename((base + *it + ".xml~").c_str(), (base + *it + ".xml").c_str());
std::remove((base + *it + ".lua").c_str());
std::rename((base + *it + ".lua~").c_str(), (base + *it + ".lua").c_str());
}
}
Menu::quit();
} else if (w == pgup) {
levelwidget->page_up();
} else if (w == pgdown) {
levelwidget->page_down();
} else if (w == start) {
levelwidget->start();
} else if (w == end) {
levelwidget->end();
}
}
void LevelPackComposer::update_info() {
// Note: all format strings have to be translated directly
// as the formatted strings can no longer be translated.
// The instant language change is guaranteed by the frequent
// call of is method!
lev::Index *ind = lev::Index::getCurrentIndex();
int size = ind->size();
lev::Proxy *curProxy = ind->getCurrent();
lbl_lpinfo->set_text(ecl::strf(_("%s: %d levels"),
ind->getName().c_str(), size));
if (size == 0) {
// empty level pack
lbl_levelname->set_text ("-");
}
else {
lbl_levelname->set_text(ecl::strf("#%d:(%s)",
ind->getCurrentLevel(),
curProxy->getNormLevelPath().c_str()));
}
int csize = clipboard->size();
lbl_clipinfo->set_text(ecl::strf(_("Clipboard: %d levels"), csize));
if (csize == 0) {
// empty level pack
lbl_clipcontent->set_text ("-");
}
else {
std::string clipstring = clipboard->getProxy(0)->getTitle();
for (int i = 1; i < csize; i++)
clipstring += ", " + clipboard->getProxy(i)->getTitle();
lbl_clipcontent->set_text(clipstring);
if (enigma::GetFont("menufont")->get_width(clipstring.c_str()) > clipContentWidth)
lbl_clipcontent->set_alignment(HALIGN_RIGHT);
else
lbl_clipcontent->set_alignment(HALIGN_CENTER);
}
}
void LevelPackComposer::draw_background(ecl::GC &gc) {
video::SetCaption(("Enigma - Level Pack Composer"));
blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg"));
if (isModified)
blit(gc, 0,0, enigma::GetImage("changed"));
}
}} // namespace enigma::gui

View File

@@ -0,0 +1,61 @@
/*
* Copyright (C) 2006 Ronald Lamprecht
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef GUI_LEVELPACKCOMPOSER_HH_INCLUDED
#define GUI_LEVELPACKCOMPOSER_HH_INCLUDED
#include "gui/Menu.hh"
#include "gui/LevelWidget.hh"
#include "lev/Proxy.hh"
#include "lev/Index.hh"
#include "lev/PersistentIndex.hh"
#include <set>
namespace enigma { namespace gui {
class LevelPackComposer : public gui::Menu {
public:
LevelPackComposer(bool enableEdit);
void tick(double dtime);
bool on_event (const SDL_Event &e);
void on_action(Widget *w);
void draw_background(ecl::GC &gc);
private:
void update_info();
static lev::PersistentIndex * clipboard;
bool isEditable;
bool isModified;
lev::PersistentIndex * curIndex;
std::set<std::string> deletions; // normLevelPath
std::set<std::string> backups; // normLevelPath
Widget *pgup, *pgdown, *start, *end;
LevelWidget *levelwidget;
Label *lbl_lpinfo; // Levelpack information
Label *lbl_levelname;
Label *lbl_clipinfo;
Label *lbl_clipcontent;
int clipContentWidth;
Widget *but_ignore;
Widget *but_back;
};
}} // namespace enigma::gui
#endif

View File

@@ -0,0 +1,540 @@
/*
* Copyright (C) 2006, 2007 Ronald Lamprecht
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "gui/LevelPackConfig.hh"
#include "gui/LevelPackComposer.hh"
#include "ecl.hh"
#include "errors.hh"
#include "nls.hh"
#include "video.hh"
#include "lev/Index.hh"
#include "main.hh"
using namespace ecl;
using namespace std;
namespace enigma { namespace gui {
GroupButton::GroupButton(std::vector<std::string> groups, int pos) :
ValueButton(0, groups.size() - 1),
position (pos), groupNames (groups) {
init();
}
int GroupButton::get_value() const {
return position;
}
void GroupButton::set_value(int value) {
position = value;
}
std::string GroupButton::get_text(int value) const {
return groupNames[value];
}
/* ------------------- LevelmodeButton -------------------- */
LevelmodeButton::LevelmodeButton(bool initialMode) :
ImageButton("ic-link_copy","ic-link_copy",this), mode (initialMode) {
update();
}
bool LevelmodeButton::isLinkOnly() {
return mode;
}
void LevelmodeButton::update() {
if (mode)
ImageButton::set_images("ic-link","ic-link_copy");
else
ImageButton::set_images("ic-link_copy","ic-link");
}
void LevelmodeButton::on_action(Widget *)
{
mode = !mode;
update();
invalidate();
}
void LevelmodeButton::draw(ecl::GC &gc, const ecl::Rect &r) {
update();
ImageButton::draw(gc, r);
}
LevelPackConfig::LevelPackConfig(std::string indexName, std::string groupName,
bool forceGroupReasign) : isReasignOnly (forceGroupReasign),
undo_quit (false), didEditMetaData (false), titleTF (NULL) {
const video::VMInfo &vminfo = *video::GetInfo();
if (indexName.empty())
// new levelpack
packIndex = new lev::PersistentIndex(" ", false,
INDEX_DEFAULT_PACK_LOCATION, "",
INDEX_STD_FILENAME, lev::Index::getCurrentGroup()); // mark as incomplete
else
packIndex = lev::Index::findIndex(indexName);
ASSERT (packIndex != NULL, XFrontend, "not existing index Name");
persIndex = dynamic_cast<lev::PersistentIndex *>(packIndex);
isPersistent = (persIndex != NULL);
isEditable = isPersistent ? persIndex->isUserEditable() : false;
// build a list of allowed group
std::vector<std::string> groups = lev::Index::getGroupNames();
// eliminate pseudo group "All Packs"
std::vector<std::string>::iterator itg = groups.begin();
while (itg != groups.end()) {
if (*itg == INDEX_ALL_PACKS) {
itg = groups.erase(itg);
break;
}
if (itg != groups.end())
itg++;
}
// add pseudo group "[Every Group]"
groups.push_back(std::string("[") + INDEX_EVERY_GROUP +"]");
intialGroupPosition = groups.size() - 1; // INDEX_EVERY_GROUP as default
// mark index's default group with square brackets and find current group
bool defaultGroupFound = false;
for (int i = 0; i < groups.size(); i++) {
if (groups[i] == packIndex->getGroupName()) {
intialGroupPosition = i;
}
if (groups[i] == packIndex->getDefaultGroupName()) {
groups[i] = std::string("[") + groups[i] +"]";
defaultGroupFound = true;
}
}
if (!defaultGroupFound) {
groups.push_back(std::string("[") + packIndex->getDefaultGroupName() +"]");
}
groupButton = new GroupButton(groups, intialGroupPosition);
// index location list setup
std::vector<lev::Index *> * allIndices = lev::Index::getGroup(INDEX_ALL_PACKS);
for (int i = 0; i < allIndices->size(); i++)
locationList.push_back((*allIndices)[i]->getName());
position = -1;
for (int i = 0; i < locationList.size(); i++) {
if (locationList[i] == indexName) {
position = i;
break;
}
}
oldPosition = position;
if (position < 0) {
// append new levelpack as last
locationList.push_back(indexName);
position = locationList.size() - 1;
}
VList * titleLeftVList = new VList;
titleLeftVList->set_spacing(11);
titleLeftVList->set_alignment(HALIGN_LEFT, VALIGN_CENTER);
titleLeftVList->set_default_size(140, 35);
Label * titleLabel = new Label(N_("Levelpack:"), HALIGN_RIGHT);
Label * ownerLabel = new Label(N_("Owner:"), HALIGN_RIGHT);
Label * groupLabel = new Label(N_("Group:"), HALIGN_RIGHT);
Label * loactionLabel1 = new Label(N_("Location"), HALIGN_LEFT);
Label * loactionLabel2 = new Label(N_("in [All Packs]:"), HALIGN_RIGHT);
titleLeftVList->add_back(titleLabel);
if (!isReasignOnly) {
titleLeftVList->add_back(ownerLabel);
}
titleLeftVList->add_back(groupLabel);
if (!isReasignOnly) {
titleLeftVList->add_back(new Label());
titleLeftVList->add_back(loactionLabel1);
titleLeftVList->add_back(loactionLabel2);
titleLeftVList->add_back(new Label());
titleLeftVList->add_back(new Label());
}
valueLeftVList = new VList;
valueLeftVList->set_spacing(11);
valueLeftVList->set_alignment(HALIGN_LEFT, VALIGN_CENTER);
valueLeftVList->set_default_size(160, 35);
titleValueLabel = new UntranslatedLabel(indexName, HALIGN_CENTER);
ownerValueLabel = new UntranslatedLabel(isPersistent ? persIndex->getOwner() : "System");
pre2Index = new UntranslatedLabel();
pre1Index = new UntranslatedLabel();
thisIndex = new UntranslatedLabel();
post1Index = new UntranslatedLabel();
post2Index = new UntranslatedLabel();
valueLeftVList->add_back(titleValueLabel);
if (!isReasignOnly) {
valueLeftVList->add_back(ownerValueLabel);
}
valueLeftVList->add_back(groupButton);
if (!isReasignOnly) {
valueLeftVList->add_back(pre2Index);
valueLeftVList->add_back(pre1Index);
valueLeftVList->add_back(thisIndex);
valueLeftVList->add_back(post1Index);
valueLeftVList->add_back(post2Index);
}
VList * scrollVList = new VList;
scrollVList->set_spacing(12);
scrollVList->set_alignment(HALIGN_LEFT, VALIGN_CENTER);
scrollVList->set_default_size(30, 35);
scrollUp = new ImageButton("ic-up", "ic-up1", this);
scrollDown = new ImageButton("ic-down", "ic-down1", this);
scrollVList->add_back(scrollUp);
scrollVList->add_back(scrollDown);
VList * metaVList = new VList;
metaVList->set_spacing(12);
metaVList->set_alignment(HALIGN_LEFT, VALIGN_CENTER);
metaVList->set_default_size(140, 35);
if (isEditable)
but_metadata = new StaticTextButton(N_("Edit Metadata"), this);
else
but_metadata = new Label();
Label * releaseLabel = new Label(N_("Release:"), HALIGN_RIGHT);
Label * revisionLabel = new Label(N_("Revision:"), HALIGN_RIGHT);
Label * compatibilityLabel = new Label(N_("Compatibility:"), HALIGN_RIGHT);
Label * defLocationLabel = new Label(N_("Default Location:"), HALIGN_RIGHT);
Label * crossmodeLabel = new Label(N_("Level types:"), HALIGN_RIGHT);
if (!isReasignOnly) {
metaVList->add_back(but_metadata);
metaVList->add_back(new Label());
if (WizardMode) {
metaVList->add_back(releaseLabel);
metaVList->add_back(revisionLabel);
} else {
metaVList->add_back(new Label());
metaVList->add_back(new Label());
}
metaVList->add_back(crossmodeLabel);
if (WizardMode) {
metaVList->add_back(compatibilityLabel);
} else {
metaVList->add_back(new Label());
}
metaVList->add_back(defLocationLabel);
metaVList->add_back(new Label());
}
valueMetaVList = new VList;
valueMetaVList->set_spacing(12);
valueMetaVList->set_alignment(HALIGN_CENTER, VALIGN_CENTER);
valueMetaVList->set_default_size(75, 35);
Widget * levelmodeWidget;
if (indexName.empty()){
levelmode = new LevelmodeButton(false);
levelmodeWidget = levelmode;
} else {
levelmodeWidget = new Image(isPersistent && !(persIndex->isCross()) ?
"ic-link_copy" : "ic-link");
}
defLocationValueLabel = new Label(ecl::strf("%g", packIndex->getDefaultLocation()));
releaseValueLabel = new Label(isPersistent ? ecl::strf("%d", persIndex->getRelease()) : "-");
revisionValueLabel = new Label(isPersistent ? ecl::strf("%d", persIndex->getRevision()) : "-");
compatibilityValueLabel = new Label(isPersistent ? ecl::strf("%.2f", persIndex->getCompatibility()) : "-");
if (!isReasignOnly) {
valueMetaVList->add_back(new Label());
valueMetaVList->add_back(new Label());
if (WizardMode) {
valueMetaVList->add_back(releaseValueLabel);
valueMetaVList->add_back(revisionValueLabel);
} else {
valueMetaVList->add_back(new Label());
valueMetaVList->add_back(new Label());
}
valueMetaVList->add_back(levelmodeWidget);
if (WizardMode) {
valueMetaVList->add_back(compatibilityValueLabel);
} else {
valueMetaVList->add_back(new Label());
}
valueMetaVList->add_back(defLocationValueLabel);
valueMetaVList->add_back(new Label());
}
if (isReasignOnly) {
this->add(titleLeftVList, Rect(vminfo.width/2 - 270, 15, 140, vminfo.height-97));
this->add(valueLeftVList, Rect(vminfo.width/2 - 80, 15, 160, vminfo.height-97));
} else {
this->add(titleLeftVList, Rect(vminfo.width/2 - 300, 15, 140, vminfo.height-97));
this->add(valueLeftVList, Rect(vminfo.width/2 - 140, 15, 160, vminfo.height-97));
this->add(scrollVList, Rect(vminfo.width/2 + 30, 15+3*(35+12) + (vminfo.height-480)/2, 30, 5*35+4*12));
this->add(metaVList, Rect(vminfo.width/2 + 80, 15, 140, vminfo.height-97));
this->add(valueMetaVList, Rect(vminfo.width/2 + 235, 15, 75, vminfo.height-97));
}
errorLabel = new Label("", HALIGN_CENTER);
this->add(errorLabel, Rect(10, vminfo.height-97, vminfo.width-20, 35));
if (isReasignOnly)
errorLabel->set_text(N_("Please reasign levelpack to another group for group deletion"));
// Create buttons - positioning identical to Levelmenu
but_edit = new StaticTextButton(N_("Compose Pack"), this);
if (isPersistent && persIndex->isUpdatable() && persIndex->isCross()) {
but_update = new StaticTextButton(N_("Update Pack"), this);
} else {
but_update = new Label();
}
but_ignore = new StaticTextButton(N_("Undo"), this);
but_back = new StaticTextButton(N_("Ok"), this);
HList * commandHList = new HList;
commandHList->set_spacing(10);
commandHList->set_alignment(HALIGN_CENTER, VALIGN_TOP);
commandHList->set_default_size(140, 35);
if (isReasignOnly) {
commandHList->add_back(new Label());
commandHList->add_back(new Label());
} else {
commandHList->add_back(but_edit);
commandHList->add_back(but_update);
}
commandHList->add_back(but_ignore);
commandHList->add_back(but_back);
this->add(commandHList, Rect(10, vminfo.height-50, vminfo.width-20, 35));
updateLocationList();
if (indexName.empty())
// new levelpack
switchToMetadataEdit();
}
void LevelPackConfig::updateLocationList() {
pre2Index->set_text((position > 1) ? locationList[position - 2] : "");
pre1Index->set_text((position > 0) ? locationList[position - 1] : "");
thisIndex->set_text(didEditMetaData ? titleTF->getText() : packIndex->getName());
post1Index->set_text((position < locationList.size() - 1) ? locationList[position + 1] : "");
post2Index->set_text((position < locationList.size() - 2) ? locationList[position + 2] : "");
}
void LevelPackConfig::switchToMetadataEdit() {
if (!didEditMetaData) {
didEditMetaData = true;
titleTF = new TextField(titleValueLabel->getText(), this);
valueLeftVList->exchange_child(titleValueLabel, titleTF);
delete titleValueLabel;
ownerTF = new TextField(ownerValueLabel->getText());
valueLeftVList->exchange_child(ownerValueLabel, ownerTF);
delete ownerValueLabel;
defLocationTF = new TextField(defLocationValueLabel->getText());
valueMetaVList->exchange_child(defLocationValueLabel, defLocationTF);
delete defLocationValueLabel;
if (WizardMode) {
releaseTF = new TextField(releaseValueLabel->getText());
valueMetaVList->exchange_child(releaseValueLabel, releaseTF);
delete releaseValueLabel;
revisionTF = new TextField(revisionValueLabel->getText());
valueMetaVList->exchange_child(revisionValueLabel, revisionTF);
delete revisionValueLabel;
compatibilityTF = new TextField(compatibilityValueLabel->getText());
valueMetaVList->exchange_child(compatibilityValueLabel, compatibilityTF);
delete compatibilityValueLabel;
}
}
}
bool LevelPackConfig::isUndoQuit() {
return undo_quit;
}
bool LevelPackConfig::doChanges() {
// change metadata
if (didEditMetaData) {
// the Index is persistent, user editabel and the user did switch to edit mode
bool needSave = false;
bool isNewIndex = persIndex->getName().empty();
// check for valid input
// title
std::string newtitle = titleTF->getText();
std::string::size_type lastChar = newtitle.find_last_not_of(" ");
if (lastChar == std::string::npos) {
// the title is effectively an empty string
errorLabel->set_text(N_("Error: empty title not allowed - press \"Undo\" to exit without modifications"));
return false;
}
// strip off trailing and leading spaces
newtitle = newtitle.substr(0 , lastChar + 1);
newtitle = newtitle.substr(newtitle.find_first_not_of(" "));
if (newtitle != persIndex->getName()) {
if (isNewIndex) {
// check for filename usability of title
const std::string validChars("_- .#0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
if (newtitle.find_first_not_of(validChars, 0) != std::string::npos ||
(newtitle.length() >= 1 && newtitle[0] == '.')) {
errorLabel->set_text(N_("Error: use only \"a-zA-Z0-9 _-#\" for levelpack title"));
return false;
}
// set packPath to cross if link only
if (levelmode->isLinkOnly())
persIndex->markNewAsCross();
}
if (!persIndex->setName(newtitle)) {
errorLabel->set_text(N_("Error: title already in use - choose another title"));
return false;
}
needSave = true;
}
// check rest for need of save
if (ownerTF->getText() != persIndex->getOwner()) {
persIndex->setOwner(ownerTF->getText());
needSave = true;
}
if (defLocationTF->getText() != ecl::strf("%g", packIndex->getDefaultLocation())) {
double d = 0;
// check value - keep old value on error
if ((sscanf(defLocationTF->getText().c_str(),"%lg", &d) == 1) &&
d > 0) {
packIndex->setDefaultLocation(d);
needSave = true;
}
}
if (WizardMode) {
if (releaseTF->getText() != ecl::strf("%d", persIndex->getRelease())) {
int i = 0;
// check value - keep old value on error
if ((sscanf(releaseTF->getText().c_str(),"%d", &i) == 1) &&
i > 0) {
persIndex->setRelease(i);
needSave = true;
}
}
if (revisionTF->getText() != ecl::strf("%d", persIndex->getRevision())) {
int i = 0;
// check value - keep old value on error
if ((sscanf(revisionTF->getText().c_str(),"%d", &i) == 1) &&
i > 0) {
persIndex->setRevision(i);
needSave = true;
}
}
if (compatibilityTF->getText() != ecl::strf("%.2f", persIndex->getCompatibility())) {
double d = 0;
// check value - keep old value on error
if ((sscanf(compatibilityTF->getText().c_str(),"%lg", &d) == 1) &&
d >= 1) {
persIndex->setCompatibility(d);
needSave = true;
}
}
}
// save
if (needSave)
persIndex->save();
if (isNewIndex) {
lev::Index::registerIndex(persIndex);
lev::Index::setCurrentIndex(persIndex->getName());
}
}
// regroup
if (groupButton->get_value() != intialGroupPosition) {
std::string newGroupName = groupButton->get_text(groupButton->get_value());
// strip off square brackets used to mark default and pseudo groups
if (newGroupName.size() > 2 && newGroupName[0] == '[' &&
newGroupName[newGroupName.size() -1] == ']') {
newGroupName = newGroupName.substr(1, newGroupName.size() - 2);
}
packIndex->moveToGroup(newGroupName);
} else if (isReasignOnly) {
// the user did not reasign - take as an undo request
undo_quit = true;
}
// relocate
if (position != oldPosition)
packIndex->locateBehind(position > 0 ? locationList[position - 1] : "");
return true;
}
void LevelPackConfig::on_action(Widget *w) {
if (w == but_back) {
if (doChanges())
Menu::quit();
else
invalidate_all();
} else if (w == but_ignore) {
if (packIndex->getName().empty()) {
delete packIndex;
}
undo_quit = true;
Menu::quit();
} else if (w == but_update) {
if (isPersistent && doChanges()) {
persIndex->load(false, true);
persIndex->save(true);
Menu::quit();
}
invalidate_all();
} else if (w == but_edit) {
if (doChanges()) {
LevelPackComposer m(isEditable);
m.manage();
Menu::quit();
} else {
invalidate_all();
}
} else if (w == scrollUp) {
if (position > 0) {
std::string tmp = locationList[position];
locationList[position] = locationList[position - 1];
locationList[position - 1] = tmp;
position--;
updateLocationList();
invalidate_all();
}
} else if (w == scrollDown) {
if (position < locationList.size() - 1) {
std::string tmp = locationList[position];
locationList[position] = locationList[position + 1];
locationList[position + 1] = tmp;
position++;
updateLocationList();
invalidate_all();
}
} else if (w == but_metadata && !didEditMetaData) {
switchToMetadataEdit();
invalidate_all();
} else if (w == titleTF && w != NULL) {
thisIndex->set_text(titleTF->getText());
}
}
void LevelPackConfig::draw_background(ecl::GC &gc) {
video::SetCaption(("Enigma - Level Pack Configuration"));
blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg"));
}
}} // namespace enigma::gui

View File

@@ -0,0 +1,112 @@
/*
* Copyright (C) 2006 Ronald Lamprecht
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef GUI_LEVELPACKCONFIG_HH_INCLUDED
#define GUI_LEVELPACKCONFIG_HH_INCLUDED
#include "gui/Menu.hh"
#include "gui/TextField.hh"
#include "lev/Index.hh"
#include "lev/PersistentIndex.hh"
namespace enigma { namespace gui {
class GroupButton : public ValueButton {
public:
GroupButton(std::vector<std::string> groups, int pos);
int get_value() const;
void set_value(int value);
std::string get_text(int value) const;
private:
std::vector<std::string> groupNames;
int position;
};
class LevelmodeButton : public ImageButton {
// ActionListener interface.
void on_action(Widget *);
public:
LevelmodeButton(bool initialMode = false);
bool isLinkOnly();
virtual void draw(ecl::GC &gc, const ecl::Rect &r);
private:
void update();
bool mode;
};
class LevelPackConfig : public gui::Menu {
public:
LevelPackConfig (std::string indexName, std::string groupName = "",
bool forceGroupReasign = false);
void on_action(Widget *w);
void draw_background(ecl::GC &gc);
bool isUndoQuit();
private:
void updateLocationList();
void switchToMetadataEdit();
bool doChanges();
lev::Index *packIndex;
lev::PersistentIndex *persIndex;
bool isPersistent;
bool isEditable;
TextField *tf_packName;
GroupButton *groupButton;
int intialGroupPosition;
VList *valueLeftVList;
Label *titleValueLabel;
TextField *titleTF;
Label *ownerValueLabel;
TextField *ownerTF;
VList *valueMetaVList;
LevelmodeButton *levelmode;
Label *defLocationValueLabel;
TextField *defLocationTF;
Label *releaseValueLabel;
TextField *releaseTF;
Label *revisionValueLabel;
TextField *revisionTF;
Label *compatibilityValueLabel;
TextField *compatibilityTF;
Label *pre2Index;
Label *pre1Index;
Label *thisIndex;
Label *post1Index;
Label *post2Index;
Widget *scrollUp;
Widget *scrollDown;
Label *errorLabel;
Widget *but_metadata;
Widget *but_up;
Widget *but_down;
Widget *but_edit;
Widget *but_update;
Widget *but_ignore;
Widget *but_back;
bool isReasignOnly;
bool didEditMetaData;
bool undo_quit;
std::vector<std::string> locationList;
int position; // new position of index in locationList that the user selected
int oldPosition; // position of index when entering menu, -1 for new index
};
}} // namespace enigma::gui
#endif

View File

@@ -0,0 +1,506 @@
/*
* Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck, Ronald Lamprecht
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "gui/LevelPackMenu.hh"
#include "gui/LevelMenu.hh"
#include "gui/LPGroupConfig.hh"
#include "gui/LevelPackConfig.hh"
#include "gui/SearchMenu.hh"
#include "gui/HelpMenu.hh"
#include "ecl.hh"
#include "errors.hh"
#include "nls.hh"
#include "video.hh"
#include "lev/Index.hh"
#include "lev/PersistentIndex.hh"
#include "main.hh"
using namespace ecl;
using namespace std;
namespace enigma { namespace gui {
/* -------------------- Level Pack Menu -------------------- */
std::map<std::string, std::string> LevelPackMenu::groupLastSelectedIndex;
std::map<std::string, int> LevelPackMenu::groupLastSelectedColumn;
std::string LevelPackMenu::lastGroupName;
int LevelPackMenu::firstDisplayedGroup = 0;
static const char *helptext[] = {
N_("Left column:"), N_("Levelpack groups"),
N_("Right columns:"), N_("Levelpacks of selected group"),
N_("Left click:"), N_("Select group or levelpack"),
N_("Right or control click:"), N_("Configure group or levelpack"),
0
};
LevelPackMenu::LevelPackMenu() : packsHList (NULL), groupsVList (NULL),
scrollLeft (NULL), scrollRight (NULL), scrollUp (NULL),
scrollDown (NULL), isLevelMenuSubmenu (false) {
const video::VMInfo &vminfo = *video::GetInfo();
vm = vminfo.videomode;
// Create buttons - positioning identical to Levelmenu
but_new = new StaticTextButton(N_("New Group"), this);
but_search = new StaticTextButton(N_("Search"), this);
but_level = new StaticTextButton(N_("Start Game"), this);
but_main = new StaticTextButton(N_("Main Menu"), this);
commandHList = new HList;
commandHList->set_spacing(10);
commandHList->set_alignment(HALIGN_CENTER, VALIGN_TOP);
commandHList->set_default_size(140, 35);
commandHList->add_back(but_new);
commandHList->add_back(but_search);
commandHList->add_back(but_level);
commandHList->add_back(but_main);
this->add(commandHList, Rect(10, vminfo.height-50, vminfo.width-20, 35));
}
void LevelPackMenu::setupMenu() {
static struct SpacingConfig {
int packcolumns, rows;
int vmargin, vrow_row;
int hmargin, hgroup_pack, hscrollbutton, hscroll_pack, hpack_pack;
} param[video::VM_COUNT] = {
{ // VM_640x480
2, 9,
15, 10,
20, 36, 22, 10, 20
},
{ // VM_640x512
2, 9,
15, 10,
20, 36, 22, 10, 20
},
{ // VM_800x600
3, 11,
15, 13,
15, 36, 22, 10, 15
},
{ // VM_1024x768
4, 15,
15, 10,
30, 36, 22, 12, 20
}
};
if (groupsVList != NULL) {
groupsVList->clear();
remove_child(groupsVList);
delete groupsVList;
groupsVList = NULL;
scrollUp = NULL; // deleted with groupsVList
scrollDown = NULL; // deleted with groupsVList
}
if (packsHList != NULL) {
packsHList->clear();
remove_child(packsHList);
delete packsHList;
packsHList = NULL;
}
if (scrollLeft != NULL) {
remove_child(scrollLeft);
delete scrollLeft;
scrollLeft = NULL;
}
if (scrollRight != NULL) {
remove_child(scrollRight);
delete scrollRight;
scrollRight = NULL;
}
packButtons.clear();
groupButtons.clear();
std::vector<std::string> groupNames = lev::Index::getGroupNames();
int groupCount = groupNames.size();
std::string curGroupName = lev::Index::getCurrentGroup();
bool needUpScroll = false;
bool needDownScroll = false;
int numDisplayGroups = param[vm].rows;
int usedGroupRows = (groupCount > numDisplayGroups) ? numDisplayGroups : groupCount;
// correct scroll attempts and screen resolution changes
firstDisplayedGroup = ecl::Clamp<int>(firstDisplayedGroup, 0,
(groupCount > numDisplayGroups) ? groupCount - numDisplayGroups : 0);
needUpScroll = firstDisplayedGroup > 0;
needDownScroll = firstDisplayedGroup < groupCount - numDisplayGroups;
if (curGroupName != lastGroupName) {
// group changed by indirect user action - ensure it is visible
int curGroupPos = getGroupPosition(&groupNames, curGroupName);
if (curGroupPos != -1) {
if (curGroupPos <= firstDisplayedGroup ) {
if (curGroupPos <= 1) {
needUpScroll = false;
firstDisplayedGroup = 0;
} else {
needUpScroll = true;
firstDisplayedGroup = curGroupPos -1;
}
needDownScroll = firstDisplayedGroup < groupCount - numDisplayGroups;
} else if (curGroupPos >= firstDisplayedGroup + numDisplayGroups - 1) {
if (curGroupPos >= groupCount - 2) {
needDownScroll = false;
firstDisplayedGroup = groupCount - numDisplayGroups;
} else {
needDownScroll = true;
firstDisplayedGroup = curGroupPos - numDisplayGroups + 2;
}
if (firstDisplayedGroup < 0)
firstDisplayedGroup = 0;
needUpScroll = firstDisplayedGroup > 0;
}
}
}
groupsVList = new VList;
groupsVList->set_spacing(param[vm].vrow_row);
groupsVList->set_alignment(HALIGN_LEFT, VALIGN_CENTER);
groupsVList->set_default_size(160, 35);
for (int i = 0; i < usedGroupRows; i++) {
if (i == 0 && needUpScroll) {
scrollUp = new ImageButton("ic-up", "ic-up1", this);
groupsVList->add_back(scrollUp);
} else if (i == usedGroupRows -1 && needDownScroll) {
scrollDown = new ImageButton("ic-down", "ic-down1", this);
groupsVList->add_back(scrollDown);
} else {
TextButton * button = new UntranslatedStaticTextButton(
groupNames[firstDisplayedGroup + i], this);;
groupButtons.push_back(button);
groupsVList->add_back(button);
}
}
this->add(groupsVList, Rect(param[vm].hmargin, param[vm].vmargin,
160, param[vm].rows * 35 +
(param[vm].rows - 1) * param[vm].vrow_row));
lastGroupName = curGroupName;
std::vector<lev::Index *> * group = lev::Index::getGroup(curGroupName);
ASSERT(group != NULL, XFrontend,"");
unsigned packCount = group->size();
int posCurrentIndex = getIndexPosition(group, lev::Index::getCurrentIndex()->getName());
int selectedColumn = lev::Index::getGroupSelectedColumn(curGroupName);
int colCurrentIndex = 0;
int nextPack = 0; // pack displayed at top of first display column
if (selectedColumn != INDEX_GROUP_COLUMN_UNKNOWN ||
groupLastSelectedIndex.find(curGroupName) == groupLastSelectedIndex.end()) {
colCurrentIndex = checkColumn(param[vm].rows, param[vm].packcolumns,
packCount, posCurrentIndex, selectedColumn);
nextPack = (posCurrentIndex / param[vm].rows - colCurrentIndex) * param[vm].rows;
} else {
// the user selected a new level pack and the column was not yet
// calculated: we try to keep the display unchanged in respect of
// of the last selected pack and if necessary scroll minimum amount
// of columns
int posLastIndex = getIndexPosition(group,groupLastSelectedIndex[curGroupName]);
int colLastIndex = checkColumn(param[vm].rows, param[vm].packcolumns,
packCount, posLastIndex, groupLastSelectedColumn[curGroupName]);
nextPack = (posLastIndex / param[vm].rows - colLastIndex) * param[vm].rows;
if (posCurrentIndex < nextPack) {
// current index would be left of display - we need to scroll left
nextPack -= (((nextPack - posCurrentIndex - 1)/param[vm].rows) + 1) *
param[vm].rows;
colCurrentIndex = 0;
} else if (posCurrentIndex < nextPack + param[vm].rows * param[vm].packcolumns) {
// current index is still visible - keep nextPack
colCurrentIndex = (posCurrentIndex - nextPack) / param[vm].rows;
} else {
// current index would be right of display - we need to scroll right
nextPack += (((posCurrentIndex - nextPack)/param[vm].rows) -
(param[vm].packcolumns - 1)) * param[vm].rows;
colCurrentIndex = param[vm].packcolumns - 1;
}
}
bool needRightScroll = packCount > nextPack + param[vm].rows * param[vm].packcolumns;
bool needLeftScroll = nextPack > 0;
lev::Index::setGroupSelectedColumn(curGroupName, colCurrentIndex);
groupLastSelectedIndex[curGroupName] = lev::Index::getCurrentIndex()->getName();
groupLastSelectedColumn[curGroupName] = colCurrentIndex;
packsHList = new HList;
packsHList->set_spacing(param[vm].hpack_pack);
packsHList->set_alignment(HALIGN_CENTER, VALIGN_TOP);
packsHList->set_default_size(160, param[vm].rows*35 +
(param[vm].rows - 1) * param[vm].vrow_row);
for (int col = 0; col < param[vm].packcolumns; col++) {
if (packCount - nextPack > 0) {
VList * pl = new VList;
pl->set_spacing (param[vm].vrow_row);
// first column is centered - if it is full it is like top alignment:
pl->set_alignment (HALIGN_LEFT, col == 0 ? VALIGN_CENTER : VALIGN_TOP);
pl->set_default_size (160, 35);
for (int row = 0; row < param[vm].rows; row++) {
if (nextPack < packCount) {
lev::Index *ind = (*group)[nextPack];
TextButton * button = new UntranslatedStaticTextButton(ind->getName(), this);
packButtons.push_back(button);
pl->add_back(button);
nextPack++;
} else
break;
}
packsHList->add_back(pl);
} else
break;
}
this->add(packsHList, Rect(param[vm].hmargin + 160 + param[vm].hgroup_pack +
param[vm].hscrollbutton + param[vm].hscroll_pack,
param[vm].vmargin,
param[vm].packcolumns * 160 + (param[vm].packcolumns - 1) *
param[vm].hpack_pack,
param[vm].rows * 35 +
(param[vm].rows - 1) * param[vm].vrow_row));
if (needLeftScroll) {
scrollLeft = new ImageButton("ic-left", "ic-left1", this);
this->add(scrollLeft, Rect(param[vm].hmargin + 160 + param[vm].hgroup_pack,
param[vm].vmargin + param[vm].rows / 2 * (35 + param[vm].vrow_row),
param[vm].hscrollbutton, 35));
}
if (needRightScroll) {
scrollRight = new ImageButton("ic-right", "ic-right1", this);
this->add(scrollRight, Rect(param[vm].hmargin + 160 + param[vm].hgroup_pack +
param[vm].hscrollbutton + 2 * param[vm].hscroll_pack +
param[vm].packcolumns * 160 + (param[vm].packcolumns - 1) *
param[vm].hpack_pack,
param[vm].vmargin + param[vm].rows / 2 * (35 + param[vm].vrow_row),
param[vm].hscrollbutton, 35));
}
}
void LevelPackMenu::manageLevelMenu() {
bool finished = false;
while (!finished) {
{
LevelMenu m;
if (!m.manage() || m.isMainQuit()) {
// ESC or Main button has been pressed in LevelMenu -
// the user wants to return
finished = true;
}
}
if (!finished) {
// the user left LevelMenu via LevelPack button
this->isLevelMenuSubmenu = true;
if (this->manage()) {
// not ESC - the user pressed Main button
finished = true;
} else {
// the user pressed ESC - return to LevelMenu
}
}
}
}
bool LevelPackMenu::manage() {
setupMenu();
updateHighlight();
return Menu::manage();
}
bool LevelPackMenu::on_event (const SDL_Event &e) {
switch (e.type) {
case SDL_KEYDOWN:
SDLKey keysym = e.key.keysym.sym;
switch (keysym) {
case SDLK_F1:
displayHelp(helptext, 200);
invalidate_all();
return true;
default:
break;
}
break;
}
return false;
}
void LevelPackMenu::on_action(Widget *w) {
if (w == but_main) {
Menu::quit();
} else if (w == but_new) {
LPGroupConfig m("");
m.manage();
setupMenu();
updateHighlight();
invalidate_all();
} else if (w == but_level) {
LevelMenu m;
if (!m.manage() && isLevelMenuSubmenu || m.isMainQuit()) {
// ESC in LevelMenu in case we are a submenu of LevelMenu or
// Main button has been pressed in LevelMenu
Menu::quit();
}
setupMenu();
updateHighlight();
invalidate_all();
} else if (w == but_search) {
SearchMenu ml;
ml.manage();
if (ml.isSearchQuit()) {
// show search result levelpack
LevelMenu ml;
if (!ml.manage() && isLevelMenuSubmenu || ml.isMainQuit()) {
// ESC in LevelMenu in cade we are a submenu of LevelMenu or
// Main button has been pressed in LevelMenu
Menu::quit();
}
}
setupMenu();
updateHighlight();
invalidate_all();
} else if (w == scrollUp) {
firstDisplayedGroup--;
reset_active_widget(); // we will delete it with setup
setupMenu();
updateHighlight();
invalidate_all();
} else if (w == scrollDown) {
firstDisplayedGroup++;
reset_active_widget(); // we will delete it with setup
setupMenu();
updateHighlight();
invalidate_all();
} else if (w->get_parent() == groupsVList){
lev::Index::setCurrentGroup(dynamic_cast<TextButton *>(w)->get_text());
if ((w->lastMouseButton() == SDL_BUTTON_RIGHT ||
w->lastModifierKeys() & KMOD_CTRL) &&
dynamic_cast<TextButton *>(w)->get_text() != INDEX_ALL_PACKS) {
// configure group
// INDEX_ALL_PACKS cannot be renamed, deleted, no packs can be created
LPGroupConfig m(dynamic_cast<TextButton *>(w)->get_text());
m.manage();
lastGroupName = ""; // the group may have moved, force a recalc
}
reset_active_widget(); // we will delete it with setup
setupMenu();
updateHighlight();
invalidate_all();
} else if (w->get_parent()->get_parent() == packsHList){
lev::Index::setCurrentIndex(dynamic_cast<TextButton *>(w)->get_text());
if (w->lastMouseButton() == SDL_BUTTON_RIGHT ||
w->lastModifierKeys() & KMOD_CTRL) {
// configure levelpack index
LevelPackConfig m(dynamic_cast<TextButton *>(w)->get_text());
m.manage();
} else {
LevelMenu m;
if (!m.manage() && isLevelMenuSubmenu || m.isMainQuit()) {
// ESC in LevelMenu in case we are a submenu of LevelMenu or
// Main button has been pressed in LevelMenu
Menu::quit();
return;
}
}
reset_active_widget(); // we will delete it with setup
setupMenu();
updateHighlight();
invalidate_all();
} else if (w == scrollLeft) {
std::string curGroupName = lev::Index::getCurrentGroup();
lev::Index::setGroupSelectedColumn(curGroupName,
lev::Index::getGroupSelectedColumn(curGroupName) + 1);
reset_active_widget(); // we will delete it with setup
setupMenu();
updateHighlight();
invalidate_all();
} else if (w == scrollRight) {
std::string curGroupName = lev::Index::getCurrentGroup();
lev::Index::setGroupSelectedColumn(curGroupName,
lev::Index::getGroupSelectedColumn(curGroupName) - 1);
reset_active_widget(); // we will delete it with setup
setupMenu();
updateHighlight();
invalidate_all();
}
}
void LevelPackMenu::updateHighlight() {
for (int i = 0; i < packButtons.size(); i++) {
TextButton * button = packButtons[i];
if (button->get_text() == lev::Index::getCurrentIndex()->getName())
button->setHighlight(true);
else
button->setHighlight(false);
}
for (int i = 0; i < groupButtons.size(); i++) {
TextButton * button = groupButtons[i];
if (button->get_text() == lev::Index::getCurrentGroup())
button->setHighlight(true);
else
button->setHighlight(false);
}
}
void LevelPackMenu::draw_background(ecl::GC &gc) {
video::SetCaption(("Enigma - Level Pack Menu"));
blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg"));
}
int LevelPackMenu::getGroupPosition(std::vector<std::string> * groups, std::string groupName) {
std::vector<std::string>::iterator it;
int i = 0;
for (it = groups->begin(); it != groups->end(); it++, i++) {
if ((*it) == groupName) {
return i;
}
}
return -1;
}
int LevelPackMenu::getIndexPosition(std::vector<lev::Index *> * group, std::string indexName) {
std::vector<lev::Index *>::iterator it;
int i = 0;
for (it = group->begin(); it != group->end(); it++, i++) {
if ((*it)->getName() == indexName) {
return i;
}
}
return -1;
}
int LevelPackMenu::checkColumn(int rows, int columns, int size,
int position, int oldColumn) {
int naturalColumn = position / rows;
int numColumns = (size - 1) / rows + 1;
if (oldColumn == INDEX_GROUP_COLUMN_UNKNOWN)
return (naturalColumn > columns) ? columns - 1 : naturalColumn;
else
return ecl::Clamp<int>(oldColumn, naturalColumn -
((numColumns > columns) ? (numColumns - columns) : 0),
naturalColumn);
}
}} // namespace enigma::gui

View File

@@ -0,0 +1,87 @@
/*
* Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck, Ronald Lamprecht
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef GUI_LEVELPACKMENU_HH_INCLUDED
#define GUI_LEVELPACKMENU_HH_INCLUDED
#include "gui/Menu.hh"
#include "video.hh"
#include "lev/Index.hh"
#include <map>
#include <string>
namespace enigma { namespace gui {
/* -------------------- LevelPackMenu -------------------- */
/**
* The levelpack selection menu.
*
* The used strategy for horizontal scrolling of the levelpack columns
* is to avoid scrolling as long as the target levelpack is still visible.
* The current levelpack may be scrolled out of visibility by using the
* scoll buttons within this menu. But if the user changes the current
* levelpack via indirect methods besides
* using the levelpack menu, f.e. the jumpto command, we ensure that
* the new current levelpack is visible when the menu is redisplayed.
* If it is necessary to scroll we scroll only the minimum amount of columns
* needed to display the current levelpack. Even on screen resolution changes
* we try to keep the column of the current levelpack unchanged.
*/
class LevelPackMenu : public Menu {
public:
LevelPackMenu();
void manageLevelMenu();
virtual bool manage();
bool on_event (const SDL_Event &e);
void on_action(Widget *w);
void draw_background(ecl::GC &gc);
private:
static std::map<std::string, std::string> groupLastSelectedIndex;
static std::map<std::string, int> groupLastSelectedColumn;
static std::string lastGroupName;
static int firstDisplayedGroup;
video::VideoModes vm;
std::vector<TextButton *> packButtons;
std::vector<TextButton *> groupButtons;
HList *packsHList;
ImageButton *scrollLeft;
ImageButton *scrollRight;
ImageButton *scrollUp;
ImageButton *scrollDown;
VList *groupsVList;
HList *commandHList;
Widget *but_search;
Widget *but_new;
Widget *but_level;
Widget *but_main;
bool isLevelMenuSubmenu;
void setupMenu();
void updateHighlight();
int getGroupPosition(std::vector<std::string> * groups, std::string groupName);
int getIndexPosition(std::vector<lev::Index *> * group, std::string indexName);
int checkColumn(int rows, int columns, int size, int position, int oldColumn);
};
}} // namespace enigma::gui
#endif

View File

@@ -0,0 +1,178 @@
/*
* Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck, Ronald Lamprecht
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "gui/LevelPreviewCache.hh"
#include "ecl.hh"
#include "file.hh"
#include "game.hh"
#include "main.hh"
#include "video.hh"
#include "lev/Proxy.hh"
#include "SDL.h"
#include <fstream>
using namespace ecl;
using namespace std;
namespace enigma { namespace gui {
/* -------------------- LevelPreviewCache -------------------- */
LevelPreviewCache *LevelPreviewCache::theSingleton = 0;
LevelPreviewCache* LevelPreviewCache::instance() {
if (theSingleton == 0) {
theSingleton = new LevelPreviewCache();
}
return theSingleton;
}
LevelPreviewCache::LevelPreviewCache() : cachedIndex (NULL) {
}
void LevelPreviewCache::clear() {
Log << "LevelPreviewCache clear\n";
cache.clear();
imgCache.clear();
}
Surface *LevelPreviewCache::getPreview(lev::Proxy *levelProxy,
bool allowGeneration, bool &didGenerate) {
didGenerate = false;
std::string mapIndex = levelProxy->getId() +
ecl::strf("#%d", levelProxy->getReleaseVersion());
// first look in cache
PreviewMap::iterator i = cache.find(mapIndex);
if (i != cache.end())
return i->second;
// we have to cache a new preview - check if we should clear cache first
if (cachedIndex == NULL) {
// remember the index we are caching
cachedIndex = lev::Index::getCurrentIndex();
} else if (cachedIndex != lev::Index::getCurrentIndex()) {
//the index changed - cache only previews from new index
clear();
cachedIndex = lev::Index::getCurrentIndex();
}
std::string previewSubPath = makePreviewPath(levelProxy);
Surface *surface = 0;
// load preview from file bundled with the level itself
std::string absLevelPath ;
std::auto_ptr<std::istream> isptr;
ByteVec imageData;
if(levelProxy->getNormPathType() == lev::Proxy::pt_resource &&
app.resourceFS->findFile ("levels/" + levelProxy->getNormLevelPath() + ".png",
absLevelPath, isptr)) {
// load plain image file or zipped image
if (isptr.get() != NULL) {
// zipped file
Readfile (*isptr, imageData);
} else {
// plain file
basic_ifstream<char> ifs(absLevelPath.c_str(), ios::binary | ios::in);
Readfile (ifs, imageData);
}
SDL_RWops *imageHandle = SDL_RWFromMem(&(imageData[0]), imageData.size());
surface = ecl::LoadImage(imageHandle, 1);
imgCache.store(previewSubPath, surface); // insert in imgCache
}
// load preview from stored file if possible
string previewFullPath;
if (!surface && app.resourceFS->findFile(previewSubPath, previewFullPath))
surface = imgCache.get(previewFullPath);
// generate new preview otherwise
if (!surface && allowGeneration) {
surface = newPreview(levelProxy);
if (surface) {
imgCache.store(previewSubPath, surface); // insert in imgCache
savePreview(levelProxy, surface); // save on filesystem
} else {
surface = enigma::GetImage("error");
}
didGenerate = true;
}
// update index
if (surface)
cache[mapIndex] = surface;
return surface;
}
ecl::Surface *LevelPreviewCache::newPreview (lev::Proxy *levelProxy) {
const video::VMInfo &vminfo = *video::GetInfo();
Surface *surface = 0;
ecl::GC gc(video::BackBuffer());
if (game::DrawLevelPreview (gc, levelProxy)) {
surface = Resample (video::BackBuffer(),
vminfo.gamearea, vminfo.thumbw, vminfo.thumbh);
}
return surface;
}
Surface *LevelPreviewCache::updatePreview (lev::Proxy *levelProxy) {
if (Surface *surface = newPreview (levelProxy)) {
std::string idx = levelProxy->getId() +
ecl::strf("#%d", levelProxy->getReleaseVersion());
cache[idx] = surface;
string preview_name = makePreviewPath(levelProxy);
imgCache.store (preview_name, surface); // insert in imgCache
savePreview(levelProxy, surface); // save on filesystem
return surface;
}
return 0;
}
std::string LevelPreviewCache::makePreviewPath(lev::Proxy *levelProxy) {
return "thumbs/" +
levelProxy->getLocalSubstitutionLevelPath() +
ecl::strf("#%d", levelProxy->getReleaseVersion()) + ".png";
}
void LevelPreviewCache::savePreview(lev::Proxy *levelProxy, ecl::Surface *s) {
std::string savePath = app.userImagePath + "/" + makePreviewPath(levelProxy);
Log << "savePreview to " << savePath << "\n";
// auto-create the directory if necessary
string directory;
if (ecl::split_path (savePath, &directory, 0) && !ecl::FolderExists(directory)) {
ecl::FolderCreate (directory);
}
ecl::SavePNG(s, savePath);
}
void LevelPreviewCache::makeSystemPreview(lev::Proxy *levelProxy, std::string systemDataPath) {
std::string savePath = systemDataPath + "/levels/" + levelProxy->getNormLevelPath() + ".png";
// auto-create the directory if necessary -- on an installed Enigma
// distribution this is of course unnecessary, but you start Enigma
// without prior installation. This is useful to get a directory with
// just the previews.
string directory;
if (ecl::split_path (savePath, &directory, 0) && !ecl::FolderExists(directory)) {
ecl::FolderCreate (directory);
}
ecl::Surface * s = newPreview(levelProxy);
if (s != NULL)
ecl::SavePNG(s, savePath);
}
}} // namespace enigma::gui

View File

@@ -0,0 +1,91 @@
/*
* Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef GUI_LEVELPREVIEWCACHE_HH_INCLUDED
#define GUI_LEVELPREVIEWCACHE_HH_INCLUDED
#include "lev/Index.hh"
#include "lev/Proxy.hh"
#include <map>
namespace enigma { namespace gui {
/**
* A singleton manager for level previews with autocaching.
* Clients just need to know the level proxy to request a preview.
* <p>
* Previews will be loaded from levelpack bundles if provided. These
* preview files have the same name as the levels themself but the
* suffix ".png". They just sit aside the levels in the same directory
* or in the same zip archive. Only local stored previews will be looked
* for.<p>
* If no bundled preview exists the preview will be loaded from prior
* generated and saved instances. Previews will be looked up at the
* resourceFS with play time generated previews stored at the userImagePath
* and installation time generated previews at the system path. The previews
* will be stored at "data/thumbs" with a subpath that reflectes the level
* subpath with critical characters replaced for url and other special levels.
* The level release number is attached to the previewname as "#n" to allow
* different previews for different releases to exist in parallel.<p>
*
* If no stored preview exists a new one will be generated by loading the
* level. The preview will be stored to the userImagePath for future use.<p>
*
* All loaded previews will be autocached. Futher requests will be served
* by the cache. The cache will be autocleared when a change in the
* current Index is detected.<p>
*
* TODO remove unused preview on filesystem
* TODO autogenerate previews on install
*/
class LevelPreviewCache {
public:
static LevelPreviewCache *instance();
static void makeSystemPreview(lev::Proxy *levelProxy, std::string systemDataPath);
~LevelPreviewCache();
ecl::Surface *getPreview (lev::Proxy *levelProxy,
bool allowGeneration, bool &didGenerate);
ecl::Surface *updatePreview (lev::Proxy *levelProxy);
void clear();
protected:
LevelPreviewCache();
private:
static LevelPreviewCache *theSingleton;
/**
* A mapping of "levelId+levelRelease" to preview surfaces
*/
typedef std::map<std::string, ecl::Surface*> PreviewMap;
// ---------- Internal methods ----------
static ecl::Surface *newPreview (lev::Proxy *levelProxy);
std::string makePreviewPath(lev::Proxy *levelProxy);
void savePreview(lev::Proxy *levelProxy, ecl::Surface *s);
// ---------- Variables ----------
PreviewMap cache; // a second mapping to avoid searched on the filesystem
enigma::ImageCache imgCache; // the owner of the preview surfaces -
// cannot be used as mapping as this cache
// uses the filepath as index and autoloads
// files on "get"-access
lev::Index *cachedIndex; // the index that is currently cached
};
}} // namespace enigma::gui
#endif

View File

@@ -0,0 +1,489 @@
/*
* Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck
* Copyright (C) 2006,2007 Ronald Lamprecht
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "gui/LevelWidget.hh"
#include "gui/LevelMenu.hh"
#include "gui/LevelInspector.hh"
#include "ecl.hh"
#include "main.hh"
#include "nls.hh"
#include "options.hh"
#include "sound.hh"
#include "StateManager.hh"
#include "video.hh"
#include "file.hh"
#include "lev/Proxy.hh"
#include <cassert>
using namespace ecl;
using namespace std;
namespace enigma { namespace gui {
/* -------------------- LevelWidget -------------------- */
LevelWidget::LevelWidget(bool withScoreIcons, bool withEditBorder) :
displayScoreIcons (withScoreIcons), displayEditBorder (withEditBorder),
width (0), height (0), m_areas(),
listener(0), isInvalidateUptodate (true), lastUpdate (0)
{
const video::VMInfo &vminfo = *video::GetInfo();
buttonw = vminfo.thumbw + 20;
buttonh = vminfo.thumbh + 28;
curIndex = lev::Index::getCurrentIndex();
iselected = curIndex->getCurrentPosition();
ifirst = curIndex->getScreenFirstPosition();
preview_cache = LevelPreviewCache::instance();
scoreMgr = lev::ScoreManager::instance();
img_link = enigma::GetImage("ic-link");
img_copy = enigma::GetImage("ic-copy");
img_feather = enigma::GetImage("ic-feather");
img_easy = enigma::GetImage("completed-easy");
img_hard = enigma::GetImage("completed");
img_changed = enigma::GetImage("changed");
img_unavailable = enigma::GetImage("unavailable");
// img_unknown = enigma::GetImage("unknown");
img_par = enigma::GetImage("par");
img_wrEasy = enigma::GetImage("ic-wr-easy");
img_wrDifficult = enigma::GetImage("ic-wr-difficult");
img_border = enigma::GetImage("thumbborder");
img_editborder = enigma::GetImage("editborder");
}
void LevelWidget::syncFromIndexMgr() {
if (curIndex != lev::Index::getCurrentIndex()) {
curIndex = lev::Index::getCurrentIndex();
iselected = curIndex->getCurrentPosition();
ifirst = curIndex->getScreenFirstPosition();
invalidate();
sound::EmitSoundEvent ("menumove");
} else if (iselected != curIndex->getCurrentPosition()) {
sound::EmitSoundEvent ("menumove");
}
// repair ifirst on boot and screen resolution changes
set_selected(curIndex->getScreenFirstPosition(),
curIndex->getCurrentPosition());
}
void LevelWidget::syncToIndexMgr() {
curIndex->setCurrentPosition(iselected);
curIndex->setScreenFirstPosition(ifirst);
}
void LevelWidget::realize (const ecl::Rect &area_)
{
Widget::realize (area_);
width = area_.w / buttonw;
height = area_.h / buttonh;
}
void LevelWidget::trigger_action() {
if (listener) {
listener->on_action(this);
}
}
void LevelWidget::scroll_up (int nlines)
{
int newFirst = ifirst;
int newSelected = iselected;
for (; nlines; --nlines) {
if (newFirst+width*height >= curIndex->size())
break;
newFirst += width;
if (newSelected < newFirst)
newSelected += width;
}
set_selected (newFirst, newSelected);
}
void LevelWidget::scroll_down(int nlines)
{
int newFirst = ifirst;
int newSelected = iselected;
for (; nlines; --nlines) {
if (newFirst == 0) {
break;
} else if (newFirst < width) {
newFirst = 0;
if (newSelected >= width*height)
newSelected = width*height - 1;
} else {
newFirst -= width;
if (newSelected >= newFirst+width*height)
newSelected -= width;
}
}
set_selected (newFirst, newSelected);
}
void LevelWidget::page_up() {
set_selected ((ifirst >= width*height ? ifirst - width*height : 0),
(iselected >= width*height ? iselected - width*height : 0));
syncToIndexMgr();
}
void LevelWidget::page_down()
{
int s = lev::Index::getCurrentIndex()->size() - 1; // last position
int lastPageFirst = (s >= width*height ? (s / width - height + 1) * width : 0);
// make sure last page is shown as a whole
int first = std::min<int> (lastPageFirst, ifirst + width*height);
// set_selected (first, s-1);
set_selected (first, iselected + width*height);
syncToIndexMgr();
}
void LevelWidget::start() {
set_selected(0,0);
syncToIndexMgr();
}
void LevelWidget::end() {
int s = lev::Index::getCurrentIndex()->size() - 1; // last position
int lastPageFirst = (s >= width*height ? (s / width - height + 1) * width : 0);
set_selected(lastPageFirst, s);
syncToIndexMgr();
}
void LevelWidget::set_current (int newsel)
{
set_selected (ifirst, newsel);
}
void LevelWidget::set_selected (int newfirst, int newsel)
{
int numlevels = curIndex->size();
newsel = Clamp<int> (newsel, 0, numlevels-1);
if (newsel < 0) newsel = 0;
if (newsel < newfirst)
newfirst = (newsel/width)*width;
if (newsel >= newfirst+width*height)
newfirst = (newsel/width-height+1)*width;
newfirst = Clamp<int> (newfirst, 0, numlevels-1);
if (newfirst < 0) newfirst = 0;
size_t oldsel = iselected;
if (newfirst != ifirst) {
ifirst = newfirst;
iselected = newsel;
if (!m_areas.empty()) {
sound::EmitSoundEvent ("menumove");
if (oldsel != newsel)
sound::EmitSoundEvent ("menuswitch");
invalidate();
}
}
else if (newsel != iselected) {
iselected = newsel;
if (!m_areas.empty()) {
sound::EmitSoundEvent ("menuswitch");
invalidate_area(m_areas[oldsel-ifirst]); // old selection
invalidate_area(m_areas[iselected-ifirst]); // new selection
}
}
}
bool LevelWidget::draw_level_preview (ecl::GC &gc, int x, int y,
lev::Proxy *proxy, bool selected, bool isCross, bool locked,
bool allowGeneration, bool &didGenerate) {
// Draw button with level preview
Surface *img = preview_cache->getPreview(proxy, allowGeneration, didGenerate);
if (img == NULL)
return false;
if (selected) {
blit (gc, x-4, y-4, displayEditBorder ? img_editborder : img_border);
blit (gc, x, y, img);
}
else {
img->set_alpha (127);
blit (gc, x, y, img);
img->set_alpha(255);
}
// Shade unavailable levels
if (locked)
blit (gc, x, y, img_unavailable);
if (displayScoreIcons) {
// Draw solved/changed icons on top of level preview
// Easy/difficult mode and solved status:
// None: Level not beaten - no easy mode available
// Feather: Level not beaten - easy mode available
// Feather + Gold: Level beaten in normal mode - easy available
// Silver: Level beaten in easy mode (normal mode available)
// Gold: Level beaten in normal mode - easy not availabe
// Silver + Gold: Level beaten in all modes - easy available
Surface *useAsEasy = NULL;
Surface *useAsDifficult = NULL;
if (proxy->hasEasymode()) {
useAsEasy = img_feather;
if (scoreMgr->isSolved(proxy, DIFFICULTY_EASY))
useAsEasy = img_easy;
}
if (scoreMgr->isSolved(proxy, DIFFICULTY_HARD))
useAsDifficult = img_hard;
if (app.state->getInt("Difficulty") == DIFFICULTY_HARD) {
// draw golden medal over silber medal
if (useAsEasy != NULL)
blit (gc, x, y, useAsEasy);
if (useAsDifficult != NULL)
blit (gc, x+5, y, useAsDifficult);
}
else {
// draw silver medal over golden medal
if (useAsDifficult != NULL)
blit (gc, x+5, y, useAsDifficult);
if (useAsEasy != NULL)
blit (gc, x, y, useAsEasy);
}
// Add warning sign if level has been changed since player solved it
if (scoreMgr->isOutdated(proxy, app.state->getInt("Difficulty")))
blit (gc, x-3, y-3, img_changed);
// Add icon if worldrecord or par
if (scoreMgr->bestScoreReached(proxy, app.state->getInt("Difficulty"))) {
blit(gc, x+35, y+5,
(app.state->getInt("Difficulty") != DIFFICULTY_HARD &&
proxy->hasEasymode()) ? img_wrEasy : img_wrDifficult);
} else if (scoreMgr->parScoreReached(proxy, app.state->getInt("Difficulty"))){
blit(gc, x+30, y+12, img_par);
}
} else {
// Draw solved/changed icons on top of level preview
if (isCross)
blit (gc, x+4, y+4, img_link);
else
blit (gc, x+4, y+4, img_copy);
}
return true;
}
void LevelWidget::draw (ecl::GC &gc, const ecl::Rect &r)
{
const video::VMInfo &vminfo = *video::GetInfo();
const int imgw = vminfo.thumbw; // Size of the preview images
const int imgh = vminfo.thumbh;
const int hgap = Max(0, (get_w() - width*buttonw) / (width));
const int vgap = Max(0, (get_h() - height*buttonh)/ (height-1));
unsigned i=ifirst; // level index
bool allowGeneration = true;
for (int y=0; y<height; y++)
{
for (int x=0; x<width; x++, i++)
{
if (i >= curIndex->size())
goto done_painting;
int xpos = get_x() + hgap/2 + x*(buttonw + hgap);
int ypos = get_y() + y*(buttonh + vgap);
Rect buttonarea(xpos, ypos, buttonw, buttonh);
if (!(r.overlaps(buttonarea) || r.w == 0))
continue; // r.w==0 if repainting whole screen
if( (i-ifirst) >= m_areas.size()) {
m_areas.push_back(buttonarea);
pending_redraws.push_back(false);
} else {
m_areas[(i-ifirst)] = buttonarea;
}
// Draw level preview
lev::Proxy *levelProxy = curIndex->getProxy(i);
int imgx = xpos+(buttonw-imgw)/2;
int imgy = ypos + 4;
if (levelProxy != NULL) {
bool didGenerate;
bool didDraw = draw_level_preview (gc, imgx, imgy, levelProxy,
i == iselected, !curIndex->isSource(levelProxy),
!curIndex->mayPlayLevel(i+1),
allowGeneration, didGenerate);
if (didGenerate) {
// do not generate more than 1 preview from level source
// per draw call
allowGeneration = false;
}
if (didDraw) {
pending_redraws[(i-ifirst)] = false;
} else {
// the button is not drawn - mark it to be drawn on
// a future tick
pending_redraws[(i-ifirst)] = true;
isInvalidateUptodate = false;
}
}
// Draw level name
Font *smallfnt = enigma::GetFont("levelmenu");
Font *altsmallfnt = enigma::GetFont("smallalternative");;
std::string caption = levelProxy->getTitle();
smallfnt->render (gc,
xpos + buttonw/2 - ecl::Min(smallfnt->get_width(caption.c_str(), altsmallfnt)/2, (buttonw+hgap)/2),
imgy + imgh + 2,
caption, altsmallfnt, buttonw + hgap);
}
}
done_painting:
m_areas.resize (i-ifirst); // Remove unused areas (if any) from the list
return;
}
void LevelWidget::tick (double time) {
if (!isInvalidateUptodate) {
// invalidate just 1 button for redraw
bool isFirst = true;
for (int i = 0; i < pending_redraws.size(); i++) {
if (pending_redraws[i] == true) {
if (isFirst) {
invalidate_area(m_areas[i]);
isInvalidateUptodate = true;
isFirst = false;
} else {
isInvalidateUptodate = false;
return;
}
}
}
}
}
bool LevelWidget::on_event(const SDL_Event &e)
{
bool handled = Widget::on_event(e);
switch (e.type) {
case SDL_MOUSEMOTION:
if (get_area().contains(e.motion.x, e.motion.y)) {
int newsel=iselected;
for (unsigned i=0; i<m_areas.size(); ++i)
if (m_areas[i].contains(e.motion.x, e.motion.y))
{
newsel = ifirst+i;
break;
}
set_current(newsel);
handled = true;
}
break;
case SDL_MOUSEBUTTONDOWN:
if (get_area().contains(e.button.x, e.button.y))
handled = handle_mousedown (&e);
break;
case SDL_KEYDOWN:
handled = handle_keydown (&e);
break;
}
syncToIndexMgr();
return handled;
}
bool LevelWidget::handle_mousedown (const SDL_Event *e)
{
switch (e->button.button) {
case SDL_BUTTON_LEFT:
for (unsigned i=0; i<m_areas.size(); ++i)
if (m_areas[i].contains(e->button.x, e->button.y))
{
sound::EmitSoundEvent ("menuok");
iselected = ifirst+i;
syncToIndexMgr();
if (SDL_GetModState() & KMOD_CTRL && !(SDL_GetModState() & KMOD_SHIFT)) {
// control key pressed - level inspector
LevelInspector m(curIndex->getProxy(iselected));
m.manage();
get_parent()->draw_all();
} else {
// no control key - start level
trigger_action();
}
return true;
}
break;
case SDL_BUTTON_RIGHT:
for (unsigned i=0; i<m_areas.size(); ++i)
if (m_areas[i].contains(e->button.x, e->button.y))
{
sound::EmitSoundEvent ("menuok");
iselected = ifirst+i;
syncToIndexMgr();
LevelInspector m(curIndex->getProxy(iselected));
m.manage();
get_parent()->draw_all();
return true;
}
break;
case 4: scroll_down(1); return true;
case 5: scroll_up(1); return true;
}
return false;
}
bool LevelWidget::handle_keydown (const SDL_Event *e)
{
switch (e->key.keysym.sym) {
case SDLK_t:
// Generate new level preview for current level
preview_cache->updatePreview(curIndex->getProxy(iselected));
invalidate();
break;
case SDLK_LEFT:
if (!(SDL_GetModState() & KMOD_ALT)) {
set_current (iselected>1 ? iselected-1 : 0);
break;
} else
return false;
case SDLK_RIGHT:
if (!(SDL_GetModState() & KMOD_ALT)) {
set_current (iselected+1);
break;
} else
return false;
case SDLK_DOWN: set_current (iselected+width); break;
case SDLK_UP: set_current (iselected>width ? iselected-width : 0); break;
case SDLK_PAGEDOWN: page_down(); break;
case SDLK_PAGEUP: page_up(); break;
case SDLK_HOME: start(); break;
case SDLK_END: end(); break;
case SDLK_RETURN:
trigger_action();
break;
default:
return false; // key not handled
}
return true;
}
}} // namespace enigma::gui

View File

@@ -0,0 +1,111 @@
/*
* Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef GUI_LEVELWIDGET_HH_INCLUDED
#define GUI_LEVELWIDGET_HH_INCLUDED
#include "gui/widgets.hh"
#include "gui/LevelPreviewCache.hh"
#include "lev/Index.hh"
#include "lev/ScoreManager.hh"
namespace enigma { namespace gui {
/* -------------------- LevelWidget -------------------- */
class LevelMenu;
/**
*
*/
class LevelWidget : public Widget {
public:
LevelWidget(bool withScoreIcons = true, bool withEditBorder = false);
//---------- Widget interface ----------//
void draw(ecl::GC &gc, const ecl::Rect &r);
void tick (double time);
void set_listener(ActionListener *al) {
listener = al;
}
void trigger_action();
virtual void realize (const ecl::Rect &r);
//---------- Cursor motion ----------//
void page_up();
void page_down();
void start();
void end();
bool on_event(const SDL_Event &e);
void syncFromIndexMgr(); // external change of currentIndex, currentLevel
private:
//---------- Private functions ----------//
void syncToIndexMgr();
void set_current (int newsel);
void scroll_up(int lines);
void scroll_down(int lines);
void set_selected (int newfirst, int newsel);
bool draw_level_preview (ecl::GC &gc, int x, int y,
lev::Proxy *proxy, bool selected, bool isCross, bool locked,
bool allowGeneration, bool &didGenerate);
bool handle_keydown (const SDL_Event *e);
bool handle_mousedown (const SDL_Event *e);
//---------- Variables ----------//
bool displayScoreIcons;
bool displayEditBorder;
LevelPreviewCache *preview_cache;
lev::ScoreManager *scoreMgr;
lev::Index *curIndex;
ActionListener *listener;
int ifirst; // Index of "upper left" level
int iselected; // Index of selected level
int width; // number of buttons in a row
int height; // number of buttons in a column
int buttonw; // pixelwidth of a button
int buttonh; // pixelheight of a button
std::vector<ecl::Rect> m_areas; // Screen areas occupied by level previews
std::vector<bool> pending_redraws;
bool isInvalidateUptodate;
double lastUpdate;
// some image pointers for efficiency
ecl::Surface *img_link;
ecl::Surface *img_copy;
ecl::Surface *img_feather;
ecl::Surface *img_easy;
ecl::Surface *img_hard;
ecl::Surface *img_changed;
ecl::Surface *img_unavailable;
// Surface *img_unknown;
ecl::Surface *img_par;
ecl::Surface *img_wrEasy;
ecl::Surface *img_wrDifficult;
ecl::Surface *img_border;
ecl::Surface *img_editborder;
};
}} // namespace enigma::gui
#endif

View File

@@ -0,0 +1,392 @@
/*
* Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "gui/MainMenu.hh"
#include "gui/LevelMenu.hh"
#include "gui/OptionsMenu.hh"
#include "gui/HelpMenu.hh"
#include "gui/InfoMenu.hh"
#include "gui/LevelPackMenu.hh"
#include "gui/LevelPreviewCache.hh"
#include "display.hh"
#include "ecl.hh"
#include "main.hh"
#include "nls.hh"
#include "options.hh"
#include "sound.hh"
#include "video.hh"
#include "world.hh"
#include "netgame.hh"
#include "editor.hh"
using namespace ecl;
using namespace std;
namespace enigma { namespace gui {
/* -------------------- Helper routines -------------------- */
// namespace
// {
/*! Change the video mode. Because this opens a new screen with a
new resolution, the display engine must be re-initialized to
load the appropriate models. */
void ChangeVideoMode()
{
world::PrepareLevel(); // make sure no references to models remain
video::ChangeVideoMode();
LevelPreviewCache::instance()->clear();
enigma::ClearImageCache();
display::Shutdown();
display::Init();
}
// }
/* -------------------- NetworkMenu -------------------- */
NetworkMenu::NetworkMenu ()
{
const video::VMInfo *vminfo = video::GetInfo();
BuildVList b(this, Rect((vminfo->width - 150)/2,150,150,40), 5);
m_startgame = b.add(new StaticTextButton(N_("Start Game"), this));
m_joingame = b.add(new StaticTextButton(N_("Join Game"), this));
m_back = b.add(new StaticTextButton(N_("Back"), this));
}
NetworkMenu::~NetworkMenu ()
{
}
bool NetworkMenu::on_event (const SDL_Event &e)
{
return false;
}
void NetworkMenu::on_action(gui::Widget *w)
{
if (w == m_startgame) {
netgame::Start();
}
else if (w == m_joingame) {
netgame::Join("localhost", 12345);
}
if (w == m_back)
Menu::quit();
}
void NetworkMenu::draw_background(ecl::GC &gc)
{
video::SetCaption (("Enigma - Network Menu"));
blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg"));
}
void NetworkMenu::tick(double dtime)
{
}
/* -------------------- Main menu -------------------- */
static const char *credit_text[] = {
N_("Main developers of the 1.0 release:"),
N_(" RONALD LAMPRECHT (lead)"),
" RAOUL BOURQUIN",
" ANDREAS LOCHMANN",
" ",
N_("Special Thanks To:"),
N_(" DANIEL HECK (project founder)"),
N_(" MEINOLF SCHNEIDER (game idea, level design)"),
" ",
N_("Please refer to the manual or the next pages for full credits."),
" ",
N_("Home Page: http://www.nongnu.org/enigma"),
N_("Contact: enigma-devel@nongnu.org"),
" ",
N_("Enigma is free software and may be distributed under the"),
N_("terms of the GNU General Public License, version 2."),
" ",
N_("Copyright (C) 2002-2007 Daniel Heck and contributors."),
0,
N_("Main developer of all releases:"),
" ",
" Raoul Bourquin (Level admin. & design, all over invaluable additions)",
" Siegfried Fennig (Level design, graphics)",
" Martin Hawlisch (Level design, graphics, programming)",
" Daniel Heck (Main developer, graphics, documentation)",
" Ronald Lamprecht (XML, Gui, portability, core prog., documentation)",
" Andreas Lochmann (Programming, level admin. & design, documentation)",
" Petr Machata (Level design, programming)",
" Nat Pryce (Level design)",
" Jacob Scott (Level design)",
" Sven Siggelkow (Level design and special Oxyd expertise)",
" Ralf Westram (Programming, level design)",
0,
N_("Special Thanks:"),
" Johannes Fortmann (Mac OS X port, some programming, graphics)",
" illmind (Forum mag-heut.net administration, Level design)",
// waiting for licence to add the sounds
// " \'Cellar of Rats\' (Sound effects)",
" Jennifer Robertson (Graphics second generation)",
" Jeremy Sawicki (Oxydlib)",
" Erich Schubert (Debian/Ubuntu packages, level design)",
" Lukas Schüller (Level design)",
" Andrew \'Necros\' Sega (Menu music \'Pentagonal Dreams\')",
" David W. Skinner (Many Sokoban Levels)",
" Clifford J. Tasner (Music second generation, Proof reading)",
0,
N_("Contributors:"),
" Roberto Bardin (Level design)",
" Alain Busser (Level design, French translation, manual)",
" Guy Busser (Level design)",
" Richi Bützer (Level design)",
" Capkoh (Level design)",
" Christoph & Anita (Level design)",
" Дремук Сергей - Serge Dremuk (Russian translation)",
" Joseph Dunne (Level design)",
" Xerxes M. Dynatos (Level design)",
" Edward (Level design)",
" Stephanie Fabian (Invaluable bug reports)",
" Roberto García (Spanish translation)",
" Andy Geldmacher (Level design)",
" Edwin Groothuis (FreeBSD port)",
" Immanuel Herrmann (Level design)",
" Máté Lehel Juhász (Hungarian translation)",
0,
" Samuele Kaplun (Italian translation)",
" Jens-Christian Korth (Level design)",
" Manuel König (Level design, bug reports)",
" Johannes Laire (Level design)",
" Joona Laire (Level design)",
" Markus Laire (Level design)",
" Dominik Lehmann (Level design)",
" Edward Leuf (Feedback, bug reports)",
" Ingo van Lil (Feedback, bug reports)",
" Frank van der Loo (Dutch translation)",
" Sidney Markowitz (Mac OS X port)",
" Barry & Lori Mead (Level design)",
" Linda Mihalic (English proof reading)",
" moonpearl (Level design)",
" Krishnamurti L.L.V. Nunes (Portuguese translation)",
" Daniel Nylander (Swedish translation)",
0,
" Andreas Persenius (Level design)",
" Peter Santo (Level design)",
" Tobias Schmidbauer (Windows installer and icon)",
" Achim Settelmeier (RPM specfile)",
" Jon Sneyers (Level design)",
" Spaceman (Level design)",
" Ulf Stegemann (Level design)",
" Jürgen Sticht (Level design)",
" Mikke Surakka (Finnish translation)",
" Andrzej Szombierski (Level design)",
" Tacvek (Lua 5.1 upgrade)",
" Michael Terry (.desktop file)",
" Ray Wick (Level design)",
" Joe Wreschnig (Manual page)",
" Юрий Жиромский - Yuriy Zhyromskiy (Russian Manual)",
0,
};
MainMenu::MainMenu()
{
build_menu();
}
void MainMenu::build_menu()
{
const video::VMInfo *vminfo = video::GetInfo();
BuildVList b(this, Rect((vminfo->width - 150)/2,150,150,40), 5);
m_startgame = b.add(new StaticTextButton(N_("Start Game"), this));
m_levelpack = b.add(new StaticTextButton(N_("Level Pack"), this));
#ifdef ENABLE_EXPERIMENTAL
m_netgame = b.add (new StaticTextButton (N_("Network Game"), this));
leveled = b.add(new StaticTextButton(N_("Editor"), this));
#endif
options = b.add(new StaticTextButton(N_("Options"), this));
credits = b.add(new StaticTextButton(N_("Credits"), this));
quit = b.add(new StaticTextButton(N_("Quit"), this));
}
void MainMenu::draw_background(ecl::GC &gc)
{
const video::VMInfo *vminfo = video::GetInfo();
video::SetCaption (("Enigma - Main Menu"));
sound::PlayMusic (options::GetString("MenuMusicFile"));
blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg"));
Font *f = enigma::GetFont("levelmenu");
Surface * logo(enigma::GetImage("enigma_logo3"));
int x0=(vminfo->width - logo->width())/2;
int y0=30;
blit(gc, x0, y0, logo);
f->render (gc, 5, vminfo->height - 20, app.getVersionInfo().c_str());
}
bool MainMenu::on_event (const SDL_Event &e) {
switch (e.type) {
case SDL_KEYDOWN:
SDLKey keysym = e.key.keysym.sym;
switch (keysym) {
case SDLK_F2:
show_paths();
invalidate_all();
return true;
default:
break;
}
break;
}
return false;
}
void MainMenu::on_action(Widget *w)
{
if (w == m_startgame) {
LevelPackMenu m;
m.manageLevelMenu();
invalidate_all();
} else if (w == m_levelpack) {
LevelPackMenu m;
m.manage();
invalidate_all();
} else if (w == credits) {
displayInfo(credit_text, 6);
invalidate_all();
} else if (w == options) {
ShowOptionsMenu(0);
#ifdef ENABLE_EXPERIMENTAL
} else if (w == m_netgame) {
ShowNetworkMenu();
} else if (w == leveled) {
editor::Run();
#endif
} else if (w == quit) {
Menu::quit();
} else
return;
invalidate_all();
}
void MainMenu::tick(double /* dtime */)
{
bool option_fullscreen = options::GetInt ("FullScreen") != 0;
if (options::GetInt ("VideoMode") != video::GetVideoMode()
|| option_fullscreen != video::IsFullScreen())
{
ChangeVideoMode();
clear();
reset_active_widget ();
build_menu();
invalidate_all();
}
}
void MainMenu::show_paths() {
const char *pathtext[25];
std::string pathstrings[25];
std::string work;
Font *menufont = enigma::GetFont("menufont");
const video::VMInfo *vminfo = video::GetInfo();
int width = vminfo->width - 80;
int i = 0;
pathtext[i++] = N_("Preferences Path:");
work = app.prefPath;
do {
std::string::size_type breakPos = breakString(menufont, work,"/\\", width);
pathstrings[i] = " " + work.substr(0,breakPos);
pathtext[i] = pathstrings[i].c_str();
work = work.substr(breakPos);
i++;
} while(!work.empty() );
pathtext[i++] = " ";
pathtext[i++] = N_("User Path:");
work = app.userPath;
do {
std::string::size_type breakPos = breakString(menufont, work,"/\\", width);
pathstrings[i] = " " + work.substr(0,breakPos);
pathtext[i] = pathstrings[i].c_str();
work = work.substr(breakPos);
i++;
} while(!work.empty() );
pathtext[i++] = " ";
pathtext[i++] = N_("User Image Path:");
work = app.userImagePath;
do {
std::string::size_type breakPos = breakString(menufont, work,"/\\", width);
pathstrings[i] = " " + work.substr(0,breakPos);
pathtext[i] = pathstrings[i].c_str();
work = work.substr(breakPos);
i++;
} while(!work.empty() );
pathtext[i++] = " ";
pathtext[i++] = N_("System Path:");
work = app.systemFS->getDataPath();
do {
std::string::size_type breakPos = breakString(menufont, work,"/\\", width);
pathstrings[i] = " " + work.substr(0,breakPos);
pathtext[i] = pathstrings[i].c_str();
work = work.substr(breakPos);
i++;
} while(!work.empty() );
pathtext[i++] = " ";
pathtext[i++] = N_("Resource Paths:");
work = app.resourceFS->getDataPath();
do {
std::string::size_type breakPos = breakString(menufont, work,"/\\", width);
pathstrings[i] = " " + work.substr(0,breakPos);
pathtext[i] = pathstrings[i].c_str();
work = work.substr(breakPos);
i++;
} while(!work.empty() );
pathtext[i++] = " ";
pathtext[i++] = N_("L10n Path:");
work = app.l10nPath;
do {
std::string::size_type breakPos = breakString(menufont, work,"/\\", width);
pathstrings[i] = " " + work.substr(0,breakPos);
pathtext[i] = pathstrings[i].c_str();
work = work.substr(breakPos);
i++;
} while(!work.empty() );
pathtext[i++] = 0;
displayInfo(pathtext, 1);
}
/* -------------------- Functions -------------------- */
void ShowMainMenu() {
MainMenu m;
m.manage();
}
void ShowNetworkMenu()
{
NetworkMenu m;
m.manage();
}
}} // namespace enigma::gui

View File

@@ -0,0 +1,83 @@
/*
* Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef GUI_MAINMENU_HH_INCLUDED
#define GUI_MAINMENU_HH_INCLUDED
#include "gui/Menu.hh"
namespace enigma { namespace gui {
/* -------------------- MainMenu -------------------- */
class MainMenu : public Menu {
public:
MainMenu();
private:
// Menu interface
void draw_background(ecl::GC &gc);
void tick(double dtime);
// Widget interface
virtual bool on_event(const SDL_Event &e);
// ActionListener interface.
void on_action(Widget *w);
// Private methods.
void show_paths();
void build_menu();
// Variables.
Widget *m_startgame;
Widget *m_levelpack;
Widget *m_netgame;
Widget *leveled;
Widget *manual;
Widget *options;
Widget *credits;
Widget *quit;
Widget *lpack;
};
/* -------------------- NetworkMenu -------------------- */
class NetworkMenu : public gui::Menu {
public:
NetworkMenu ();
~NetworkMenu ();
private:
// ActionListener interface.
bool on_event (const SDL_Event &e);
void on_action(gui::Widget *w);
// Menu interface.
void draw_background(ecl::GC &gc);
void tick(double dtime);
// Variables.
gui::Widget *m_startgame;
gui::Widget *m_joingame;
gui::Widget *m_back;
};
/* -------------------- Functions -------------------- */
void ShowMainMenu();
void ShowNetworkMenu();
}} // namespace enigma::gui
#endif

View File

@@ -0,0 +1,213 @@
/*
* Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "gui/Menu.hh"
#include "sound.hh"
#include "video.hh"
#include "options.hh"
#include "nls.hh"
#include "ecl.hh"
#include <cassert>
#include <algorithm>
#include <iostream>
using namespace ecl;
using namespace std;
#define SCREEN ecl::Screen::get_instance()
namespace enigma { namespace gui {
/* -------------------- Menu -------------------- */
Menu::Menu()
: active_widget(0), quitp(false), abortp(false) {
}
void Menu::add(Widget *w) {
Container::add_child(w);
}
void Menu::add(Widget *w, ecl::Rect r) {
w->move (r.x, r.y);
w->resize (r.w, r.h);
add(w);
}
void Menu::quit() {
quitp=true;
}
void Menu::abort() {
abortp=true;
}
bool Menu::manage() {
quitp=abortp=false;
SDL_Event e;
Uint32 enterTickTime = SDL_GetTicks(); // protection against ESC D.o.S. attacks
while (SDL_PollEvent(&e)) {} // clear event queue
draw_all();
while (!(quitp || abortp)) {
SCREEN->flush_updates();
while (SDL_PollEvent(&e)) {
handle_event(e);
}
SDL_Delay(10);
if(active_widget) active_widget->tick(0.01);
tick (0.01);
refresh();
}
sound::EmitSoundEvent ("menuexit");
// protection against ESC D.o.S. attacks
Uint32 menuTickDuration = SDL_GetTicks() - enterTickTime;
Uint32 minMenuTickDuration = 300;
if (menuTickDuration < minMenuTickDuration)
SDL_Delay(minMenuTickDuration - menuTickDuration);
while (SDL_PollEvent(&e)) {} // clear event queue
return !abortp;
}
void Menu::goto_adjacent_widget(int xdir, int ydir) {
Widget *next_widget = 0;
if (active_widget) {
next_widget = find_adjacent_widget(active_widget, xdir, ydir);
}
else { // no active_widget yet
if ((xdir+ydir)>0) { // take first
next_widget = *begin();
}
else { // take last
iterator e = end();
for (iterator i = begin(); i != e; ++i) {
next_widget = *i;
}
}
}
if (next_widget) {
switch_active_widget(next_widget);
}
else { // no more widgets into that direction found
sound::EmitSoundEvent ("menustop");
}
}
void Menu::handle_event(const SDL_Event &e)
{
if (e.type == SDL_KEYDOWN &&
e.key.keysym.sym == SDLK_RETURN &&
e.key.keysym.mod & KMOD_ALT)
{
video::ToggleFullscreen();
return;
}
if (on_event(e))
return;
switch (e.type) {
case SDL_QUIT:
abort();
break;
case SDL_MOUSEMOTION:
track_active_widget( e.motion.x, e.motion.y );
break;
case SDL_KEYDOWN:
if (!active_widget || !active_widget->on_event(e)) {
// if not handled by active_widget
switch (e.key.keysym.sym) {
case SDLK_ESCAPE:
abort();
break;
case SDLK_DOWN: goto_adjacent_widget( 0, 1); break;
case SDLK_UP: goto_adjacent_widget( 0, -1); break;
case SDLK_RIGHT: goto_adjacent_widget( 1, 0); break;
case SDLK_LEFT: goto_adjacent_widget(-1, 0); break;
default:
break;
}
}
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
track_active_widget( e.button.x, e.button.y );
if (active_widget) active_widget->on_event(e);
break;
case SDL_VIDEOEXPOSE:
draw_all();
break;
default:
if (active_widget) active_widget->on_event(e);
}
}
void Menu::switch_active_widget(Widget *to_activate) {
if (to_activate != active_widget) {
if (active_widget)
active_widget->deactivate();
if (to_activate)
to_activate->activate();
active_widget = to_activate;
}
}
void Menu::track_active_widget( int x, int y ) {
switch_active_widget(find_widget(x, y));
}
void Menu::center() {
if (m_widgets.size() > 0) {
using std::min;
using std::max;
Rect a = m_widgets[0]->get_area();
for (unsigned i=1; i<m_widgets.size(); ++i)
{
Rect r = m_widgets[i]->get_area();
a.x = min(r.x, a.x);
a.y = min(r.y, a.y);
a.w += max(0, r.x+r.w-a.x-a.w);
a.h += max(0, r.y+r.h-a.y-a.h);
}
Rect c=ecl::center(SCREEN->size(), a);
int dx = c.x-a.x;
int dy = c.y-a.y;
for (unsigned i=0; i<m_widgets.size(); ++i) {
Rect r = m_widgets[i]->get_area();
r.x += dx;
r.y += dy;
m_widgets[i]->move (r.x, r.y);
m_widgets[i]->resize (r.w, r.h);
}
}
}
void Menu::draw (ecl::GC &gc, const ecl::Rect &r)
{
clip(gc, r);
draw_background(gc);
Container::draw(gc,r);
}
}} // namespace enigma::gui

View File

@@ -0,0 +1,111 @@
/*
* Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef GUI_MENU_HH_INCLUDED
#define GUI_MENU_HH_INCLUDED
#include "gui/widgets.hh"
#include "gui/Menu.hh"
#include "ecl_fwd.hh"
#include "ecl_geom.hh"
#include "SDL.h"
#include <cmath>
#include <vector>
namespace enigma { namespace gui {
/* -------------------- Menu -------------------- */
class Menu : public Container {
public:
Menu();
//! true: ok, false: menu aborted by user
virtual bool manage();
void add(Widget *w);
void add(Widget *w, ecl::Rect r);
void center();
void draw (ecl::GC &gc, const ecl::Rect &r);
virtual void quit();
void abort();
protected:
void reset_active_widget()
{ active_widget = 0; }
// Menu interface.
virtual void draw_background(ecl::GC &/*gc*/) {}
private:
void handle_event(const SDL_Event &e);
void switch_active_widget(Widget *to_activate);
void track_active_widget( int x, int y ); // used by mouse
void goto_adjacent_widget(int xdir, int ydir); // used by keyboard
// Variables.
Widget *active_widget;
bool quitp, abortp;
};
class BuildVList {
ecl::Rect r;
Menu *container;
int skip;
public:
BuildVList(Menu *cc, const ecl::Rect &rr, int s)
: r(rr), container(cc), skip(s)
{}
Widget *add(Widget *w) {
container->add(w, r);
r.y += r.h+skip;
return w;
}
ecl::Rect pos() const { return r; }
};
class BuildHList {
ecl::Rect r;
Menu *container;
int skip;
public:
BuildHList(Menu *cc, const ecl::Rect &rr, int s)
: r(rr), container(cc), skip(s)
{}
Widget * add(Widget *w) {
container->add(w, r);
r.x += r.w+skip;
return w;
}
Widget *add (Widget *w, int width) {
ecl::Rect rr(r.x, r.y, width, r.h);
container->add(w, rr);
r.x += width + skip;
return w;
}
ecl::Rect pos() const { return r; }
};
}} // namespace enigma::gui
#endif

View File

@@ -0,0 +1,85 @@
/*
* Copyright (C) 2006 Ronald Lamprecht
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "gui/MonospacedLabel.hh"
#include "ecl.hh"
#include "nls.hh"
using namespace ecl;
using namespace std;
namespace enigma { namespace gui {
MonospacedLabel::MonospacedLabel (const std::string &text, char widthSample,
std::string monospaceCharacters, HAlignment halign, VAlignment valign) :
Label (text, halign, valign), sampleChar (widthSample),
monoChars (monospaceCharacters) {
}
void MonospacedLabel::draw (ecl::GC &gc, const ecl::Rect &) {
Font *f = m_font;
int w, h;
naturalsize (w, h);
int x = get_x(), y=get_y();
switch (m_halign) {
case HALIGN_LEFT: break;
case HALIGN_RIGHT: x += get_w() - w; break;
case HALIGN_CENTER: x += (get_w()-w)/2; break;
}
switch (m_valign) {
case VALIGN_TOP: break;
case VALIGN_BOTTOM: y += get_h() - h; break;
case VALIGN_CENTER: y += (get_h()-h)/2; break;
}
// translate if not an empty string
const char * translation = _(m_text.c_str());
int len = strlen(translation);
int monoWidth = m_font->get_width(sampleChar);
char c[] = " ";
for (int i = 0; i<len; i++) {
c[0] = translation[i];
if (monoChars.empty() || monoChars.find_first_of(c[0]) !=
std::string::npos) {
int charWidth = m_font->get_width(c[0]);
// centere char into monoWodth
f->render (gc, x + (monoWidth-charWidth)/2, y, c);
x += monoWidth;
} else {
f->render (gc, x, y, c);
x += m_font->get_width(c);
}
}
}
void MonospacedLabel::naturalsize (int &w, int &h) const {
h = m_font->get_height();
w = 0;
const char * translation = _(m_text.c_str());
int len = strlen(translation);
int monoWidth = m_font->get_width(sampleChar);
for (int i = 0; i<len; i++) {
if (monoChars.empty() || monoChars.find_first_of(translation[i]) !=
std::string::npos) {
w += monoWidth;
} else {
w += m_font->get_width(translation[i]);
}
}
}
}} // namespace enigma::lev

View File

@@ -0,0 +1,57 @@
/*
* Copyright (C) 2006 Ronald Lamprecht
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef GUI_MONOSPACEDLABEL_HH_INCLUDED
#define GUI_MONOSPACEDLABEL_HH_INCLUDED
#include "gui/widgets.hh"
namespace enigma { namespace gui {
/**
* A text label that uses the same width for each character even for
* proportional fonts. Character positions of the label text is predictable
* and alignment of several labels is thus possible. Usefull for output of
* formatted numbers like score values.<p>
* Not all characters have to be monospaced. The set of characters that
* should have a constant width can be limited f.e. to the numbers 0-9.
*/
class MonospacedLabel : public Label {
public:
/**
* The standard constructor.
* @arg text The constant output text.
* @arg widthSample The character that defines the width. Default is 'm'.
* @arg monospaceCharacters The set of monospace characters. An empty
* string means all characters. Example "0123456789".
* @arg halign
* @arg valign
*/
MonospacedLabel (const std::string &text="", char widthSample = 'm',
std::string monospaceCharacters = "",
HAlignment halign=HALIGN_CENTER, VAlignment valign=VALIGN_CENTER);
// Widget interface
virtual void draw (ecl::GC &gc, const ecl::Rect &r);
virtual void naturalsize (int &w, int &h) const;
protected:
char sampleChar;
std::string monoChars;
};
}} // namespace enigma::gui
#endif

View File

@@ -0,0 +1,477 @@
/*
* Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "gui/OptionsMenu.hh"
#include "ecl.hh"
#include "enigma.hh"
#include "lev/ScoreManager.hh"
#include "LocalToXML.hh"
#include "main.hh"
#include "nls.hh"
#include "options.hh"
#include "oxyd.hh"
#include "sound.hh"
#include "Utf8ToXML.hh"
#include "video.hh"
#include "XMLtoLocal.hh"
#include "XMLtoUtf8.hh"
#include <cassert>
using namespace ecl;
using namespace std;
namespace enigma { namespace gui {
/* -------------------- Buttons for Options -------------------- */
class MouseSpeedButton : public ValueButton {
int get_value() const {
return ecl::round_nearest<int>(options::GetMouseSpeed());
}
void set_value(int value) {
options::SetMouseSpeed (value);
}
string get_text(int value) const {
return strf("%d", value);
}
public:
MouseSpeedButton()
: ValueButton(options::MIN_MouseSpeed, options::MAX_MouseSpeed)
{ init(); }
};
class SoundVolumeButton : public ValueButton {
int get_value() const {
return round_nearest<int>(options::GetDouble("SoundVolume")*10.0);
}
void set_value(int value) {
options::SetOption("SoundVolume", value/10.0);
options::UpdateVolume();
}
string get_text(int value) const {
if (value == 0) {
return _("muted");
}
else {
return strf("%d", value);
}
}
public:
SoundVolumeButton() : ValueButton(0, 10) { init(); }
};
class MusicVolumeButton : public ValueButton {
int get_value() const {
return round_nearest<int> (options::GetDouble("MusicVolume")*10.0);
}
void set_value(int value) {
options::SetOption("MusicVolume", value/10.0);
options::UpdateVolume();
}
string get_text(int value) const {
if (value == 0)
return _("muted");
else
return strf("%d", value);
}
public:
MusicVolumeButton() : ValueButton(0, 10) { init(); }
};
class InGameMusicButton : public BoolOptionButton {
void on_action(Widget *) {
if (toggle())
sound::PlayMusic (options::GetString("LevelMusicFile"));
else
sound::StopMusic (options::GetString("LevelMusicFile"));
}
public:
InGameMusicButton() :
BoolOptionButton("InGameMusic", N_("Music in game"), N_("No music in game"), this)
{ }
};
struct SkipSolvedButton : public BoolOptionButton {
SkipSolvedButton() : BoolOptionButton("SkipSolvedLevels", N_("Yes"), N_("No"), this) {}
};
struct TimeHuntButton : public BoolOptionButton {
TimeHuntButton() : BoolOptionButton("TimeHunting", N_("Yes"), N_("No"), this) {}
};
struct RatingsUpdateButton : public BoolOptionButton {
RatingsUpdateButton() : BoolOptionButton("RatingsAutoUpdate", N_("Auto"), N_("Never"), this) {}
};
class VideoModeButton : public TextButton {
video::VideoModes get_mode() const {
int mode = Clamp(options::GetInt("VideoMode"), 0, int(video::VM_COUNT));
return static_cast<video::VideoModes>(mode);
}
string get_text() const {
return GetInfo(get_mode())->name;
}
void on_action(Widget *) {
int mode = get_mode();
// cycle at most once through all available video modes
do {
mode += 1;
if (mode >= video::VM_COUNT)
mode = 0;
const video::VMInfo *vminfo = GetInfo (static_cast<video::VideoModes>(mode));
if (vminfo->available) {
options::SetOption("VideoMode", mode);
invalidate();
break;
}
} while (mode != get_mode());
}
public:
VideoModeButton() : TextButton(this) { }
};
/* -------------------- SoundSetButton -------------------- */
SoundSetButton::SoundSetButton() : ValueButton(0, 1) {
int numAvail = sound::GetOptionSoundSetCount();
setMaxValue(numAvail - 1);
init();
}
int SoundSetButton::get_value() const {
return sound::GetOptionSoundSet();
}
void SoundSetButton::set_value(int value) {
sound::SetOptionSoundSet(value);
}
string SoundSetButton::get_text(int value) const {
return _(sound::GetOptionSoundSetText(value).c_str());
}
/* -------------------- StereoButton -------------------- */
StereoButton::StereoButton() : ValueButton(-1, 1)
{
init();
}
int StereoButton::get_value() const
{
double separation = options::GetDouble("StereoSeparation");
if (separation == 0)
return 0;
else
return (separation > 0) ? 1 : -1;
}
void StereoButton::set_value(int value)
{
if (value == 0)
options::SetOption("StereoSeparation", 0.0);
else if (value > 0)
options::SetOption("StereoSeparation", 10.0);
else
options::SetOption("StereoSeparation", -10.0);
}
string StereoButton::get_text(int value) const
{
switch (value) {
case -1: return _("reversed");
case 0: return _("mono");
case 1: return _("normal");
}
assert(0);
return string();
}
/* -------------------- FullscreenButton -------------------- */
FullscreenButton::FullscreenButton()
: BoolOptionButton("FullScreen", N_("Yes"), N_("No"), this)
{
}
/* -------------------- LanguageButton -------------------- */
struct Language {
const char *name;
const char *localename;
};
Language languages[] = {
{ "default", "" },
{ "Deutsch", "de_DE" },
{ "English", "en_EN" },
{ "Español", "es_ES" },
{ "Français", "fr_FR" },
{ "Italiano", "it_IT" },
{ "Nederlands", "nl_NL" },
{ "Svenska", "sv_SE" },
{ "Русский", "ru_RU" },
{ "Magyar", "hu_HU" },
{ "Português", "pt_BR" },
{ "Suomi", "fi_FI" },
};
int LanguageButton::get_value() const
{
string localename; // = ecl::DefaultMessageLocale ();
options::GetOption ("Language", localename);
int lang = 0; // unknown language
for (size_t i=0; i<NUMENTRIES(languages); ++i) {
if (localename == languages[i].localename)
lang = int(i);
}
return lang;
}
void LanguageButton::set_value(int value)
{
options::SetOption ("Language", languages[value].localename);
if ( not inInit) {
// change language only on user action
app.setLanguage(languages[value].localename);
myListener->on_action(this);
}
}
string LanguageButton::get_text(int value) const
{
if (value == -1)
return _("unknown");
else
return languages[value].name;
}
LanguageButton::LanguageButton (ActionListener *al)
: ValueButton(0, NUMENTRIES(languages)-1), myListener(al)
{
inInit = true;
init();
inInit = false;
}
/* -------------------- GammaButton -------------------- */
GammaButton::GammaButton()
: ValueButton(1, 10)
{
init();
}
void GammaButton::set_value(int value)
{
double gamma = double(value) / 5.0;
options::SetOption ("Gamma", gamma);
video::UpdateGamma();
}
int GammaButton::get_value() const
{
double gamma = options::GetDouble ("Gamma");
int value = round_down<int>(gamma * 5.0);
return value;
}
string GammaButton::get_text(int value) const
{
return ecl::strf ("%d", value-5);
}
/* -------------------- Options Menu -------------------- */
OptionsMenu::OptionsMenu(ecl::Surface *background_)
: back(new StaticTextButton(N_("Back"), this)),
fullscreen(new FullscreenButton),
m_restartinfo (new Label("")),
background(background_),
previous_caption(video::GetCaption())
{
const int spacing = 5;
const int big_spacing = 60;
const int label_width = 180;
const int but_width = 100;
const int but_height = 30;
const video::VMInfo *vminfo = video::GetInfo();
int hmargin = vminfo->width < 660 ? 10 : (vminfo->width < 900 ? 20 : 80);
int midspacing = vminfo->width - 2*hmargin - 2*but_width - 2*label_width;
BuildVList leftlabels (this, Rect(-label_width, 0, label_width, but_height), spacing);
BuildVList left (this, Rect(0, 0, but_width, but_height), spacing);
BuildVList rightlabels (this, Rect(but_width+midspacing, 0, label_width, but_height), spacing);
BuildVList right(this, Rect(but_width+midspacing+label_width, 0, but_width, but_height), spacing);
leftlabels.add (new Label(N_("Language: "), HALIGN_RIGHT));
leftlabels.add (new Label(N_("Fullscreen: "), HALIGN_RIGHT));
leftlabels.add (new Label(N_("Video mode: "), HALIGN_RIGHT));
leftlabels.add (new Label(N_("Gamma correction: "), HALIGN_RIGHT));
leftlabels.add (new Label(N_("Mouse speed: "), HALIGN_RIGHT));
language = new LanguageButton(this);
left.add (language);
left.add (fullscreen);
left.add (new VideoModeButton);
left.add (new GammaButton);
left.add (new MouseSpeedButton);
rightlabels.add (new Label(N_("Sound volume: "), HALIGN_RIGHT));
rightlabels.add (new Label(N_("Sound set: "), HALIGN_RIGHT));
rightlabels.add (new Label(N_("Music volume: "), HALIGN_RIGHT));
rightlabels.add (new Label(N_("Stereo: "), HALIGN_RIGHT));
rightlabels.add (new Label(N_("Ratings update: "), HALIGN_RIGHT));
right.add (new SoundVolumeButton);
right.add (new SoundSetButton);
right.add (new MusicVolumeButton);
// right.add (new InGameMusicButton);Über
right.add (new StereoButton);
right.add (new RatingsUpdateButton);
Rect l = left.pos();
Rect r = right.pos();
BuildVList bottomlabels (this, Rect(-label_width, Max(l.y, r.y), label_width, but_height), spacing);
BuildVList bottom (this, Rect(0, Max(l.y, r.y), vminfo->width - 2*hmargin - label_width, but_height), spacing);
bottomlabels.add (new Label(N_("User name: "), HALIGN_RIGHT));
bottomlabels.add (new Label(N_("User path: "), HALIGN_RIGHT));
bottomlabels.add (new Label(N_("User image path: "), HALIGN_RIGHT));
userNameTF = new TextField(app.state->getString("UserName"));
userNameTF->setMaxChars(20);
userNameTF->setInvalidChars("+");
bottom.add (userNameTF);
userPathTF = new TextField(XMLtoUtf8(LocalToXML(app.userPath.c_str()).x_str()).c_str());
bottom.add (userPathTF);
userImagePathTF = new TextField(XMLtoUtf8(LocalToXML(app.userImagePath.c_str()).x_str()).c_str());
bottom.add (userImagePathTF);
// add (m_restartinfo, Rect (l.x, l.y + 15, 400, 20));
// m_restartinfo->set_alignment (HALIGN_LEFT);
// update_info();
Rect b = bottom.pos();
l.x = (l.x+r.x)/2;
l.y = b.y+big_spacing;
add(back, l);
}
OptionsMenu::~OptionsMenu() {
video::SetCaption(previous_caption.c_str());
}
// void OptionsMenu::update_info()
// {
// if (options::MustRestart)
// m_restartinfo->set_text (
// N_("Please restart Enigma to activate your changes!"));
// else
// m_restartinfo->set_text ("");
// }
void OptionsMenu::quit() {
std::string tfUserPathLocal = XMLtoLocal(Utf8ToXML(userPathTF->getText().c_str()).x_str()).c_str();
std::string tfUserImageLocal = XMLtoLocal(Utf8ToXML(userImagePathTF->getText().c_str()).x_str()).c_str();
if ((app.state->getString("UserName") != userNameTF->getText())
|| (app.userPath != tfUserPathLocal ) || (app.userImagePath != tfUserImageLocal)) {
// ensure that enigma.score is saved with new Username or to new location
lev::ScoreManager::instance()->markModified();
}
// strip off leading and trailing whitespace from user name
std::string userName = userNameTF->getText();
std::string::size_type firstChar = userName.find_first_not_of(" ");
std::string::size_type lastChar = userName.find_last_not_of(" ");
if (firstChar != std::string::npos)
app.state->setProperty("UserName", userName.substr(firstChar, lastChar - firstChar + 1));
else
app.state->setProperty("UserName", std::string(""));
app.setUserPath(tfUserPathLocal.c_str());
app.setUserImagePath(tfUserImageLocal.c_str());
Menu::quit();
}
bool OptionsMenu::on_event (const SDL_Event &e)
{
bool handled=false;
if (e.type == SDL_MOUSEBUTTONDOWN
&& e.button.button == SDL_BUTTON_RIGHT)
{
quit();
handled = true;
}
else if (e.type == SDL_KEYUP) {
if ((e.key.keysym.sym==SDLK_RETURN) &&
(e.key.keysym.mod & KMOD_ALT))
{
// update state of FullscreenButton :
fullscreen->invalidate();
handled = true;
}
}
return handled;
}
void OptionsMenu::on_action(Widget *w)
{
if (w == back)
quit();
else if (w == language)
// language changed - retranslate and redraw everything
invalidate_all();
}
void OptionsMenu::tick (double)
{
// update_info();
}
void OptionsMenu::draw_background(ecl::GC &gc)
{
video::SetCaption(("Enigma - Options Menu"));
// blit(gc, 0,0, enigma::GetImage("menu_bg"));
blit(gc, 0,0, background);
}
/* -------------------- Functions -------------------- */
void ShowOptionsMenu(Surface *background) {
if (background == 0)
background = enigma::GetImage("menu_bg", ".jpg");
OptionsMenu m(background);
m.center();
m.manage();
}
}} // namespace enigma::gui

View File

@@ -0,0 +1,104 @@
/*
* Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef GUI_OPTIONSMENU_HH_INCLUDED
#define GUI_OPTIONSMENU_HH_INCLUDED
#include "gui/Menu.hh"
#include "gui/TextField.hh"
namespace enigma { namespace gui {
/* -------------------- OptionsMenu -------------------- */
class OptionsMenu : public Menu {
public:
OptionsMenu(ecl::Surface *background_);
~OptionsMenu();
virtual void quit();
private:
void update_info();
// ActionListener interface.
bool on_event (const SDL_Event &e);
void on_action(gui::Widget *w);
// Menu interface.
void draw_background(ecl::GC &gc);
void tick(double dtime);
// Variables.
gui::Widget *back, *fullscreen, *language;
gui::TextField *userNameTF;
gui::TextField *userPathTF;
gui::TextField *userImagePathTF;
gui::Label *m_restartinfo;
ecl::Surface *background;
std::string previous_caption;
};
/* -------------------- Options Buttons -------------------- */
struct FullscreenButton : public BoolOptionButton {
FullscreenButton();
};
class StereoButton : public ValueButton {
int get_value() const;
void set_value(int value);
std::string get_text(int value) const;
public:
StereoButton();
};
class SoundSetButton : public ValueButton {
public:
SoundSetButton();
int get_value() const;
void set_value(int value);
std::string get_text(int value) const;
};
class LanguageButton : public ValueButton {
int get_value() const;
void set_value(int value);
std::string get_text(int value) const;
bool inInit;
ActionListener *myListener;
public:
// second user action listener: first one is misused by ValueButton
LanguageButton (ActionListener *al = 0);
};
class GammaButton : public ValueButton {
int get_value() const;
void set_value(int value);
std::string get_text(int value) const;
public:
GammaButton ();
};
/* -------------------- Functions -------------------- */
void ShowOptionsMenu(ecl::Surface *background);
}} // namespace enigma::gui
#endif

View File

@@ -0,0 +1,109 @@
/*
* Copyright (C) 2006 Ronald Lamprecht
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "gui/ScreenshotViewer.hh"
#include "gui/HelpMenu.hh"
#include "ecl.hh"
#include "enigma.hh"
#include "main.hh"
#include "nls.hh"
#include "video.hh"
using namespace ecl;
using namespace std;
namespace enigma { namespace gui {
static const char *helptext[] = {
N_("Escape:"), N_("Back"),
"F1:", N_("Show this help"),
N_("Page Up:"), N_("Show previous screenshot"),
N_("Page Down:"), N_("Show next screenshot"),
0
};
ScreenshotViewer::ScreenshotViewer(lev::Proxy *aLevel) :
levelProxy (aLevel), shotNumber (0) {
}
ScreenshotViewer::~ScreenshotViewer() {
}
bool ScreenshotViewer::on_event (const SDL_Event &e) {
switch (e.type) {
case SDL_MOUSEBUTTONDOWN:
if (e.button.button == SDL_BUTTON_RIGHT) {
Menu::quit();
return true;
}
break;
case SDL_KEYDOWN:
SDLKey keysym = e.key.keysym.sym;
switch (keysym) {
case SDLK_PAGEUP:
if (shotNumber > 0 )
--shotNumber;
invalidate_all();
return true;
case SDLK_PAGEDOWN:
shotNumber++;
invalidate_all();
return true;
// eat up widget activation keys - we have no widgets
case SDLK_DOWN:
case SDLK_UP:
case SDLK_RIGHT:
case SDLK_LEFT:
return true;
case SDLK_F1:
displayHelp(helptext, 200);
invalidate_all();
return true;
default:
break;
}
break;
}
return false;
}
void ScreenshotViewer::draw_background (ecl::GC &gc) {
std::string filename = "screenshots/" +
levelProxy->getLocalSubstitutionLevelPath() +
(shotNumber > 0 ? ecl::strf("#%d", shotNumber) : "") + ".png";
std::string fullPath;
if (app.resourceFS->findFile(filename, fullPath)) {
const video::VMInfo *vminfo = video::GetInfo();
ecl::Surface * image = ecl::LoadImage(fullPath.c_str());
if (image->width() == vminfo->width && image->height() == vminfo->height) {
blit(gc, 0,0, image);
} else {
ecl::Surface * imageZoomed = image->zoom(vminfo->width, vminfo->height);
blit(gc, 0,0, imageZoomed);
delete imageZoomed;
}
delete image;
} else {
blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg"));
Font *f = enigma::GetFont("menufont");
f->render (gc, 30, 60, _("No screenshot available:"));
f->render (gc, 30, 100, filename.c_str());
}
}
}} // namespace enigma::gui

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2006 Ronald Lamprecht
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef GUI_SCREENSHOTVIEWER_HH_INCLUDED
#define GUI_SCREENSHOTVIEWER_HH_INCLUDED
#include "gui/Menu.hh"
#include "lev/Proxy.hh"
namespace enigma { namespace gui {
/**
* TODO delete CTRL-D with confirmation panel - rename of following shots?
* TODO shift up, down for reorder/rename of shots
*/
class ScreenshotViewer : public gui::Menu {
public:
ScreenshotViewer (lev::Proxy *aLevel);
~ScreenshotViewer ();
// Widget interface
virtual bool on_event (const SDL_Event &e);
protected:
// Menu interface.
virtual void draw_background (ecl::GC &gc);
private:
lev::Proxy *levelProxy;
std::string basePath;
int shotNumber;
};
}} // namespace enigma::gui
#endif

View File

@@ -0,0 +1,91 @@
/*
* Copyright (C) 2006 Ronald Lamprecht
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "gui/SearchMenu.hh"
#include "ecl.hh"
#include "enigma.hh"
#include "errors.hh"
#include "lev/Index.hh"
#include "lev/Proxy.hh"
#include "nls.hh"
#include "video.hh"
#include "main.hh"
using namespace ecl;
using namespace std;
namespace enigma { namespace gui {
SearchMenu::SearchMenu() : didSearch (false) {
const video::VMInfo &vminfo = *video::GetInfo();
ecl::Font *menufont = enigma::GetFont("menufont");
Label * shallowTitle = new Label(N_("Shallow Search:"), HALIGN_LEFT);
// TRANSLATORS: the translation can have double size of the english text
std::string helpText = _("case independent search in title, author, filename");
std::string workString = helpText;
std::string::size_type breakPos = breakString(menufont, workString,
" ", 380);
Label * shallowHelp1 = new UntranslatedLabel(workString.substr(0,breakPos), HALIGN_LEFT);
Label * shallowHelp2 = new UntranslatedLabel(workString.substr(breakPos), HALIGN_LEFT);
shallowSearch = new TextField("", this);
this->add(shallowTitle, Rect(vminfo.width/2 - 190, vminfo.height/2 - 100, 380, 35));
this->add(shallowHelp1, Rect(vminfo.width/2 - 190, vminfo.height/2 - 40, 380, 25));
this->add(shallowHelp2, Rect(vminfo.width/2 - 190, vminfo.height/2 - 10, 380, 25));
this->add(shallowSearch, Rect(vminfo.width/2 - 190, vminfo.height/2 + 55, 380, 35));
// Create buttons - positioning identical to Levelmenu
Label * dummy1 = new Label();
Label * dummy2 = new Label();
but_ignore = new StaticTextButton(N_("Undo"), this);
but_search = new StaticTextButton(N_("Search"), this);
HList * commandHList = new HList;
commandHList->set_spacing(10);
commandHList->set_alignment(HALIGN_CENTER, VALIGN_TOP);
commandHList->set_default_size(140, 35);
commandHList->add_back(dummy1);
commandHList->add_back(dummy2);
commandHList->add_back(but_ignore);
commandHList->add_back(but_search);
this->add(commandHList, Rect(10, vminfo.height-50, vminfo.width-20, 35));
}
bool SearchMenu::isSearchQuit() {
return didSearch;
}
void SearchMenu::on_action(Widget *w) {
if (w == but_search ||
(w == shallowSearch && shallowSearch->wasLastActionReturn())) {
lev::Index::setCurrentIndex(lev::Proxy::search(shallowSearch->getText()));
didSearch = true;
Menu::quit();
} else if (w == but_ignore) {
Menu::quit();
}
}
void SearchMenu::draw_background(ecl::GC &gc) {
video::SetCaption(("Enigma - Search Menu"));
blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg"));
}
}} // namespace enigma::gui

View File

@@ -0,0 +1,43 @@
/*
* Copyright (C) 2006 Ronald Lamprecht
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef GUI_SEARCHMENU_HH_INCLUDED
#define GUI_SEARCHMENU_HH_INCLUDED
#include "gui/Menu.hh"
#include "gui/TextField.hh"
namespace enigma { namespace gui {
class SearchMenu : public gui::Menu {
public:
SearchMenu ();
void on_action(Widget *w);
void draw_background(ecl::GC &gc);
bool isSearchQuit();
private:
TextField *shallowSearch;
Widget *but_ignore;
Widget *but_search;
bool didSearch;
};
}} // namespace enigma::gui
#endif

View File

@@ -0,0 +1,281 @@
/*
* Copyright (C) 2005 Ronald Lamprecht
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "main.hh"
#include "gui/widgets.hh"
#include "gui/TextField.hh"
#include "ecl_utf.hh"
#include "enigma.hh"
#include "sound.hh"
#include "video.hh"
#include "options.hh"
#include "nls.hh"
#include "ecl.hh"
#include <cassert>
#include <algorithm>
#include <iostream>
using namespace enigma::gui;
using namespace ecl;
using namespace std;
#define SCREEN ecl::Screen::get_instance()
ecl::Font *TextField::menufont = 0;
TextField::TextField(const std::string &t, ActionListener *al) : cursorTime(0),
showCursor(true), isLastActionReturn (false), invalidChars(""),
isLimitedToValidChars(false), maxChars(-1) {
if (menufont == 0) {
menufont = enigma::GetFont("menufont");
}
set_listener(al);
textPreCursor = t;
ecl::utf8CharSizes(textPreCursor, charSizesPreCursor);
textPostCursor= "";
}
void TextField::set_text(const std::string &t) {
textPreCursor = t;
charSizesPreCursor.clear();
ecl::utf8CharSizes(textPreCursor, charSizesPreCursor);
textPostCursor= "";
charSizesPostCursor.clear();
}
std::string TextField::getText() {
string total = textPreCursor;
total += textPostCursor;
return total;
}
void TextField::setInvalidChars(std::string forbiddenChars) {
invalidChars = forbiddenChars;
}
void TextField::setMaxChars(int max) {
maxChars = max;
}
bool TextField::wasLastActionReturn() {
return isLastActionReturn;
}
void TextField::tick (double dtime) {
cursorTime += dtime;
if (cursorTime > 0.5) {
cursorTime = 0.0;
showCursor = !showCursor;
invalidate();
}
}
void TextField::draw(ecl::GC &gc, const ecl::Rect &r) {
Button::draw(gc,r);
Font *f = menufont;
int h = f->get_height();
int w_pre = f->get_width(textPreCursor.c_str());
int w_post = f->get_width(textPostCursor.c_str());
int w_cursor = m_activep ? 1 : 0;
int x = get_x() + (get_w()- w_pre - w_post - w_cursor )/2;
int y = get_y() + (get_h()-h)/2;
// cursor always visible
if (x + w_pre < get_x() + 5)
// cursor would be left of textfield - shift centered text right
x = get_x() + 5 - w_pre;
if (x + w_pre > get_x() + get_w() - 5)
// cursor would be right of textfiled - shift centered text left
x = get_x() + get_w() - 5 - w_pre;
f->render (gc, x, y, textPreCursor.c_str());
x += w_pre;
if (m_activep) {
if (showCursor) {
set_color(gc, 200,200,200);
vline(gc, x, y, h);
}
x += w_cursor;
}
f->render (gc, x, y, textPostCursor.c_str());
}
bool TextField::on_event(const SDL_Event &e) {
bool handeled = false;
bool modified = false;
switch (e.type) {
case SDL_MOUSEBUTTONDOWN:
// set cursor
break;
case SDL_KEYDOWN:
switch (e.key.keysym.sym) {
case SDLK_RETURN:
handeled = true;
isLastActionReturn = true;
invoke_listener();
break;
case SDLK_RIGHT:
if(textPostCursor.size() > 0) {
int size = charSizesPostCursor.back();
charSizesPostCursor.pop_back();
charSizesPreCursor.push_back(size);
textPreCursor.append(textPostCursor, 0, size);
textPostCursor.erase(0, size);
}
invalidate();
handeled = true;
break;
case SDLK_LEFT:
if(textPreCursor.size() > 0) {
int size = charSizesPreCursor.back();
charSizesPreCursor.pop_back();
charSizesPostCursor.push_back(size);
textPostCursor.insert(0, textPreCursor.substr(textPreCursor.size() - size));
textPreCursor.erase(textPreCursor.size() - size);
}
invalidate();
handeled = true;
break;
case SDLK_INSERT:
handeled = true;
break;
case SDLK_HOME:
if(textPreCursor.size() > 0) {
int i;
int preChars = charSizesPreCursor.size();
for (i = 0; i < preChars; i++) {
int size = charSizesPreCursor.back();
charSizesPreCursor.pop_back();
charSizesPostCursor.push_back(size);
}
textPostCursor.insert(0, textPreCursor);
textPreCursor.clear();
}
invalidate();
handeled = true;
break;
case SDLK_END:
if(textPostCursor.size() > 0) {
int size;
int i;
int postChars = charSizesPostCursor.size();
for (i = 0; i < postChars; i++) {
size = charSizesPostCursor.back();
charSizesPostCursor.pop_back();
charSizesPreCursor.push_back(size);
}
textPreCursor.append(textPostCursor);
textPostCursor.clear();
}
invalidate();
handeled = true;
break;
case SDLK_DELETE:
if(textPostCursor.size() > 0) {
int size = charSizesPostCursor.back();
textPostCursor.erase(0, size);
charSizesPostCursor.pop_back();
}
invalidate();
handeled = true;
modified = true;
break;
case SDLK_BACKSPACE:
if(textPreCursor.size() > 0) {
int size = charSizesPreCursor.back();
textPreCursor.erase(textPreCursor.size() - size);
charSizesPreCursor.pop_back();
}
invalidate();
handeled = true;
modified = true;
break;
case SDLK_ESCAPE:
case SDLK_DOWN:
case SDLK_UP:
// menu active widget movements
break;
default:
// get char
if (e.key.keysym.unicode != 0 ) {
UTF16 realChar;
if (e.key.keysym.unicode >= 0x20 &&
(e.key.keysym.unicode < 0x80 || // key pad
e.key.keysym.sym < 0x80)) { // windows umlaute
// the unicode is correct in these cases
realChar = e.key.keysym.unicode;
}
else if (e.key.keysym.unicode >= 0x80 &&
e.key.keysym.sym < 0x100) {
// Linux: bad unicode but sym is ISO-8859-1
// incomplete workaround - runs only for some lower
// case umlauts
// we would need to handle shift key in language
// dependent manner -- or fix SDL Linux
realChar = e.key.keysym.sym;
}
else {
// chars like ctrl-a - ctrl-z
sound::EmitSoundEvent ("menustop");
break;
}
if ((maxChars >= 0 && (charSizesPreCursor.size() + charSizesPostCursor.size()) >= maxChars) ||
realChar < 0x100 && invalidChars.find((char)realChar) != std::string::npos) {
// string too long or invalid char
sound::EmitSoundEvent ("menustop");
break;
}
unsigned char utf8Char[4];
UTF16 const * utf16Ptr = (UTF16 *)&realChar;
UTF8 * utf8Ptr = utf8Char;
ConversionResult result;
result = ConvertUTF16toUTF8 (&utf16Ptr, utf16Ptr + 1,
&utf8Ptr, utf8Char + 4, strictConversion);
*utf8Ptr = 0;
textPreCursor += (const char *)utf8Char;
charSizesPreCursor.push_back(utf8Ptr - utf8Char);
invalidate();
handeled = true;
modified = true;
break;
}
if (e.key.keysym.sym < 300 || e.key.keysym.sym > 314 ){
// chars like PageUp, F1 but not key state modifier
// like shift, alt,...
sound::EmitSoundEvent ("menustop");
}
break;
}
break;
default:
break;
}
if (modified) {
isLastActionReturn = false;
invoke_listener();
}
return handeled;
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright (C) 2005 Ronald Lamprecht
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef GUI_TF_HH_INCLUDED
#define GUI_TF_HH_INCLUDED
#include "gui/widgets.hh"
namespace enigma { namespace gui {
/**
* Gui widget for simple text insertion. Cursor keys, backspace and delete,
* home and end are supported. As a widget it handels utf-8 strings. But
* in contrast to other widget there is of course no gettext l10n translation.
* This widget is designed to be part of a menu.
* <par>
* ToDo: supply full Linux umlaut support;
* add hook for external or subclass char insertion validation
* (NumberField, ...);
* move mouse cursor out of Textfield without deselection
*/
class TextField : public Button {
public:
/**
* Creates a boarderd text input field.
* @param t preset utf-8 input string
* @param al currently unused
*/
TextField(const std::string &t = "", ActionListener *al=0);
/**
* resets the input string.
* @param t new utf-8 input string
*/
void set_text(const std::string &t);
/**
* returns the inserted text
* @return the utf-8 coded string
*/
std::string getText();
void setInvalidChars(std::string forbiddenChars); // currently limited to 1st coding page
void setMaxChars(int max);
bool wasLastActionReturn();
// Widget interface.
virtual void tick (double dtime);
virtual bool on_event(const SDL_Event &/*e*/);
void draw(ecl::GC &gc, const ecl::Rect &r);
protected:
double cursorTime;
bool showCursor;
private:
std::string textPreCursor;
std::string textPostCursor;
/**
* a byte vector describing for each utf-8 character the number of
* occupied bytes in the string.
*/
std::vector<unsigned char> charSizesPreCursor;
/**
* a byte vector describing for each utf-8 character the number of
* occupied bytes in the string. This vector is in reverse order to
* the string itself!!
*/
std::vector<unsigned char> charSizesPostCursor;
bool isLimitedToValidChars;
std::string validChars;
std::string invalidChars;
int maxChars;
bool isLastActionReturn;
static ecl::Font *menufont;
};
}} // namespace enigma::gui
#endif

View File

@@ -0,0 +1,943 @@
/*
* Copyright (C) 2002,2003,2004 Daniel Heck
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "gui/widgets.hh"
#include "enigma.hh"
#include "sound.hh"
#include "video.hh"
#include "options.hh"
#include "nls.hh"
#include "ecl.hh"
#include "ecl_font.hh"
#include <cassert>
#include <algorithm>
#include <iostream>
#include <climits>
using namespace enigma::gui;
using namespace ecl;
using namespace std;
#define SCREEN ecl::Screen::get_instance()
/* -------------------- Widget -------------------- */
Widget::Widget(Container *parent)
: area(), m_parent(parent), m_listener(0)
{}
void Widget::invalidate() {
if (m_parent)
m_parent->invalidate_area(get_area());
}
void Widget::invalidate_area(const ecl::Rect& r) {
if (m_parent)
m_parent->invalidate_area(r);
}
void Widget::reconfigure()
{
if (m_parent)
m_parent->reconfigure_child(this);
}
void Widget::invoke_listener() {
if (m_listener)
m_listener->on_action(this);
}
void Widget::move(int x, int y) {
area.x = x; area.y = y;
}
void Widget::resize (int w, int h) {
area.w = w; area.h = h;
}
bool Widget::on_event(const SDL_Event &e) {
switch (e.type) {
case SDL_KEYDOWN:
modifierKeys = e.key.keysym.mod;
mouseButton = SDL_BUTTON_LEFT;
break;
case SDL_MOUSEBUTTONDOWN:
modifierKeys = SDL_GetModState();
mouseButton = e.button.button;
break;
}
return false;
}
/* -------------------- Image -------------------- */
void Image::draw (ecl::GC &gc, const ecl::Rect &/*r*/) {
// if (ecl::Surface *s = enigma::GetImage(imgname.c_str()))
// blit(gc, get_x(), get_y(), s);
if (ecl::Surface *s = enigma::GetImage(imgname.c_str())) {
int w=s->width();
int h=s->height();
int x = get_x() + (get_w()-w)/2;
int y = get_y() + (get_h()-h)/2;
blit(gc, x, y, s);
}
}
/* -------------------- AreaManager -------------------- */
AreaManager::AreaManager(Container *c)
: top_container(c)
{
assert(top_container->get_parent() == 0); // otherwise it's not the top_container
}
void AreaManager::invalidate_area(const ecl::Rect &r) {
dirtyrects.add(r);
}
void AreaManager::invalidate_all() {
dirtyrects.clear();
dirtyrects.push_back(SCREEN->size());
}
void AreaManager::refresh() {
if (!dirtyrects.empty()) {
video::HideMouse();
GC gc(SCREEN->get_surface());
for (RectList::iterator i = dirtyrects.begin(); i!=dirtyrects.end(); ++i) {
top_container->draw(gc, *i);
SCREEN->update_rect(*i);
}
video::ShowMouse();
dirtyrects.clear();
}
}
/* -------------------- Container -------------------- */
Container::Container()
: managed_by(0)
{
}
Container::~Container() {
clear();
delete managed_by;
}
AreaManager *Container::getAreaManager() {
if (Container *p = get_parent()) {
assert(!managed_by);
return p->getAreaManager();
}
if (!managed_by) {
managed_by = new AreaManager(this);
}
return managed_by;
}
void Container::invalidate_area(const ecl::Rect &r) { getAreaManager()->invalidate_area(r); }
void Container::invalidate_all() { getAreaManager()->invalidate_all(); }
void Container::refresh() { getAreaManager()->refresh(); }
void Container::clear() {
delete_sequence(m_widgets.begin(), m_widgets.end());
m_widgets.clear();
}
void Container::add_child (Widget *w) {
if (w && w != this) {
m_widgets.push_back(w);
w->set_parent(this);
// w->move (get_x() + w->get_x(),
// get_y() + w->get_y());
}
}
void Container::remove_child (Widget *w) {
for (iterator it = begin(); it != end(); it++) {
if (*it == w) {
m_widgets.erase(it);
return;
}
}
}
void Container::exchange_child (Widget *oldChild, Widget *newChild) {
for (int i = 0; i < m_widgets.size(); i++) {
if (m_widgets[i] == oldChild) {
m_widgets[i] = newChild;
newChild->set_parent(this);
return;
}
}
}
void Container::draw (ecl::GC& gc, const ecl::Rect &r) {
for (iterator i=begin(); i!=end(); ++i) {
Widget *w = *i;
Rect rr = intersect(r, w->get_area());
clip(gc, rr);
w->draw(gc,rr);
}
}
void Container::draw_all() {
invalidate_all();
refresh();
}
void Container::reconfigure_child (Widget *)
{
}
Widget * Container::find_widget(int x, int y) {
for (iterator i=begin(); i!=end(); ++i) {
Widget *w = *i;
if (w->get_area().contains(x,y)) {
Container *c = dynamic_cast<Container *> (w);
if (c) {
w = c->find_widget (x, y);
return w ? w : c;
}
return w;
}
}
return 0;
}
Widget * Container::find_adjacent_widget(Widget *from, int x, int y) {
// valid values for x/y : 1/0, -1/0, 0/1, 0/-1
assert(from && x>=-1 && x<=1 && y>=-1 && y<=1 && abs(x+y) == 1);
if (!from) return 0;
int best_distance = INT_MAX;
Widget *best_widget = 0;
ecl::Rect farea = from->get_area();
for (iterator i=begin(); i!=end(); ++i) {
Widget *w = *i;
ecl::Rect warea = w->get_area();
bool adjacent = true;
int distance = 0;
if (x) { // check for y-overlap
if (farea.y>(warea.y+warea.h-1) || warea.y>(farea.y+farea.h-1)) {
adjacent = false;
}
else {
distance = (warea.x-farea.x)*x;
}
}
else { // check for x-overlap
if (farea.x>(warea.x+warea.h-1) || warea.x>(farea.x+farea.h-1)) {
adjacent = false;
}
else {
distance = (warea.y-farea.y)*y;
}
}
if (adjacent && distance>0 && distance<best_distance) {
best_distance = distance;
best_widget = w;
}
}
return best_widget;
}
void Container::move (int x, int y) {
Rect a = get_area();
a.x = x;
a.y = y;
this->set_area (a);
int dx = x-get_x();
int dy = y-get_y();
for (iterator i=begin(); i!=end(); ++i) {
Widget *w = *i;
w->move(dx + w->get_x(), dy+w->get_y());
}
}
ecl::Rect Container::boundingbox() {
if (!m_widgets.empty()) {
iterator i=begin();
Rect bbox=(*i)->get_area();
for (++i; i!=end(); ++i)
bbox = ecl::boundingbox(bbox, (*i)->get_area());
return bbox;
} else
return get_area();
}
/* -------------------- List -------------------- */
List::List (int spacing)
: m_spacing(spacing),
has_default_size (false),
defaultw (0),
defaulth (0),
m_halign (HALIGN_LEFT),
m_valign (VALIGN_TOP)
{}
void List::remove_child (Widget *w) {
Container::remove_child(w);
recalc();
}
void List::exchange_child(Widget *oldChild, Widget *newChild) {
Container::exchange_child(oldChild, newChild);
recalc();
}
void List::set_spacing (int pixels)
{
m_spacing = pixels;
}
int List::get_spacing () const
{
return m_spacing;
}
int List::calc_minimum_height() const
{
int sum=0;
const WidgetList &wl = m_widgets;
if (!wl.empty()) {
sum = (wl.size() - 1) * m_spacing;
for (WidgetList::const_iterator i=wl.begin(); i!=wl.end(); ++i) {
int nw, nh;
get_size (*i, nw, nh);
sum += nh;
}
}
return sum;
}
int List::calc_minimum_width () const
{
int sum=0;
const WidgetList &wl = m_widgets;
if (!wl.empty()) {
sum = (wl.size() - 1) * m_spacing;
for (WidgetList::const_iterator i=wl.begin(); i!=wl.end(); ++i) {
int nw, nh;
get_size (*i, nw, nh);
sum += nw;
}
}
return sum;
}
void List::set_default_size (int w, int h)
{
has_default_size = true;
defaultw = w;
defaulth = h;
}
void List::get_size (const Widget *widget, int &w, int &h) const
{
if (has_default_size)
w = defaultw, h = defaulth;
else
widget->naturalsize (w, h);
}
void List::resize (int w, int h)
{
Container::resize (w, h);
recalc();
}
void List::move (int x, int y)
{
Container::move (x, y);
// recalc();
}
void List::reconfigure_child (Widget *w)
{
Container::reconfigure_child (w);
recalc();
invalidate();
}
void List::add_back (Widget *w, ExpansionMode m)
{
add_child (w);
m_expansionmodes.push_back (m);
recalc();
}
void List::set_alignment (HAlignment halign, VAlignment valign)
{
if (halign != m_halign || valign != m_valign) {
m_halign = halign;
m_valign = valign;
recalc();
}
}
/* -------------------- HList -------------------- */
void HList::recalc()
{
int targetw = this->get_w(); // The available space
int naturalw= calc_minimum_width();
int excessw = targetw - naturalw;
int num_expand = std::count (m_expansionmodes.begin(),
m_expansionmodes.end(),
List::EXPAND);
WidgetList::iterator i = m_widgets.begin(),
end = m_widgets.end();
int x = get_x(), y = get_y();
size_t j = 0;
if (num_expand == 0 && excessw > 0) {
switch (m_halign) {
case HALIGN_CENTER:
x += excessw / 2;
excessw = 0;
break;
default:
break;
}
}
for (; i != end; ++i, ++j) {
int w, h;
List::get_size (*i, w, h);
if (excessw > 0 && m_expansionmodes[j] == List::EXPAND) {
w += excessw / num_expand;
excessw -= excessw / num_expand;
num_expand -= 1;
}
(*i)->move (x, y);
(*i)->resize (w, get_h());
x += w + get_spacing();
}
}
bool HList::fits() {
int targetw = this->get_w(); // The available space
int naturalw= calc_minimum_width();
return targetw >= naturalw;
}
/* -------------------- VList -------------------- */
void VList::recalc()
{
int targeth = this->get_h(); // The available space
int naturalh= calc_minimum_height();
int excessh = targeth - naturalh;
int num_expand = std::count (m_expansionmodes.begin(),
m_expansionmodes.end(),
List::EXPAND);
WidgetList::iterator i = m_widgets.begin(),
end = m_widgets.end();
int x = get_x(), y = get_y();
size_t j = 0;
if (num_expand == 0 && excessh > 0) {
switch (m_valign) {
case VALIGN_CENTER:
y += excessh / 2;
excessh = 0;
break;
default:
break;
}
}
for (; i != end; ++i, ++j) {
int w, h;
List::get_size (*i, w, h);
if (excessh > 0 && m_expansionmodes[j] == List::EXPAND) {
h += excessh / num_expand;
excessh -= excessh / num_expand;
num_expand -= 1;
}
(*i)->move (x, y);
(*i)->resize (get_w(), h);
y += h + get_spacing();
}
}
bool VList::fits() {
int targeth = this->get_h(); // The available space
int naturalh= calc_minimum_height();
return targeth >= naturalh;
}
/* -------------------- Label -------------------- */
Label::Label (const std::string &text,
HAlignment halign,
VAlignment valign)
: m_text (text),
m_font(enigma::GetFont("menufont")),
m_halign(halign),
m_valign(valign)
{}
void Label::set_text (const std::string &text) {
if (text != m_text) {
m_text = text;
reconfigure();
invalidate();
}
}
string Label::get_text() const {
return _(m_text.c_str());
}
string Label::getText() const {
return m_text;
}
void Label::set_font (ecl::Font *font) {
if (m_font != font) {
m_font = font;
reconfigure();
invalidate();
}
}
bool Label::text_fits(double area_fraction) {
int w, h;
naturalsize (w, h);
return w <= get_w()*area_fraction;
}
void Label::draw (ecl::GC &gc, const ecl::Rect &)
{
Font *f = m_font;
int w, h;
naturalsize (w, h);
int x = get_x(), y=get_y();
switch (m_halign) {
case HALIGN_LEFT: break;
case HALIGN_RIGHT: x += get_w() - w; break;
case HALIGN_CENTER: x += (get_w()-w)/2; break;
}
switch (m_valign) {
case VALIGN_TOP: break;
case VALIGN_BOTTOM: y += get_h() - h; break;
case VALIGN_CENTER: y += (get_h()-h)/2; break;
}
// translate if not an empty string
f->render (gc, x, y, m_text == "" ? "" : get_text().c_str());
}
void Label::set_alignment (HAlignment halign, VAlignment valign) {
if (halign != m_halign || valign != m_valign) {
m_halign = halign;
m_valign = valign;
invalidate();
}
}
void Label::naturalsize (int &w, int &h) const
{
h = m_font->get_height();
// width of translation if not an empty string
w = m_font->get_width (m_text == "" ? "" : get_text().c_str());
}
/* -------------------- UntranslatedLabel -------------------- */
UntranslatedLabel::UntranslatedLabel (const std::string &text,
HAlignment halign, VAlignment valign) : Label(text, halign, valign) {
}
string UntranslatedLabel::get_text() const {
return Label::m_text;
}
/* -------------------- Button -------------------- */
Button::Button() : m_activep (false), highlight (false) {
}
void Button::activate()
{
sound::EmitSoundEvent ("menuswitch");
m_activep = true;
invalidate();
}
void Button::deactivate() {
m_activep = false;
invalidate();
}
void Button::setHighlight(bool shouldHighlight) {
highlight = shouldHighlight;
invalidate();
}
bool Button::isHighlight() {
return highlight;
}
void Button::draw(ecl::GC &gc, const ecl::Rect &r) {
const int borderw = 4;
ecl::Surface *s = enigma::GetImage (m_activep ? "buttonhl" : "button");
if (s) { // Ugly, but hey, it works
Rect srcrect (0,0,borderw, borderw);
Rect area = get_area();
// background
if (highlight)
set_color (gc, 70, 70, 70);
else
set_color (gc, 0,0,0);
box (gc, smaller(area, borderw));
set_color (gc, 0,0,0);
// corners
blit (gc, area.x, area.y, s, srcrect);
srcrect.x += s->width()-borderw;
blit (gc, area.x+area.w-borderw, area.y, s, srcrect);
srcrect.x = 0;
srcrect.y += s->height()-borderw;
blit (gc, area.x, area.y+area.h-borderw, s, srcrect);
srcrect.x += s->width()-borderw;
blit (gc, area.x+area.w-borderw, area.y+area.h-borderw, s, srcrect);
// horizontal borders
{
int tilew = s->width() - 2*borderw;
int ntiles = (area.w - 2*borderw) / tilew;
int x = area.x + borderw;
for (int i=0; i<ntiles; ++i) {
blit (gc, x, area.y, s, Rect (borderw, 0, tilew, borderw));
blit (gc, x, area.y+area.h-borderw, s,
Rect (borderw, s->height()-borderw, tilew, borderw));
x += tilew;
}
int restw = (area.w - 2*borderw) - tilew*ntiles;
blit (gc, x, area.y, s, Rect (borderw, 0, restw, borderw));
blit (gc, x, area.y+area.h-borderw, s,
Rect (borderw, s->height()-borderw, restw, borderw));
}
// vertical borders
{
int tileh = s->height() - 2*borderw;
int ntiles = (area.h - 2*borderw) / tileh;
int y = area.y + borderw;
for (int i=0; i<ntiles; ++i) {
blit (gc, area.x, y, s, Rect (0, borderw, borderw, tileh));
blit (gc, area.x+area.w-borderw, y, s,
Rect (s->width()-borderw, borderw, borderw, tileh));
y += tileh;
}
int resth = (area.h - 2*borderw) - tileh*ntiles;
blit (gc, area.x, y, s, Rect (0, borderw, borderw, resth));
blit (gc, area.x+area.w-borderw, y, s,
Rect (s->width()-borderw, borderw, borderw, resth));
}
}
else {
set_color (gc, 0,0,0);
box (gc, r);
set_color (gc, 160,160,160);
frame (gc, r);
frame (gc, smaller(r, 1));
}
}
/* -------------------- PushButton -------------------- */
PushButton::PushButton() : m_pressedp (false) {
}
bool PushButton::on_event(const SDL_Event &e) {
Widget::on_event(e);
bool was_pressed = m_pressedp;
switch (e.type) {
case SDL_KEYDOWN:
if (e.key.keysym.sym != SDLK_RETURN &&
e.key.keysym.sym != SDLK_SPACE) break;
// fall-through
case SDL_MOUSEBUTTONDOWN:
m_pressedp = true;
break;
case SDL_KEYUP:
if (e.key.keysym.sym != SDLK_RETURN &&
e.key.keysym.sym != SDLK_SPACE &&
e.key.keysym.sym != SDLK_PAGEDOWN &&
e.key.keysym.sym != SDLK_PAGEUP) break;
lastUpSym = e.key.keysym.sym;
lastUpBotton = 0;
m_pressedp = false;
break;
case SDL_MOUSEBUTTONUP:
lastUpSym = SDLK_UNKNOWN;
lastUpBotton = e.button.button;
m_pressedp = false;
break;
}
bool changed = (was_pressed != m_pressedp);
if (changed) {
invalidate();
if (!m_pressedp) {
if (soundOk())
sound::EmitSoundEvent("menuok");
invoke_listener();
}
}
return changed;
}
void PushButton::deactivate() {
m_pressedp = false;
lastUpSym = SDLK_UNKNOWN;
lastUpBotton = 0;
invalidate();
Button::deactivate();
}
SDLKey PushButton::getLastUpSym() {
return lastUpSym;
}
Uint8 PushButton::getLastUpButton() {
return lastUpBotton;
}
bool PushButton::soundOk() {
return true;
}
/* -------------------- TextButton -------------------- */
ecl::Font *TextButton::menufont = 0;
ecl::Font *TextButton::menufont_pressed = 0;
TextButton::TextButton(ActionListener *al) {
if (menufont == 0) {
menufont = enigma::GetFont("menufont");
menufont_pressed = enigma::GetFont("menufontsel");
}
set_listener(al);
}
void TextButton::draw(ecl::GC &gc, const ecl::Rect &r) {
Button::draw(gc,r);
Font *f = is_pressed() ? menufont_pressed : menufont;
string text = get_text();
int h = f->get_height();
int w = f->get_width(text.c_str());
int x = get_x() + (get_w()-w)/2;
int y = get_y() + (get_h()-h)/2;
f->render (gc, x, y, text.c_str());
}
/* -------------------- StaticTextButton -------------------- */
StaticTextButton::StaticTextButton(const string &t, ActionListener *al)
: TextButton(al),
text(t)
{
}
void StaticTextButton::set_text(const std::string &t) {
if (t != text) {
text = t;
invalidate();
}
}
string StaticTextButton::get_text() const {
return _(text.c_str()); // translate
}
/* -------------------- UntranslatedStaticTextButton -------------------- */
UntranslatedStaticTextButton::UntranslatedStaticTextButton(const string &t,
ActionListener *al)
: StaticTextButton(t, al)
{
}
string UntranslatedStaticTextButton::get_text() const {
return StaticTextButton::text;
}
/* -------------------- Buttons for Options -------------------- */
BoolOptionButton::BoolOptionButton(const char *option_name,
const string& true_text, const string& false_text,
ActionListener *al)
: TextButton(al),
optionName(option_name),
trueText(true_text),
falseText(false_text)
{
}
bool BoolOptionButton::toggle() {
bool newval = !enigma_options::GetBool(optionName);
enigma_options::SetOption(optionName, newval);
invalidate();
return newval;
}
void BoolOptionButton::on_action(Widget *) {
toggle();
}
string BoolOptionButton::get_text() const {
return enigma_options::GetBool(optionName) ? _(trueText.c_str()) : _(falseText.c_str());
}
/* -------------------- ValueButton -------------------- */
ValueButton::ValueButton(int min_value_, int max_value_)
: TextButton(this),
min_value(min_value_),
max_value(max_value_)
{
}
void ValueButton::setMaxValue(int max) {
max_value = max;
}
void ValueButton::init() {
update_value(-1, get_value()); // fixes wrong values (e.g. from .enimarc)
}
bool ValueButton::inc_value(int offset) {
int old_value = get_value();
return update_value(old_value, old_value+offset);
}
string ValueButton::get_text() const {
return get_text(get_value());
}
bool ValueButton::update_value(int old_value, int new_value) {
new_value = Clamp(new_value, min_value, max_value);
if (new_value != old_value) {
set_value(new_value);
invalidate();
return true;
}
return false;
}
void ValueButton::on_action(Widget *) {
int incr = 1;
bool stop = false;
if (getLastUpSym() == SDLK_PAGEDOWN || getLastUpButton() == SDL_BUTTON_RIGHT ||
getLastUpButton() == 5) { // wheel down
incr = -1;
}
if (getLastUpSym() == SDLK_PAGEDOWN || getLastUpSym() == SDLK_PAGEUP ||
getLastUpButton() == SDL_BUTTON_RIGHT ||
getLastUpButton() == 4 || getLastUpButton() == 5) {
stop = true;
}
if (inc_value(incr)) {
sound::EmitSoundEvent("menuswitch");
} else {
if (stop) {
sound::EmitSoundEvent("menustop");
} else {
sound::EmitSoundEvent("menuswitch");
if (incr == 1)
update_value(get_value(), min_value);
else
update_value(get_value(), max_value);
}
}
}
bool ValueButton::soundOk() {
return false;
}
/* -------------------- ImageButton -------------------- */
ImageButton::ImageButton(const string &unselected,
const string &selected,
ActionListener *al)
: fname_sel(selected), fname_unsel(unselected)
{
set_listener(al);
}
void ImageButton::set_images(const string &unselected, const string &selected) {
fname_sel = selected;
fname_unsel = unselected;
}
void ImageButton::draw(ecl::GC &gc, const ecl::Rect &r) {
Button::draw(gc, r);
string &fname = is_pressed() ? fname_sel : fname_unsel;
if (Surface *s = enigma::GetImage(fname.c_str())) {
int w=s->width();
int h=s->height();
int x = get_x() + (get_w()-w)/2;
int y = get_y() + (get_h()-h)/2;
blit(gc, x, y, s);
}
}

View File

@@ -0,0 +1,465 @@
/*
* Copyright (C) 2002,2003 Daniel Heck
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef WIDGETS_HH_INCLUDED
#define WIDGETS_HH_INCLUDED
#include "ecl_fwd.hh"
#include "ecl_geom.hh"
#include "SDL.h"
namespace enigma { namespace gui {
/* -------------------- Alignment -------------------- */
enum HAlignment {
HALIGN_LEFT,
HALIGN_CENTER,
HALIGN_RIGHT
};
enum VAlignment {
VALIGN_TOP,
VALIGN_CENTER,
VALIGN_BOTTOM
};
/* -------------------- Events & Event Handlers -------------------- */
class Widget;
class ActionListener {
public:
virtual ~ActionListener() {}
virtual void on_action(Widget *) {};
};
/* -------------------- GUI Widgets -------------------- */
class Container;
class Widget : public ActionListener {
public:
/* ---------- Widget interface ---------- */
virtual void draw (ecl::GC &gc, const ecl::Rect &r) = 0;
virtual void activate() {}
virtual void deactivate() {}
virtual void realize (const ecl::Rect &r) {
set_area (r);
}
virtual bool on_event(const SDL_Event &/*e*/);
Uint8 lastMouseButton() {return mouseButton;}
SDLMod lastModifierKeys() { return modifierKeys; }
virtual void move (int x, int y);
virtual void resize (int w, int h);
virtual void naturalsize (int &w, int &h) const {
w = h = 5;
}
/* ---------- Accessors ---------- */
void set_size(int w, int h) {area.w = w; area.h = h;}
ecl::Rect get_area() const { return area; }
void set_area(const ecl::Rect &r) { area = r; }
int get_x() const { return area.x; }
int get_y() const { return area.y; }
int get_w() const { return area.w; }
int get_h() const { return area.h; }
void set_parent(Container *parent) { m_parent = parent; }
Container *get_parent () const { return m_parent; }
void set_listener(ActionListener *al) {
m_listener = al;
}
void invalidate();
virtual void tick (double /*dtime*/) {}
protected:
Widget(Container *parent=0);
/* ---------- Functions ---------- */
void reconfigure();
void invalidate_area(const ecl::Rect &r);
void invoke_listener();
private:
ecl::Rect area;
Container *m_parent;
ActionListener *m_listener;
SDLMod modifierKeys;
Uint8 mouseButton;
};
#if 0
/* -------------------- EmptyWidget -------------------- */
class EmptyWidget : public Widget {
public:
EmptyWidget () : Widget ()
{}
virtual void draw (ecl::GC &gc, const ecl::Rect &r)
{}
virtual void naturalsize (int &w, int &h) const {
w = h = 0;
}
};
#endif
/* -------------------- AreaManager -------------------- */
class AreaManaged {
public:
virtual ~AreaManaged() {}
virtual void invalidate_area(const ecl::Rect &r) = 0;
virtual void invalidate_all() = 0;
virtual void refresh() = 0;
};
// The AreaManager perform refreshes of invalidated regions.
// It gets attached to the top-level Container during the first invalidation
// or refresh request.
class AreaManager : public AreaManaged {
public:
AreaManager(Container *managed);
void invalidate_area(const ecl::Rect &r);
void invalidate_all();
void refresh();
private:
ecl::RectList dirtyrects;
Container *top_container;
};
/* -------------------- Container -------------------- */
class Container : public Widget, public AreaManaged {
public:
Container();
~Container();
void add_child (Widget *w);
virtual void remove_child (Widget *w);
virtual void exchange_child (Widget *oldChild, Widget *newChild);
virtual void reconfigure_child (Widget *w);
Widget *find_widget(int x, int y);
Widget *find_adjacent_widget(Widget *from, int x, int y);
void clear();
void draw_all();
// Widget interface.
void draw (ecl::GC& gc, const ecl::Rect &r);
void move (int x, int y);
// AreaManaged interface.
void invalidate_area(const ecl::Rect &r);
void invalidate_all();
void refresh();
protected:
typedef std::vector<Widget *> WidgetList;
typedef WidgetList::iterator iterator;
iterator begin() { return m_widgets.begin(); }
iterator end() { return m_widgets.end(); }
WidgetList m_widgets;
private:
ecl::Rect boundingbox();
AreaManager *getAreaManager();
AreaManager *managed_by;
};
/* -------------------- List, HList, VList -------------------- */
class List : public Container {
public:
void set_spacing (int pixels);
enum ExpansionMode {
EXPAND,
TIGHT
};
void add_back (Widget *w, ExpansionMode m = List::TIGHT);
virtual void remove_child (Widget *w);
virtual void exchange_child (Widget *oldChild, Widget *newChild);
void set_default_size (int w, int h);
void set_alignment (HAlignment halign, VAlignment valign);
virtual bool fits() = 0;
protected:
List(int spacing=5);
int calc_minimum_height () const;
int calc_minimum_width () const;
int get_spacing () const;
void get_size (const Widget *widget, int &w, int &h) const;
// ---------- Widget interface ----------
virtual void move (int x, int y);
virtual void resize(int w, int h);
// ---------- List interface ----------
virtual void recalc() = 0;
// ---------- Container interface ----------
virtual void reconfigure_child (Widget *w);
protected:
std::vector<ExpansionMode> m_expansionmodes;
private:
int m_spacing; // # of pixels between container items
bool has_default_size;
int defaultw, defaulth;
protected:
HAlignment m_halign;
VAlignment m_valign;
};
class HList : public List {
public:
HList() : List() {}
virtual bool fits();
private:
// List interface
virtual void recalc();
};
class VList : public List {
public:
VList() : List() {}
virtual bool fits();
private:
// List interface
virtual void recalc();
};
/* -------------------- Image -------------------- */
class Image : public Widget {
public:
Image (const std::string &iname) : imgname(iname) {}
void draw (ecl::GC &gc, const ecl::Rect &r);
private:
std::string imgname;
};
/* -------------------- Label -------------------- */
class Label : public Widget {
public:
Label (const std::string &text="",
HAlignment halign=HALIGN_CENTER,
VAlignment valign=VALIGN_CENTER);
// Widget interface
virtual void draw (ecl::GC &gc, const ecl::Rect &r);
virtual void naturalsize (int &w, int &h) const;
// Methods
void set_text (const std::string &text);
virtual std::string get_text() const; // translated
std::string getText() const;
void set_font (ecl::Font *font);
void set_alignment (HAlignment halign, VAlignment valign=VALIGN_CENTER);
bool text_fits(double area_fraction = 1.0);
protected:
// Variables.
std::string m_text;
ecl::Font *m_font;
HAlignment m_halign;
VAlignment m_valign;
};
/* -------------------- UntranslatedLabel -------------------- */
class UntranslatedLabel : public Label {
public:
UntranslatedLabel(const std::string &text="",
HAlignment halign=HALIGN_CENTER,
VAlignment valign=VALIGN_CENTER);
// TextButton interface.
virtual std::string get_text() const;
};
/* -------------------- Button -------------------- */
class Button : public Widget {
public:
void setHighlight(bool shouldHighlight);
bool isHighlight();
protected:
Button();
// Widget interface.
void draw(ecl::GC &gc, const ecl::Rect &r);
void activate();
void deactivate();
bool m_activep;
bool highlight;
};
/* -------------------- PushButton -------------------- */
class PushButton : public Button {
public:
PushButton();
bool is_pressed() { return m_pressedp; }
protected:
bool on_event(const SDL_Event &e);
void deactivate();
SDLKey getLastUpSym();
Uint8 getLastUpButton();
virtual bool soundOk();
private:
bool m_pressedp;
SDLKey lastUpSym;
Uint8 lastUpBotton;
};
/* -------------------- TextButton -------------------- */
class TextButton : public PushButton {
public:
TextButton(ActionListener *al=0);
virtual std::string get_text() const = 0;
private:
// Widget interface.
void draw(ecl::GC &gc, const ecl::Rect &r);
// Variables.
static ecl::Font *menufont, *menufont_pressed;
};
/* -------------------- StaticTextButton -------------------- */
class StaticTextButton : public TextButton {
public:
StaticTextButton(const std::string &t, ActionListener *al=0);
virtual void set_text(const std::string &t);
// TextButton interface.
std::string get_text() const;
protected:
// Variables.
std::string text;
};
/* -------------------- UntranslatedStaticTextButton -------------------- */
class UntranslatedStaticTextButton : public StaticTextButton {
public:
UntranslatedStaticTextButton(const std::string &t, ActionListener *al=0);
// TextButton interface.
std::string get_text() const;
};
/* -------------------- BoolOptionButton -------------------- */
class BoolOptionButton : public TextButton {
public:
BoolOptionButton(const char *option_name,
const std::string& true_text,
const std::string& false_text,
ActionListener *al = 0);
bool toggle(); // returns new value
void on_action(Widget *);
// TextButton interface.
std::string get_text() const;
private:
const char *optionName;
std::string trueText;
std::string falseText;
};
/* -------------------- ValueButton -------------------- */
class ValueButton: public TextButton {
public:
ValueButton(int min_value_, int max_value_);
virtual int get_value() const = 0;
virtual void set_value(int value) = 0;
void setMaxValue(int max);
bool inc_value(int offset);
// TextButton interface.
virtual std::string get_text() const;
// Widget interface.
virtual void on_action(Widget *w);
protected:
void init(); // called in ctor of derived
virtual bool soundOk();
private:
int min_value;
int max_value;
bool update_value(int old_value, int new_value);
virtual std::string get_text(int value) const = 0;
};
/* -------------------- ImageButton -------------------- */
class ImageButton : public PushButton {
public:
ImageButton(const std::string &unselected,
const std::string &selected,
ActionListener *al = 0);
void set_images(const std::string &unselected, const std::string &selected);
// Widget interface.
virtual void draw(ecl::GC &gc, const ecl::Rect &r);
private:
std::string fname_sel, fname_unsel;
};
}} // namespace enigma::gui
#endif