1/* 2 * Copyright 2010-2014 Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * John Scipione, jscipione@gmail.com 7 * Clemens Zeidler, haiku@clemens-zeidler.de 8 */ 9 10 11#include "SATWindow.h" 12 13#include <Debug.h> 14 15#include "StackAndTilePrivate.h" 16 17#include "Desktop.h" 18#include "SATGroup.h" 19#include "ServerApp.h" 20#include "Window.h" 21 22 23using namespace BPrivate; 24 25 26// #pragma mark - 27 28 29SATWindow::SATWindow(StackAndTile* sat, Window* window) 30 : 31 fWindow(window), 32 fStackAndTile(sat), 33 34 fWindowArea(NULL), 35 36 fOngoingSnapping(NULL), 37 fSATStacking(this), 38 fSATTiling(this) 39{ 40 fId = _GenerateId(); 41 42 fDesktop = fWindow->Desktop(); 43 44 // read initial limit values 45 fWindow->GetSizeLimits(&fOriginalMinWidth, &fOriginalMaxWidth, 46 &fOriginalMinHeight, &fOriginalMaxHeight); 47 BRect frame = fWindow->Frame(); 48 fOriginalWidth = frame.Width(); 49 fOriginalHeight = frame.Height(); 50 51 fSATSnappingBehaviourList.AddItem(&fSATStacking); 52 fSATSnappingBehaviourList.AddItem(&fSATTiling); 53} 54 55 56SATWindow::~SATWindow() 57{ 58 if (fWindowArea != NULL) 59 fWindowArea->Group()->RemoveWindow(this); 60} 61 62 63SATDecorator* 64SATWindow::GetDecorator() const 65{ 66 return static_cast<SATDecorator*>(fWindow->Decorator()); 67} 68 69 70SATGroup* 71SATWindow::GetGroup() 72{ 73 if (fWindowArea == NULL) { 74 SATGroup* group = new (std::nothrow)SATGroup; 75 if (group == NULL) 76 return group; 77 BReference<SATGroup> groupRef; 78 groupRef.SetTo(group, true); 79 80 /* AddWindow also will trigger the window to hold a reference on the new 81 group. */ 82 if (group->AddWindow(this, NULL, NULL, NULL, NULL) == false) 83 return NULL; 84 } 85 86 ASSERT(fWindowArea != NULL); 87 88 // manually set the tabs of the single window 89 if (PositionManagedBySAT() == false) { 90 BRect frame = CompleteWindowFrame(); 91 fWindowArea->LeftTopCrossing()->VerticalTab()->SetPosition(frame.left); 92 fWindowArea->LeftTopCrossing()->HorizontalTab()->SetPosition(frame.top); 93 fWindowArea->RightBottomCrossing()->VerticalTab()->SetPosition( 94 frame.right); 95 fWindowArea->RightBottomCrossing()->HorizontalTab()->SetPosition( 96 frame.bottom); 97 } 98 99 return fWindowArea->Group(); 100} 101 102 103bool 104SATWindow::HandleMessage(SATWindow* sender, BPrivate::LinkReceiver& link, 105 BPrivate::LinkSender& reply) 106{ 107 int32 target; 108 link.Read<int32>(&target); 109 if (target == kStacking) 110 return StackingEventHandler::HandleMessage(sender, link, reply); 111 112 return false; 113} 114 115 116bool 117SATWindow::PropagateToGroup(SATGroup* group) 118{ 119 if (fWindowArea == NULL) 120 return false; 121 return fWindowArea->PropagateToGroup(group); 122} 123 124 125bool 126SATWindow::AddedToGroup(SATGroup* group, WindowArea* area) 127{ 128 STRACE_SAT("SATWindow::AddedToGroup group: %p window %s\n", group, 129 fWindow->Title()); 130 fWindowArea = area; 131 return true; 132} 133 134 135bool 136SATWindow::RemovedFromGroup(SATGroup* group, bool stayBelowMouse) 137{ 138 STRACE_SAT("SATWindow::RemovedFromGroup group: %p window %s\n", group, 139 fWindow->Title()); 140 141 _RestoreOriginalSize(stayBelowMouse); 142 if (group->CountItems() == 1) 143 group->WindowAt(0)->_RestoreOriginalSize(false); 144 145 return true; 146} 147 148 149bool 150SATWindow::StackWindow(SATWindow* child) 151{ 152 SATGroup* group = GetGroup(); 153 WindowArea* area = GetWindowArea(); 154 if (!group || !area) 155 return false; 156 157 if (group->AddWindow(child, area, this) == false) 158 return false; 159 160 DoGroupLayout(); 161 162 if (fWindow->AddWindowToStack(child->GetWindow()) == false) { 163 group->RemoveWindow(child); 164 DoGroupLayout(); 165 return false; 166 } 167 168 return true; 169} 170 171 172void 173SATWindow::RemovedFromArea(WindowArea* area) 174{ 175 SATDecorator* decorator = GetDecorator(); 176 if (decorator != NULL) 177 fOldTabLocatiom = decorator->TabRect(fWindow->PositionInStack()).left; 178 179 fWindow->DetachFromWindowStack(true); 180 for (int i = 0; i < fSATSnappingBehaviourList.CountItems(); i++) 181 fSATSnappingBehaviourList.ItemAt(i)->RemovedFromArea(area); 182 183 fWindowArea = NULL; 184} 185 186 187void 188SATWindow::WindowLookChanged(window_look look) 189{ 190 for (int i = 0; i < fSATSnappingBehaviourList.CountItems(); i++) 191 fSATSnappingBehaviourList.ItemAt(i)->WindowLookChanged(look); 192} 193 194 195void 196SATWindow::FindSnappingCandidates() 197{ 198 fOngoingSnapping = NULL; 199 200 if (fWindow->Feel() != B_NORMAL_WINDOW_FEEL) 201 return; 202 203 GroupIterator groupIterator(fStackAndTile, GetWindow()->Desktop()); 204 for (SATGroup* group = groupIterator.NextGroup(); group; 205 group = groupIterator.NextGroup()) { 206 if (group->CountItems() == 1 207 && group->WindowAt(0)->GetWindow()->Feel() != B_NORMAL_WINDOW_FEEL) 208 continue; 209 for (int i = 0; i < fSATSnappingBehaviourList.CountItems(); i++) { 210 if (fSATSnappingBehaviourList.ItemAt(i)->FindSnappingCandidates( 211 group)) { 212 fOngoingSnapping = fSATSnappingBehaviourList.ItemAt(i); 213 return; 214 } 215 } 216 } 217} 218 219 220bool 221SATWindow::JoinCandidates() 222{ 223 if (!fOngoingSnapping) 224 return false; 225 bool status = fOngoingSnapping->JoinCandidates(); 226 fOngoingSnapping = NULL; 227 228 return status; 229} 230 231 232void 233SATWindow::DoGroupLayout() 234{ 235 if (!PositionManagedBySAT()) 236 return; 237 238 if (fWindowArea != NULL) 239 fWindowArea->DoGroupLayout(); 240} 241 242 243void 244SATWindow::AdjustSizeLimits(BRect targetFrame) 245{ 246 SATDecorator* decorator = GetDecorator(); 247 if (decorator == NULL) 248 return; 249 250 targetFrame.right -= 2 * (int32)decorator->BorderWidth(); 251 targetFrame.bottom -= 2 * (int32)decorator->BorderWidth() 252 + (int32)decorator->TabHeight() + 1; 253 254 int32 minWidth, maxWidth; 255 int32 minHeight, maxHeight; 256 GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight); 257 258 if (maxWidth < targetFrame.Width()) 259 maxWidth = targetFrame.IntegerWidth(); 260 if (maxHeight < targetFrame.Height()) 261 maxHeight = targetFrame.IntegerHeight(); 262 263 fWindow->SetSizeLimits(minWidth, maxWidth, minHeight, maxHeight); 264} 265 266 267void 268SATWindow::GetSizeLimits(int32* minWidth, int32* maxWidth, int32* minHeight, 269 int32* maxHeight) const 270{ 271 *minWidth = fOriginalMinWidth; 272 *minHeight = fOriginalMinHeight; 273 *maxWidth = fOriginalMaxWidth; 274 *maxHeight = fOriginalMaxHeight; 275 276 SATDecorator* decorator = GetDecorator(); 277 if (decorator == NULL) 278 return; 279 280 int32 minDecorWidth = 1, maxDecorWidth = 1; 281 int32 minDecorHeight = 1, maxDecorHeight = 1; 282 decorator->GetSizeLimits(&minDecorWidth, &minDecorHeight, 283 &maxDecorWidth, &maxDecorHeight); 284 285 // if no size limit is set but the window is not resizeable choose the 286 // current size as limit 287 if (IsHResizeable() == false && fOriginalMinWidth <= minDecorWidth) 288 *minWidth = (int32)fOriginalWidth; 289 if (IsVResizeable() == false && fOriginalMinHeight <= minDecorHeight) 290 *minHeight = (int32)fOriginalHeight; 291 292 if (*minWidth > *maxWidth) 293 *maxWidth = *minWidth; 294 if (*minHeight > *maxHeight) 295 *maxHeight = *minHeight; 296} 297 298 299void 300SATWindow::AddDecorator(int32* minWidth, int32* maxWidth, int32* minHeight, 301 int32* maxHeight) 302{ 303 SATDecorator* decorator = GetDecorator(); 304 if (decorator == NULL) 305 return; 306 307 *minWidth += 2 * (int32)decorator->BorderWidth(); 308 *minHeight += 2 * (int32)decorator->BorderWidth() 309 + (int32)decorator->TabHeight() + 1; 310 *maxWidth += 2 * (int32)decorator->BorderWidth(); 311 *maxHeight += 2 * (int32)decorator->BorderWidth() 312 + (int32)decorator->TabHeight() + 1; 313} 314 315 316void 317SATWindow::AddDecorator(BRect& frame) 318{ 319 SATDecorator* decorator = GetDecorator(); 320 if (!decorator) 321 return; 322 frame.left -= decorator->BorderWidth(); 323 frame.right += decorator->BorderWidth() + 1; 324 frame.top -= decorator->BorderWidth() + decorator->TabHeight() + 1; 325 frame.bottom += decorator->BorderWidth(); 326} 327 328 329void 330SATWindow::SetOriginalSizeLimits(int32 minWidth, int32 maxWidth, 331 int32 minHeight, int32 maxHeight) 332{ 333 fOriginalMinWidth = minWidth; 334 fOriginalMaxWidth = maxWidth; 335 fOriginalMinHeight = minHeight; 336 fOriginalMaxHeight = maxHeight; 337 338 if (fWindowArea != NULL) 339 fWindowArea->UpdateSizeLimits(); 340} 341 342 343void 344SATWindow::Resized() 345{ 346 bool hResizeable = IsHResizeable(); 347 bool vResizeable = IsVResizeable(); 348 if (hResizeable == false && vResizeable == false) 349 return; 350 351 BRect frame = fWindow->Frame(); 352 if (hResizeable) 353 fOriginalWidth = frame.Width(); 354 if (vResizeable) 355 fOriginalHeight = frame.Height(); 356 357 if (fWindowArea != NULL) 358 fWindowArea->UpdateSizeConstaints(CompleteWindowFrame()); 359} 360 361 362bool 363SATWindow::IsHResizeable() const 364{ 365 if (fWindow->Look() == B_MODAL_WINDOW_LOOK 366 || fWindow->Look() == B_BORDERED_WINDOW_LOOK 367 || fWindow->Look() == B_NO_BORDER_WINDOW_LOOK 368 || (fWindow->Flags() & B_NOT_RESIZABLE) != 0 369 || (fWindow->Flags() & B_NOT_H_RESIZABLE) != 0) 370 return false; 371 return true; 372} 373 374 375bool 376SATWindow::IsVResizeable() const 377{ 378 if (fWindow->Look() == B_MODAL_WINDOW_LOOK 379 || fWindow->Look() == B_BORDERED_WINDOW_LOOK 380 || fWindow->Look() == B_NO_BORDER_WINDOW_LOOK 381 || (fWindow->Flags() & B_NOT_RESIZABLE) != 0 382 || (fWindow->Flags() & B_NOT_V_RESIZABLE) != 0) 383 return false; 384 return true; 385} 386 387 388BRect 389SATWindow::CompleteWindowFrame() 390{ 391 BRect frame = fWindow->Frame(); 392 if (fDesktop 393 && fDesktop->CurrentWorkspace() != fWindow->CurrentWorkspace()) { 394 window_anchor& anchor = fWindow->Anchor(fWindow->CurrentWorkspace()); 395 if (anchor.position != kInvalidWindowPosition) 396 frame.OffsetTo(anchor.position); 397 } 398 399 AddDecorator(frame); 400 return frame; 401} 402 403 404bool 405SATWindow::PositionManagedBySAT() 406{ 407 if (fWindowArea == NULL || fWindowArea->Group()->CountItems() == 1) 408 return false; 409 410 return true; 411} 412 413 414bool 415SATWindow::HighlightTab(bool active) 416{ 417 SATDecorator* decorator = GetDecorator(); 418 if (!decorator) 419 return false; 420 421 int32 tabIndex = fWindow->PositionInStack(); 422 BRegion dirty; 423 uint8 highlight = active ? SATDecorator::HIGHLIGHT_STACK_AND_TILE : 0; 424 decorator->SetRegionHighlight(Decorator::REGION_TAB, highlight, &dirty, 425 tabIndex); 426 decorator->SetRegionHighlight(Decorator::REGION_CLOSE_BUTTON, highlight, 427 &dirty, tabIndex); 428 decorator->SetRegionHighlight(Decorator::REGION_ZOOM_BUTTON, highlight, 429 &dirty, tabIndex); 430 431 fWindow->TopLayerStackWindow()->ProcessDirtyRegion(dirty); 432 return true; 433} 434 435 436bool 437SATWindow::HighlightBorders(Decorator::Region region, bool active) 438{ 439 SATDecorator* decorator = GetDecorator(); 440 if (!decorator) 441 return false; 442 443 BRegion dirty; 444 uint8 highlight = active ? SATDecorator::HIGHLIGHT_STACK_AND_TILE : 0; 445 decorator->SetRegionHighlight(region, highlight, &dirty); 446 447 fWindow->ProcessDirtyRegion(dirty); 448 return true; 449} 450 451 452uint64 453SATWindow::Id() 454{ 455 return fId; 456} 457 458 459bool 460SATWindow::SetSettings(const BMessage& message) 461{ 462 uint64 id; 463 if (message.FindInt64("window_id", (int64*)&id) != B_OK) 464 return false; 465 fId = id; 466 return true; 467} 468 469 470void 471SATWindow::GetSettings(BMessage& message) 472{ 473 message.AddInt64("window_id", fId); 474} 475 476 477uint64 478SATWindow::_GenerateId() 479{ 480 bigtime_t time = real_time_clock_usecs(); 481 srand(time); 482 int16 randNumber = rand(); 483 return (time & ~0xFFFF) | randNumber; 484} 485 486 487void 488SATWindow::_RestoreOriginalSize(bool stayBelowMouse) 489{ 490 // restore size 491 fWindow->SetSizeLimits(fOriginalMinWidth, fOriginalMaxWidth, 492 fOriginalMinHeight, fOriginalMaxHeight); 493 BRect frame = fWindow->Frame(); 494 float x = 0, y = 0; 495 if (IsHResizeable() == false) 496 x = fOriginalWidth - frame.Width(); 497 if (IsVResizeable() == false) 498 y = fOriginalHeight - frame.Height(); 499 fDesktop->ResizeWindowBy(fWindow, x, y); 500 501 if (!stayBelowMouse) 502 return; 503 // verify that the window stays below the mouse 504 BPoint mousePosition; 505 int32 buttons; 506 fDesktop->GetLastMouseState(&mousePosition, &buttons); 507 SATDecorator* decorator = GetDecorator(); 508 if (decorator == NULL) 509 return; 510 BRect tabRect = decorator->TitleBarRect(); 511 if (mousePosition.y < tabRect.bottom && mousePosition.y > tabRect.top 512 && mousePosition.x <= frame.right + decorator->BorderWidth() +1 513 && mousePosition.x >= frame.left + decorator->BorderWidth()) { 514 // verify mouse stays on the tab 515 float oldOffset = mousePosition.x - fOldTabLocatiom; 516 float deltaX = mousePosition.x - (tabRect.left + oldOffset); 517 fDesktop->MoveWindowBy(fWindow, deltaX, 0); 518 } else { 519 // verify mouse stays on the border 520 float deltaX = 0; 521 float deltaY = 0; 522 BRect newFrame = fWindow->Frame(); 523 if (x != 0 && mousePosition.x > frame.left 524 && mousePosition.x > newFrame.right) { 525 deltaX = mousePosition.x - newFrame.right; 526 if (mousePosition.x > frame.right) 527 deltaX -= mousePosition.x - frame.right; 528 } 529 if (y != 0 && mousePosition.y > frame.top 530 && mousePosition.y > newFrame.bottom) { 531 deltaY = mousePosition.y - newFrame.bottom; 532 if (mousePosition.y > frame.bottom) 533 deltaY -= mousePosition.y - frame.bottom; 534 } 535 fDesktop->MoveWindowBy(fWindow, deltaX, deltaY); 536 } 537} 538