# $Id: macros.py,v 1.26 2003/12/26 02:52:04 pervago Exp $
import kahakai
from events import *
from events import _modifiers
from actions import *
from actions import _processFunction,init,finish
###### directional focusing
def directionalFocus(currentWindow, event, action):
_directionalSearchAndFocus(currentWindow, action.param)
directionalFocusOnlyVisibleToggle = True
def _directionalSearchAndFocus(currentWindow, direction):
# thanks to fvwm and tore for this code
if currentWindow.id == screen().id: return #root window
bestScore = -1
bestClient = None
diagonals = ( "UpRight", "UpLeft", "DownRight", "DownLeft" )
verticals = ( "Up", "Down", "UpRight", "DownLeft" )
horizontals = ( "Right", "Left", "DownRight", "UpLeft" )
if not direction in verticals + horizontals + diagonals:
raise ValueError, "%s not valid direction" % direction
# c is for center
my_cx = currentWindow.GetFrameLeft() + (currentWindow.GetFrameWidth() / 2)
my_cy = currentWindow.GetFrameTop() + (currentWindow.GetFrameHeight() / 2)
for win in screen().kawindow_list:
if win.id == currentWindow.id: continue # we want a *different* window
# don't display windows not on screen
if directionalFocusOnlyVisibleToggle and not win.IsVisible(): continue
if not win.OnCurrentDesktop(): continue
if not win.flags.tasklist: continue # skip windows in the dock, etc
# center of $win, relative to current window's center
his_cx = (win.GetFrameLeft() - my_cx) + (win.GetFrameWidth() / 2)
his_cy = (win.GetFrameTop() - my_cy) + (win.GetFrameHeight() / 2)
if direction in diagonals:
# rotate 45 degrees
tx = his_cx + his_cy
his_cy = -his_cx + his_cy
his_cx = tx
if direction in verticals:
offset = abs(his_cx)
distance = direction.find("Up") > -1 and -his_cy or his_cy
elif direction in horizontals:
offset = abs(his_cy)
distance = direction.find("Left") > -1 and -his_cx or his_cx
if distance <= 0: continue # target must be in the requested direction
# Calculate score for this window. The smaller the better.
score = distance + offset
# windows more than 45 degrees off the direction are heavily penalized
# and will only be chosen if nothing else is within a million pixels
if offset > distance: score += 1000000
if bestScore == -1 or score < bestScore:
bestClient = win
bestScore = score
if bestClient: bestClient.FocusVis()
###### end directional focusing
###### directional snapping/growing/shrinking
def directionalSnapToEdge(currentWindow, event, action):
if currentWindow.id == screen().id: return #root window
direction = action.param
edgeFound = directionalEdgeSearch(currentWindow, direction)
if direction == "Up": currentWindow.SetFrameTop(edgeFound)
elif direction == "Down": currentWindow.SetFrameBottom(edgeFound)
elif direction == "Left": currentWindow.SetFrameLeft(edgeFound)
elif direction == "Right": currentWindow.SetFrameRight(edgeFound)
currentWindow.RedrawWindow()
def directionalResizeToEdge(currentWindow, event, action):
if currentWindow.id == screen().id: return #root window
direction = action.param
directions = ( "Up", "Down", "Left", "Right" )
horizontals = ( "Left", "Right" )
verticals = ( "Up", "Down" )
if not direction in directions:
raise ValueError, "`%s' not a valid direction" % direction
screenEdges = {
"Up": 0, "Down": screen().height, "Left": 0, "Right": screen().width }
windowEdges = {
"Up": currentWindow.GetFrameTop(),
"Down": currentWindow.GetFrameBottom(),
"Left": currentWindow.GetFrameLeft(),
"Right": currentWindow.GetFrameRight() }
windowEdge = windowEdges[direction]
screenEdge = screenEdges[direction]
# if we're not to the edge yet, grow
if windowEdge != screenEdge:
destination = directionalEdgeSearch(currentWindow, direction)
if direction in horizontals:
oldWidth = currentWindow.GetFrameWidth()
elif direction in verticals:
oldHeight = currentWindow.GetFrameHeight()
if direction == "Up":
currentWindow.SetFrameHeight(
oldHeight + currentWindow.GetFrameTop() - destination)
currentWindow.SetFrameTop(destination)
elif direction == "Down":
currentWindow.SetFrameHeight(
oldHeight + destination - currentWindow.GetFrameBottom())
currentWindow.SetFrameBottom(destination)
elif direction == "Left":
currentWindow.SetFrameWidth(
oldWidth + currentWindow.GetFrameLeft() - destination)
currentWindow.SetFrameLeft(destination)
elif direction == "Right":
currentWindow.SetFrameWidth(
oldWidth + destination - currentWindow.GetFrameRight())
currentWindow.SetFrameRight(destination)
# we are at the edge already, so shrink
else:
if direction == "Up":
destination = currentWindow.GetFrameHeight() / 2
currentWindow.SetFrameHeight(destination)
currentWindow.SetFrameBottom(destination)
elif direction == "Down":
destination = screen().height - (currentWindow.GetFrameHeight() /2)
currentWindow.SetFrameHeight(currentWindow.GetFrameHeight() / 2)
currentWindow.SetFrameTop(destination)
elif direction == "Left":
destination = currentWindow.GetFrameWidth() / 2
currentWindow.SetFrameWidth(destination)
currentWindow.SetFrameRight(destination)
elif direction == "Right":
destination = screen().width - (currentWindow.GetFrameWidth() / 2)
currentWindow.SetFrameWidth(currentWindow.GetFrameWidth() / 2)
currentWindow.SetFrameLeft(destination)
currentWindow.RedrawWindow()
def directionalEdgeSearch(currentWindow, direction):
sides = { "Up": ("Left", "Right"), "Down": ("Left", "Right"),
"Left": ("Top", "Bottom"), "Right": ("Top", "Bottom") }
edges = { "Up": "Top", "Down": "Bottom", "Left": "Left", "Right": "Right" }
theirEdges = { "Up":"Bottom", "Down":"Top", "Left":"Right", "Right":"Left" }
defaults = { "Up":0, "Down":screen().height, "Left":0, "Right":screen().width }
mySides = [ getattr(currentWindow, "GetFrame"+side)()
for side in sides[direction] ]
myEdge = getattr(currentWindow, "GetFrame"+edges[direction])()
closestEdge = defaults[direction]
for win in screen().kawindow_list:
hisSides = [
getattr(win, "GetFrame"+side)() for side in sides[direction] ]
hisEdge = getattr(win, "GetFrame"+theirEdges[direction])()
if direction in [ "Up", "Left" ]:
if myEdge < hisEdge + 1 or closestEdge > hisEdge: continue
elif direction in [ "Down", "Right" ]:
if myEdge > hisEdge - 1 or closestEdge < hisEdge: continue
if not (hisSides[1] < mySides[0] or hisSides[0] > mySides[1]):
closestEdge = hisEdge
return closestEdge
###### end
### helper macros
_Focus = kahakai.WindowEvFocus()
_Raise = kahakai.WindowEvRaise()
_Lower = kahakai.WindowEvLower()
_MenuFocus = kahakai.MenuEvFocus()
_MenuRaise = kahakai.MenuEvRaise()
_MenuLower = kahakai.MenuEvLower()
_WindowMenuRemapFocused = kahakai.WindowEvMenuRemapFocused()
_WindowMenuUnmap = kahakai.WindowEvMenuUnmap()
_ScreenMenuRemapFocused = kahakai.ScreenEvMenuRemapFocused()
_ScreenMenuUnmap = kahakai.ScreenEvMenuUnmap()
_Move = kahakai.WindowEvMove()
_MoveOpaque = kahakai.WindowEvMoveOpaque()
_SmartResize = kahakai.WindowEvSmartResize()
_SmartResizeOpaque = kahakai.WindowEvSmartResizeOpaque()
_EndMoveResize = _processFunction("EndMoveResize", "window")
_MenuEndMoveResize = _processFunction("EndMoveResize", "menu")
_NextItem = kahakai.MenuEvNextItem()
_UnmapTree = kahakai.MenuEvUnmapTree()
_UnmapSubmenus = kahakai.MenuEvUnmapSubmenus()
_RemapSubmenuFocused = kahakai.MenuEvRemapSubmenuFocused()
_Exec = kahakai.MenuEvExec()
_Func = kahakai.MenuEvFunc()
def clickFocus(passthru=True):
allWindowActions( [ (ButtonPress('Button1', replay=passthru), _Focus) ] )
def sloppyFocus(delay=0):
windowActions('window.frame',
[ (EnterNotify(delay=delay), _Focus) ])
def mouseFocus():
sloppyFocus()
screenActions( [ (EnterNotify(), _Focus) ] )
def focusNewWindows():
windowActions('window.passiveclient', [ (MapRequest(), _Focus) ])
def wheelStacking(raiseButton=5, lowerButton=4, modifier=Alt):
raiseButton = "Button%d" % raiseButton
lowerButton = "Button%d" % lowerButton
allWindowActions([ (ButtonPress(lowerButton), _Lower),
(ButtonPress(raiseButton), _Raise) ], ["title","label"])
allWindowActions([ (ButtonPress(modifier, lowerButton), _Lower),
(ButtonPress(modifier, raiseButton), _Raise) ])
def clickToRaise(passthru=True):
allWindowActions([ (ButtonPress('Button1', replay=passthru), _Raise) ])
def mouseRaise(delay=0):
"""
Raises window when mouse passes over them.
"""
windowActions('window.frame', [ (EnterNotify(delay=delay), _Raise) ])
_edgeActions = {
'n': 'MoveViewportUp',
's': 'MoveViewportDown',
'e': 'MoveViewportRight',
'w': 'MoveViewportLeft'
}
def viewportSurf(modifier=-Ctrl+Alt, wrap=False):
"""
Installs MoveViewport{Up,Down,Left,Right} actions for edges, activated by
the specified modifier(s). This also allows you to drag windows around if
you're holding your appropriate modifier(s) while doing so.
"""
for edge, action in _edgeActions.items():
if wrap: action = action.replace("Viewport", "ViewportWrap")
edgeActions([(EnterNotify(modifier), action)], edge)
def viewportKnock(button=1, modifier=-Ctrl-Alt, wrap=False):
"""
Allows you to "knock" on screen edges with a mouse button to flip
viewports.
"""
for edge, action in _edgeActions.items():
if wrap: action = action.replace("Viewport", "ViewportWrap")
edgeActions([(ButtonPress(modifier, "Button%d"%button), action)], edge)
def windowSnapping(pixels=15):
"""
Enables window snapping with specified snap distance.
"""
screen().config.snapping = True
screen().config.snapDistance = pixels
def desktopKeys(modifier=Ctrl+Alt-AltGr, keys="%d"):
"""
Keys for switching desktops. Parameter "modifier" should be the modifiers you
wish to use, keys should either be a string containing a %d so that the
desktop number will be filled in for each (for e.g. F1,F2,etc), or a string
containing a list of keys seperated by commas that will switch to each
respective viewport. This string-list of keys should have the same or more
keys than the number of desktops.
"""
numDesktops = len(screen().desktop_list)
if keys.find("%d") != -1:
ourKeys = [ keys % (int(n)+1) for n in range(numDesktops) ]
elif "," in keys:
ourKeys = keys.split(",")
if len(ourKeys) < numDesktops:
warn(("desktopKeys(): only %d keys defined, " +
"%d needed using defaults.") % (len(ourKeys), numDesktops))
ourKeys = map(str, range(numDesktops))
GoToDesktop = lambda i: _processFunction(("GoToDesktop", str(i)), "screen")
actions = [ (KeyPress(modifier, ourKeys[i]), GoToDesktop(i))
for i in range(numDesktops) ]
globalActions(actions)
def altTab(action="TaskSwitcher"):
"""
Installs an action for Alt+Tab. Default is TaskSwitcher, you can change
this by passing an argument of a different action.
"""
globalActions([ (KeyPress(-Ctrl-AltGr+Alt, "Tab"), action) ])
_b = lambda num: ButtonPress(-Ctrl-Alt, "Button%d"%num)
def rootMenuClick(buttons):
for button in buttons:
screenActions([ (_b(button), (_ScreenMenuRemapFocused, "rootmenu")) ])
def rootMenuUnmap(buttons):
for button in buttons:
screenActions([ (_b(button), (_ScreenMenuUnmap, "rootmenu")) ])
def windowMenuClick(buttons):
for button in buttons:
allWindowActions([ (_b(button), (_WindowMenuRemapFocused, "window")) ],
["label", "title", "handle", "leftgrip", "rightgrip"])
def windowMenuKeyPress(modifier, key):
allWindowActions([ (KeyPress(modifier, key),
(_WindowMenuRemapFocused, "window")) ])
def windowMenuUnmap(buttons):
for button in buttons:
allWindowActions([ (_b(button), (_WindowMenuUnmap, "window")) ],
["label", "title", "handle", "leftgrip", "rightgrip"])
def desktopWheelScroll(upaction="PreviousDesktop", downaction="NextDesktop", modifier=None):
if modifier:
up = (modifier, "Button4")
down = (modifier, "Button5")
else:
up = ("Button4",)
down = ("Button5",)
screenActions( [(ButtonPress(*up), upaction),
(ButtonPress(*down), downaction)] )
def smartWindowPlacement():
allWindowActions([ (MapRequest(), 'MoveWindowToSmartPlace') ])
def moveWindowWithModifier(modifier=Alt, opaque=True, button=1):
if opaque:
move = _MoveOpaque
else:
move = _Move
allWindowActions(
[ (ButtonPress(modifier, "Button%d"%button), move),
(ButtonRelease(MoveResizeMask, "Button%d"%button), _EndMoveResize) ])
def resizeWindowWithModifier(modifier=Alt, opaque=False, button=3, smart=True):
if opaque:
if smart: resize = _SmartResizeOpaque
else: resize = "ResizeRightOpaque"
else:
if smart: resize = _SmartResize
else: resize = "ResizeRight"
allWindowActions( [ (ButtonPress(modifier, "Button%d"%button), resize),
(ButtonRelease(MoveResizeMask, "Button%d"%button), _EndMoveResize) ])
def lowerWindowWithModifier(modifier=Alt, button=2):
allWindowActions([ (ButtonPress(modifier, "Button%d"%button), _Lower) ])
def moveByTitlebar(buttons=[1], opaque=True):
if opaque:
move = 'MoveOpaque'
else:
move = 'Move'
for button in buttons:
allWindowActions(
[ (_b(button), move),
(ButtonRelease(MoveResizeMask,'Button%d'%button),_EndMoveResize) ],
["label", "title", "handle"] )
def doubleClickTitlebarShade(buttons=[1]):
doubleClickTitlebarAction(buttons, -Ctrl-Alt, "ToggleShade")
def doubleClickTitlebarMaximize(buttons=[1]):
doubleClickTitlebarAction(buttons, -Ctrl-Alt, "ToggleMaximize")
def doubleClickTitlebarAction(buttons, modifier, action):
for button in buttons:
allWindowActions([ (DoubleClick(modifier, "Button%d"%button),
action) ], ["label", "title"] )
def resizeWithGrips(buttons=[1], opaque=False):
if opaque:
left = "ResizeLeftOpaque"
right = "ResizeRightOpaque"
else:
left = "ResizeLeft"
right = "ResizeRight"
for button in buttons:
windowActions("window.leftgrip", [ (_b(button), left ) ] )
windowActions("window.rightgrip", [ (_b(button), right) ] )
def shadeButton(buttons=[1]):
for button in buttons:
windowActions("window.button_shade",
[ (ButtonRelease(-Ctrl-Alt, 'Button%d'%button), 'ToggleShade') ] )
def minimizeButton(buttons=[1]):
for button in buttons:
windowActions("window.button_minimize",
[ (ButtonRelease(-Ctrl-Alt, 'Button%d'%button), 'ToggleMinimize')] )
def closeButton(buttons=[1]):
for button in buttons:
windowActions("window.button_close",
[ (ButtonRelease(-Ctrl-Alt, 'Button%d'%button), 'Close') ] )
def maximizeButton(both="Button1", horiz="Button3", vert="Button2"):
windowActions("window.button_maximize",
[ (ButtonRelease(-Ctrl-Alt, both ), 'ToggleMaximize'),
(ButtonRelease(-Ctrl-Alt, vert ), 'ToggleMaximizeVertically'),
(ButtonRelease(-Ctrl-Alt, horiz), 'ToggleMaximizeHorizontally') ] )
def vMaximizeButton(buttons=[1]):
for button in buttons:
windowActions("window.button_vmaximize",
[ (ButtonRelease(-Ctrl-Alt, "Button%d"%button), 'ToggleMaximizeVertically') ] )
def hMaximizeButton(buttons=[1]):
for button in buttons:
windowActions("window.button_hmaximize",
[ (ButtonRelease(-Ctrl-Alt, "Button%d"%button), 'ToggleMaximizeHorizontally') ] )
def windowButtons():
shadeButton()
minimizeButton()
closeButton()
maximizeButton()
hMaximizeButton()
vMaximizeButton()
def keyboardMenuNavigation(closeOnExec=True):
allMenuActions([ (KeyPress('Tab'), _NextItem),
(KeyPress('Down'), _NextItem),
(KeyPress('Up'), 'PreviousItem'),
(KeyPress('Left'), _UnmapSubmenus),
(KeyPress('Escape'), _UnmapTree),
(KeyPress('Return'), _Exec),
(KeyPress('Return'), _Func) ],
["item", "sub", "checkbox"])
if closeOnExec:
allMenuActions([ (KeyPress('Return'), _UnmapTree) ],
["item", "sub", "checkbox"])
allMenuActions([ (KeyPress('space'), _Exec),
(KeyPress('space'), _Func) ],
["checkbox"])
allMenuActions([ (KeyPress('Right'), _RemapSubmenuFocused),
(KeyPress('Return'), _RemapSubmenuFocused) ],
["sub"])
def mouseMenuNavigation(closeOnExec=True, moveWithModifier=Alt, closeButtons=[3]):
if moveWithModifier:
allMenuActions([
(ButtonPress(moveWithModifier, 'Button1'), _MenuRaise),
(ButtonPress(moveWithModifier, 'Button1'), 'MoveOpaque'),
(ButtonPress(moveWithModifier, 'Button1'), 'UnLinkMenu'),
(ButtonRelease(MoveResizeMask, 'Button1'), _MenuEndMoveResize)
])
for button in closeButtons:
allMenuActions([(ButtonPress(-Ctrl-Alt, "Button%d"%button),_UnmapTree)])
allMenuActions([ (ButtonPress('Button%d'%button), _UnmapSubmenus),
(ButtonPress('Button%d'%button), 'UnmapMenu') ],
["title"])
allMenuActions([ (ButtonPress(-Ctrl-Alt, 'Button1'), _Exec),
(ButtonPress(-Ctrl-Alt, 'Button1'), _Func) ],
["item", "sub", "checkbox"])
if closeOnExec:
allMenuActions([ (ButtonPress(-Ctrl-Alt, 'Button1'), _UnmapTree) ],
["item", "sub", "checkbox"])
allMenuActions([ (ButtonPress('Button1'), _MenuRaise),
(ButtonPress(-Alt, 'Button1'), 'UnLinkMenu'),
(ButtonPress(-Alt, 'Button1'), 'Move') ],
["title"])
allMenuActions([ (EnterNotify(), _UnmapSubmenus),
(ButtonRelease('AnyButton'), _UnmapTree) ],
["item"])
allMenuActions([ (EnterNotify(), _UnmapSubmenus) ], ["checkbox"])
allMenuActions([ (EnterNotify(), 'MapSubmenuOnly'),
(ButtonPress('AnyButton'), 'RemapSubmenu') ],
["sub"])
def ignoreModifiers(modifiers):
mods = []
for mod in modifiers:
if _modifiers.has_key(mod):
mods.append(_modifiers[mod])
# weird code that builds masks for all combinations of modifiers
# don't ask how.. don't ask why.. don't ask questions..
masks = []
for i in range(len(mods)):
masks.append(mods[i])
for j in mods[i:]:
if mods[i] | j not in masks:
masks.append(mods[i] | j)
for k in masks[:]:
if j | k not in masks:
masks.append(j|k)
for mask in masks:
screen().ImodAdd(mask)
CapsLock = "Caps_Lock"
NumLock = "Num_Lock"
def registerWindowFunction(name, function):
if not isinstance(function, kahakai.WindowFunction):
function = kahakai.createWindowFunction(function)
screen().kahakai.registerWindowFunction(name, function)
def registerScreenFunction(name, function):
if not isinstance(function, kahakai.ScreenFunction):
function = kahakai.createScreenFunction(function)
screen().kahakai.registerScreenFunction(name, function)
def registerMenuFunction(name, function):
if not isinstance(function, kahakai.MenuFunction):
function = kahakai.createMenuFunction(function)
screen().kahakai.registerMenuFunction(name, function)
|