]> Dogcows Code - chaz/openbox/commitdiff
add the new cycle module with super snazzy new Cycle classes. yay KatanaLynx!
authorDana Jansens <danakj@orodu.net>
Sun, 16 Feb 2003 23:14:30 +0000 (23:14 +0000)
committerDana Jansens <danakj@orodu.net>
Sun, 16 Feb 2003 23:14:30 +0000 (23:14 +0000)
scripts/Makefile.am
scripts/cycle.py [new file with mode: 0644]
scripts/defaults.py
scripts/focuscycle.py [deleted file]
scripts/stackedcycle.py [deleted file]

index f547b0c98a5ef051b996c18717c9c211d164bde8..01c1aa922d033b65bd9313c8683c02d0d719f2eb 100644 (file)
@@ -2,7 +2,7 @@ scriptdir = $(libdir)/openbox/python
 MAINTAINERCLEANFILES = Makefile.in
 script_PYTHON = config.py defaults.py focus.py callbacks.py \
                 focusmodel.py windowplacement.py behavior.py motion.py \
-                historyplacement.py stackedcycle.py focuscycle.py
+                historyplacement.py cycle.py
 
 distclean-local:
        $(RM) *\~ .\#*
diff --git a/scripts/cycle.py b/scripts/cycle.py
new file mode 100644 (file)
index 0000000..6165afa
--- /dev/null
@@ -0,0 +1,486 @@
+import ob, otk
+class _Cycle:
+    """
+    This is a basic cycling class for anything, from xOr's stackedcycle.py, 
+    that pops up a cycling menu when there's more than one thing to be cycled
+    to.
+    An example of inheriting from and modifying this class is _CycleWindows,
+    which allows users to cycle around windows.
+
+    This class could conceivably be used to cycle through anything -- desktops,
+    windows of a specific class, XMMS playlists, etc.
+    """
+
+    """This specifies a rough limit of characters for the cycling list titles.
+       Titles which are larger will be chopped with an elipsis in their
+       center."""
+    TITLE_SIZE_LIMIT = 80
+
+    """If this is non-zero then windows will be activated as they are
+       highlighted in the cycling list (except iconified windows)."""
+    ACTIVATE_WHILE_CYCLING = 0
+
+    """If this is true, we start cycling with the next (or previous) thing 
+       selected."""
+    START_WITH_NEXT = 1
+
+    """If this is true, a popup window will be displayed with the options
+       while cycling."""
+    SHOW_POPUP = 1
+
+    def __init__(self):
+        """Initialize an instance of this class.  Subclasses should 
+           do any necessary event binding in their constructor as well.
+           """
+        self.cycling = 0   # internal var used for going through the menu
+        self.items = []    # items to cycle through
+
+        self.widget = None    # the otk menu widget
+        self.menuwidgets = [] # labels in the otk menu widget TODO: RENAME
+
+    def createPopup(self):
+        """Creates the cycling popup menu.
+        """
+        self.widget = otk.Widget(self.screen.number(), ob.openbox,
+                                 otk.Widget.Vertical, 0, 1)
+
+    def destroyPopup(self):
+        """Destroys (or rather, cleans up after) the cycling popup menu.
+        """
+        self.menuwidgets = []
+        self.widget = 0
+
+    def populateItems(self):
+        """Populate self.items with the appropriate items that can currently 
+           be cycled through.  self.items may be cleared out before this 
+           method is called.
+           """
+        pass
+
+    def menuLabel(self, item):
+        """Return a string indicating the menu label for the given item.
+           Don't worry about title truncation.
+           """
+        pass
+
+    def itemEqual(self, item1, item2):
+        """Compare two items, return 1 if they're "equal" for purposes of 
+           cycling, and 0 otherwise.
+           """
+        # suggestion: define __eq__ on item classes so that this works 
+        # in the general case.  :)
+        return item1 == item2
+
+    def populateLists(self):
+        """Populates self.items and self.menuwidgets, and then shows and
+           positions the cycling popup.  You probably shouldn't mess with 
+           this function; instead, see populateItems and menuLabel.
+           """
+        self.widget.hide()
+
+        try:
+            current = self.items[self.menupos]
+        except IndexError: 
+            current = None
+        oldpos = self.menupos
+        self.menupos = -1
+
+        self.items = []
+        self.populateItems()
+
+        # make the widgets
+        i = 0
+        self.menuwidgets = []
+        for i in range(len(self.items)):
+            c = self.items[i]
+
+            w = otk.Label(self.widget)
+            # current item might have shifted after a populateItems() 
+            # call, so we need to do this test.
+            if current and self.itemEqual(c, current):
+                self.menupos = i
+                w.setHilighted(1)
+            self.menuwidgets.append(w)
+
+            t = self.menuLabel(c)
+            # TODO: maybe subclasses will want to truncate in different ways?
+            if len(t) > self.TITLE_SIZE_LIMIT: # limit the length of titles
+                t = t[:self.TITLE_SIZE_LIMIT / 2 - 2] + "..." + \
+                    t[0 - self.TITLE_SIZE_LIMIT / 2 - 2:]
+            w.setText(t)
+
+        # The item we were on might be gone entirely
+        if self.menupos < 0:
+            # try stay at the same spot in the menu
+            if oldpos >= len(self.items):
+                self.menupos = len(self.items) - 1
+            else:
+                self.menupos = oldpos
+
+        # find the size for the popup
+        width = 0
+        height = 0
+        for w in self.menuwidgets:
+            size = w.minSize()
+            if size.width() > width: width = size.width()
+            height += size.height()
+
+        # show or hide the list and its child widgets
+        if len(self.items) > 1:
+            size = self.screeninfo.size()
+            self.widget.moveresize(otk.Rect((size.width() - width) / 2,
+                                            (size.height() - height) / 2,
+                                            width, height))
+            if self.SHOW_POPUP: self.widget.show(1)
+
+    def activateTarget(self, 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.
+           """
+        pass
+
+    def setDataInfo(self, data):
+        """Retrieve and/or calculate information when we start cycling, 
+           preferably caching it.  Data is what's given to callback functions.
+           """
+        self.screen = ob.openbox.screen(data.screen)
+        self.screeninfo = otk.display.screenInfo(data.screen)
+
+    def chooseStartPos(self):
+        """Set self.menupos to a number between 0 and len(self.items) - 1.
+           By default the initial menupos is 0, but this can be used to change
+           it to some other position."""
+        pass
+
+    def cycle(self, data, forward):
+        """Does the actual job of cycling through windows.  data is a callback 
+           parameter, while forward is a boolean indicating whether the
+           cycling goes forwards (true) or backwards (false).
+           """
+
+        initial = 0
+
+        if not self.cycling:
+            ob.kgrab(data.screen, self.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
+            ob.mgrab(data.screen)
+
+            self.cycling = 1
+            self.state = data.state
+            self.menupos = 0
+
+            self.setDataInfo(data)
+
+            self.createPopup()
+            self.items = [] # so it doesnt try start partway through the list
+            self.populateLists()
+
+            self.chooseStartPos()
+            self.initpos = self.menupos
+
+            initial = 1
+        
+        if not self.items: return # don't bother doing anything
+        
+        self.menuwidgets[self.menupos].setHighlighted(0)
+
+        if initial and not self.START_WITH_NEXT:
+            pass
+        else:
+            if forward:
+                self.menupos += 1
+            else:
+                self.menupos -= 1
+        # wrap around
+        if self.menupos < 0: self.menupos = len(self.items) - 1
+        elif self.menupos >= len(self.items): self.menupos = 0
+        self.menuwidgets[self.menupos].setHighlighted(1)
+        if self.ACTIVATE_WHILE_CYCLING:
+            self.activateTarget(0) # activate, but dont deiconify/unshade/raise
+
+    def grabfunc(self, data):
+        """A callback method that grabs away all keystrokes so that navigating 
+           the cycling menu is possible."""
+        done = 0
+        notreverting = 1
+        # have all the modifiers this started with been released?
+        if not self.state & data.state:
+            done = 1
+        elif data.action == ob.KeyAction.Press:
+            # has Escape been pressed?
+            if data.key == "Escape":
+                done = 1
+                notreverting = 0
+                # revert
+                self.menupos = self.initpos
+            # has Enter been pressed?
+            elif data.key == "Return":
+                done = 1
+
+        if done:
+            # activate, and deiconify/unshade/raise
+            self.activateTarget(notreverting)
+            self.destroyPopup()
+            self.cycling = 0
+            ob.kungrab()
+            ob.mungrab()
+
+    def next(self, data):
+        """Focus the next window."""
+        if not data.state:
+            raise RuntimeError("next must be bound to a key" +
+                               "combination with at least one modifier")
+        self.cycle(data, 1)
+        
+    def previous(self, data):
+        """Focus the previous window."""
+        if not data.state:
+            raise RuntimeError("previous must be bound to a key" +
+                               "combination with at least one modifier")
+        self.cycle(data, 0)
+
+#---------------------- Window Cycling --------------------
+import focus
+class _CycleWindows(_Cycle):
+    """
+    This is a basic cycling class for Windows.
+
+    An example of inheriting from and modifying this class is _ClassCycleWindows,
+    which allows users to cycle around windows of a certain application
+    name/class only.
+
+    This class has an underscored name because I use the singleton pattern 
+    (so CycleWindows is an actual instance of this class).  This doesn't have 
+    to be followed, but if it isn't followed then the user will have to create 
+    their own instances of your class and use that (not always a bad thing).
+
+    An example of using the CycleWindows singleton:
+
+        from cycle import CycleWindows
+        CycleWindows.INCLUDE_ICONS = 0  # I don't like cycling to icons
+        ob.kbind(["A-Tab"], ob.KeyContext.All, CycleWindows.next)
+        ob.kbind(["A-S-Tab"], ob.KeyContext.All, CycleWindows.previous)
+    """
+
+    """If this is non-zero then windows from all desktops will be included in
+       the stacking list."""
+    INCLUDE_ALL_DESKTOPS = 0
+
+    """If this is non-zero then windows which are iconified on the current 
+       desktop will be included in the stacking list."""
+    INCLUDE_ICONS = 1
+
+    """If this is non-zero then windows which are iconified from all desktops
+       will be included in the stacking list."""
+    INCLUDE_ICONS_ALL_DESKTOPS = 1
+
+    """If this is non-zero then windows which are on all-desktops at once will
+       be included."""
+    INCLUDE_OMNIPRESENT = 1
+
+    """A better default for window cycling than generic cycling."""
+    ACTIVATE_WHILE_CYCLING = 1
+
+    """When cycling focus, raise the window chosen as well as focusing it."""
+    RAISE_WINDOW = 1
+
+    def __init__(self):
+        _Cycle.__init__(self)
+
+        def newwindow(data):
+            if self.cycling: self.populateLists()
+        def closewindow(data):
+            if self.cycling: self.populateLists()
+
+        ob.ebind(ob.EventAction.NewWindow, newwindow)
+        ob.ebind(ob.EventAction.CloseWindow, closewindow)
+
+    def shouldAdd(self, client):
+        """Determines if a client should be added to the cycling list."""
+        curdesk = self.screen.desktop()
+        desk = client.desktop()
+
+        if not client.normal(): return 0
+        if not (client.canFocus() or client.focusNotify()): return 0
+        if focus.AVOID_SKIP_TASKBAR and client.skipTaskbar(): return 0
+
+        if client.iconic():
+            if self.INCLUDE_ICONS:
+                if self.INCLUDE_ICONS_ALL_DESKTOPS: return 1
+                if desk == curdesk: return 1
+            return 0
+        if self.INCLUDE_OMNIPRESENT and desk == 0xffffffff: return 1
+        if self.INCLUDE_ALL_DESKTOPS: return 1
+        if desk == curdesk: return 1
+
+        return 0
+
+    def populateItems(self):
+        # get the list of clients, keeping iconic windows at the bottom
+        iconic_clients = []
+        for c in focus._clients:
+            if self.shouldAdd(c):
+                if c.iconic(): iconic_clients.append(c)
+                else: self.items.append(c)
+        self.items.extend(iconic_clients)
+
+    def menuLabel(self, client):
+        if client.iconic(): t = '[' + client.iconTitle() + ']'
+        else: t = client.title()
+
+        if self.INCLUDE_ALL_DESKTOPS:
+            d = client.desktop()
+            if d == 0xffffffff: d = self.screen.desktop()
+            t = self.screen.desktopName(d) + " - " + t
+
+        return t
+    
+    def itemEqual(self, client1, client2):
+        return client1.window() == client2.window()
+
+    def activateTarget(self, 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."""
+        try:
+            client = self.items[self.menupos]
+        except IndexError: return # empty list
+
+        # move the to client's desktop if required
+        if not (client.iconic() or client.desktop() == 0xffffffff or \
+                client.desktop() == self.screen.desktop()):
+            root = self.screeninfo.rootWindow()
+            ob.send_client_msg(root, otk.atoms.net_current_desktop,
+                               root, client.desktop())
+        
+        # send a net_active_window message for the target
+        if final or not client.iconic():
+            if final: r = self.RAISE_WINDOW
+            else: r = 0
+            ob.send_client_msg(self.screeninfo.rootWindow(),
+                               otk.atoms.openbox_active_window,
+                               client.window(), final, r)
+            if not final:
+                focus._skip += 1
+
+# The singleton.
+CycleWindows = _CycleWindows()
+
+#---------------------- Window Cycling --------------------
+import focus
+class _CycleWindowsLinear(_CycleWindows):
+    """
+    This class is an example of how to inherit from and make use of the
+    _CycleWindows class.  This class also uses the singleton pattern.
+
+    An example of using the CycleWindowsLinear singleton:
+
+        from cycle import CycleWindowsLinear
+        CycleWindows.ALL_DESKTOPS = 1  # I want all my windows in the list
+        ob.kbind(["A-Tab"], ob.KeyContext.All, CycleWindowsLinear.next)
+        ob.kbind(["A-S-Tab"], ob.KeyContext.All, CycleWindowsLinear.previous)
+    """
+
+    """When cycling focus, raise the window chosen as well as focusing it."""
+    RAISE_WINDOW = 0
+
+    """If this is true, a popup window will be displayed with the options
+       while cycling."""
+    SHOW_POPUP = 0
+
+    def __init__(self):
+        _CycleWindows.__init__(self)
+
+    def shouldAdd(self, client):
+        """Determines if a client should be added to the cycling list."""
+        curdesk = self.screen.desktop()
+        desk = client.desktop()
+
+        if not client.normal(): return 0
+        if not (client.canFocus() or client.focusNotify()): return 0
+        if focus.AVOID_SKIP_TASKBAR and client.skipTaskbar(): return 0
+
+        if client.iconic(): return 0
+        if self.INCLUDE_OMNIPRESENT and desk == 0xffffffff: return 1
+        if self.INCLUDE_ALL_DESKTOPS: return 1
+        if desk == curdesk: return 1
+
+        return 0
+
+    def populateItems(self):
+        # get the list of clients, keeping iconic windows at the bottom
+        iconic_clients = []
+        for i in range(self.screen.clientCount()):
+            c = self.screen.client(i)
+            if self.shouldAdd(c):
+                self.items.append(c)
+
+    def chooseStartPos(self):
+        if focus._clients:
+            t = focus._clients[0]
+            for i,c in zip(range(len(self.items)), self.items):
+                if self.itemEqual(c, t):
+                    self.menupos = i
+                    break
+        
+    def menuLabel(self, client):
+        t = client.title()
+
+        if self.INCLUDE_ALL_DESKTOPS:
+            d = client.desktop()
+            if d == 0xffffffff: d = self.screen.desktop()
+            t = self.screen.desktopName(d) + " - " + t
+
+        return t
+    
+# The singleton.
+CycleWindowsLinear = _CycleWindowsLinear()
+
+#----------------------- Desktop Cycling ------------------
+class _CycleDesktops(_Cycle):
+    """
+    Example of usage:
+
+       from cycle import CycleDesktops
+       ob.kbind(["W-d"], ob.KeyContext.All, CycleDesktops.next)
+       ob.kbind(["W-S-d"], ob.KeyContext.All, CycleDesktops.previous)
+    """
+    class Desktop:
+        def __init__(self, name, index):
+            self.name = name
+            self.index = index
+        def __eq__(self, other):
+            return other.index == self.index
+
+    START_WITH_NEXT = 0
+
+    def __init__(self):
+        _Cycle.__init__(self)
+
+    def populateItems(self):
+        for i in range(self.screen.numDesktops()):
+            self.items.append(
+                _CycleDesktops.Desktop(self.screen.desktopName(i), i))
+
+    def menuLabel(self, desktop):
+        return desktop.name
+
+    def chooseStartPos(self):
+        self.menupos = self.screen.desktop()
+
+    def activateTarget(self, final):
+        # TODO: refactor this bit
+        try:
+            desktop = self.items[self.menupos]
+        except IndexError: return
+
+        root = self.screeninfo.rootWindow()
+        ob.send_client_msg(root, otk.atoms.net_current_desktop,
+                           root, desktop.index)
+
+CycleDesktops = _CycleDesktops()
+
+print "Loaded cycle.py"
index 7b4145c0b47144722c4687ab602046c6f66f8d7a..459c0d6159720f62dcb840ca277e6f877541621f 100644 (file)
@@ -38,16 +38,20 @@ ob.kbind(["A-F4"], ob.KeyContext.All, callbacks.close)
 ob.kbind(["W-d"], ob.KeyContext.All, callbacks.toggle_show_desktop)
 
 # focus bindings
-import stackedcycle # functions for doing stacked 'kde-style' cycling
-ob.kbind(["A-Tab"], ob.KeyContext.All, stackedcycle.next)
-ob.kbind(["A-S-Tab"], ob.KeyContext.All, stackedcycle.previous)
 
-# if you want linear cycling instead of stacked cycling, comment out the focus
+from cycle import CycleWindows
+ob.kbind(["A-Tab"], ob.KeyContext.All, CycleWindows.next)
+ob.kbind(["A-S-Tab"], ob.KeyContext.All, CycleWindows.previous)
+
+# if you want linear cycling instead of stacked cycling, comment out the two
 # bindings above, and use these instead.
-#import focuscycle # functions for doing linear cycling
-#focuscycle.RAISE_WINDOW = 0 # don't raise windows when they're activated
-#ob.kbind(["A-Tab"], ob.KeyContext.All, focuscycle.next)
-#ob.kbind(["A-S-Tab"], ob.KeyContext.All, focuscycle.previous)
+#from cycle import CycleWindowsLinear
+#ob.kbind(["A-Tab"], ob.KeyContext.All, CycleWindows.next)
+#ob.kbind(["A-S-Tab"], ob.KeyContext.All, CycleWindows.previous)
+
+from cycle import CycleDesktops
+ob.kbind(["C-Tab"], ob.KeyContext.All, CycleDesktops.next)
+ob.kbind(["C-S-Tab"], ob.KeyContext.All, CycleDesktops.previous)
 
 # desktop changing bindings
 ob.kbind(["C-1"], ob.KeyContext.All, lambda(d): callbacks.change_desktop(d, 0))
diff --git a/scripts/focuscycle.py b/scripts/focuscycle.py
deleted file mode 100644 (file)
index 37d5643..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-###########################################################################
-### Functions for cycling focus (in a 'linear' order) between windows.  ###
-###########################################################################
-
-###########################################################################
-###     Options that affect the behavior of the focuscycle module.      ###
-###########################################################################
-RAISE_WINDOW = 1
-"""When cycling focus, raise the window chosen as well as focusing it. This
-   does not affect fallback focusing behavior."""
-# See focus.AVOID_SKIP_TASKBAR
-###########################################################################
-
-def next(data, num=1):
-    """Focus the next window."""
-    _cycle(data, num, 1)
-
-def previous(data, num=1):
-    """Focus the previous window."""
-    _cycle(data, num, 0)
-
-###########################################################################
-###########################################################################
-
-###########################################################################
-###      Internal stuff, should not be accessed outside the module.     ###
-###########################################################################
-
-import ob
-import focus
-
-def _cycle(data, num, forward):
-    screen = ob.openbox.screen(data.screen)
-    count = screen.clientCount()
-
-    if not count: return # no clients
-    
-    target = 0
-    if data.client:
-        client_win = data.client.window()
-        found = 0
-        r = range(count)
-        if not forward:
-            r.reverse()
-        for i in r:
-            if found:
-                target = i
-                found = 2
-                break
-            elif screen.client(i).window() == client_win:
-                found = 1
-        if found == 1: # wraparound
-            if forward: target = 0
-            else: target = count - 1
-
-        t = target
-        desktop = screen.desktop()
-        while 1:
-            client = screen.client(t)
-            if client and focus._focusable(client, desktop) and client.focus():
-                if RAISE_WINDOW:
-                    screen.raiseWindow(client)
-                return
-            if forward:
-                t += num
-                if t >= count: t -= count
-            else:
-                t -= num
-                if t < 0: t += count
-            if t == target: return # nothing to focus
-            
-print "Loaded focuscycle.py"
diff --git a/scripts/stackedcycle.py b/scripts/stackedcycle.py
deleted file mode 100644 (file)
index 76658ae..0000000
+++ /dev/null
@@ -1,256 +0,0 @@
-###########################################################################
-### Functions for cycling focus (in a 'stacked' order) between windows. ###
-###########################################################################
-
-###########################################################################
-###    Options that affect the behavior of the stackedcycle module.     ###
-###########################################################################
-INCLUDE_ALL_DESKTOPS = 0
-"""If this is non-zero then windows from all desktops will be included in
-   the stacking list."""
-INCLUDE_ICONS = 1
-"""If this is non-zero then windows which are iconified on the current desktop
-   will be included in the stacking list."""
-INCLUDE_ICONS_ALL_DESKTOPS = 1
-"""If this is non-zero then windows which are iconified from all desktops
-   will be included in the stacking list."""
-INCLUDE_OMNIPRESENT = 1
-"""If this is non-zero then windows which are on all-desktops at once will
-   be included."""
-TITLE_SIZE_LIMIT = 80
-"""This specifies a rough limit of characters for the cycling list titles.
-   Titles which are larger will be chopped with an elipsis in their
-   center."""
-ACTIVATE_WHILE_CYCLING = 1
-"""If this is non-zero then windows will be activated as they are
-   highlighted in the cycling list (except iconified windows)."""
-# See focus.AVOID_SKIP_TASKBAR
-# See focuscycle.RAISE_WINDOW
-###########################################################################
-
-def next(data):
-    """Focus the next window."""
-    if not data.state:
-        raise RuntimeError("stackedcycle.next must be bound to a key" +
-                           "combination with at least one modifier")
-    _o.cycle(data, 1)
-    
-def previous(data):
-    """Focus the previous window."""
-    if not data.state:
-        raise RuntimeError("stackedcycle.previous must be bound to a key" +
-                           "combination with at least one modifier")
-    _o.cycle(data, 0)
-
-###########################################################################
-###########################################################################
-
-###########################################################################
-###      Internal stuff, should not be accessed outside the module.     ###
-###########################################################################
-
-import otk
-import ob
-import focus
-import focuscycle
-
-class _cycledata:
-    def __init__(self):
-        self.cycling = 0
-
-    def createpopup(self):
-        self.widget = otk.Widget(self.screen.number(), ob.openbox,
-                                 otk.Widget.Vertical, 0, 1)
-
-    def destroypopup(self):
-        self.menuwidgets = []
-        self.widget = 0
-
-    def shouldadd(self, client):
-        """Determines if a client should be added to the list."""
-        curdesk = self.screen.desktop()
-        desk = client.desktop()
-
-        if not client.normal(): return 0
-        if not (client.canFocus() or client.focusNotify()): return 0
-        if focus.AVOID_SKIP_TASKBAR and client.skipTaskbar(): return 0
-
-        if client.iconic():
-            if INCLUDE_ICONS:
-                if INCLUDE_ICONS_ALL_DESKTOPS: return 1
-                if desk == curdesk: return 1
-            return 0
-        if INCLUDE_OMNIPRESENT and desk == 0xffffffff: return 1
-        if INCLUDE_ALL_DESKTOPS: return 1
-        if desk == curdesk: return 1
-
-        return 0
-
-    def populatelist(self):
-        """Populates self.clients and self.menuwidgets, and then shows and
-           positions the cycling popup."""
-
-        self.widget.hide()
-
-        try:
-            current = self.clients[self.menupos]
-        except IndexError: current = 0
-        oldpos = self.menupos
-        self.menupos = -1
-
-        # get the list of clients, keeping iconic windows at the bottom
-        self.clients = []
-        iconic_clients = []
-        for c in focus._clients:
-            if c.iconic(): iconic_clients.append(c)
-            else: self.clients.append(c)
-        self.clients.extend(iconic_clients)
-
-        # make the widgets
-        i = 0
-        self.menuwidgets = []
-        while i < len(self.clients):
-            c = self.clients[i]
-            if not self.shouldadd(c):
-                # make the clients and menuwidgets lists match
-                self.clients.pop(i) 
-                continue
-            
-            w = otk.Label(self.widget)
-            if current and c.window() == current.window():
-                self.menupos = i
-                w.setHighlighted(1)
-            self.menuwidgets.append(w)
-
-            if c.iconic(): t = c.iconTitle()
-            else: t = c.title()
-
-            if INCLUDE_ALL_DESKTOPS:
-                d = c.desktop()
-                if d == 0xffffffff: d = self.screen.desktop()
-                t = self.screen.desktopName(d) + " - " + t
-            
-            if len(t) > TITLE_SIZE_LIMIT: # limit the length of titles
-                t = t[:TITLE_SIZE_LIMIT / 2 - 2] + "..." + \
-                    t[0 - TITLE_SIZE_LIMIT / 2 - 2:]
-            w.setText(t)
-
-            i += 1
-
-        # the window we were on may be gone
-        if self.menupos < 0:
-            # try stay at the same spot in the menu
-            if oldpos >= len(self.clients):
-                self.menupos = len(self.clients) - 1
-            else:
-                self.menupos = oldpos
-
-        # find the size for the popup
-        width = 0
-        height = 0
-        for w in self.menuwidgets:
-            size = w.minSize()
-            if size.width() > width: width = size.width()
-            height += size.height()
-        
-        # show or hide the list and its child widgets
-        if len(self.clients) > 1:
-            size = self.screeninfo.size()
-            self.widget.moveresize(otk.Rect((size.width() - width) / 2,
-                                            (size.height() - height) / 2,
-                                            width, height))
-            self.widget.show(1)
-
-    def activatetarget(self, final):
-        try:
-            client = self.clients[self.menupos]
-        except IndexError: return # empty list makes for this
-
-        # move the to client's desktop if required
-        if not (client.iconic() or client.desktop() == 0xffffffff or \
-                client.desktop() == self.screen.desktop()):
-            root = self.screeninfo.rootWindow()
-            ob.send_client_msg(root, otk.atoms.net_current_desktop,
-                               root, client.desktop())
-        
-        # send a net_active_window message for the target
-        if final or not client.iconic():
-            if final: r = focuscycle.RAISE_WINDOW
-            else: r = 0
-            ob.send_client_msg(self.screeninfo.rootWindow(),
-                               otk.atoms.openbox_active_window,
-                               client.window(), final, r)
-            if not final:
-                focus._skip += 1
-
-    def cycle(self, data, forward):
-        if not self.cycling:
-            ob.kgrab(data.screen, _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
-            ob.mgrab(data.screen)
-
-            self.cycling = 1
-            self.state = data.state
-            self.screen = ob.openbox.screen(data.screen)
-            self.screeninfo = otk.display.screenInfo(data.screen)
-            self.menupos = 0
-            self.createpopup()
-            self.clients = [] # so it doesnt try start partway through the list
-            self.populatelist()
-        
-        if not len(self.clients): return # don't both doing anything
-        
-        self.menuwidgets[self.menupos].setHighlighted(0)
-        if forward:
-            self.menupos += 1
-        else:
-            self.menupos -= 1
-        # wrap around
-        if self.menupos < 0: self.menupos = len(self.clients) - 1
-        elif self.menupos >= len(self.clients): self.menupos = 0
-        self.menuwidgets[self.menupos].setHighlighted(1)
-        if ACTIVATE_WHILE_CYCLING:
-            self.activatetarget(0) # activate, but dont deiconify/unshade/raise
-
-    def grabfunc(self, data):
-        done = 0
-        notreverting = 1
-        # have all the modifiers this started with been released?
-        if not self.state & data.state:
-            done = 1
-        elif data.action == ob.KeyAction.Press:
-            # has Escape been pressed?
-            if data.key == "Escape":
-                done = 1
-                notreverting = 0
-                # revert
-                self.menupos = 0
-            # has Enter been pressed?
-            elif data.key == "Return":
-                done = 1
-
-        if done:
-            # activate, and deiconify/unshade/raise
-            self.activatetarget(notreverting)
-            self.destroypopup()
-            self.cycling = 0
-            ob.kungrab()
-            ob.mungrab()
-
-def _newwindow(data):
-    if _o.cycling: _o.populatelist()
-        
-def _closewindow(data):
-    if _o.cycling: _o.populatelist()
-        
-def _grabfunc(data):
-    _o.grabfunc(data)
-
-ob.ebind(ob.EventAction.NewWindow, _newwindow)
-ob.ebind(ob.EventAction.CloseWindow, _closewindow)
-
-_o = _cycledata()
-
-print "Loaded stackedcycle.py"
This page took 0.037399 seconds and 4 git commands to generate.