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