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 
sum_windows(int nr,LayerItem * item)42 int sum_windows(int nr, LayerItem* item) {
43     return nr + item->numWindows();
44 }
45 
count_windows(const FbTk::Layer::ItemList & items)46 int count_windows(const FbTk::Layer::ItemList& items) {
47     return std::accumulate(items.begin(), items.end(), 0, sum_windows);
48 }
49 
50 
extract_windows_to_stack(const LayerItem::Windows & windows,std::vector<Window> & stack)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 
extract_windows_to_stack(const FbTk::Layer::ItemList & items,LayerItem * temp_raised,std::vector<Window> & stack)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 
restack(const FbTk::Layer::ItemList & items,LayerItem * temp_raised)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 
restack(const std::vector<Layer * > & layers)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 
Layer(MultLayers & manager,int layernum)101 Layer::Layer(MultLayers &manager, int layernum):
102     m_manager(manager), m_layernum(layernum), m_needs_restack(false) {
103 }
104 
~Layer()105 Layer::~Layer() {
106 
107 }
108 
restack()109 void Layer::restack() {
110     if (m_manager.isUpdatable()) {
111         ::restack(itemList(), 0);
112         m_needs_restack = false;
113     }
114 }
115 
restackAndTempRaise(LayerItem & item)116 void Layer::restackAndTempRaise(LayerItem &item) {
117     ::restack(itemList(), &item);
118 }
119 
countWindows()120 int Layer::countWindows() {
121     return ::count_windows(itemList());
122 }
123 
124 
125 // Stack all windows associated with 'item' below the 'above' item
stackBelowItem(LayerItem & item,LayerItem * above)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
alignItem(LayerItem & item)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 
insert(LayerItem & item,unsigned int pos)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 
remove(LayerItem & item)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 
raise(LayerItem & item)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 
tempRaise(LayerItem & item)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 
lower(LayerItem & item)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 
raiseLayer(LayerItem & item)291 void Layer::raiseLayer(LayerItem &item) {
292     m_manager.raiseLayer(item);
293 }
294 
lowerLayer(LayerItem & item)295 void Layer::lowerLayer(LayerItem &item) {
296     m_manager.lowerLayer(item);
297 }
298 
moveToLayer(LayerItem & item,int layernum)299 void Layer::moveToLayer(LayerItem &item, int layernum) {
300     m_manager.moveToLayer(item, layernum);
301 }
302 
303 
getLowestItem()304 LayerItem *Layer::getLowestItem() {
305     if (itemList().empty())
306         return 0;
307     else
308         return itemList().back();
309 }
310 
311