diff options
Diffstat (limited to 'src/FbTk/Layer.cc')
-rw-r--r-- | src/FbTk/Layer.cc | 310 |
1 files changed, 310 insertions, 0 deletions
diff --git a/src/FbTk/Layer.cc b/src/FbTk/Layer.cc new file mode 100644 index 0000000..d3c6615 --- /dev/null +++ b/src/FbTk/Layer.cc | |||
@@ -0,0 +1,310 @@ | |||
1 | // Layer.cc for FbTk - fluxbox toolkit | ||
2 | // Copyright (c) 2003 - 2006 Henrik Kinnunen (fluxgen at fluxbox dot org) | ||
3 | // and Simon Bowden (rathnor at users.sourceforge.net) | ||
4 | // | ||
5 | // Permission is hereby granted, free of charge, to any person obtaining a | ||
6 | // copy of this software and associated documentation files (the "Software"), | ||
7 | // to deal in the Software without restriction, including without limitation | ||
8 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
9 | // and/or sell copies of the Software, and to permit persons to whom the | ||
10 | // Software is furnished to do so, subject to the following conditions: | ||
11 | // | ||
12 | // The above copyright notice and this permission notice shall be included in | ||
13 | // all copies or substantial portions of the Software. | ||
14 | // | ||
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
18 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
20 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
21 | // DEALINGS IN THE SOFTWARE. | ||
22 | |||
23 | #include "Layer.hh" | ||
24 | #include "LayerItem.hh" | ||
25 | #include "App.hh" | ||
26 | #include "FbWindow.hh" | ||
27 | #include "MultLayers.hh" | ||
28 | |||
29 | #include <iostream> | ||
30 | #include <algorithm> | ||
31 | #include <numeric> | ||
32 | |||
33 | using namespace FbTk; | ||
34 | |||
35 | #ifdef DEBUG | ||
36 | using std::cerr; | ||
37 | using std::endl; | ||
38 | #endif // DEBUG | ||
39 | |||
40 | namespace { | ||
41 | |||
42 | int sum_windows(int nr, LayerItem* item) { | ||
43 | return nr + item->numWindows(); | ||
44 | } | ||
45 | |||
46 | int count_windows(const FbTk::Layer::ItemList& items) { | ||
47 | return std::accumulate(items.begin(), items.end(), 0, sum_windows); | ||
48 | } | ||
49 | |||
50 | |||
51 | void extract_windows_to_stack(const LayerItem::Windows& windows, std::vector<Window>& stack) { | ||
52 | LayerItem::Windows::const_iterator i = windows.begin(); | ||
53 | LayerItem::Windows::const_iterator end = windows.end(); | ||
54 | for (; i != end; ++i) { | ||
55 | Window w = (*i)->window(); | ||
56 | if (w) | ||
57 | stack.push_back(w); | ||
58 | } | ||
59 | } | ||
60 | |||
61 | void extract_windows_to_stack(const FbTk::Layer::ItemList& items, LayerItem* temp_raised, std::vector<Window>& stack) { | ||
62 | |||
63 | if (temp_raised) { // add windows that go on top | ||
64 | extract_windows_to_stack(temp_raised->getWindows(), stack); | ||
65 | } | ||
66 | |||
67 | FbTk::Layer::ItemList::const_iterator it = items.begin(); | ||
68 | FbTk::Layer::ItemList::const_iterator it_end = items.end(); | ||
69 | for (; it != it_end; ++it) { // add all the windows from each other item | ||
70 | if (*it == temp_raised) { | ||
71 | continue; | ||
72 | } | ||
73 | extract_windows_to_stack((*it)->getWindows(), stack); | ||
74 | } | ||
75 | } | ||
76 | |||
77 | void restack(const FbTk::Layer::ItemList& items, LayerItem* temp_raised) { | ||
78 | |||
79 | std::vector<Window> stack; | ||
80 | extract_windows_to_stack(items, temp_raised, stack); | ||
81 | |||
82 | if (!stack.empty()) | ||
83 | XRestackWindows(FbTk::App::instance()->display(), &stack[0], stack.size()); | ||
84 | } | ||
85 | |||
86 | } // end of anonymous namespace | ||
87 | |||
88 | |||
89 | void Layer::restack(const std::vector<Layer*>& layers) { | ||
90 | |||
91 | std::vector<Window> stack; | ||
92 | std::vector<Layer*>::const_iterator l; | ||
93 | for (l = layers.begin(); l != layers.end(); ++l) { | ||
94 | extract_windows_to_stack((*l)->itemList(), 0, stack); | ||
95 | } | ||
96 | |||
97 | if (!stack.empty()) | ||
98 | XRestackWindows(FbTk::App::instance()->display(), &stack[0], stack.size()); | ||
99 | } | ||
100 | |||
101 | Layer::Layer(MultLayers &manager, int layernum): | ||
102 | m_manager(manager), m_layernum(layernum), m_needs_restack(false) { | ||
103 | } | ||
104 | |||
105 | Layer::~Layer() { | ||
106 | |||
107 | } | ||
108 | |||
109 | void Layer::restack() { | ||
110 | if (m_manager.isUpdatable()) { | ||
111 | ::restack(itemList(), 0); | ||
112 | m_needs_restack = false; | ||
113 | } | ||
114 | } | ||
115 | |||
116 | void Layer::restackAndTempRaise(LayerItem &item) { | ||
117 | ::restack(itemList(), &item); | ||
118 | } | ||
119 | |||
120 | int Layer::countWindows() { | ||
121 | return ::count_windows(itemList()); | ||
122 | } | ||
123 | |||
124 | |||
125 | // Stack all windows associated with 'item' below the 'above' item | ||
126 | void Layer::stackBelowItem(LayerItem &item, LayerItem *above) { | ||
127 | if (!m_manager.isUpdatable()) | ||
128 | return; | ||
129 | |||
130 | // if there are no windows provided for above us, | ||
131 | // then we must restack the entire layer | ||
132 | // we can't do XRaiseWindow because a restack then causes OverrideRedirect | ||
133 | // windows to get pushed to the bottom | ||
134 | if (!above || m_needs_restack) { // must need to go right to top | ||
135 | restack(); | ||
136 | return; | ||
137 | } | ||
138 | |||
139 | std::vector<Window> stack; | ||
140 | |||
141 | // We do have a window to stack below | ||
142 | // so we put it on top, and fill the rest of the array with the ones to go below it. | ||
143 | // assume that above's window exists | ||
144 | stack.push_back(above->getWindows().back()->window()); | ||
145 | |||
146 | // fill the rest of the array | ||
147 | extract_windows_to_stack(item.getWindows(), stack); | ||
148 | |||
149 | XRestackWindows(FbTk::App::instance()->display(), &stack[0], stack.size()); | ||
150 | } | ||
151 | |||
152 | // We can't just use Restack here, because it won't do anything if they're | ||
153 | // already in the same relative order excluding other windows | ||
154 | void Layer::alignItem(LayerItem &item) { | ||
155 | if (itemList().front() == &item) { | ||
156 | stackBelowItem(item, m_manager.getLowestItemAboveLayer(m_layernum)); | ||
157 | return; | ||
158 | } | ||
159 | |||
160 | // Note: some other things effectively assume that the window list is | ||
161 | // sorted from highest to lowest | ||
162 | // get our item | ||
163 | iterator myit = std::find(itemList().begin(), itemList().end(), &item); | ||
164 | iterator it = myit; | ||
165 | |||
166 | // go to the one above it in our layer (top is front, so we decrement) | ||
167 | --it; | ||
168 | |||
169 | // keep going until we find one that is currently visible to the user | ||
170 | while (it != itemList().begin() && !(*it)->visible()) | ||
171 | --it; | ||
172 | |||
173 | if (it == itemList().begin() && !(*it)->visible()) | ||
174 | // reached front item, but it wasn't visible, therefore it was already raised | ||
175 | stackBelowItem(item, m_manager.getLowestItemAboveLayer(m_layernum)); | ||
176 | else | ||
177 | stackBelowItem(item, *it); | ||
178 | |||
179 | } | ||
180 | |||
181 | Layer::iterator Layer::insert(LayerItem &item, unsigned int pos) { | ||
182 | #ifdef DEBUG | ||
183 | // at this point we don't support insertions into a layer other than at the top | ||
184 | if (pos != 0) | ||
185 | cerr<<__FILE__<<"("<<__LINE__<<"): Insert using non-zero position not valid in Layer"<<endl; | ||
186 | #endif // DEBUG | ||
187 | |||
188 | itemList().push_front(&item); | ||
189 | // restack below next window up | ||
190 | stackBelowItem(item, m_manager.getLowestItemAboveLayer(m_layernum)); | ||
191 | return itemList().begin(); | ||
192 | } | ||
193 | |||
194 | void Layer::remove(LayerItem &item) { | ||
195 | iterator it = itemList().begin(); | ||
196 | iterator it_end = itemList().end(); | ||
197 | for (; it != it_end; ++it) { | ||
198 | if (*it == &item) { | ||
199 | itemList().erase(it); | ||
200 | break; | ||
201 | } | ||
202 | } | ||
203 | } | ||
204 | |||
205 | void Layer::raise(LayerItem &item) { | ||
206 | // assume it is already in this layer | ||
207 | |||
208 | if (&item == itemList().front()) { | ||
209 | if (m_needs_restack) | ||
210 | restack(); | ||
211 | return; // nothing to do | ||
212 | } | ||
213 | |||
214 | |||
215 | iterator it = std::find(itemList().begin(), itemList().end(), &item); | ||
216 | if (it != itemList().end()) | ||
217 | itemList().erase(it); | ||
218 | else { | ||
219 | #ifdef DEBUG | ||
220 | cerr<<__FILE__<<"("<<__LINE__<<"): WARNING: raise on item not in layer["<<m_layernum<<"]"<<endl; | ||
221 | #endif // DEBUG | ||
222 | return; | ||
223 | } | ||
224 | |||
225 | itemList().push_front(&item); | ||
226 | stackBelowItem(item, m_manager.getLowestItemAboveLayer(m_layernum)); | ||
227 | |||
228 | } | ||
229 | |||
230 | void Layer::tempRaise(LayerItem &item) { | ||
231 | // assume it is already in this layer | ||
232 | |||
233 | if (!m_needs_restack && &item == itemList().front()) | ||
234 | return; // nothing to do | ||
235 | |||
236 | iterator it = std::find(itemList().begin(), itemList().end(), &item); | ||
237 | if (it == itemList().end()) { | ||
238 | #ifdef DEBUG | ||
239 | cerr<<__FILE__<<"("<<__LINE__<<"): WARNING: raise on item not in layer["<<m_layernum<<"]"<<endl; | ||
240 | #endif // DEBUG | ||
241 | return; | ||
242 | } | ||
243 | |||
244 | if (m_needs_restack) | ||
245 | restackAndTempRaise(item); | ||
246 | else | ||
247 | stackBelowItem(item, m_manager.getLowestItemAboveLayer(m_layernum)); | ||
248 | |||
249 | m_needs_restack = true; | ||
250 | } | ||
251 | |||
252 | void Layer::lower(LayerItem &item) { | ||
253 | // assume already in this layer | ||
254 | |||
255 | // is it already the lowest? | ||
256 | if (&item == itemList().back()) { | ||
257 | if (m_needs_restack) | ||
258 | restack(); | ||
259 | return; // nothing to do | ||
260 | } | ||
261 | |||
262 | iterator it = std::find(itemList().begin(), itemList().end(), &item); | ||
263 | if (it != itemList().end()) | ||
264 | // remove this item | ||
265 | itemList().erase(it); | ||
266 | #ifdef DEBUG | ||
267 | else { | ||
268 | cerr<<__FILE__<<"("<<__LINE__<<"): WARNING: lower on item not in layer"<<endl; | ||
269 | return; | ||
270 | } | ||
271 | #endif // DEBUG | ||
272 | |||
273 | // add it to the bottom | ||
274 | itemList().push_back(&item); | ||
275 | |||
276 | // find the item we need to stack below | ||
277 | // start at the end | ||
278 | it = itemList().end(); | ||
279 | |||
280 | // go up one so we have an object (which must exist, since at least this item is in the layer) | ||
281 | it--; | ||
282 | |||
283 | // go down another one | ||
284 | // must exist, otherwise our item must == itemList().back() | ||
285 | it--; | ||
286 | |||
287 | // and restack our window below that one. | ||
288 | stackBelowItem(item, *it); | ||
289 | } | ||
290 | |||
291 | void Layer::raiseLayer(LayerItem &item) { | ||
292 | m_manager.raiseLayer(item); | ||
293 | } | ||
294 | |||
295 | void Layer::lowerLayer(LayerItem &item) { | ||
296 | m_manager.lowerLayer(item); | ||
297 | } | ||
298 | |||
299 | void Layer::moveToLayer(LayerItem &item, int layernum) { | ||
300 | m_manager.moveToLayer(item, layernum); | ||
301 | } | ||
302 | |||
303 | |||
304 | LayerItem *Layer::getLowestItem() { | ||
305 | if (itemList().empty()) | ||
306 | return 0; | ||
307 | else | ||
308 | return itemList().back(); | ||
309 | } | ||
310 | |||