123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888 |
- /* GCSx
- ** POPUP.CPP
- **
- ** Popup and drop-down menus
- */
- /*****************************************************************************
- ** Copyright (C) 2003-2006 Janson
- **
- ** 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
- *****************************************************************************/
- #include "all.h"
- const char* PopupItem::accelLabelsLower[ACCEL_LABEL_LOWER_LAST - ACCEL_LABEL_LOWER_FIRST + 1] = {
- // (starts at 8)
- "Backspace",
- "Tab",
- NULL, NULL, NULL, // 10-12
- "Enter",
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 14-21
- NULL, NULL, NULL, NULL, NULL, // 22-26
- "Escape",
- NULL, NULL, NULL, NULL, // 28-31
- "Space",
- "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", "<", "-", ">", "/",
- "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
- ":", ";", "<", "+", ">", "?", "@",
- "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
- "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
- "[", "\\", "]", "^", "_", "`",
- "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
- "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
- "{", "|", "}", "~",
- "Delete"
- };
- const char* PopupItem::accelLabelsUpper[ACCEL_LABEL_UPPER_LAST - ACCEL_LABEL_UPPER_FIRST + 1] = {
- // (starts at 273)
- "Up", "Down", "Right", "Left",
- "Insert", "Home", "End", "PageUp", "PageDown",
- "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12",
- "F13", "F14", "F15"
- };
- const string PopupMenu::wtSubmenu("e");
- const string PopupMenu::emptyItem("(empty)");
- int PopupMenu::checkboxSize = 0;
- void PopupItem::prepAccellabel(Sint32 pKey) { start_func
- const char* key = NULL;
- int pKeyMod = (pKey >> 16) & 0xFFFF;
- int pKeySym = pKey & 0xFFFF;
- if ((pKeySym >= ACCEL_LABEL_LOWER_FIRST) && (pKeySym <= ACCEL_LABEL_LOWER_LAST)) {
- key = accelLabelsLower[pKeySym - ACCEL_LABEL_LOWER_FIRST];
- }
- else if ((pKeySym >= ACCEL_LABEL_UPPER_FIRST) && (pKeySym <= ACCEL_LABEL_UPPER_LAST)) {
- key = accelLabelsUpper[pKeySym - ACCEL_LABEL_UPPER_FIRST];
- }
-
- if (key) {
- accelLabel = "(";
- if (pKeyMod & KMOD_SHIFT) accelLabel += "Shift+";
- if (pKeyMod & KMOD_CTRL) accelLabel += "Ctrl+";
- if (pKeyMod & KMOD_ALT) accelLabel += "Alt+";
- accelLabel += key;
- accelLabel += ")";
- }
- else {
- accelLabel = blankString;
- }
-
- // Certain keys become modified for actual accellerator
- // Letters always lowercase
- if ((pKeySym >= 'A') && (pKeySym <= 'Z')) pKeySym += 'a' - 'A';
- else {
- // Punctuation on 0-9 becomes shifted 0-9
- // Punctuation on other keys becomes unshifted version
- switch (pKeySym) {
- case '!': pKeySym = 1; pKeyMod |= KMOD_SHIFT; break;
- case '@': pKeySym = 2; pKeyMod |= KMOD_SHIFT; break;
- case '#': pKeySym = 3; pKeyMod |= KMOD_SHIFT; break;
- case '$': pKeySym = 4; pKeyMod |= KMOD_SHIFT; break;
- case '%': pKeySym = 5; pKeyMod |= KMOD_SHIFT; break;
- case '^': pKeySym = 6; pKeyMod |= KMOD_SHIFT; break;
- case '&': pKeySym = 7; pKeyMod |= KMOD_SHIFT; break;
- case '*': pKeySym = 8; pKeyMod |= KMOD_SHIFT; break;
- case '(': pKeySym = 9; pKeyMod |= KMOD_SHIFT; break;
- case ')': pKeySym = 0; pKeyMod |= KMOD_SHIFT; break;
- case '~': pKeySym = '`'; break;
- case '_': pKeySym = '-'; break;
- case '+': pKeySym = '='; break;
- case '{': pKeySym = '['; break;
- case '}': pKeySym = ']'; break;
- case '|': pKeySym = '\\'; break;
- case ':': pKeySym = ';'; break;
- case '"': pKeySym = '\''; break;
- case '<': pKeySym = ','; break;
- case '>': pKeySym = '.'; break;
- case '?': pKeySym = '/'; break;
- }
- }
-
- accel = pKey;
- }
-
- const string& PopupItem::getLabel() const { start_func
- return label;
- }
- void PopupItem::setLabel(const string& newLabel) { start_func
- label = newLabel;
- underline = convertGuiText(&label, &shortcut);
- if (label.length() > POPUP_MAX_WIDTH) label = label.substr(0, POPUP_MAX_WIDTH) + "...";
- }
- PopupItem::PopupItem() { start_func
- }
- PopupItem::PopupItem(int pCode, const char* pLabel, Sint32 pKey, int dynamic, class PopupMenu* pSubmenu) { start_func
- code = pCode;
- if (pLabel == NULL) {
- label = "";
- state = POPUP_SEPARATOR;
- }
- else {
- setLabel(pLabel);
- state = POPUP_NONE;
- }
- if (dynamic) state = (PopupState)(state | POPUP_DYNAMIC);
- submenu = pSubmenu;
- prepAccellabel(pKey);
- }
- PopupMenu::PopupMenu(int menubar) : Window(), items() { start_func
- isMenubar = menubar;
- fromMenubar = 0;
-
- numItems = 0;
-
- checkboxSize = fontHeight() * 4 / 5;
- }
- void PopupMenu::add(PopupItem& item) { start_func
- items.push_back(item);
- ++numItems;
- }
- PopupMenu::~PopupMenu() { start_func
- desktop->removeWindow(this);
- vector<PopupItem>::iterator end = items.end();
- for (vector<PopupItem>::iterator pos = items.begin(); pos != end; ++pos) {
- delete (*pos).submenu;
- }
- }
- #ifndef NDEBUG
- const char* PopupMenu::debugDump() const { start_func
- return (isMenubar ? "MENUBAR" : "POPUP");
- }
- #endif
- void PopupMenu::predraw(int xPos, int yPos) { start_func
- currSel = -1;
- int newWidth = 0;
- int newHeight = GUI_POPUP_EDGEPAD;
- int largestItem = 0;
- int largestAccel = 0;
- if (isMenubar) {
- xPos = 0;
- yPos = 0;
- newWidth = screenWidth;
- newHeight = GUI_MENUBAR_TOPPAD + GUI_MENUBAR_BOTTOMPAD + fontHeight(FONT_MENU);
- isEmpty = 0;
- int xPos = GUI_MENUBAR_SIDEPAD + GUI_MENUBAR_LEFTPAD;
- vector<PopupItem>::iterator end = items.end();
- for (vector<PopupItem>::iterator pos = items.begin(); pos != end; ++pos) {
- (*pos).pos = xPos;
- xPos += GUI_MENUBAR_SIDEPAD * 2 + fontWidth((*pos).getLabel(), FONT_MENU);
- }
- }
- else {
- // Calculate size, determine disabled and checkbox states and Y positions
- Window* win = desktop->findPreviousFocusWindow();
- // Hide any separators at top of menu
- int hideSeparator = -1;
- int numPos = 0;
- vector<PopupItem>::iterator end = items.end();
- for (vector<PopupItem>::iterator pos = items.begin(); pos != end; ++pos, ++numPos) {
- PopupItem& currItem = *pos;
- if (currItem.state & PopupItem::POPUP_DYNAMIC) {
- // Clear all temporary states
- currItem.state = PopupItem::POPUP_DYNAMIC;
- Window::CommandSupport support = Window::COMMAND_HIDE;
- // Window, if any, gets to "vote" on this item
- if (win) support = win->supportsCommand(currItem.code);
- // Desktop calls global handler to "vote" on this item also
- support = desktop->supportsCommand(currItem.code, support);
- // Hidden?
- if (support == Window::COMMAND_HIDE) currItem.state = (PopupItem::PopupState)(currItem.state | PopupItem::POPUP_HIDDEN);
- // Grey?
- if (!(support & Window::COMMAND_ENABLE)) currItem.state = (PopupItem::PopupState)(currItem.state | PopupItem::POPUP_GRAYED);
- // Add check/radio?
- if (support & Window::COMMAND_CHECKBOX) currItem.state = (PopupItem::PopupState)(currItem.state | PopupItem::POPUP_CHECKBOX);
- if (support & Window::COMMAND_RADIO) currItem.state = (PopupItem::PopupState)(currItem.state | PopupItem::POPUP_RADIO);
- if (support & Window::COMMAND_SELECTED) currItem.state = (PopupItem::PopupState)(currItem.state | PopupItem::POPUP_CHECKED);
- }
-
- // Y position
- currItem.pos = newHeight;
-
- if (currItem.state & PopupItem::POPUP_SEPARATOR) {
- if (hideSeparator) {
- // Hide
- currItem.state = (PopupItem::PopupState)(currItem.state | PopupItem::POPUP_HIDDEN);
- continue;
- }
- // Ensure unhidden
- currItem.state = (PopupItem::PopupState)(currItem.state & ~PopupItem::POPUP_HIDDEN);
- newHeight += GUI_POPUP_SEPHEIGHT;
- // Hide any separators after this one; note which separator was last
- // (this will never be 0, because a separator at pos 0 would be hidden)
- hideSeparator = numPos;
- }
- else if (currItem.state & PopupItem::POPUP_HIDDEN) continue;
- else {
- newHeight += fontHeight(FONT_MENU) + GUI_POPUP_LINEPAD;
- // Real item- can show separators again
- hideSeparator = 0;
- }
- int size = fontWidth(currItem.getLabel(), FONT_MENU);
- if (size > largestItem) largestItem = size;
- size = fontWidth(currItem.accelLabel, FONT_MENU);
- if (size > largestAccel) largestAccel = size;
- }
-
- // Hide last separator?
- if (hideSeparator > 0) {
- items[hideSeparator].state = (PopupItem::PopupState)(items[hideSeparator].state | PopupItem::POPUP_HIDDEN);
- newHeight -= GUI_POPUP_SEPHEIGHT;
- }
-
- if (newHeight > GUI_POPUP_EDGEPAD) {
- isEmpty = 0;
- if (largestAccel) newWidth = largestItem + GUI_POPUP_GUTTER + largestAccel;
- else newWidth = largestItem;
- accelOffset = largestItem + GUI_POPUP_GUTTER;
- }
- else {
- // If no items, create room for one (empty) fake item
- isEmpty = 1;
- newWidth = fontWidth(emptyItem, FONT_MENU);
- newHeight += fontHeight(FONT_MENU) + GUI_POPUP_LINEPAD;
- accelOffset = 0;
- }
- newWidth += GUI_POPUP_EDGEPAD * 2 + GUI_POPUP_MARGIN * 2;
- newHeight += GUI_POPUP_EDGEPAD + GUI_POPUP_LINEPAD;
-
- // Move as needed
- if (xPos + newWidth > screenWidth) xPos = screenWidth - newWidth;
- if (yPos + newHeight > screenHeight) yPos = screenHeight - newHeight;
- if (xPos < 0) xPos = 0;
- if (yPos < 0) yPos = 0;
-
- // Checkbox size/position
- checkboxX = (GUI_POPUP_MARGIN - fontHeight(FONT_WIDGET)) / 2;
- checkboxY = fontAscent(FONT_MENU) - fontAscent(FONT_WIDGET);
- }
- move(xPos, yPos);
- resize(newWidth, newHeight);
- }
- void PopupMenu::display(SDL_Surface* destSurface, Rect& toDisplay, const Rect& clipArea, int xOffset, int yOffset) { start_func
- assert(destSurface);
- if (visible) {
- // If dirty, redraw all
- if (dirty) {
- getRect(toDisplay);
- toDisplay.x += xOffset;
- toDisplay.y += yOffset;
- dirty = 0;
- intersectRects(toDisplay, clipArea);
- }
-
- // Anything to draw?
- if (toDisplay.w) {
- SDL_SetClipRect(destSurface, &toDisplay);
-
- xOffset += x;
- yOffset += y;
- // This doesn't attempt to skip items outside dirty area, but they
- // will be clipped away when drawn.
- // Background
- if (isMenubar) {
- drawRect(xOffset, yOffset, width, height - 2, guiPacked[COLOR_FILL], destSurface);
- drawHLine(xOffset, yOffset + width - 1, height - 2, guiPacked[COLOR_DARK1], destSurface);
- drawHLine(xOffset, yOffset + width - 1, height - 1, guiPacked[COLOR_DARK2], destSurface);
- }
- else {
- drawGuiBox(xOffset, yOffset, width, height, 2, destSurface);
- }
- // Draw all popup entries we can see
- if (isEmpty) {
- drawText(emptyItem, guiRGB[COLOR_LIGHT1], xOffset + GUI_POPUP_EDGEPAD + GUI_POPUP_MARGIN + 1, yOffset + GUI_POPUP_EDGEPAD + 1, destSurface, FONT_MENU);
- drawText(emptyItem, guiRGB[COLOR_DARK1], xOffset + GUI_POPUP_EDGEPAD + GUI_POPUP_MARGIN, yOffset + GUI_POPUP_EDGEPAD, destSurface, FONT_MENU);
- }
- else {
- int numPos = 0;
- vector<PopupItem>::iterator end = items.end();
- for (vector<PopupItem>::iterator pos = items.begin(); pos != end; ++pos, ++numPos) {
- PopupItem& currItem = *pos;
-
- if (currItem.state & PopupItem::POPUP_HIDDEN) continue;
- int xPos, yPos, xOffs;
- if (isMenubar) {
- xPos = xOffset + currItem.pos;
- xOffs = 0;
- yPos = yOffset + GUI_MENUBAR_TOPPAD;
- }
- else {
- xPos = xOffset + GUI_POPUP_EDGEPAD;
- xOffs = GUI_POPUP_MARGIN;
- yPos = yOffset + currItem.pos;
- }
-
- // Separators
- if (currItem.state & PopupItem::POPUP_SEPARATOR) {
- drawGuiBoxInvert(xPos, yPos + GUI_POPUP_SEPHEIGHT / 2, width - GUI_POPUP_EDGEPAD * 2, GUI_POPUP_SEPLINE, 1, destSurface);
- }
- else {
- // Border of selection
- if (currSel == numPos) {
- if (isMenubar) {
- // Non-gradient: nothing
- drawGradient(xPos - GUI_MENUBAR_SIDEPAD, yPos, fontWidth(currItem.getLabel(), FONT_MENU) + GUI_MENUBAR_SIDEPAD * 2, fontHeight(FONT_MENU) + GUI_POPUP_LINEPAD + 1, guiRGB[COLOR_POPUPFILL1], guiRGB[COLOR_POPUPFILL2], destSurface);
- drawPixel(xPos - GUI_MENUBAR_SIDEPAD + fontWidth(currItem.getLabel(), FONT_MENU) + GUI_MENUBAR_SIDEPAD * 2 - 1, yPos, guiPacked[COLOR_FILL], destSurface);
- drawPixel(xPos - GUI_MENUBAR_SIDEPAD, yPos + fontHeight(FONT_MENU) + GUI_POPUP_LINEPAD, guiPacked[COLOR_FILL], destSurface);
- drawGuiBoxInvert(xPos - GUI_MENUBAR_SIDEPAD, yPos, fontWidth(currItem.getLabel(), FONT_MENU) + GUI_MENUBAR_SIDEPAD * 2, fontHeight(FONT_MENU) + GUI_POPUP_LINEPAD + 1, 1, destSurface);
- }
- else {
- // Non-gradient: nothing
- drawGradient(xPos, yPos, width - GUI_POPUP_EDGEPAD * 2, fontHeight(FONT_MENU) + GUI_POPUP_LINEPAD + 1, guiRGB[COLOR_POPUPFILL1], guiRGB[COLOR_POPUPFILL2], destSurface);
- drawPixel(xPos + width - GUI_POPUP_EDGEPAD * 2 - 1, yPos, guiPacked[COLOR_FILL], destSurface);
- drawPixel(xPos, yPos + fontHeight(FONT_MENU) + GUI_POPUP_LINEPAD, guiPacked[COLOR_FILL], destSurface);
- drawGuiBoxInvert(xPos, yPos, width - GUI_POPUP_EDGEPAD * 2, fontHeight(FONT_MENU) + GUI_POPUP_LINEPAD + 1, 1, destSurface);
- }
- }
-
- // Text, including checkbox/arrow/underline
- if (currItem.state & PopupItem::POPUP_GRAYED) {
- if (currItem.state & PopupItem::POPUP_CHECKBOX) {
- drawCheckbox(0, currItem.state & PopupItem::POPUP_CHECKED, 1, xPos + checkboxX, yPos + checkboxY, checkboxSize, destSurface);
- }
- else if (currItem.state & PopupItem::POPUP_RADIO) {
- drawCheckbox(1, currItem.state & PopupItem::POPUP_CHECKED, 1, xPos + checkboxX, yPos + checkboxY, checkboxSize, destSurface);
- }
- if ((currItem.submenu != NULL) && (!isMenubar)) {
- drawText(wtSubmenu, guiRGB[COLOR_LIGHT1], xPos + width - checkboxX - GUI_POPUP_EDGEPAD * 2 - fontWidth(wtSubmenu, FONT_WIDGET), yPos + checkboxY + 1, destSurface, FONT_WIDGET);
- drawText(wtSubmenu, guiRGB[COLOR_DARK1], xPos + width - checkboxX - GUI_POPUP_EDGEPAD * 2 - fontWidth(wtSubmenu, FONT_WIDGET), yPos + checkboxY, destSurface, FONT_WIDGET);
- }
- if (currSel != numPos) {
- drawText(currItem.getLabel(), guiRGB[COLOR_LIGHT1], xPos + xOffs + 1, yPos + 1, destSurface, FONT_MENU);
- drawText(currItem.accelLabel, guiRGB[COLOR_LIGHT1], xPos + xOffs + 1 + accelOffset, yPos + 1, destSurface, FONT_MENU);
- }
- drawText(currItem.getLabel(), guiRGB[COLOR_DARK1], xPos + xOffs, yPos, destSurface, FONT_MENU);
- drawText(currItem.accelLabel, guiRGB[COLOR_DARK1], xPos + xOffs + accelOffset, yPos, destSurface, FONT_MENU);
- }
- else {
- if (currItem.state & PopupItem::POPUP_CHECKBOX) {
- drawCheckbox(0, currItem.state & PopupItem::POPUP_CHECKED, 0, xPos + checkboxX, yPos + checkboxY, checkboxSize, destSurface);
- }
- else if (currItem.state & PopupItem::POPUP_RADIO) {
- drawCheckbox(1, currItem.state & PopupItem::POPUP_CHECKED, 0, xPos + checkboxX, yPos + checkboxY, checkboxSize, destSurface);
- }
- if ((currItem.submenu != NULL) && (!isMenubar)) {
- drawText(wtSubmenu, guiRGB[currSel == numPos ? COLOR_POPUPTEXT : COLOR_TEXT], xPos + width - checkboxX - GUI_POPUP_EDGEPAD * 2 - fontWidth(wtSubmenu, FONT_WIDGET), yPos + checkboxY, destSurface, FONT_WIDGET);
- }
- drawText(currItem.getLabel(), guiRGB[currSel == numPos ? COLOR_POPUPTEXT : COLOR_TEXT], xPos + xOffs, yPos, destSurface, FONT_MENU);
- drawText(currItem.accelLabel, guiRGB[currSel == numPos ? COLOR_POPUPTEXT : COLOR_TEXT], xPos + xOffs + accelOffset, yPos, destSurface, FONT_MENU);
- if (currItem.underline) drawTextUnderline(currItem.underline, guiPacked[currSel == numPos ? COLOR_POPUPTEXT : COLOR_TEXT], xPos + xOffs, yPos, destSurface, FONT_MENU);
- }
- }
- }
- }
- }
- }
- }
- // (close all open (popup) submenus at a certain level or higher)
- int PopupMenu::closeAllSubmenus(int level) { start_func
- PopupMenu* next;
- int pos = 0;
- int count = 0;
- // (assignment intentional)
- while ( (next = dynamic_cast<PopupMenu*>(desktop->findWindow(WINDOW_POPUPMENU, pos))) ) {
- if (next->submenuLevel >= level) {
- desktop->removeWindow(next);
- next->resize(0, 0);
- ++count;
- }
- else ++pos;
- }
- return count;
- }
- void PopupMenu::popup(int xPos, int yPos, int newSubmenuLevel, int isFromMenubar) { start_func
- assert(!isMenubar);
- submenuLevel = newSubmenuLevel;
- fromMenubar = isFromMenubar;
- predraw(xPos, yPos);
- setDirty();
- desktop->addWindow(this, 1);
- }
- void PopupMenu::menubar(int show) { start_func
- assert(isMenubar);
- submenuLevel = -1;
- if (show) {
- predraw(0, 0);
- setDirty();
- desktop->addWindow(this);
- }
- else {
- desktop->removeWindow(this);
- resize(0, 0);
- }
- }
- int PopupMenu::wantsToBeDeleted() const { start_func
- return !isMenubar;
- }
- void PopupMenu::resolutionChange(int fromW, int fromH, int fromBpp, int toW, int toH, int toBpp) { start_func
- if (isMenubar) {
- if (visible) predraw(0, 0);
- }
- else {
- Window::resolutionChange(fromW, fromH, fromBpp, toW, toH, toBpp);
- }
- }
- int PopupMenu::event(int hasFocus, const SDL_Event* event) { start_func
- assert(event);
- int prevSel = currSel;
- int expand = 0;
- int select = 0;
- int item = -1;
- Sint32 key = 0;
- int used = 0;
- switch (event->type) {
- case SDL_CLOSE:
- if (isMenubar) {
- // Close all submenus and ourself
- closeAllSubmenus(0);
- menubar(0);
- }
- else {
- // Close all submenus at or below our submenu level
- closeAllSubmenus(submenuLevel);
- }
- return 1;
- case SDL_MOUSEBUTTONDBL:
- case SDL_MOUSEBUTTONDOWN:
- case SDL_MOUSEBUTTONUP:
- if (event->button.button != SDL_BUTTON_LEFT) return 0;
- case SDL_MOUSEMOTION:
- int targetX, targetY;
-
- if (event->type == SDL_MOUSEMOTION) {
- targetX = event->motion.x;
- targetY = event->motion.y;
- }
- else {
- targetX = event->button.x;
- targetY = event->button.y;
- }
- if (isMenubar) {
- // Don't cancel selection on menubar if a popup is open
- if ((desktop->findFocusWindow() == NULL) || (desktop->findFocusWindow()->windowType() != WINDOW_POPUPMENU)) currSel = -1;
-
- if ((targetY > GUI_MENUBAR_TOPPAD) && (targetY <= fontHeight(FONT_MENU) + GUI_MENUBAR_TOPPAD + GUI_POPUP_LINEPAD)) {
- int row = targetX -= GUI_MENUBAR_LEFTPAD;
- if (row >= 0) {
- int numPos = 0;
- vector<PopupItem>::iterator end = items.end();
- for (vector<PopupItem>::iterator pos = items.begin(); pos != end; ++pos, ++numPos) {
- PopupItem& currItem = *pos;
- if (currItem.state & PopupItem::POPUP_HIDDEN) continue;
- int size = fontWidth(currItem.getLabel(), FONT_MENU) + GUI_MENUBAR_SIDEPAD * 2;
- if (row < size) {
- currSel = numPos;
- break;
- }
- else {
- row -= size;
- }
- }
- }
- }
-
- // If nothing selected, refuse focus
- if (currSel == -1) {
- desktop->sendToBottom(this);
- }
- }
- else {
- currSel = -1;
-
- if ((targetX > GUI_POPUP_EDGEPAD) && (targetX < width - GUI_POPUP_EDGEPAD)) {
- int row = targetY - GUI_POPUP_EDGEPAD;
- if (row >= 0) {
- int numPos = 0;
- vector<PopupItem>::iterator end = items.end();
- for (vector<PopupItem>::iterator pos = items.begin(); pos != end; ++pos, ++numPos) {
- PopupItem& currItem = *pos;
- if (currItem.state & PopupItem::POPUP_HIDDEN) continue;
- if (currItem.state & PopupItem::POPUP_SEPARATOR) {
- if (row < GUI_POPUP_SEPHEIGHT) break;
- row -= GUI_POPUP_SEPHEIGHT;
- }
- else {
- if (row < (fontHeight(FONT_MENU) + GUI_POPUP_LINEPAD)) {
- currSel = numPos;
- break;
- }
- else {
- row -= fontHeight(FONT_MENU) + GUI_POPUP_LINEPAD;
- }
- }
- }
- }
- }
- }
-
- if ((event->type == SDL_MOUSEBUTTONDOWN) && (event->button.button == SDL_BUTTON_LEFT)) {
- expand = 1;
- }
- if ((event->type == SDL_MOUSEBUTTONUP) && (event->button.button == SDL_BUTTON_LEFT)) {
- expand = 1;
- if (currSel != -1) select = 1;
- }
-
- used = 1;
- break;
-
- case SDL_MOUSEFOCUS:
- if (event->user.code & 1) return 0;
- else {
- // Cancel selection unless this is menubar and a popup is open
- if ((!isMenubar) || (desktop->findFocusWindow() == NULL) || (desktop->findFocusWindow()->windowType() != WINDOW_POPUPMENU)) currSel = -1;
- // Refuse input focus if menubar
- if (isMenubar) desktop->sendToBottom(this);
- }
- used = 1;
- break;
-
- case SDL_INPUTFOCUS:
- if (event->user.code & 1) return 0;
- else {
- if ((desktop->findFocusWindow() == NULL) ||
- ((desktop->findFocusWindow()->windowType() != WINDOW_POPUPMENU) &&
- (desktop->findFocusWindow()->windowType() != WINDOW_MENUBAR))
- ) {
- currSel = -1;
- select = 1;
- }
- }
- used = 1;
- break;
-
- case SDL_SYSKEY:
- // If ALT being held and we're the menubar, scan for shortcut
- if ((event->key.keysym.mod == KMOD_ALT) && (isMenubar)) {
- key = event->key.keysym.sym;
- }
- case SDL_KEYDOWN:
- if (event->type != SDL_SYSKEY) key = event->key.keysym.sym;
- key = combineKey(key, event->key.keysym.mod);
-
- // Translate keys for menubar
- if (isMenubar) {
- switch (key) {
- case SDLK_DOWN:
- key = SDLK_RIGHT;
- break;
- case SDLK_LEFT:
- key = SDLK_UP;
- break;
- case SDLK_RIGHT:
- key = SDLK_DOWN;
- break;
- }
- }
-
- switch (key) {
- case SDLK_ESCAPE:
- // If menubar, no current selection, and no submenus close,
- // ignore key (allows ESC to filter through in game mode)
- if ((isMenubar) && (currSel == -1) && (closeAllSubmenus(0) == 0))
- break;
- used = 1;
- item = 0;
- break;
-
- case SDLK_UP:
-
- used = 1;
- do {
- if (currSel < 1) currSel = numItems - 1;
- else --currSel;
- if (currSel == prevSel) break;
- } while (items[currSel].state & (PopupItem::POPUP_SEPARATOR | PopupItem::POPUP_HIDDEN));
- break;
-
- case SDLK_DOWN:
- if (isEmpty) return 0;
- used = 1;
- do {
- if (currSel < 0) currSel = 0;
- else if (++currSel >= numItems) currSel = 0;
- if (currSel == prevSel) break;
- } while (items[currSel].state & (PopupItem::POPUP_SEPARATOR | PopupItem::POPUP_HIDDEN));
- break;
- case SDLK_RIGHT:
- used = 1;
- expand = 1;
- if (currSel < 0) expand = 0;
- else if (items[currSel].submenu == NULL) expand = 0;
-
- if ((!expand) && (fromMenubar)) {
- // Go to one item right on menubar
- closeAllSubmenus(0);
- PopupMenu* next;
- // (assignment intentional)
- if ( (next = dynamic_cast<PopupMenu*>(desktop->findWindow(WINDOW_MENUBAR, 0))) ) {
- SDL_Event myEvent = *event;
- myEvent.key.keysym.sym = SDLK_DOWN;
- // Activate, send RIGHT, then DOWN
- desktop->bringToTop(next);
- next->event(0, event);
- return next->event(0, &myEvent);
- }
-
- return 1;
- }
- break;
- case SDLK_LEFT:
- used = 1;
- if (submenuLevel >= 0) {
- // Activate submenu above this, if there is one
- PopupMenu* next;
- int pos = 0;
- // (assignment intentional)
- while ( (next = dynamic_cast<PopupMenu*>(desktop->findWindow(WINDOW_POPUPMENU, pos))) ) {
- if (next->submenuLevel == submenuLevel - 1) {
- // Close all submenus at or below our level
- closeAllSubmenus(submenuLevel);
- desktop->bringToTop(next);
- return 1;
- }
- else pos++;
- }
-
- // Go to one item left on menubar?
- if (fromMenubar) {
- closeAllSubmenus(0);
-
- // (assignment intentional)
- if ( (next = dynamic_cast<PopupMenu*>(desktop->findWindow(WINDOW_MENUBAR, 0))) ) {
- SDL_Event myEvent = *event;
- myEvent.key.keysym.sym = SDLK_DOWN;
- // Activate, send LEFT, then DOWN
- desktop->bringToTop(next);
- next->event(0, event);
- return next->event(0, &myEvent);
- }
- }
-
- return 1;
- }
- break;
- case SDLK_RETURN:
- case SDLK_KP_ENTER:
- used = 1;
- expand = 1;
- select = 1;
- break;
-
- default:
- // Look for appropriate shortcut key; remove ALT
- key &= ~(KMOD_ALT << 16);
- // (make sure there's still a key)
- if (key) {
- int numPos = 0;
- vector<PopupItem>::iterator end = items.end();
- for (vector<PopupItem>::iterator pos = items.begin(); pos != end; ++pos, ++numPos) {
- PopupItem& currItem = *pos;
- // Grayed includes Temphidden
- if (currItem.state & (PopupItem::POPUP_HIDDEN | PopupItem::POPUP_SEPARATOR | PopupItem::POPUP_GRAYED)) continue;
- if (currItem.shortcut == key) {
- used = 1;
- currSel = numPos;
- expand = 1;
- select = 1;
- break;
- }
- }
- }
- break;
- }
-
- break;
-
- default:
- return 0;
- }
- if (currSel != prevSel) {
- // Selection changed and not empty- close submenus, open this one if any closed
- if (currSel != -1) {
- if (closeAllSubmenus(submenuLevel + 1)) expand = 1;
- }
- setDirty();
- }
- if ((expand) && (!isEmpty) && (currSel >= 0) && (items[currSel].submenu != NULL) && (!(items[currSel].state & PopupItem::POPUP_GRAYED))) {
- // Close all submenus at or below the submenu level
- closeAllSubmenus(submenuLevel + 1);
-
- if (isMenubar) {
- items[currSel].submenu->popup(x + items[currSel].pos - GUI_MENUBAR_SIDEPAD, y + fontHeight(FONT_MENU) + GUI_MENUBAR_TOPPAD + GUI_POPUP_LINEPAD + 1, submenuLevel + 1, 1);
- }
- else {
- items[currSel].submenu->popup(x + width - GUI_POPUP_EDGEPAD, y + items[currSel].pos - GUI_POPUP_EDGEPAD, submenuLevel + 1, fromMenubar);
- }
- return 1;
- }
-
- else if (select) {
- if ((currSel < 0) || (isEmpty)) item = 0;
- else if (items[currSel].state & PopupItem::POPUP_GRAYED) item = 0;
- else item = items[currSel].code;
- }
-
- if (item >= 0) {
- // Close all popup menus at any level
- closeAllSubmenus(0);
-
- // If we're menubar, refuse focus
- if (isMenubar) {
- desktop->sendToBottom(this);
- }
-
- // Tell menubar to cancel showing current selection
- PopupMenu* next;
- // (assignment intentional)
- if ( (next = dynamic_cast<PopupMenu*>(desktop->findWindow(WINDOW_MENUBAR, 0))) ) {
- SDL_Event myEvent = *event;
- myEvent.type = SDL_MOUSEFOCUS;
- myEvent.user.code = 0;
- next->event(0, &myEvent);
- }
-
- // Send event for command selected
- if (item > 0) {
- desktop->broadcastEvent(SDL_COMMAND, item);
- desktop->broadcastEvent(SDL_COMMAND, CMD_RELEASE);
- }
-
- return 1;
- }
-
- return used;
- }
- int PopupMenu::isDocked() const { start_func
- return isMenubar;
- }
- int PopupMenu::tempFocus() const { start_func
- return 1;
- }
- Window::WindowType PopupMenu::windowType() const { start_func
- return isMenubar ? WINDOW_MENUBAR : WINDOW_POPUPMENU;
- }
- Window::WindowSort PopupMenu::windowSort() const { start_func
- return isMenubar ? WINDOWSORT_DOCKED : WINDOWSORT_POPUP;
- }
- PopupMenu* PopupMenu::compileMenu(const PopupGen* menuData, int isMenuBar, int* pos) { start_func
- PopupMenu* myMenu = NULL;
- PopupMenu* subMenu = NULL;
- PopupItem* subItem = NULL;
- int dummyPos = 0;
- if (!pos) pos = &dummyPos;
- myMenu = new PopupMenu(isMenuBar);
-
- while (menuData[*pos].code) {
- // Submenu
- if (menuData[*pos].code == MENU_SUB) {
- int oldPos = *pos;
-
- ++*pos;
- subMenu = compileMenu(menuData, 0, pos);
- subItem = new PopupItem(menuData[oldPos].dynamic, menuData[oldPos].name,
- 0, menuData[oldPos].dynamic ? 1 : 0, subMenu);
- // subMenu not "owned" by anything yet
- }
- // Separator
- else if (menuData[*pos].code == MENU_SEP) {
- subItem = new PopupItem(0, NULL, 0, 0);
- }
- // Standard menu item
- else {
- subItem = new PopupItem(menuData[*pos].code, menuData[*pos].name,
- config->readShortcut(menuData[*pos].code),
- menuData[*pos].dynamic);
- }
-
- myMenu->add(*subItem);
- // subMenu now owned by myMenu
- subMenu = NULL;
- delete subItem;
- subItem = NULL;
-
- ++*pos;
- }
-
- return myMenu;
- }
|