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 23 using namespace BPrivate; 24 25 26 // #pragma mark - 27 28 29 SATWindow::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 56 SATWindow::~SATWindow() 57 { 58 if (fWindowArea != NULL) 59 fWindowArea->Group()->RemoveWindow(this); 60 } 61 62 63 SATDecorator* 64 SATWindow::GetDecorator() const 65 { 66 return static_cast<SATDecorator*>(fWindow->Decorator()); 67 } 68 69 70 SATGroup* 71 SATWindow::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 103 bool 104 SATWindow::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 116 bool 117 SATWindow::PropagateToGroup(SATGroup* group) 118 { 119 if (fWindowArea == NULL) 120 return false; 121 return fWindowArea->PropagateToGroup(group); 122 } 123 124 125 bool 126 SATWindow::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 135 bool 136 SATWindow::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 149 bool 150 SATWindow::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 172 void 173 SATWindow::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 187 void 188 SATWindow::WindowLookChanged(window_look look) 189 { 190 for (int i = 0; i < fSATSnappingBehaviourList.CountItems(); i++) 191 fSATSnappingBehaviourList.ItemAt(i)->WindowLookChanged(look); 192 } 193 194 195 void 196 SATWindow::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 220 bool 221 SATWindow::JoinCandidates() 222 { 223 if (!fOngoingSnapping) 224 return false; 225 bool status = fOngoingSnapping->JoinCandidates(); 226 fOngoingSnapping = NULL; 227 228 return status; 229 } 230 231 232 void 233 SATWindow::DoGroupLayout() 234 { 235 if (!PositionManagedBySAT()) 236 return; 237 238 if (fWindowArea != NULL) 239 fWindowArea->DoGroupLayout(); 240 } 241 242 243 void 244 SATWindow::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 267 void 268 SATWindow::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 299 void 300 SATWindow::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 316 void 317 SATWindow::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 329 void 330 SATWindow::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 343 void 344 SATWindow::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 362 bool 363 SATWindow::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 375 bool 376 SATWindow::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 388 BRect 389 SATWindow::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 404 bool 405 SATWindow::PositionManagedBySAT() 406 { 407 if (fWindowArea == NULL || fWindowArea->Group()->CountItems() == 1) 408 return false; 409 410 return true; 411 } 412 413 414 bool 415 SATWindow::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 436 bool 437 SATWindow::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 452 uint64 453 SATWindow::Id() 454 { 455 return fId; 456 } 457 458 459 bool 460 SATWindow::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 470 void 471 SATWindow::GetSettings(BMessage& message) 472 { 473 message.AddInt64("window_id", fId); 474 } 475 476 477 uint64 478 SATWindow::_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 487 void 488 SATWindow::_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