mirror of
https://github.com/edeproject/ede.git
synced 2023-08-10 21:13:03 +03:00
1039 lines
33 KiB
C++
1039 lines
33 KiB
C++
|
//
|
||
|
// ActionHandler.cc for pekwm
|
||
|
// Copyright © 2002-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 "ActionHandler.hh"
|
||
|
|
||
|
#include "PWinObj.hh"
|
||
|
#include "PDecor.hh"
|
||
|
#include "PMenu.hh"
|
||
|
#include "PScreen.hh"
|
||
|
#include "Frame.hh"
|
||
|
#include "Client.hh"
|
||
|
#include "Config.hh"
|
||
|
#include "CmdDialog.hh"
|
||
|
#include "SearchDialog.hh"
|
||
|
#include "Workspaces.hh"
|
||
|
#include "WindowManager.hh"
|
||
|
#include "Util.hh"
|
||
|
#include "RegexString.hh"
|
||
|
#include "WorkspaceIndicator.hh"
|
||
|
#include "Harbour.hh"
|
||
|
#include "MenuHandler.hh"
|
||
|
#include "PDecor.hh"
|
||
|
#include "PMenu.hh"
|
||
|
#include "WORefMenu.hh"
|
||
|
#include "ActionMenu.hh"
|
||
|
#include "FrameListMenu.hh"
|
||
|
#include "DecorMenu.hh"
|
||
|
#include "HarbourMenu.hh"
|
||
|
|
||
|
#include <memory>
|
||
|
|
||
|
extern "C" {
|
||
|
#include <X11/keysym.h>
|
||
|
}
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
#include <iostream>
|
||
|
using std::cerr;
|
||
|
using std::endl;
|
||
|
#endif // DEBUG
|
||
|
using std::auto_ptr;
|
||
|
using std::string;
|
||
|
using std::list;
|
||
|
using std::find;
|
||
|
using std::map;
|
||
|
|
||
|
ActionHandler *ActionHandler::_instance = 0;
|
||
|
|
||
|
//! @brief ActionHandler constructor
|
||
|
ActionHandler::ActionHandler(void)
|
||
|
{
|
||
|
_instance = this;
|
||
|
|
||
|
// Initialize state_to_keycode map
|
||
|
for (uint i = 0; i < PScreen::MODIFIER_TO_MASK_NUM; ++i) {
|
||
|
uint modifier = PScreen::MODIFIER_TO_MASK[i];
|
||
|
_state_to_keycode[modifier] = PScreen::instance()->getKeycodeFromMask(modifier);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//! @brief ActionHandler destructor
|
||
|
ActionHandler::~ActionHandler(void)
|
||
|
{
|
||
|
_instance = 0;
|
||
|
}
|
||
|
|
||
|
//! @brief Executes an ActionPerformed event.
|
||
|
void
|
||
|
ActionHandler::handleAction(const ActionPerformed &ap)
|
||
|
{
|
||
|
PWinObj *wo = ap.wo;
|
||
|
Client *client = 0;
|
||
|
Frame *frame = 0;
|
||
|
PMenu *menu = 0;
|
||
|
PDecor *decor = 0;
|
||
|
bool matched = false;
|
||
|
|
||
|
// go through the list of actions and execute them
|
||
|
list<Action>::const_iterator it = ap.ae.action_list.begin();
|
||
|
for (; it != ap.ae.action_list.end(); ++it, matched = false) {
|
||
|
// Determine what type if any of the window object that is focused
|
||
|
// and check if it is still alive.
|
||
|
lookupWindowObjects(&wo, &client, &frame, &menu, &decor);
|
||
|
|
||
|
// actions valid for all PWinObjs
|
||
|
if (! matched && wo) {
|
||
|
matched = true;
|
||
|
switch (it->getAction()) {
|
||
|
case ACTION_FOCUS:
|
||
|
wo->giveInputFocus();
|
||
|
break;
|
||
|
case ACTION_UNFOCUS:
|
||
|
PWinObj::getRootPWinObj()->giveInputFocus();
|
||
|
break;
|
||
|
case ACTION_FOCUS_DIRECTIONAL:
|
||
|
actionFocusDirectional(wo, DirectionType(it->getParamI(0)), it->getParamI(1));
|
||
|
break;
|
||
|
case ACTION_SEND_KEY:
|
||
|
actionSendKey(wo, it->getParamS());
|
||
|
break;
|
||
|
default:
|
||
|
matched = false;
|
||
|
break;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
// actions valid for Clients and Frames
|
||
|
if (! matched && frame) {
|
||
|
matched = true;
|
||
|
switch (it->getAction()) {
|
||
|
case ACTION_GROUPING_DRAG:
|
||
|
if (ap.type == MotionNotify) {
|
||
|
frame->doGroupingDrag(ap.event.motion, client, it->getParamI(0));
|
||
|
}
|
||
|
break;
|
||
|
case ACTION_ACTIVATE_CLIENT:
|
||
|
if ((ap.type == ButtonPress) || (ap.type == ButtonRelease))
|
||
|
frame->activateChild(frame->getChildFromPos(ap.event.button->x));
|
||
|
break;
|
||
|
case ACTION_GOTO_CLIENT:
|
||
|
gotoClient(client);
|
||
|
break;
|
||
|
case ACTION_MAXFILL:
|
||
|
frame->setStateMaximized(STATE_SET,
|
||
|
it->getParamI(0), it->getParamI(1), true);
|
||
|
break;
|
||
|
case ACTION_GROW_DIRECTION:
|
||
|
frame->growDirection(it->getParamI(0));
|
||
|
break;
|
||
|
case ACTION_RESIZE:
|
||
|
if (ap.type == MotionNotify && ! it->getParamI(0))
|
||
|
frame->doResize(ap.event.motion);
|
||
|
else
|
||
|
frame->doResize( BorderPosition(it->getParamI(0)-1) );
|
||
|
break;
|
||
|
case ACTION_MOVE_RESIZE:
|
||
|
frame->doKeyboardMoveResize();
|
||
|
break;
|
||
|
case ACTION_CLOSE:
|
||
|
client->close();
|
||
|
break;
|
||
|
case ACTION_CLOSE_FRAME:
|
||
|
frame->close();
|
||
|
break;
|
||
|
case ACTION_KILL:
|
||
|
client->kill();
|
||
|
break;
|
||
|
case ACTION_RAISE:
|
||
|
if (it->getParamI(0)) {
|
||
|
WindowManager::instance()->familyRaiseLower(client, true);
|
||
|
} else {
|
||
|
frame->raise();
|
||
|
}
|
||
|
break;
|
||
|
case ACTION_LOWER:
|
||
|
if (it->getParamI(0)) {
|
||
|
WindowManager::instance()->familyRaiseLower(client, false);
|
||
|
} else {
|
||
|
frame->lower();
|
||
|
}
|
||
|
break;
|
||
|
case ACTION_ACTIVATE_OR_RAISE:
|
||
|
if ((ap.type == ButtonPress) || (ap.type == ButtonRelease)) {
|
||
|
if (ap.event.button->window == frame->getTitleWindow()) {
|
||
|
frame->activateChild(frame->getChildFromPos(ap.event.button->x));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (frame->isFocused()) {
|
||
|
frame->raise();
|
||
|
} else {
|
||
|
wo->giveInputFocus();
|
||
|
}
|
||
|
break;
|
||
|
case ACTION_MOVE_TO_EDGE:
|
||
|
frame->moveToEdge(OrientationType(it->getParamI(0)));
|
||
|
break;
|
||
|
case ACTION_ACTIVATE_CLIENT_REL:
|
||
|
frame->activateChildRel(it->getParamI(0));
|
||
|
break;
|
||
|
case ACTION_MOVE_CLIENT_REL:
|
||
|
frame->moveChildRel(it->getParamI(0));
|
||
|
break;
|
||
|
case ACTION_ACTIVATE_CLIENT_NUM:
|
||
|
frame->activateChildNum(it->getParamI(0));
|
||
|
break;
|
||
|
case ACTION_SEND_TO_WORKSPACE:
|
||
|
actionSendToWorkspace(decor, it->getParamI(0));
|
||
|
break;
|
||
|
case ACTION_DETACH:
|
||
|
frame->detachClient(client);
|
||
|
break;
|
||
|
case ACTION_ATTACH_MARKED:
|
||
|
WindowManager::instance()->attachMarked(frame);
|
||
|
break;
|
||
|
case ACTION_ATTACH_CLIENT_IN_NEXT_FRAME:
|
||
|
WindowManager::instance()->attachInNextPrevFrame(client, false, true);
|
||
|
break;
|
||
|
case ACTION_ATTACH_CLIENT_IN_PREV_FRAME:
|
||
|
WindowManager::instance()->attachInNextPrevFrame(client, false, false);
|
||
|
break;
|
||
|
case ACTION_ATTACH_FRAME_IN_NEXT_FRAME:
|
||
|
WindowManager::instance()->attachInNextPrevFrame(client, true, true);
|
||
|
break;
|
||
|
case ACTION_ATTACH_FRAME_IN_PREV_FRAME:
|
||
|
WindowManager::instance()->attachInNextPrevFrame(client, true, false);
|
||
|
break;
|
||
|
#ifdef OPACITY
|
||
|
case ACTION_SET_OPACITY:
|
||
|
actionSetOpacity(client, frame, it->getParamI(0), it->getParamI(1));
|
||
|
break;
|
||
|
#endif // OPACITY
|
||
|
default:
|
||
|
matched = false;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Actions valid for Menus
|
||
|
if (! matched && menu) {
|
||
|
matched = true;
|
||
|
switch (it->getAction()) {
|
||
|
// menu navigation
|
||
|
case ACTION_MENU_NEXT:
|
||
|
menu->selectNextItem();
|
||
|
break;
|
||
|
case ACTION_MENU_PREV:
|
||
|
menu->selectPrevItem();
|
||
|
break;
|
||
|
case ACTION_MENU_SELECT:
|
||
|
menu->exec(menu->getItemCurr());
|
||
|
|
||
|
// special case: execItem can cause an reload to be issued, if that's
|
||
|
// the case it causes the list (ae) to change and therefore
|
||
|
// it can't be used anymore
|
||
|
return;
|
||
|
case ACTION_MENU_ENTER_SUBMENU:
|
||
|
if (menu->getItemCurr() &&
|
||
|
menu->getItemCurr()->getWORef() &&
|
||
|
(menu->getItemCurr()->getWORef()->getType() == PWinObj::WO_MENU)) {
|
||
|
menu->mapSubmenu(static_cast<PMenu*>(menu->getItemCurr()->getWORef()), true);
|
||
|
}
|
||
|
break;
|
||
|
case ACTION_MENU_LEAVE_SUBMENU:
|
||
|
menu->gotoParentMenu();
|
||
|
break;
|
||
|
case ACTION_CLOSE:
|
||
|
menu->unmapAll();
|
||
|
WindowManager::instance()->findWOAndFocus(0);
|
||
|
break;
|
||
|
default:
|
||
|
matched = false;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
// actions valid for pdecor
|
||
|
if (! matched && decor) {
|
||
|
int x_root, y_root;
|
||
|
|
||
|
matched = true;
|
||
|
switch (it->getAction()) {
|
||
|
case ACTION_MOVE:
|
||
|
// Get root position, previously event positions was
|
||
|
// used however this seems to be error prone on
|
||
|
// Xinerama setups
|
||
|
PScreen::instance()->getMousePosition(x_root, y_root);
|
||
|
|
||
|
decor->doMove(x_root, y_root);
|
||
|
break;
|
||
|
case ACTION_CLOSE:
|
||
|
decor->unmapWindow();
|
||
|
if (decor->getType() == PWinObj::WO_CMD_DIALOG
|
||
|
|| decor->getType() == PWinObj::WO_SEARCH_DIALOG) {
|
||
|
WindowManager::instance()->findWOAndFocus(0);
|
||
|
}
|
||
|
break;
|
||
|
case ACTION_WARP_TO_WORKSPACE:
|
||
|
actionWarpToWorkspace(decor, it->getParamI(0));
|
||
|
break;
|
||
|
default:
|
||
|
matched = false;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Actions valid from everywhere
|
||
|
if (! matched) {
|
||
|
matched = true;
|
||
|
switch (it->getAction()) {
|
||
|
case ACTION_SET:
|
||
|
case ACTION_UNSET:
|
||
|
case ACTION_TOGGLE:
|
||
|
handleStateAction(*it, wo, client, frame);
|
||
|
break;
|
||
|
case ACTION_NEXT_FRAME:
|
||
|
actionFocusToggle(ap.ae.sym, it->getParamI(0), 1 /* Direction */,
|
||
|
it->getParamI(1), false);
|
||
|
break;
|
||
|
case ACTION_NEXT_FRAME_MRU:
|
||
|
actionFocusToggle(ap.ae.sym, it->getParamI(0), 1 /* Direction */,
|
||
|
it->getParamI(1), true);
|
||
|
break;
|
||
|
case ACTION_PREV_FRAME:
|
||
|
actionFocusToggle(ap.ae.sym, it->getParamI(0), -1 /* Direction */,
|
||
|
it->getParamI(1), false);
|
||
|
break;
|
||
|
case ACTION_PREV_FRAME_MRU:
|
||
|
actionFocusToggle(ap.ae.sym, it->getParamI(0), -1 /* Direction */,
|
||
|
it->getParamI(1), true);
|
||
|
break;
|
||
|
case ACTION_GOTO_WORKSPACE:
|
||
|
// Events caused by a motion event ( dragging frame to
|
||
|
// the edge ) or enter event ( moving the pointer to
|
||
|
// the edge ) should warp the pointer.
|
||
|
actionGotoWorkspace(it->getParamI(0), (ap.type == MotionNotify) || (ap.type == EnterNotify));
|
||
|
break;
|
||
|
case ACTION_FIND_CLIENT:
|
||
|
actionFindClient(Util::to_wide_str(it->getParamS()));
|
||
|
break;
|
||
|
case ACTION_GOTO_CLIENT_ID:
|
||
|
actionGotoClientID(it->getParamI(0));
|
||
|
break;
|
||
|
case ACTION_EXEC:
|
||
|
actionExec(client, it->getParamS());
|
||
|
break;
|
||
|
case ACTION_SHOW_MENU:
|
||
|
actionShowMenu(it->getParamS(), it->getParamI(0),
|
||
|
ap.type, client ? client : wo);
|
||
|
break;
|
||
|
case ACTION_HIDE_ALL_MENUS:
|
||
|
MenuHandler::instance()->hideAllMenus();
|
||
|
break;
|
||
|
case ACTION_RELOAD:
|
||
|
WindowManager::instance()->reload();
|
||
|
break;
|
||
|
case ACTION_RESTART:
|
||
|
WindowManager::instance()->restart();
|
||
|
break;
|
||
|
case ACTION_RESTART_OTHER:
|
||
|
if (it->getParamS().size())
|
||
|
WindowManager::instance()->restart(it->getParamS());
|
||
|
break;
|
||
|
case ACTION_EXIT:
|
||
|
WindowManager::instance()->shutdown();
|
||
|
break;
|
||
|
case ACTION_SHOW_CMD_DIALOG:
|
||
|
if (WindowManager::instance()->getCmdDialog()->isMapped()) {
|
||
|
WindowManager::instance()->getCmdDialog()->unmapWindow();
|
||
|
} else {
|
||
|
WindowManager::instance()->getCmdDialog()->mapCentered(it->getParamS(), true,
|
||
|
frame ? frame : wo);
|
||
|
}
|
||
|
break;
|
||
|
case ACTION_SHOW_SEARCH_DIALOG:
|
||
|
if (WindowManager::instance()->getSearchDialog()->isMapped()) {
|
||
|
WindowManager::instance()->getSearchDialog()->unmapWindow();
|
||
|
} else {
|
||
|
WindowManager::instance()->getSearchDialog()->mapCentered(it->getParamS(), true, frame ? frame : wo);
|
||
|
}
|
||
|
break;
|
||
|
case ACTION_HIDE_WORKSPACE_INDICATOR:
|
||
|
WindowManager::instance()->getWorkspaceIndicator()->unmapWindow();
|
||
|
break;
|
||
|
default:
|
||
|
matched = false;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
ActionHandler::lookupWindowObjects(PWinObj **wo, Client **client, Frame **frame,
|
||
|
PMenu **menu, PDecor **decor)
|
||
|
{
|
||
|
*client = 0;
|
||
|
*frame = 0;
|
||
|
*menu = 0;
|
||
|
*decor = 0;
|
||
|
|
||
|
if (PWinObj::windowObjectExists(*wo)) {
|
||
|
if ((*wo)->getType() == PWinObj::WO_CLIENT) {
|
||
|
*client = static_cast<Client*>(*wo);
|
||
|
*frame = static_cast<Frame*>((*client)->getParent());
|
||
|
*decor = static_cast<PDecor*>(*frame);
|
||
|
} else if ((*wo)->getType() == PWinObj::WO_FRAME) {
|
||
|
*frame = static_cast<Frame*>(*wo);
|
||
|
*client = static_cast<Client*>((*frame)->getActiveChild());
|
||
|
*decor = static_cast<PDecor*>(*wo);
|
||
|
} else if ((*wo)->getType() == PWinObj::WO_MENU) {
|
||
|
*menu = static_cast<PMenu*>(*wo);
|
||
|
*decor = static_cast<PDecor*>(*wo);
|
||
|
} else {
|
||
|
*decor = dynamic_cast<PDecor*>(*wo);
|
||
|
}
|
||
|
} else {
|
||
|
*wo = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//! @brief Handles state actions
|
||
|
void
|
||
|
ActionHandler::handleStateAction(const Action &action, PWinObj *wo,
|
||
|
Client *client, Frame *frame)
|
||
|
{
|
||
|
StateAction sa = static_cast<StateAction>(action.getAction()); // convenience
|
||
|
|
||
|
bool matched = false;
|
||
|
|
||
|
// check for frame actions
|
||
|
if (! matched && frame) {
|
||
|
matched = true;
|
||
|
switch (action.getParamI(0)) {
|
||
|
case ACTION_STATE_MAXIMIZED:
|
||
|
frame->setStateMaximized(sa, action.getParamI(1),
|
||
|
action.getParamI(2), false);
|
||
|
break;
|
||
|
case ACTION_STATE_FULLSCREEN:
|
||
|
frame->setStateFullscreen(sa);
|
||
|
break;
|
||
|
case ACTION_STATE_SHADED:
|
||
|
frame->setShaded(sa);
|
||
|
break;
|
||
|
case ACTION_STATE_STICKY:
|
||
|
frame->setStateSticky(sa);
|
||
|
break;
|
||
|
case ACTION_STATE_ALWAYS_ONTOP:
|
||
|
frame->setStateAlwaysOnTop(sa);
|
||
|
break;
|
||
|
case ACTION_STATE_ALWAYS_BELOW:
|
||
|
frame->setStateAlwaysBelow(sa);
|
||
|
break;
|
||
|
case ACTION_STATE_DECOR_BORDER:
|
||
|
frame->setStateDecorBorder(sa);
|
||
|
break;
|
||
|
case ACTION_STATE_DECOR_TITLEBAR:
|
||
|
frame->setStateDecorTitlebar(sa);
|
||
|
break;
|
||
|
case ACTION_STATE_ICONIFIED:
|
||
|
frame->setStateIconified(sa);
|
||
|
break;
|
||
|
case ACTION_STATE_TAGGED:
|
||
|
frame->setStateTagged(sa, action.getParamI(1));
|
||
|
break;
|
||
|
case ACTION_STATE_MARKED:
|
||
|
frame->setStateMarked(sa, client);
|
||
|
break;
|
||
|
case ACTION_STATE_SKIP:
|
||
|
frame->setStateSkip(sa, action.getParamI(1));
|
||
|
break;
|
||
|
case ACTION_STATE_CFG_DENY:
|
||
|
client->setStateCfgDeny(sa, action.getParamI(1));
|
||
|
break;
|
||
|
case ACTION_STATE_DECOR:
|
||
|
frame->setDecorOverride(sa, action.getParamS());
|
||
|
break;
|
||
|
case ACTION_STATE_TITLE:
|
||
|
frame->setStateTitle(sa, client, Util::to_wide_str(action.getParamS()));
|
||
|
break;
|
||
|
#ifdef OPACITY
|
||
|
case ACTION_STATE_OPAQUE:
|
||
|
frame->setStateOpaque(sa);
|
||
|
break;
|
||
|
#endif // OPACITY
|
||
|
default:
|
||
|
matched = false;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// check for menu actions
|
||
|
if (! matched && wo &&
|
||
|
(wo->getType() == PWinObj::WO_MENU)) {
|
||
|
matched = true;
|
||
|
switch (action.getParamI(0)) {
|
||
|
case ACTION_STATE_STICKY:
|
||
|
wo->stick();
|
||
|
break;
|
||
|
default:
|
||
|
matched = false;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (! matched) {
|
||
|
matched = true;
|
||
|
switch (action.getParamI(0)) {
|
||
|
case ACTION_STATE_HARBOUR_HIDDEN:
|
||
|
WindowManager::instance()->getHarbour()->setStateHidden(sa);
|
||
|
break;
|
||
|
case ACTION_STATE_GLOBAL_GROUPING:
|
||
|
WindowManager::instance()->setStateGlobalGrouping(sa);
|
||
|
break;
|
||
|
default:
|
||
|
matched = false;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//! @brief Checks if motion threshold is within bounds.
|
||
|
bool
|
||
|
ActionHandler::checkAEThreshold(int x, int y, int x_t, int y_t, uint t)
|
||
|
{
|
||
|
if (((x > x_t) ? (x > (x_t + signed(t))) : (x < (x_t - signed(t)))) ||
|
||
|
((y > y_t) ? (y > (y_t + signed(t))) : (y < (y_t - signed(t))))) {
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//! @brief Searches the actions list for an matching event
|
||
|
ActionEvent*
|
||
|
ActionHandler::findMouseAction(uint button, uint state, MouseEventType type, std::list<ActionEvent> *actions)
|
||
|
{
|
||
|
if (! actions) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
PScreen::stripStateModifiers(&state);
|
||
|
PScreen::stripButtonModifiers(&state);
|
||
|
|
||
|
list<ActionEvent>::iterator it(actions->begin());
|
||
|
for (; it != actions->end(); ++it) {
|
||
|
if ((it->type == unsigned(type))
|
||
|
&& ((it->mod == MOD_ANY) || (it->mod == state))
|
||
|
&& ((it->sym == BUTTON_ANY) || (it->sym == button))) {
|
||
|
return &*it;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Execute action, setting client environment before (if any).
|
||
|
*/
|
||
|
void
|
||
|
ActionHandler::actionExec(Client *client, const std::string &command)
|
||
|
{
|
||
|
if (command.size()) {
|
||
|
Client::setClientEnvironment(client);
|
||
|
Util::forkExec(command);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//! @brief Searches for a client matching titles and makes it visible
|
||
|
void
|
||
|
ActionHandler::actionFindClient(const std::wstring &title)
|
||
|
{
|
||
|
Client *client = findClientFromTitle(title);
|
||
|
if (client) {
|
||
|
gotoClient(client);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//! @brief Goto workspace
|
||
|
//! @param workspace Workspace to got to
|
||
|
//! @param warp If true, warp pointer as well
|
||
|
void
|
||
|
ActionHandler::actionGotoWorkspace(uint workspace, bool warp)
|
||
|
{
|
||
|
Workspaces::instance()->gotoWorkspace(workspace, warp);
|
||
|
}
|
||
|
|
||
|
//! @brief Focus client with id.
|
||
|
//! @param id Client id.
|
||
|
void
|
||
|
ActionHandler::actionGotoClientID(uint id)
|
||
|
{
|
||
|
Client *client = Client::findClientFromID(id);
|
||
|
if (client) {
|
||
|
gotoClient(client);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//! @brief Sends client to specified workspace
|
||
|
//! @param direction Workspace to send to, or special meaning relative space.
|
||
|
void
|
||
|
ActionHandler::actionSendToWorkspace(PDecor *decor, int direction)
|
||
|
{
|
||
|
// Convenience
|
||
|
Workspaces *ws = Workspaces::instance();
|
||
|
uint per_row = Config::instance()->getWorkspacesPerRow();
|
||
|
uint cur_row = ws->getRow(), row_min = ws->getRowMin(), row_max = ws->getRowMax();
|
||
|
|
||
|
switch (direction) {
|
||
|
case WORKSPACE_LEFT:
|
||
|
case WORKSPACE_PREV:
|
||
|
if (ws->getActive() > row_min) {
|
||
|
decor->setWorkspace(ws->getActive() - 1);
|
||
|
} else if (static_cast<uint>(direction) == WORKSPACE_PREV) {
|
||
|
decor->setWorkspace(row_max);
|
||
|
}
|
||
|
break;
|
||
|
case WORKSPACE_NEXT:
|
||
|
case WORKSPACE_RIGHT:
|
||
|
if (ws->getActive() < row_max) {
|
||
|
decor->setWorkspace(ws->getActive() + 1);
|
||
|
} else if (static_cast<uint>(direction) == WORKSPACE_NEXT) {
|
||
|
decor->setWorkspace(row_min);
|
||
|
}
|
||
|
break;
|
||
|
case WORKSPACE_PREV_V:
|
||
|
case WORKSPACE_UP:
|
||
|
if (ws->getActive() >= per_row) {
|
||
|
decor->setWorkspace(ws->getActive() - per_row);
|
||
|
} else if (static_cast<uint>(direction) == WORKSPACE_PREV_V) {
|
||
|
// Bottom left + column
|
||
|
decor->setWorkspace(ws->size() - per_row
|
||
|
+ ws->getActive() - cur_row * per_row);
|
||
|
}
|
||
|
break;
|
||
|
case WORKSPACE_NEXT_V:
|
||
|
case WORKSPACE_DOWN:
|
||
|
if ((ws->getActive() + per_row) < ws->size()) {
|
||
|
decor->setWorkspace(ws->getActive() + per_row);
|
||
|
} else if (static_cast<uint>(direction) == WORKSPACE_NEXT_V) {
|
||
|
decor->setWorkspace(ws->getActive() - cur_row * per_row);
|
||
|
}
|
||
|
break;
|
||
|
case WORKSPACE_LAST:
|
||
|
decor->setWorkspace(ws->getPrevious());
|
||
|
break;
|
||
|
default:
|
||
|
decor->setWorkspace(direction);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//! @brief
|
||
|
void
|
||
|
ActionHandler::actionWarpToWorkspace(PDecor *decor, uint direction)
|
||
|
{
|
||
|
// Removing the already accumulated motion events can help
|
||
|
// to avoid skipping workspaces (see task #77).
|
||
|
PScreen::removeMotionEvents();
|
||
|
|
||
|
// actually did move
|
||
|
if (Workspaces::instance()->gotoWorkspace(DirectionType(direction), true)) {
|
||
|
int x, y;
|
||
|
PScreen::instance()->getMousePosition(x, y);
|
||
|
|
||
|
decor->move(decor->getClickX() + x - decor->getPointerX(),
|
||
|
decor->getClickY() + y - decor->getPointerY());
|
||
|
decor->setWorkspace(Workspaces::instance()->getActive());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//! @brief Tries to find the next/prev frame relative to the focused client
|
||
|
void
|
||
|
ActionHandler::actionFocusToggle(uint button, uint raise, int off,
|
||
|
bool show_iconified, bool mru)
|
||
|
{
|
||
|
PMenu *p_menu;
|
||
|
|
||
|
if (mru) {
|
||
|
p_menu = createMRUMenu(show_iconified);
|
||
|
} else {
|
||
|
p_menu = createNextPrevMenu(show_iconified);
|
||
|
}
|
||
|
|
||
|
auto_ptr<PMenu> menu(p_menu);
|
||
|
|
||
|
// no clients in the list
|
||
|
if (menu->size() == 0) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// unable to grab keyboard
|
||
|
if (! PScreen::instance()->grabKeyboard(PScreen::instance()->getRoot())) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// find the focused window object
|
||
|
PWinObj *fo_wo = 0;
|
||
|
if (PWinObj::isFocusedPWinObj(PWinObj::WO_CLIENT)) {
|
||
|
fo_wo = PWinObj::getFocusedPWinObj()->getParent();
|
||
|
|
||
|
list<PMenu::Item*>::iterator it(menu->m_begin());
|
||
|
for (; it != menu->m_end(); ++it) {
|
||
|
if ((*it)->getWORef() == fo_wo) {
|
||
|
menu->selectItem(it);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
fo_wo->setFocused(false);
|
||
|
}
|
||
|
|
||
|
if (Config::instance()->getShowFrameList()) {
|
||
|
menu->buildMenu();
|
||
|
|
||
|
Geometry head;
|
||
|
PScreen::instance()->getHeadInfo(PScreen::instance()->getCurrHead(), head);
|
||
|
menu->move(head.x + ((head.width - menu->getWidth()) / 2),
|
||
|
head.y + ((head.height - menu->getHeight()) / 2));
|
||
|
menu->setFocused(true);
|
||
|
menu->mapWindowRaised();
|
||
|
}
|
||
|
|
||
|
menu->selectItemRel(off);
|
||
|
fo_wo = menu->getItemCurr()->getWORef();
|
||
|
|
||
|
XEvent ev;
|
||
|
bool cycling = true;
|
||
|
bool was_iconified = false;
|
||
|
|
||
|
while (cycling) {
|
||
|
if (fo_wo) {
|
||
|
fo_wo->setFocused(true);
|
||
|
if (Raise(raise) == ALWAYS_RAISE) {
|
||
|
// Make sure it's not iconified if raise is on.
|
||
|
if (fo_wo->isIconified()) {
|
||
|
was_iconified = true;
|
||
|
fo_wo->mapWindow();
|
||
|
}
|
||
|
fo_wo->raise();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
XMaskEvent(PScreen::instance()->getDpy(), KeyPressMask|KeyReleaseMask, &ev);
|
||
|
if (ev.type == KeyPress) {
|
||
|
if (ev.xkey.keycode == button) {
|
||
|
if (fo_wo) {
|
||
|
// Restore iconified state
|
||
|
if (was_iconified) {
|
||
|
was_iconified = false;
|
||
|
fo_wo->iconify();
|
||
|
}
|
||
|
fo_wo->setFocused(false);
|
||
|
}
|
||
|
|
||
|
menu->selectItemRel(off);
|
||
|
fo_wo = menu->getItemCurr()->getWORef();
|
||
|
} else {
|
||
|
XPutBackEvent (PScreen::instance()->getDpy(), &ev);
|
||
|
cycling = false;
|
||
|
}
|
||
|
} else if (ev.type == KeyRelease) {
|
||
|
if (IsModifierKey(XKeycodeToKeysym(PScreen::instance()->getDpy(),
|
||
|
ev.xkey.keycode, 0))) {
|
||
|
cycling = false;
|
||
|
}
|
||
|
} else {
|
||
|
XPutBackEvent(PScreen::instance()->getDpy(), &ev);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
PScreen::instance()->ungrabKeyboard();
|
||
|
|
||
|
// Got something to focus
|
||
|
if (fo_wo) {
|
||
|
// De-iconify if iconified, user probably wants this
|
||
|
if (fo_wo->isIconified()) {
|
||
|
// If the window was iconfied, and sticky
|
||
|
fo_wo->setWorkspace(Workspaces::instance()->getActive());
|
||
|
fo_wo->mapWindow();
|
||
|
fo_wo->raise();
|
||
|
} else if (Raise(raise) == END_RAISE) {
|
||
|
fo_wo->raise();
|
||
|
}
|
||
|
|
||
|
// Give focus
|
||
|
fo_wo->giveInputFocus();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//! @brief Finds a window in direction and gives it focus
|
||
|
void
|
||
|
ActionHandler::actionFocusDirectional(PWinObj *wo, DirectionType dir, bool raise)
|
||
|
{
|
||
|
PWinObj *wo_focus =
|
||
|
Workspaces::instance()->findDirectional(wo, dir, SKIP_FOCUS_TOGGLE);
|
||
|
|
||
|
if (wo_focus) {
|
||
|
if (raise) {
|
||
|
wo_focus->raise();
|
||
|
}
|
||
|
wo_focus->giveInputFocus();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Parse key information and send to wo.
|
||
|
*
|
||
|
* To handle this smoothly first all state modifiers are pressed one
|
||
|
* by one adding to the state, then the real key is pressed and
|
||
|
* released, then the state modifiers are released.
|
||
|
*
|
||
|
* @param win Window to send key to.
|
||
|
* @param key_str String definition of key to send.
|
||
|
* @return true if key was parsed ok, no validation of sending is done.
|
||
|
*/
|
||
|
bool
|
||
|
ActionHandler::actionSendKey(PWinObj *wo, const std::string &key_str)
|
||
|
{
|
||
|
uint mod, key;
|
||
|
if (! Config::instance()->parseKey(key_str, mod, key)) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
XEvent ev;
|
||
|
initSendKeyEvent(ev, wo);
|
||
|
|
||
|
// Press state modifiers
|
||
|
map<uint, uint>::iterator it;
|
||
|
for (it = _state_to_keycode.begin(); it != _state_to_keycode.end(); ++it) {
|
||
|
if (mod & it->first) {
|
||
|
ev.xkey.keycode = it->second;
|
||
|
XSendEvent(PScreen::instance()->getDpy(), wo->getWindow(), True, KeyPressMask, &ev);
|
||
|
ev.xkey.state |= it->first;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Send press and release of main key
|
||
|
ev.xkey.keycode = key;
|
||
|
XSendEvent(PScreen::instance()->getDpy(), wo->getWindow(), True, KeyPressMask, &ev);
|
||
|
ev.type = KeyRelease;
|
||
|
XSendEvent(PScreen::instance()->getDpy(), wo->getWindow(), True, KeyPressMask, &ev);
|
||
|
|
||
|
// Release state modifiers
|
||
|
for (it = _state_to_keycode.begin(); it != _state_to_keycode.end(); ++it) {
|
||
|
if (mod & it->first) {
|
||
|
ev.xkey.keycode = it->second;
|
||
|
XSendEvent(PScreen::instance()->getDpy(), wo->getWindow(), True, KeyPressMask, &ev);
|
||
|
ev.xkey.state &= ~it->first;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
#ifdef OPACITY
|
||
|
void
|
||
|
ActionHandler::actionSetOpacity(PWinObj *client, PWinObj *frame, uint focus, uint unfocus)
|
||
|
{
|
||
|
if (! unfocus) {
|
||
|
unfocus = focus;
|
||
|
}
|
||
|
CONV_OPACITY(focus);
|
||
|
CONV_OPACITY(unfocus);
|
||
|
client->setOpacity(focus, unfocus);
|
||
|
frame->setOpacity(client);
|
||
|
}
|
||
|
#endif // OPACITY
|
||
|
|
||
|
//! @brief Toggles visibility of menu.
|
||
|
//! @param name Name of menu to toggle visibilty of
|
||
|
//! @param stick Stick menu when showing
|
||
|
//! @param e_type Event type triggered this action
|
||
|
//! @param wo_ref Reference to active window object
|
||
|
void
|
||
|
ActionHandler::actionShowMenu(const std::string &name, bool stick,
|
||
|
uint e_type, PWinObj *wo_ref)
|
||
|
{
|
||
|
PMenu *menu = MenuHandler::instance()->getMenu(name);
|
||
|
if (! menu) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (menu->isMapped()) {
|
||
|
menu->unmapAll();
|
||
|
|
||
|
} else {
|
||
|
// if it's a WORefMenu, set referencing client
|
||
|
WORefMenu *wo_ref_menu = dynamic_cast<WORefMenu*>(menu);
|
||
|
if (wo_ref_menu
|
||
|
// Don't set reference on these, we don't want a funky title
|
||
|
&& (wo_ref_menu->getMenuType() != ROOTMENU_TYPE)
|
||
|
&& (wo_ref_menu->getMenuType() != ROOTMENU_STANDALONE_TYPE)) {
|
||
|
wo_ref_menu->setWORef(wo_ref);
|
||
|
}
|
||
|
|
||
|
// mapping can fail because of empty menu, like iconmenu, so we check
|
||
|
menu->mapUnderMouse();
|
||
|
if (menu->isMapped()) {
|
||
|
menu->giveInputFocus();
|
||
|
if (stick) {
|
||
|
menu->setSticky(true);
|
||
|
}
|
||
|
|
||
|
// if we opened the menu with the keyboard, select item 0
|
||
|
if (e_type == KeyPress) {
|
||
|
menu->selectItemNum(0);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//! @brief Creates a menu containing a list of Frames currently visible
|
||
|
//! @param show_iconified Flag to show/hide iconified windows
|
||
|
PMenu*
|
||
|
ActionHandler::createNextPrevMenu(bool show_iconified)
|
||
|
{
|
||
|
ActionEvent ae; // empty ae, used when inserting
|
||
|
PMenu *menu =
|
||
|
new PMenu(WindowManager::instance()->getTheme(), L"Windows",
|
||
|
"" /* Empty name*/);
|
||
|
|
||
|
Frame *fr;
|
||
|
list<Frame*>::iterator f_it(Frame::frame_begin());
|
||
|
for (; f_it != Frame::frame_end(); ++f_it) {
|
||
|
fr = static_cast<Frame*>(*f_it);
|
||
|
if (createMenuInclude(fr, show_iconified)) {
|
||
|
menu->insert(static_cast<Client*>(fr->getActiveChild())->getTitle()->getVisible(),
|
||
|
ae, fr, static_cast<Client*>(fr->getActiveChild())->getIcon());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return menu;
|
||
|
}
|
||
|
|
||
|
//! @brief Creates a menu containing a list of Frames currently visible ( MRU order )
|
||
|
//! @param show_iconified Flag to show/hide iconified windows
|
||
|
PMenu*
|
||
|
ActionHandler::createMRUMenu(bool show_iconified)
|
||
|
{
|
||
|
ActionEvent ae; // empty ae, used when inserting
|
||
|
PMenu *menu = new PMenu(WindowManager::instance()->getTheme(),
|
||
|
L"MRU Windows", "" /* Empty name */);
|
||
|
|
||
|
Frame *fr;
|
||
|
list<PWinObj*>::reverse_iterator f_it = WindowManager::instance()->mru_rbegin();
|
||
|
for (; f_it != WindowManager::instance()->mru_rend(); ++f_it) {
|
||
|
fr = static_cast<Frame*>(*f_it);
|
||
|
if (createMenuInclude(fr, show_iconified)) {
|
||
|
menu->insert(static_cast<Client*>(fr->getActiveChild())->getTitle()->getVisible(),
|
||
|
ae, fr, static_cast<Client*>(fr->getActiveChild())->getIcon());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return menu;
|
||
|
}
|
||
|
|
||
|
//! @brief Helper to decide wheter or not to include Frame in menu
|
||
|
//! @param frame Frame to check
|
||
|
//! @param show_iconified Wheter or not to include iconified windows
|
||
|
//! @return true if it should be included, else false
|
||
|
bool
|
||
|
ActionHandler::createMenuInclude(Frame *frame, bool show_iconified)
|
||
|
{
|
||
|
// Make sure the frame is mapped, or on the correct workspace if
|
||
|
// it's iconified. Also make sure it's possible to give it focus
|
||
|
// and should not be skipped. The condition is rather complex, so
|
||
|
// we split it up for readability.
|
||
|
|
||
|
// focw == frame on current workspace
|
||
|
bool focw = frame->isSticky() || frame->getWorkspace() == Workspaces::instance()->getActive();
|
||
|
|
||
|
// ibs == iconified but should be shown
|
||
|
bool ibs = show_iconified && frame->isIconified() && focw;
|
||
|
|
||
|
// mos == mapped or shown nonetheless
|
||
|
bool mos = frame->isMapped() || ibs;
|
||
|
|
||
|
return ! frame->isSkip(SKIP_FOCUS_TOGGLE) && frame->isFocusable() && mos;
|
||
|
}
|
||
|
|
||
|
//! @brief Searches the client list for a client with a title matching title
|
||
|
Client*
|
||
|
ActionHandler::findClientFromTitle(const std::wstring &or_title)
|
||
|
{
|
||
|
RegexString o_rs;
|
||
|
|
||
|
if (o_rs.parse_match(or_title, true)) {
|
||
|
list<Client*>::iterator it (Client::client_begin());
|
||
|
for (; it != Client::client_end (); ++it) {
|
||
|
if (o_rs == (*it)->getTitle()->getVisible()) {
|
||
|
return (*it);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//! @brief Makes sure Client gets visible and focus it.
|
||
|
//! @param client Client to activate.
|
||
|
void
|
||
|
ActionHandler::gotoClient(Client *client)
|
||
|
{
|
||
|
Frame *frame = dynamic_cast<Frame*>(client->getParent());
|
||
|
if (! frame) {
|
||
|
#ifdef DEBUG
|
||
|
cerr << __FILE__ << "@" << __LINE__ << ": "
|
||
|
<< "ActionHandler(" << this << ")::gotoClient(" << client << ")"
|
||
|
<< endl << " *** parent is not a Frame!" << endl;
|
||
|
#endif // DEBUG
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Make sure it's visible
|
||
|
if (! frame->isSticky()
|
||
|
&& (frame->getWorkspace() != Workspaces::instance()->getActive())) {
|
||
|
Workspaces::instance()->setWorkspace(frame->getWorkspace(), false);
|
||
|
}
|
||
|
|
||
|
if (! frame->isMapped()) {
|
||
|
frame->mapWindow();
|
||
|
}
|
||
|
|
||
|
// Activate it within the Frame.
|
||
|
frame->activateChild(client);
|
||
|
frame->raise();
|
||
|
frame->giveInputFocus();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Initilalize XEvent for sending KeyPress/KeyRelease events.
|
||
|
*
|
||
|
* @param ev Reference to event.
|
||
|
* @param wo PWinObj key event is aimed for.
|
||
|
*/
|
||
|
void
|
||
|
ActionHandler::initSendKeyEvent(XEvent &ev, PWinObj *wo)
|
||
|
{
|
||
|
ev.type = KeyPress;
|
||
|
ev.xkey.display = PScreen::instance()->getDpy();
|
||
|
ev.xkey.root = PScreen::instance()->getRoot();
|
||
|
ev.xkey.window = wo->getWindow();
|
||
|
ev.xkey.time = CurrentTime;
|
||
|
ev.xkey.x = 1;
|
||
|
ev.xkey.y = 1;
|
||
|
PScreen::instance()->getMousePosition(ev.xkey.x_root, ev.xkey.y_root);
|
||
|
ev.xkey.same_screen = True;
|
||
|
ev.xkey.type = KeyPress;
|
||
|
ev.xkey.state = 0;
|
||
|
}
|