2013-08-12 00:46:04 +03:00
|
|
|
// Various authors.
|
|
|
|
// License: GPL version 2 or later.
|
|
|
|
|
|
|
|
#include "contextmenu.h"
|
|
|
|
|
|
|
|
#include "gmenu2x.h"
|
|
|
|
#include "linkapp.h"
|
|
|
|
#include "menu.h"
|
|
|
|
#include "utilities.h"
|
|
|
|
|
|
|
|
#include <algorithm>
|
2014-08-16 08:07:41 +03:00
|
|
|
#include <functional>
|
2013-08-12 00:46:04 +03:00
|
|
|
|
|
|
|
|
|
|
|
struct ContextMenu::MenuOption {
|
2014-08-16 08:07:41 +03:00
|
|
|
typedef std::function<void(void)> Action;
|
|
|
|
MenuOption(std::string text, Action action)
|
2013-08-12 00:46:04 +03:00
|
|
|
: text(text), action(action) {}
|
|
|
|
std::string text;
|
2014-08-16 08:07:41 +03:00
|
|
|
Action action;
|
2013-08-12 00:46:04 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
ContextMenu::ContextMenu(GMenu2X &gmenu2x, Menu &menu)
|
|
|
|
: gmenu2x(gmenu2x)
|
|
|
|
, menu(menu)
|
|
|
|
, selected(0)
|
|
|
|
{
|
|
|
|
Translator &tr = gmenu2x.tr;
|
2014-08-01 00:10:16 +03:00
|
|
|
Font& font = *gmenu2x.font;
|
2013-08-12 00:46:04 +03:00
|
|
|
LinkApp* app = menu.selLinkApp();
|
|
|
|
|
|
|
|
// Init menu options:
|
|
|
|
|
|
|
|
options.push_back(std::make_shared<MenuOption>(
|
|
|
|
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<MenuOption>(
|
|
|
|
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.
|
|
|
|
*/
|
2013-08-12 01:13:18 +03:00
|
|
|
if (!app->isOpk()
|
|
|
|
#if defined(ENABLE_CPUFREQ)
|
|
|
|
|| true
|
2013-08-12 00:46:04 +03:00
|
|
|
#endif
|
2013-08-12 01:13:18 +03:00
|
|
|
|| !app->getSelectorDir().empty()
|
|
|
|
) {
|
2013-08-12 00:46:04 +03:00
|
|
|
options.push_back(std::make_shared<MenuOption>(
|
|
|
|
tr.translate("Edit $1", app->getTitle().c_str(), NULL),
|
|
|
|
std::bind(&GMenu2X::editLink, &gmenu2x)));
|
|
|
|
}
|
2013-08-12 01:13:18 +03:00
|
|
|
if (!app->isOpk()) {
|
2013-08-12 00:46:04 +03:00
|
|
|
options.push_back(std::make_shared<MenuOption>(
|
2014-06-12 23:37:08 +03:00
|
|
|
tr.translate("Delete $1", app->getTitle().c_str(), NULL),
|
2013-08-12 00:46:04 +03:00
|
|
|
std::bind(&GMenu2X::deleteLink, &gmenu2x)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
options.push_back(std::make_shared<MenuOption>(
|
|
|
|
tr["Add section"],
|
|
|
|
std::bind(&GMenu2X::addSection, &gmenu2x)));
|
|
|
|
options.push_back(std::make_shared<MenuOption>(
|
|
|
|
tr["Rename section"],
|
|
|
|
std::bind(&GMenu2X::renameSection, &gmenu2x)));
|
|
|
|
options.push_back(std::make_shared<MenuOption>(
|
|
|
|
tr["Delete section"],
|
|
|
|
std::bind(&GMenu2X::deleteSection, &gmenu2x)));
|
|
|
|
|
|
|
|
// Compute bounding box.
|
|
|
|
int w = 0;
|
|
|
|
for (auto option : options) {
|
2014-08-01 00:10:16 +03:00
|
|
|
w = std::max(w, font.getTextWidth(option->text));
|
2013-08-12 00:46:04 +03:00
|
|
|
}
|
|
|
|
w += 23;
|
2014-08-01 00:10:16 +03:00
|
|
|
const int h = (font.getLineSpacing() + 2) * options.size() + 8;
|
2013-08-12 00:46:04 +03:00
|
|
|
box = {
|
|
|
|
static_cast<Sint16>((gmenu2x.resX - w) / 2),
|
|
|
|
static_cast<Sint16>((gmenu2x.resY - h) / 2),
|
|
|
|
static_cast<Uint16>(w),
|
|
|
|
static_cast<Uint16>(h)
|
|
|
|
};
|
|
|
|
|
|
|
|
// Init background fade animation.
|
|
|
|
tickStart = SDL_GetTicks();
|
2013-08-12 20:41:56 +03:00
|
|
|
fadeAlpha = 0;
|
2013-08-12 00:46:04 +03:00
|
|
|
}
|
|
|
|
|
2013-08-12 20:41:56 +03:00
|
|
|
bool ContextMenu::runAnimations()
|
2013-08-12 00:46:04 +03:00
|
|
|
{
|
|
|
|
if (fadeAlpha < 200) {
|
|
|
|
const long tickNow = SDL_GetTicks();
|
|
|
|
fadeAlpha = intTransition(0, 200, tickStart, 500, tickNow);
|
|
|
|
}
|
2013-08-12 20:41:56 +03:00
|
|
|
return fadeAlpha < 200;
|
2013-08-12 00:46:04 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void ContextMenu::paint(Surface &s)
|
|
|
|
{
|
2014-08-01 00:10:16 +03:00
|
|
|
Font& font = *gmenu2x.font;
|
2013-08-12 00:46:04 +03:00
|
|
|
|
|
|
|
// 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.
|
2014-08-01 00:10:16 +03:00
|
|
|
const int h = font.getLineSpacing();
|
2013-08-12 00:46:04 +03:00
|
|
|
SDL_Rect selbox = {
|
|
|
|
static_cast<Sint16>(box.x + 4),
|
|
|
|
static_cast<Sint16>(box.y + 4 + (h + 2) * selected),
|
|
|
|
static_cast<Uint16>(box.w - 8),
|
|
|
|
static_cast<Uint16>(h + 2)
|
|
|
|
};
|
|
|
|
s.box(selbox, gmenu2x.skinConfColors[COLOR_MESSAGE_BOX_SELECTION]);
|
|
|
|
|
|
|
|
// List options.
|
|
|
|
for (uint i = 0; i < options.size(); i++) {
|
2014-08-10 05:26:59 +03:00
|
|
|
font.write(s, options[i]->text, box.x + 12, box.y + 5 + (h + 2) * i,
|
2013-08-12 00:46:04 +03:00
|
|
|
Font::HAlignLeft, Font::VAlignTop);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ContextMenu::handleButtonPress(InputManager::Button button) {
|
|
|
|
switch (button) {
|
|
|
|
case InputManager::MENU:
|
|
|
|
dismiss();
|
|
|
|
break;
|
|
|
|
case InputManager::UP:
|
2013-08-12 00:50:20 +03:00
|
|
|
selected--;
|
|
|
|
if (selected < 0) selected = options.size() - 1;
|
2013-08-12 00:46:04 +03:00
|
|
|
break;
|
|
|
|
case InputManager::DOWN:
|
2013-08-12 00:50:20 +03:00
|
|
|
selected++;
|
|
|
|
if (selected >= static_cast<int>(options.size())) selected = 0;
|
2013-08-12 00:46:04 +03:00
|
|
|
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<int>(options.size()) - 1,
|
2014-06-18 23:07:09 +03:00
|
|
|
(ts.getY() - (box.y + 4)) / (gmenu2x.font->getLineSpacing() + 2)));
|
2013-08-12 00:46:04 +03:00
|
|
|
if (ts.released()) {
|
|
|
|
options[i]->action();
|
|
|
|
dismiss();
|
|
|
|
} else if (ts.pressed()) {
|
|
|
|
selected = i;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (ts.released()) {
|
|
|
|
dismiss();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|