ede/pekwm/MenuHandler.cc

295 lines
7.7 KiB
C++

//
// MenuHandler.cc for pekwm
// Copyright © 2009 Claes Nästén <me@pekdon.net>
//
// This program is licensed under the GNU GPL.
// See the LICENSE file for more information.
//
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif // HAVE_CONFIG_H
#include <cassert>
#include <map>
#include <string>
#include "PWinObj.hh"
#include "PMenu.hh"
#include "PScreen.hh"
#include "MenuHandler.hh"
#include "ActionHandler.hh"
#include "WORefMenu.hh"
#include "ActionMenu.hh"
#include "FrameListMenu.hh"
#include "DecorMenu.hh"
using std::map;
using std::string;
MenuHandler *MenuHandler::_instance = 0;
/**
* List of reserved names of built-in menus.
*/
const char *MenuHandler::MENU_NAMES_RESERVED[] = {
"ATTACHCLIENTINFRAME",
"ATTACHCLIENT",
"ATTACHFRAMEINFRAME",
"ATTACHFRAME",
"DECORMENU",
"GOTOCLIENT",
"GOTO",
"ICON",
"ROOTMENU",
"ROOT", // To avoid name conflict, ROOTMENU -> ROOT
"WINDOWMENU",
"WINDOW" // To avoid name conflict, WINDOWMENU -> WINDOW
};
const unsigned int MenuHandler::MENU_NAMES_RESERVED_COUNT =
sizeof(MenuHandler::MENU_NAMES_RESERVED)
/ sizeof(MenuHandler::MENU_NAMES_RESERVED[0]);
/**
* Comparsion for binary search
*/
bool
str_comparator(const string &lhs, const string &rhs) {
return strcasecmp(lhs.c_str(), rhs.c_str()) < 0;
}
/**
* Initialize menu handler instance.
*/
void
MenuHandler::init(Theme *theme)
{
if (_instance) {
delete _instance;
}
_instance = new MenuHandler(theme);
}
/**
* Destroy menu handler instance.
*/
void
MenuHandler::destroy(void)
{
delete _instance;
_instance = 0;
}
/**
* Menu handler constructor, create menus.
*
* Requires Config, PScreen and ActionHandler to be constructed.
*/
MenuHandler::MenuHandler(Theme *theme)
: _theme(theme)
{
createMenus();
}
/**
* Menu handler destructor, delete menus.
*/
MenuHandler::~MenuHandler(void)
{
deleteMenus();
}
/**
* Hides all menus
*/
void
MenuHandler::hideAllMenus(void)
{
map<std::string, PMenu*>::iterator it(_menu_map.begin());
for (; it != _menu_map.end(); ++it) {
it->second->unmapAll();
}
}
/**
* Creates reserved menus and populates _menu_map
*/
void
MenuHandler::createMenus(void)
{
PScreen *screen = PScreen::instance();
ActionHandler *action_handler = ActionHandler::instance();
PMenu *menu = 0;
menu = new FrameListMenu(screen, _theme, ATTACH_CLIENT_IN_FRAME_TYPE,
L"Attach Client In Frame",
"AttachClientInFrame");
_menu_map["ATTACHCLIENTINFRAME"] = menu;
menu = new FrameListMenu(screen, _theme, ATTACH_CLIENT_TYPE,
L"Attach Client", "AttachClient");
_menu_map["ATTACHCLIENT"] = menu;
menu = new FrameListMenu(screen, _theme, ATTACH_FRAME_IN_FRAME_TYPE,
L"Attach Frame In Frame",
"AttachFrameInFrame");
_menu_map["ATTACHFRAMEINFRAME"] = menu;
menu = new FrameListMenu(screen, _theme, ATTACH_FRAME_TYPE,
L"Attach Frame", "AttachFrame");
_menu_map["ATTACHFRAME"] = menu;
menu = new FrameListMenu(screen, _theme, GOTOCLIENTMENU_TYPE,
L"Focus Client", "GotoClient");
_menu_map["GOTOCLIENT"] = menu;
menu = new FrameListMenu(screen, _theme, GOTOMENU_TYPE,
L"Focus Frame", "Goto");
_menu_map["GOTO"] = menu;
menu = new FrameListMenu(screen, _theme, ICONMENU_TYPE,
L"Focus Iconified Frame", "Icon");
_menu_map["ICON"] = menu;
menu = new DecorMenu(screen, _theme, action_handler, "DecorMenu");
_menu_map["DECORMENU"] = menu;
menu = new ActionMenu(ROOTMENU_TYPE, L"", "RootMenu");
_menu_map["ROOT"] = menu;
menu = new ActionMenu(WINDOWMENU_TYPE, L"", "WindowMenu");
_menu_map["WINDOW"] = menu;
// As the previous step is done manually, make sure it's done correct.
assert(_menu_map.size() == (MENU_NAMES_RESERVED_COUNT - 2));
createMenusLoadConfiguration();
}
/**
* Initial load of menu configuration.
*/
void
MenuHandler::createMenusLoadConfiguration(void)
{
// Load configuration, pass specific section to loading
CfgParser menu_cfg;
if (menu_cfg.parse(Config::instance()->getMenuFile())
|| menu_cfg.parse (string(SYSCONFDIR "/menu"))) {
_menu_state = menu_cfg.get_file_list();
CfgParser::Entry *root_entry = menu_cfg.get_entry_root();
// Load standard menus
map<string, PMenu*>::iterator it = _menu_map.begin();
for (; it != _menu_map.end(); ++it) {
it->second->reload(root_entry->find_section(it->second->getName()));
}
// Load standalone menus
reloadStandaloneMenus(menu_cfg.get_entry_root());
}
}
/**
* (re)loads the menus in the menu configuration if the file has been
* updated since last load.
*/
void
MenuHandler::reloadMenus(void)
{
string menu_file(Config::instance()->getMenuFile());
if (! Util::requireReload(_menu_state, menu_file)) {
return;
}
CfgParser cfg;
bool cfg_ok = loadMenuConfig(menu_file, cfg);
CfgParser::Entry *root = cfg.get_entry_root();
// Update, delete standalone root menus, load decors on others
map<string, PMenu*>::iterator it(_menu_map.begin());
for (; it != _menu_map.end(); ++it) {
if (it->second->getMenuType() == ROOTMENU_STANDALONE_TYPE) {
delete it->second;
_menu_map.erase(it);
} else if (cfg_ok) {
// Only reload the menu if we got a ok configuration
it->second->reload(root->find_section(it->second->getName()));
}
}
// Update standalone root menus (name != ROOTMENU)
reloadStandaloneMenus(root);
}
/**
* Load menu configuration from menu_file resetting menu state.
*/
bool
MenuHandler::loadMenuConfig(const std::string &menu_file, CfgParser &menu_cfg)
{
bool cfg_ok = true;
if (! menu_cfg.parse(menu_file)) {
if (! menu_cfg.parse(string(SYSCONFDIR "/menu"))) {
cfg_ok = false;
}
}
// Make sure menu is reloaded next time as content is dynamically
// generated from the configuration file.
if (! cfg_ok || menu_cfg.is_dynamic_content()) {
_menu_state.clear();
} else {
_menu_state = menu_cfg.get_file_list();
}
return cfg_ok;
}
/**
* Updates standalone root menus
*/
void
MenuHandler::reloadStandaloneMenus(CfgParser::Entry *section)
{
// Temporary name, as names are stored uppercase
string menu_name, menu_name_upper;
// Go through all but reserved section names and create menus
CfgParser::iterator it(section->begin());
for (; it != section->end(); ++it) {
// Uppercase name
menu_name = (*it)->get_name();
menu_name_upper = menu_name;
Util::to_upper(menu_name_upper);
// Create new menus, if the name is not reserved and not used
if (! isReservedName(menu_name_upper) && ! getMenu(menu_name_upper)) {
// Create, parse and add to map
PMenu *menu = new ActionMenu(ROOTMENU_STANDALONE_TYPE,
L"", menu_name);
menu->reload((*it)->get_section());
_menu_map[menu_name_upper] = menu;
}
}
}
/**
* Clears the menu map and frees up resources used by menus
*/
void
MenuHandler::deleteMenus(void)
{
map<std::string, PMenu*>::iterator it(_menu_map.begin());
for (; it != _menu_map.end(); ++it) {
delete it->second;
}
_menu_map.clear();
}
/**
* Check if name is reserved, return true if it is.
*/
bool
MenuHandler::isReservedName(const std::string &name)
{
return binary_search(MENU_NAMES_RESERVED,
MENU_NAMES_RESERVED + MENU_NAMES_RESERVED_COUNT,
name, str_comparator);
}