// Various authors. // License: GPL version 2 or later. #include "contextmenu.h" #include "delegate.h" #include "gmenu2x.h" #include "linkapp.h" #include "menu.h" #include "utilities.h" #include struct ContextMenu::MenuOption { MenuOption(std::string text, function_t action) : text(text), action(action) {} std::string text; function_t action; }; ContextMenu::ContextMenu(GMenu2X &gmenu2x, Menu &menu) : gmenu2x(gmenu2x) , menu(menu) , selected(0) { Translator &tr = gmenu2x.tr; Font *font = gmenu2x.font; LinkApp* app = menu.selLinkApp(); // Init menu options: options.push_back(std::make_shared( tr.translate("Add link in $1", menu.selSection().c_str(), NULL), std::bind(&GMenu2X::addLink, &gmenu2x))); if (app && !app->getManual().empty()) { options.push_back(std::make_shared( tr.translate("Show manual of $1", app->getTitle().c_str(), NULL), std::bind(&GMenu2X::showManual, &gmenu2x))); } if (app && app->isEditable()) { /* FIXME(percuei): This permits to mask the "Edit link" entry * on the contextual menu in case CPUFREQ support is * not compiled in and the link corresponds to an OPK. * This is not a good idea as it'll break things if * a new config option is added to the contextual menu. */ if (!app->isOpk() #if defined(ENABLE_CPUFREQ) || true #endif || !app->getSelectorDir().empty() ) { options.push_back(std::make_shared( tr.translate("Edit $1", app->getTitle().c_str(), NULL), std::bind(&GMenu2X::editLink, &gmenu2x))); } if (!app->isOpk()) { options.push_back(std::make_shared( tr.translate("Delete $1 link", app->getTitle().c_str(), NULL), std::bind(&GMenu2X::deleteLink, &gmenu2x))); } } options.push_back(std::make_shared( tr["Add section"], std::bind(&GMenu2X::addSection, &gmenu2x))); options.push_back(std::make_shared( tr["Rename section"], std::bind(&GMenu2X::renameSection, &gmenu2x))); options.push_back(std::make_shared( tr["Delete section"], std::bind(&GMenu2X::deleteSection, &gmenu2x))); options.push_back(std::make_shared( tr["Scan for applications and games"], std::bind(&GMenu2X::scanner, &gmenu2x))); // Compute bounding box. int w = 0; for (auto option : options) { w = std::max(w, font->getTextWidth(option->text)); } w += 23; const int h = (font->getHeight() + 2) * options.size() + 8; box = { static_cast((gmenu2x.resX - w) / 2), static_cast((gmenu2x.resY - h) / 2), static_cast(w), static_cast(h) }; // Init background fade animation. tickStart = SDL_GetTicks(); fadeAlpha = 0; } bool ContextMenu::runAnimations() { if (fadeAlpha < 200) { const long tickNow = SDL_GetTicks(); fadeAlpha = intTransition(0, 200, tickStart, 500, tickNow); } return fadeAlpha < 200; } void ContextMenu::paint(Surface &s) { Font *font = gmenu2x.font; // Darken background. s.box(0, 0, gmenu2x.resX, gmenu2x.resY, 0, 0, 0, fadeAlpha); // Draw popup box. s.box(box, gmenu2x.skinConfColors[COLOR_MESSAGE_BOX_BG]); s.rectangle(box.x + 2, box.y + 2, box.w - 4, box.h - 4, gmenu2x.skinConfColors[COLOR_MESSAGE_BOX_BORDER]); // Draw selection background. const int h = font->getHeight(); SDL_Rect selbox = { static_cast(box.x + 4), static_cast(box.y + 4 + (h + 2) * selected), static_cast(box.w - 8), static_cast(h + 2) }; s.box(selbox, gmenu2x.skinConfColors[COLOR_MESSAGE_BOX_SELECTION]); // List options. for (uint i = 0; i < options.size(); i++) { s.write(font, options[i]->text, box.x + 12, box.y + 5 + (h + 2) * i, Font::HAlignLeft, Font::VAlignTop); } } bool ContextMenu::handleButtonPress(InputManager::Button button) { switch (button) { case InputManager::MENU: dismiss(); break; case InputManager::UP: selected--; if (selected < 0) selected = options.size() - 1; break; case InputManager::DOWN: selected++; if (selected >= static_cast(options.size())) selected = 0; break; case InputManager::ACCEPT: options[selected]->action(); dismiss(); break; default: break; } return true; } bool ContextMenu::handleTouchscreen(Touchscreen &ts) { if (ts.inRect(box)) { int i = std::max(0, std::min(static_cast(options.size()) - 1, (ts.getY() - (box.y + 4)) / (gmenu2x.font->getHeight() + 2))); if (ts.released()) { options[i]->action(); dismiss(); } else if (ts.pressed()) { selected = i; } } else { if (ts.released()) { dismiss(); } } return true; }