all repos — openbox @ 6e29605d41cab8fd7e8ad6f28d6132bbd521f106

openbox fork - make it a bit more like ryudo

python/stackedcycle.py (raw)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
import ob, config, hooks, focus
from input import Pointer, Keyboard

config.add('stackedcycle',
           'activate_while_cycling',
           'Activate While Cycling',
           "If this is True then windows will be activated as they are" + \
           "highlighted in the cycling list (except iconified windows).",
           'boolean',
           True)

config.add('stackedcycle',
           'raise_window',
           'Raise After Cycling',
           "If this is True, the selected window will be raised as well as " +\
           "focused.",
           'boolean',
           True)

config.add('stackedcycle',
           'include_all_desktops',
           'Include Windows From All Desktops',
           "If this is True then windows from all desktops will be included" +\
           " in the stacking list.",
           'boolean',
           False)

config.add('stackedcycle',
           'include_icons',
           'Include Icons',
           "If this is True then windows which are iconified on the current" +\
           " desktop will be included in the stacking list.",
           'boolean',
           False)

config.add('stackedcycle',
           'include_icons_all_desktops',
           'Include Icons From All Desktops',
           "If this is True then windows which are iconified from all " +\
           "desktops will be included in the stacking list (if Include Icons"+\
           " is also True).",
           'boolean',
           True)

config.add('stackedcycle',
           'include_omnipresent',
           'Include Omnipresent Windows',
           "If this is True then windows which are on all-desktops at once " +\
           "will be included in the stacking list.",
           'boolean',
           True)

def next(keydata, client): _cycle(keydata, client, True)
def previous(keydata, client): _cycle(keydata, client, False)

def _shouldAdd(client):
    """Determines if a client should be added to the cycling list."""
    curdesk = ob.Openbox.desktop()
    desk = client.desktop()

    if not (client.normal() and client.canFocus()): return False
    if config.get('focus', 'avoid_skip_taskbar') and client.skipTaskbar():
        return False

    if client.iconic():
        if config.get('stackedcycle', 'include_icons'):
            if config.get('stackedcycle', 'include_icons_all_desktops'):
                return True
            if desk == curdesk: return True
        return False
    if config.get('stackedcycle', 'include_omnipresent') and \
       desk == 0xffffffff: return True
    if config.get('stackedcycle', 'include_all_desktops'): return True
    if desk == curdesk: return True

    return False

def _populateItems():
    global _items
    # get the list of clients, keeping iconic windows at the bottom
    _items = []
    iconic_clients = []
    for c in focus._clients:
        if _shouldAdd(c):
            if c.iconic(): iconic_clients.append(c)
            else: _items.append(c)
    _items.extend(iconic_clients)

def _populate():
    global _pos, _items
    try:
        current = _items[_pos]
    except IndexError: 
        current = None
    oldpos = _pos
    _pos = -1

    _populateItems()

    i = 0
    for item in _items:
        # current item might have shifted after a populateItems() 
        # call, so we need to do this test.
        if current == item:
            _pos = i
        i += 1

        # The item we were on might be gone entirely
        if _pos < 0:
            # try stay at the same spot in the menu
            if oldpos >= len(_items):
                _pos = len(_items) - 1
            else:
                _pos = oldpos


def _activate(final):
    """
    Activates (focuses and, if the user requested it, raises a window).
    If final is True, then this is the very last window we're activating
    and the user has finished cycling.
    """
    print "_activate"
    try:
        client = _items[_pos]
    except IndexError: return # empty list
    print client

    # move the to client's desktop if required
    if not (client.iconic() or client.desktop() == 0xffffffff or \
            client.desktop() == ob.Openbox.desktop()):
        ob.Openbox.setDesktop(client.desktop())
        
    if final or not client.iconic():
        if final: r = config.get('stackedcycle', 'raise_window')
        else: r = False
        client.focus(True)
        if final and client.shaded(): client.setShaded(False)
        print "final", final, "raising", r
        if r: client.raiseWindow()
        if not final:
            focus._skip += 1

def _cycle(keydata, client, forward):
    global _cycling, _state, _pos, _inititem, _items

    if not _cycling:
        _items = [] # so it doesnt try start partway through the list
        _populate()

        if not _items: return # don't bother doing anything

        Keyboard.grab(_grabfunc)
        # the pointer grab causes pointer events during the keyboard grab
        # to go away, which means we don't get enter notifies when the
        # popup disappears, screwing up the focus
        Pointer.grabPointer(True)

        _cycling = True
        _state = keydata.state
        _pos = 0
        _inititem = _items[_pos]

    if forward:
        _pos += 1
    else:
        _pos -= 1
    # wrap around
    if _pos < 0: _pos = len(_items) - 1
    elif _pos >= len(_items): _pos = 0
    if config.get('stackedcycle', 'activate_while_cycling'):
        _activate(False) # activate, but dont deiconify/unshade/raise

def _grabfunc(keydata, client):
    global _cycling
    
    done = False
    notreverting = True
    # have all the modifiers this started with been released?
    if not _state & keydata.state:
        done = True
    elif keydata.press:
        # has Escape been pressed?
        if keydata.keychain == "Escape":
            done = True
            notreverting = False
            # revert
            try:
                _pos = _items.index(_inititem)
            except:
                _pos = -1
        # has Enter been pressed?
        elif keydata.keychain == "Return":
            done = True

    if done:
        # activate, and deiconify/unshade/raise
        _activate(notreverting)
        _cycling = False
        Keyboard.ungrab()
        Pointer.grabPointer(False)


_cycling  = False
_pos = 0
_inititem = None
_items = []
_state = 0

def _newwin(data):
    if _cycling: _populate()
def _closewin(data):
    if _cycling: _populate()

hooks.managed.append(_newwin)
hooks.closed.append(_closewin)