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) |