// // MenuHandler.cc for pekwm // Copyright © 2009 Claes Nästén // // 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 #include #include #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::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::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::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::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); }