1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // Workspace.cc for Blackbox - an X11 Window manager
3 // Copyright (c) 2001 - 2002 Sean 'Shaleh' Perry <shaleh@debian.org>
4 // Copyright (c) 1997 - 2000 Brad Hughes (bhughes@tcac.net)
6 // Permission is hereby granted, free of charge, to any person obtaining a
7 // copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the
11 // Software is furnished to do so, subject to the following conditions:
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 // DEALINGS IN THE SOFTWARE.
25 # include "../config.h"
26 #endif // HAVE_CONFIG_H
30 #include <X11/Xatom.h>
34 #endif // HAVE_STDIO_H
38 #endif // HAVE_STRING_H
49 #include "blackbox.hh"
50 #include "Clientmenu.hh"
57 #include "Workspace.hh"
58 #include "Windowmenu.hh"
62 Workspace::Workspace(BScreen
*scrn
, unsigned int i
) {
64 xatom
= screen
->getBlackbox()->getXAtom();
66 cascade_x
= cascade_y
= 0;
73 clientmenu
= new Clientmenu(this);
75 lastfocus
= (BlackboxWindow
*) 0;
81 void Workspace::addWindow(BlackboxWindow
*w
, bool place
, bool sticky
) {
84 if (place
) placeWindow(w
);
86 stackingList
.push_front(w
);
91 w
->setWindowNumber(windowList
.size());
94 windowList
.push_back(w
);
96 clientmenu
->insert(w
->getTitle());
100 screen
->updateNetizenWindowAdd(w
->getClientWindow(), id
);
102 if (screen
->doFocusNew() || (w
->isTransient() && w
->getTransientFor() &&
103 w
->getTransientFor()->isFocused())) {
104 if (id
!= screen
->getCurrentWorkspaceID()) {
106 not on the focused workspace, so the window is not going to get focus
107 but if the user wants new windows focused, then it should get focus
108 when this workspace does become focused.
115 // just give it some number, else bad things happen as it is assumed to not
117 w
->setWindowNumber(0);
120 if (! w
->isDesktop())
127 void Workspace::removeWindow(BlackboxWindow
*w
, bool sticky
) {
130 stackingList
.remove(w
);
132 // pass focus to the next appropriate window
133 if ((w
->isFocused() || w
== lastfocus
) &&
134 ! screen
->getBlackbox()->doShutdown()) {
138 if (! w
->isNormal()) return;
140 BlackboxWindowList::iterator it
, end
= windowList
.end();
142 for (i
= 0, it
= windowList
.begin(); it
!= end
; ++it
, ++i
)
147 windowList
.erase(it
);
148 clientmenu
->remove(i
);
149 clientmenu
->update();
152 screen
->updateNetizenWindowDel(w
->getClientWindow());
154 BlackboxWindowList::iterator it
= windowList
.begin();
155 const BlackboxWindowList::iterator end
= windowList
.end();
157 for (; it
!= end
; ++it
, ++i
)
158 (*it
)->setWindowNumber(i
);
162 cascade_x
= cascade_y
= 0;
170 void Workspace::focusFallback(const BlackboxWindow
*old_window
) {
171 BlackboxWindow
*newfocus
= 0;
173 if (id
== screen
->getCurrentWorkspaceID()) {
174 // The window is on the visible workspace.
176 // if it's a transient, then try to focus its parent
177 if (old_window
&& old_window
->isTransient()) {
178 newfocus
= old_window
->getTransientFor();
181 newfocus
->isIconic() || // do not focus icons
182 newfocus
->getWorkspaceNumber() != id
|| // or other workspaces
183 ! newfocus
->setInputFocus())
188 BlackboxWindowList::iterator it
= stackingList
.begin(),
189 end
= stackingList
.end();
190 for (; it
!= end
; ++it
) {
191 BlackboxWindow
*tmp
= *it
;
192 if (tmp
&& tmp
->isNormal() && tmp
->setInputFocus()) {
193 // we found our new focus target
200 screen
->getBlackbox()->setFocusedWindow(newfocus
);
202 // The window is not on the visible workspace.
204 if (old_window
&& lastfocus
== old_window
) {
205 // The window was the last-focus target, so we need to replace it.
206 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
207 if (! stackingList
.empty())
208 win
= stackingList
.front();
209 setLastFocusedWindow(win
);
215 void Workspace::setFocused(const BlackboxWindow
*w
, bool focused
) {
216 BlackboxWindowList::iterator it
, end
= windowList
.end();
218 for (i
= 0, it
= windowList
.begin(); it
!= end
; ++it
, ++i
)
221 // if its == end, then a window thats not in the windowList
222 // got focused, such as a !isNormal() window.
224 clientmenu
->setItemSelected(i
, focused
);
228 void Workspace::removeAll(void) {
229 while (! windowList
.empty())
230 windowList
.front()->iconify();
233 void Workspace::showAll(void) {
234 BlackboxWindowList::iterator it
= stackingList
.begin();
235 const BlackboxWindowList::iterator end
= stackingList
.end();
236 for (; it
!= end
; ++it
) {
237 BlackboxWindow
*bw
= *it
;
238 // not normal windows cant focus from mouse enters anyways, so we dont
239 // need to unmap/remap them on workspace changes
240 if (! bw
->isStuck() || bw
->isNormal())
246 void Workspace::hideAll(void) {
247 // withdraw in reverse order to minimize the number of Expose events
249 BlackboxWindowList
lst(stackingList
.rbegin(), stackingList
.rend());
251 BlackboxWindowList::iterator it
= lst
.begin();
252 const BlackboxWindowList::iterator end
= lst
.end();
253 for (; it
!= end
; ++it
) {
254 BlackboxWindow
*bw
= *it
;
255 // not normal windows cant focus from mouse enters anyways, so we dont
256 // need to unmap/remap them on workspace changes
257 if (! bw
->isStuck() || bw
->isNormal())
265 * returns the number of transients for win, plus the number of transients
266 * associated with each transient of win
268 static unsigned int countTransients(const BlackboxWindow
* const win
) {
269 BlackboxWindowList transients
= win
->getTransients();
270 if (transients
.empty()) return 0;
272 unsigned int ret
= transients
.size();
273 BlackboxWindowList::const_iterator it
= transients
.begin(),
274 end
= transients
.end();
275 for (; it
!= end
; ++it
)
276 ret
+= countTransients(*it
);
283 * puts the transients of win into the stack. windows are stacked above
284 * the window before it in the stackvector being iterated, meaning
285 * stack[0] is on bottom, stack[1] is above stack[0], stack[2] is above
288 void Workspace::raiseTransients(const BlackboxWindow
* const win
,
289 StackVector::iterator
&stack
) {
290 if (win
->getTransients().empty()) return; // nothing to do
292 // put win's transients in the stack
293 BlackboxWindowList::const_iterator it
, end
= win
->getTransients().end();
294 for (it
= win
->getTransients().begin(); it
!= end
; ++it
) {
295 BlackboxWindow
*w
= *it
;
296 *stack
++ = w
->getFrameWindow();
297 screen
->updateNetizenWindowRaise(w
->getClientWindow());
299 if (! w
->isIconic()) {
300 Workspace
*wkspc
= screen
->getWorkspace(w
->getWorkspaceNumber());
301 wkspc
->stackingList
.remove(w
);
302 wkspc
->stackingList
.push_front(w
);
306 // put transients of win's transients in the stack
307 for (it
= win
->getTransients().begin(); it
!= end
; ++it
)
308 raiseTransients(*it
, stack
);
312 void Workspace::lowerTransients(const BlackboxWindow
* const win
,
313 StackVector::iterator
&stack
) {
314 if (win
->getTransients().empty()) return; // nothing to do
316 // put transients of win's transients in the stack
317 BlackboxWindowList::const_reverse_iterator it
,
318 end
= win
->getTransients().rend();
319 for (it
= win
->getTransients().rbegin(); it
!= end
; ++it
)
320 lowerTransients(*it
, stack
);
322 // put win's transients in the stack
323 for (it
= win
->getTransients().rbegin(); it
!= end
; ++it
) {
324 BlackboxWindow
*w
= *it
;
325 *stack
++ = w
->getFrameWindow();
326 screen
->updateNetizenWindowLower(w
->getClientWindow());
328 if (! w
->isIconic()) {
329 Workspace
*wkspc
= screen
->getWorkspace(w
->getWorkspaceNumber());
330 wkspc
->stackingList
.remove(w
);
331 wkspc
->stackingList
.push_back(w
);
337 void Workspace::raiseWindow(BlackboxWindow
*w
) {
338 BlackboxWindow
*win
= w
;
340 if (win
->isDesktop()) return;
342 // walk up the transient_for's to the window that is not a transient
343 while (win
->isTransient() && win
->getTransientFor())
344 win
= win
->getTransientFor();
346 // get the total window count (win and all transients)
347 unsigned int i
= 1 + countTransients(win
);
349 // stack the window with all transients above
350 StackVector
stack_vector(i
);
351 StackVector::iterator stack
= stack_vector
.begin();
353 *(stack
++) = win
->getFrameWindow();
354 screen
->updateNetizenWindowRaise(win
->getClientWindow());
355 if (! (win
->isIconic() || win
->isDesktop())) {
356 Workspace
*wkspc
= screen
->getWorkspace(win
->getWorkspaceNumber());
357 wkspc
->stackingList
.remove(win
);
358 wkspc
->stackingList
.push_front(win
);
361 raiseTransients(win
, stack
);
363 screen
->raiseWindows(&stack_vector
[0], stack_vector
.size());
367 void Workspace::lowerWindow(BlackboxWindow
*w
) {
368 BlackboxWindow
*win
= w
;
370 // walk up the transient_for's to the window that is not a transient
371 while (win
->isTransient() && win
->getTransientFor())
372 win
= win
->getTransientFor();
374 // get the total window count (win and all transients)
375 unsigned int i
= 1 + countTransients(win
);
377 // stack the window with all transients above
378 StackVector
stack_vector(i
);
379 StackVector::iterator stack
= stack_vector
.begin();
381 lowerTransients(win
, stack
);
383 *(stack
++) = win
->getFrameWindow();
384 screen
->updateNetizenWindowLower(win
->getClientWindow());
385 if (! (win
->isIconic() || win
->isDesktop())) {
386 Workspace
*wkspc
= screen
->getWorkspace(win
->getWorkspaceNumber());
387 wkspc
->stackingList
.remove(win
);
388 wkspc
->stackingList
.push_back(win
);
391 screen
->lowerWindows(&stack_vector
[0], stack_vector
.size());
395 void Workspace::reconfigure(void) {
396 clientmenu
->reconfigure();
397 std::for_each(windowList
.begin(), windowList
.end(),
398 std::mem_fun(&BlackboxWindow::reconfigure
));
402 BlackboxWindow
*Workspace::getWindow(unsigned int index
) {
403 if (index
< windowList
.size()) {
404 BlackboxWindowList::iterator it
= windowList
.begin();
405 while (index
-- > 0) // increment to index
415 Workspace::getNextWindowInList(BlackboxWindow
*w
) {
416 BlackboxWindowList::iterator it
= std::find(windowList
.begin(),
419 assert(it
!= windowList
.end()); // window must be in list
421 if (it
== windowList
.end())
422 return windowList
.front(); // if we walked off the end, wrap around
428 BlackboxWindow
* Workspace::getPrevWindowInList(BlackboxWindow
*w
) {
429 BlackboxWindowList::iterator it
= std::find(windowList
.begin(),
432 assert(it
!= windowList
.end()); // window must be in list
433 if (it
== windowList
.begin())
434 return windowList
.back(); // if we walked of the front, wrap around
440 BlackboxWindow
* Workspace::getTopWindowOnStack(void) const {
441 assert(! stackingList
.empty());
442 return stackingList
.front();
446 void Workspace::sendWindowList(Netizen
&n
) {
447 BlackboxWindowList::iterator it
= windowList
.begin(),
448 end
= windowList
.end();
449 for(; it
!= end
; ++it
)
450 n
.sendWindowAdd((*it
)->getClientWindow(), getID());
454 unsigned int Workspace::getCount(void) const {
455 return windowList
.size();
459 void Workspace::appendStackOrder(BlackboxWindowList
&stack_order
) const {
460 BlackboxWindowList::const_reverse_iterator it
= stackingList
.rbegin();
461 const BlackboxWindowList::const_reverse_iterator end
= stackingList
.rend();
462 for (; it
!= end
; ++it
)
463 if ((*it
)->isNormal())
464 stack_order
.push_back(*it
);
468 bool Workspace::isCurrent(void) const {
469 return (id
== screen
->getCurrentWorkspaceID());
473 bool Workspace::isLastWindow(const BlackboxWindow
* const w
) const {
474 return (w
== windowList
.back());
478 void Workspace::setCurrent(void) {
479 screen
->changeWorkspaceID(id
);
483 void Workspace::readName(void) {
484 XAtom::StringVect namesList
;
485 unsigned long numnames
= id
+ 1;
487 // attempt to get from the _NET_WM_DESKTOP_NAMES property
488 if (xatom
->getValue(screen
->getRootWindow(), XAtom::net_desktop_names
,
489 XAtom::utf8
, numnames
, namesList
) &&
490 namesList
.size() > id
) {
491 name
= namesList
[id
];
493 clientmenu
->setLabel(name
);
494 clientmenu
->update();
497 Use a default name. This doesn't actually change the class. That will
498 happen after the setName changes the root property, and that change
499 makes its way back to this function.
501 string tmp
=i18n(WorkspaceSet
, WorkspaceDefaultNameFormat
,
503 assert(tmp
.length() < 32);
504 char default_name
[32];
505 sprintf(default_name
, tmp
.c_str(), id
+ 1);
507 setName(default_name
); // save this into the _NET_WM_DESKTOP_NAMES property
512 void Workspace::setName(const string
& new_name
) {
513 // set the _NET_WM_DESKTOP_NAMES property with the new name
514 XAtom::StringVect namesList
;
515 unsigned long numnames
= (unsigned) -1;
516 if (xatom
->getValue(screen
->getRootWindow(), XAtom::net_desktop_names
,
517 XAtom::utf8
, numnames
, namesList
) &&
518 namesList
.size() > id
)
519 namesList
[id
] = new_name
;
521 namesList
.push_back(new_name
);
523 xatom
->setValue(screen
->getRootWindow(), XAtom::net_desktop_names
,
524 XAtom::utf8
, namesList
);
529 * Calculate free space available for window placement.
531 typedef std::vector
<Rect
> rectList
;
533 static rectList
calcSpace(const Rect
&win
, const rectList
&spaces
) {
536 rectList::const_iterator siter
, end
= spaces
.end();
537 for (siter
= spaces
.begin(); siter
!= end
; ++siter
) {
538 const Rect
&curr
= *siter
;
540 if(! win
.intersects(curr
)) {
541 result
.push_back(curr
);
545 /* Use an intersection of win and curr to determine the space around
546 * curr that we can use.
548 * NOTE: the spaces calculated can overlap.
553 extra
.setCoords(curr
.left(), curr
.top(),
554 isect
.left() - 1, curr
.bottom());
555 if (extra
.valid()) result
.push_back(extra
);
558 extra
.setCoords(curr
.left(), curr
.top(),
559 curr
.right(), isect
.top() - 1);
560 if (extra
.valid()) result
.push_back(extra
);
563 extra
.setCoords(isect
.right() + 1, curr
.top(),
564 curr
.right(), curr
.bottom());
565 if (extra
.valid()) result
.push_back(extra
);
568 extra
.setCoords(curr
.left(), isect
.bottom() + 1,
569 curr
.right(), curr
.bottom());
570 if (extra
.valid()) result
.push_back(extra
);
576 static bool rowRLBT(const Rect
&first
, const Rect
&second
) {
577 if (first
.bottom() == second
.bottom())
578 return first
.right() > second
.right();
579 return first
.bottom() > second
.bottom();
582 static bool rowRLTB(const Rect
&first
, const Rect
&second
) {
583 if (first
.y() == second
.y())
584 return first
.right() > second
.right();
585 return first
.y() < second
.y();
588 static bool rowLRBT(const Rect
&first
, const Rect
&second
) {
589 if (first
.bottom() == second
.bottom())
590 return first
.x() < second
.x();
591 return first
.bottom() > second
.bottom();
594 static bool rowLRTB(const Rect
&first
, const Rect
&second
) {
595 if (first
.y() == second
.y())
596 return first
.x() < second
.x();
597 return first
.y() < second
.y();
600 static bool colLRTB(const Rect
&first
, const Rect
&second
) {
601 if (first
.x() == second
.x())
602 return first
.y() < second
.y();
603 return first
.x() < second
.x();
606 static bool colLRBT(const Rect
&first
, const Rect
&second
) {
607 if (first
.x() == second
.x())
608 return first
.bottom() > second
.bottom();
609 return first
.x() < second
.x();
612 static bool colRLTB(const Rect
&first
, const Rect
&second
) {
613 if (first
.right() == second
.right())
614 return first
.y() < second
.y();
615 return first
.right() > second
.right();
618 static bool colRLBT(const Rect
&first
, const Rect
&second
) {
619 if (first
.right() == second
.right())
620 return first
.bottom() > second
.bottom();
621 return first
.right() > second
.right();
625 bool Workspace::smartPlacement(Rect
& win
) {
628 //initially the entire screen is free
630 if (screen
->isXineramaActive() &&
631 screen
->getBlackbox()->doXineramaPlacement()) {
632 RectList availableAreas
= screen
->allAvailableAreas();
633 RectList::iterator it
, end
= availableAreas
.end();
635 for (it
= availableAreas
.begin(); it
!= end
; ++it
)
636 spaces
.push_back(*it
);
639 spaces
.push_back(screen
->availableArea());
642 BlackboxWindowList::const_iterator wit
= windowList
.begin(),
643 end
= windowList
.end();
645 for (; wit
!= end
; ++wit
) {
646 const BlackboxWindow
* const curr
= *wit
;
648 // watch for shaded windows and full-maxed windows
649 if (curr
->isShaded()) {
650 if (screen
->getPlaceIgnoreShaded()) continue;
651 } else if (curr
->isMaximizedFull()) {
652 if (screen
->getPlaceIgnoreMaximized()) continue;
655 tmp
.setRect(curr
->frameRect().x(), curr
->frameRect().y(),
656 curr
->frameRect().width() + screen
->getBorderWidth(),
657 curr
->frameRect().height() + screen
->getBorderWidth());
659 spaces
= calcSpace(tmp
, spaces
);
662 if (screen
->getPlacementPolicy() == BScreen::RowSmartPlacement
) {
663 if(screen
->getRowPlacementDirection() == BScreen::LeftRight
) {
664 if(screen
->getColPlacementDirection() == BScreen::TopBottom
)
665 std::sort(spaces
.begin(), spaces
.end(), rowLRTB
);
667 std::sort(spaces
.begin(), spaces
.end(), rowLRBT
);
669 if(screen
->getColPlacementDirection() == BScreen::TopBottom
)
670 std::sort(spaces
.begin(), spaces
.end(), rowRLTB
);
672 std::sort(spaces
.begin(), spaces
.end(), rowRLBT
);
675 if(screen
->getColPlacementDirection() == BScreen::TopBottom
) {
676 if(screen
->getRowPlacementDirection() == BScreen::LeftRight
)
677 std::sort(spaces
.begin(), spaces
.end(), colLRTB
);
679 std::sort(spaces
.begin(), spaces
.end(), colRLTB
);
681 if(screen
->getRowPlacementDirection() == BScreen::LeftRight
)
682 std::sort(spaces
.begin(), spaces
.end(), colLRBT
);
684 std::sort(spaces
.begin(), spaces
.end(), colRLBT
);
688 rectList::const_iterator sit
= spaces
.begin(), spaces_end
= spaces
.end();
689 for(; sit
!= spaces_end
; ++sit
) {
690 if (sit
->width() >= win
.width() && sit
->height() >= win
.height())
694 if (sit
== spaces_end
)
697 //set new position based on the empty space found
698 const Rect
& where
= *sit
;
702 // adjust the location() based on left/right and top/bottom placement
703 if (screen
->getPlacementPolicy() == BScreen::RowSmartPlacement
) {
704 if (screen
->getRowPlacementDirection() == BScreen::RightLeft
)
705 win
.setX(where
.right() - win
.width());
706 if (screen
->getColPlacementDirection() == BScreen::BottomTop
)
707 win
.setY(where
.bottom() - win
.height());
709 if (screen
->getColPlacementDirection() == BScreen::BottomTop
)
710 win
.setY(win
.y() + where
.height() - win
.height());
711 if (screen
->getRowPlacementDirection() == BScreen::RightLeft
)
712 win
.setX(win
.x() + where
.width() - win
.width());
718 bool Workspace::underMousePlacement(Rect
&win
) {
722 XQueryPointer(screen
->getBlackbox()->getXDisplay(), screen
->getRootWindow(),
723 &r
, &c
, &rx
, &ry
, &x
, &y
, &m
);
727 if (screen
->isXineramaActive() &&
728 screen
->getBlackbox()->doXineramaPlacement()) {
729 RectList availableAreas
= screen
->allAvailableAreas();
730 RectList::iterator it
, end
= availableAreas
.end();
732 for (it
= availableAreas
.begin(); it
!= end
; ++it
)
733 if (it
->contains(rx
, ry
)) break;
734 assert(it
!= end
); // the mouse isn't inside an area?
738 area
= screen
->availableArea();
740 x
= rx
- win
.width() / 2;
741 y
= ry
- win
.height() / 2;
747 if (x
+ win
.width() > area
.x() + area
.width())
748 x
= area
.x() + area
.width() - win
.width();
749 if (y
+ win
.height() > area
.y() + area
.height())
750 y
= area
.y() + area
.height() - win
.height();
759 bool Workspace::cascadePlacement(Rect
&win
, const int offset
) {
763 if (screen
->isXineramaActive() &&
764 screen
->getBlackbox()->doXineramaPlacement()) {
765 area
= screen
->allAvailableAreas()[cascade_region
];
768 area
= screen
->availableArea();
770 if ((static_cast<signed>(cascade_x
+ win
.width()) > area
.right() + 1) ||
771 (static_cast<signed>(cascade_y
+ win
.height()) > area
.bottom() + 1)) {
772 cascade_x
= cascade_y
= 0;
774 if (screen
->isXineramaActive() &&
775 screen
->getBlackbox()->doXineramaPlacement()) {
776 // go to the next xinerama region, and use its area
777 if (++cascade_region
>= screen
->allAvailableAreas().size())
779 area
= screen
->allAvailableAreas()[cascade_region
];
784 if (cascade_x
== 0) {
785 cascade_x
= area
.x() + offset
;
786 cascade_y
= area
.y() + offset
;
789 win
.setPos(cascade_x
, cascade_y
);
798 void Workspace::placeWindow(BlackboxWindow
*win
) {
799 Rect
new_win(0, 0, win
->frameRect().width(), win
->frameRect().height());
802 switch (screen
->getPlacementPolicy()) {
803 case BScreen::RowSmartPlacement
:
804 case BScreen::ColSmartPlacement
:
805 placed
= smartPlacement(new_win
);
807 case BScreen::UnderMousePlacement
:
808 case BScreen::ClickMousePlacement
:
809 placed
= underMousePlacement(new_win
);
811 break; // handled below
815 cascadePlacement(new_win
, (win
->getTitleHeight() +
816 screen
->getBorderWidth() * 2));
818 if (new_win
.right() > screen
->availableArea().right())
819 new_win
.setX(screen
->availableArea().left());
820 if (new_win
.bottom() > screen
->availableArea().bottom())
821 new_win
.setY(screen
->availableArea().top());
823 win
->configure(new_win
.x(), new_win
.y(), new_win
.width(), new_win
.height());