/* * 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 LevelPackMenu::groupLastSelectedIndex; std::map 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_level = new StaticTextButton(N_("Start Game"), this); but_main = new StaticTextButton(N_("Main Menu"), this); commandHList = new HList; commandHList->set_spacing(60); commandHList->set_alignment(HALIGN_CENTER, VALIGN_TOP); commandHList->set_default_size(160, 50); commandHList->add_back(but_level); commandHList->add_back(but_main); this->add(commandHList, Rect(130, vminfo.height-70, vminfo.width-260, 50)); } 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, 8, 15, 10, 8, 36, 35, 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 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(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 * 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 * (25 + param[vm].vrow_row), param[vm].hscrollbutton, 45)); } 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 * (25 + param[vm].vrow_row), param[vm].hscrollbutton, 45)); } } 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_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 == 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(w)->get_text()); if ((w->lastMouseButton() == SDL_BUTTON_RIGHT || w->lastModifierKeys() & KMOD_CTRL) && dynamic_cast(w)->get_text() != INDEX_ALL_PACKS) { // configure group // INDEX_ALL_PACKS cannot be renamed, deleted, no packs can be created LPGroupConfig m(dynamic_cast(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(w)->get_text()); if (w->lastMouseButton() == SDL_BUTTON_RIGHT || w->lastModifierKeys() & KMOD_CTRL) { // configure levelpack index LevelPackConfig m(dynamic_cast(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 * groups, std::string groupName) { std::vector::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 * group, std::string indexName) { std::vector::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(oldColumn, naturalColumn - ((numColumns > columns) ? (numColumns - columns) : 0), naturalColumn); } }} // namespace enigma::gui