remove debug prints
[mikachu/openbox.git] / scripts / stackedcycle.py
1 ###########################################################################
2 ### Functions for cycling focus (in a 'stacked' order) between windows. ###
3 ###########################################################################
4
5 ###########################################################################
6 ###    Options that affect the behavior of the stackedcycle module.     ###
7 ###              Also see the options in the focus module.              ###
8 ###########################################################################
9 include_all_desktops = 0
10 include_icons = 1
11 include_omnipresent = 1
12 title_size_limit = 80
13 activate_while_cycling = 1
14 ###########################################################################
15
16 def next(data):
17     """Focus the next window."""
18     _o.cycle(data, 1)
19     
20 def previous(data):
21     """Focus the previous window."""
22     _o.cycle(data, 0)
23
24 ###########################################################################
25 ###########################################################################
26
27 ###########################################################################
28 ###      Internal stuff, should not be accessed outside the module.     ###
29 ###########################################################################
30
31 import otk
32 import ob
33 import focus
34
35 class cycledata:
36     def __init__(self):
37         self.cycling = 0
38
39     def createpopup(self):
40         self.style = self.screen.style()
41         self.widget = otk.Widget(ob.openbox, self.style, otk.Widget.Vertical,
42                                  0, self.style.bevelWidth(), 1)
43         self.widget.setTexture(self.style.titlebarFocusBackground())
44
45     def destroypopup(self):
46         self.menuwidgets = []
47         self.widget = 0
48
49     def shouldadd(self, client):
50         """Determines if a client should be added to the list."""
51         curdesk = self.screen.desktop()
52         desk = client.desktop()
53
54         if not client.normal(): return 0
55         if not (client.canFocus() or client.focusNotify()): return 0
56         if focus.avoid_skip_taskbar and client.skipTaskbar(): return 0
57
58         if include_icons and client.iconic(): return 1
59         if include_omnipresent and desk == 0xffffffff: return 1
60         if include_all_desktops: return 1
61         if desk == curdesk: return 1
62
63         return 0
64
65     def populatelist(self):
66         """Populates self.clients and self.menuwidgets, and then shows and
67            positions the cycling popup."""
68
69         self.widget.hide()
70
71         try:
72             current = self.clients[self.menupos]
73         except IndexError: current = 0
74         oldpos = self.menupos
75         self.menupos = -1
76
77         # get the list of clients
78         self.clients = []
79         for i in focus._clients:
80             c = ob.openbox.findClient(i)
81             if c: self.clients.append(c)
82
83         font = self.style.labelFont()
84         longest = 0
85         height = font.height()
86             
87         # make the widgets
88         i = 0
89         self.menuwidgets = []
90         while i < len(self.clients):
91             c = self.clients[i]
92             if not self.shouldadd(c):
93                 # make the clients and menuwidgets lists match
94                 self.clients.pop(i) 
95                 continue
96             
97             w = otk.FocusLabel(self.widget)
98             if current and c.window() == current.window():
99                 self.menupos = i
100                 w.focus()
101             else:
102                 w.unfocus()
103             self.menuwidgets.append(w)
104
105             if c.iconic(): t = c.iconTitle()
106             else: t = c.title()
107             if len(t) > title_size_limit: # limit the length of titles
108                 t = t[:title_size_limit / 2 - 2] + "..." + \
109                     t[0 - title_size_limit / 2 - 2:]
110             length = font.measureString(t)
111             if length > longest: longest = length
112             w.setText(t)
113
114             i += 1
115
116         # the window we were on may be gone
117         if self.menupos < 0:
118             # try stay at the same spot in the menu
119             if oldpos >= len(self.clients):
120                 self.menupos = len(self.clients) - 1
121             else:
122                 self.menupos = oldpos
123
124         # fit to the largest item in the menu
125         for w in self.menuwidgets:
126             w.fitSize(longest, height)
127
128         # show or hide the list and its child widgets
129         if len(self.clients) > 1:
130             area = self.screeninfo.rect()
131             self.widget.update()
132             self.widget.move(area.x() + (area.width() -
133                                          self.widget.width()) / 2,
134                              area.y() + (area.height() -
135                                          self.widget.height()) / 2)
136             self.widget.show(1)
137
138     def activatetarget(self, final):
139         try:
140             client = self.clients[self.menupos]
141         except IndexError: return # empty list makes for this
142
143         # move the to client's desktop if required
144         if not (client.iconic() or client.desktop() == 0xffffffff or \
145                 client.desktop() == self.screen.desktop()):
146             root = self.screeninfo.rootWindow()
147             ob.send_client_msg(root, otk.Property_atoms().net_current_desktop,
148                                root, client.desktop())
149         
150         # send a net_active_window message for the target
151         if final or not client.iconic():
152             if final: r = focus.raise_window
153             else: r = 0
154             ob.send_client_msg(self.screeninfo.rootWindow(),
155                                otk.Property_atoms().openbox_active_window,
156                                client.window(), final, r)
157
158     def cycle(self, data, forward):
159         if not self.cycling:
160             self.cycling = 1
161             focus._disable = 1
162             self.state = data.state
163             self.screen = ob.openbox.screen(data.screen)
164             self.screeninfo = otk.display.screenInfo(data.screen)
165             self.menupos = 0
166             self.createpopup()
167             self.clients = [] # so it doesnt try start partway through the list
168             self.populatelist()
169         
170             ob.kgrab(self.screen.number(), _grabfunc)
171             # the pointer grab causes pointer events during the keyboard grab
172             # to go away, which means we don't get enter notifies when the
173             # popup disappears, screwing up the focus
174             ob.mgrab(self.screen.number())
175
176         self.menuwidgets[self.menupos].unfocus()
177         if forward:
178             self.menupos += 1
179         else:
180             self.menupos -= 1
181         # wrap around
182         if self.menupos < 0: self.menupos = len(self.clients) - 1
183         elif self.menupos >= len(self.clients): self.menupos = 0
184         self.menuwidgets[self.menupos].focus()
185         if activate_while_cycling:
186             self.activatetarget(0) # activate, but dont deiconify/unshade/raise
187
188     def grabfunc(self, data):
189         if data.action == ob.KeyAction.Release:
190             # have all the modifiers this started with been released?
191             if not self.state & data.state:
192                 self.cycling = 0
193                 focus._disable = 0
194                 self.activatetarget(1) # activate, and deiconify/unshade/raise
195                 self.destroypopup()
196                 ob.kungrab()
197                 ob.mungrab()
198
199
200 def _newwindow(data):
201     if _o.cycling: _o.populatelist()
202         
203 def _closewindow(data):
204     if _o.cycling: _o.populatelist()
205         
206 def _grabfunc(data):
207     _o.grabfunc(data)
208
209 ob.ebind(ob.EventAction.NewWindow, _newwindow)
210 ob.ebind(ob.EventAction.CloseWindow, _closewindow)
211
212 _o = cycledata()