* Copyright 2010-2014 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* John Scipione, jscipione@gmail.com
* Clemens Zeidler, haiku@clemens-zeidler.de
*/
#include "SATWindow.h"
#include <Debug.h>
#include "StackAndTilePrivate.h"
#include "Desktop.h"
#include "SATGroup.h"
#include "ServerApp.h"
#include "Window.h"
using namespace BPrivate;
SATWindow::SATWindow(StackAndTile* sat, Window* window)
:
fWindow(window),
fStackAndTile(sat),
fWindowArea(NULL),
fOngoingSnapping(NULL),
fSATStacking(this),
fSATTiling(this)
{
fId = _GenerateId();
fDesktop = fWindow->Desktop();
fWindow->GetSizeLimits(&fOriginalMinWidth, &fOriginalMaxWidth,
&fOriginalMinHeight, &fOriginalMaxHeight);
BRect frame = fWindow->Frame();
fOriginalWidth = frame.Width();
fOriginalHeight = frame.Height();
fSATSnappingBehaviourList.AddItem(&fSATStacking);
fSATSnappingBehaviourList.AddItem(&fSATTiling);
}
SATWindow::~SATWindow()
{
if (fWindowArea != NULL)
fWindowArea->Group()->RemoveWindow(this);
}
SATDecorator*
SATWindow::GetDecorator() const
{
return static_cast<SATDecorator*>(fWindow->Decorator());
}
SATGroup*
SATWindow::GetGroup()
{
if (fWindowArea == NULL) {
SATGroup* group = new (std::nothrow)SATGroup;
if (group == NULL)
return group;
BReference<SATGroup> groupRef;
groupRef.SetTo(group, true);
group. */
if (group->AddWindow(this, NULL, NULL, NULL, NULL) == false)
return NULL;
}
ASSERT(fWindowArea != NULL);
if (PositionManagedBySAT() == false) {
BRect frame = CompleteWindowFrame();
fWindowArea->LeftTopCrossing()->VerticalTab()->SetPosition(frame.left);
fWindowArea->LeftTopCrossing()->HorizontalTab()->SetPosition(frame.top);
fWindowArea->RightBottomCrossing()->VerticalTab()->SetPosition(
frame.right);
fWindowArea->RightBottomCrossing()->HorizontalTab()->SetPosition(
frame.bottom);
}
return fWindowArea->Group();
}
bool
SATWindow::HandleMessage(SATWindow* sender, BPrivate::LinkReceiver& link,
BPrivate::LinkSender& reply)
{
int32 target;
link.Read<int32>(&target);
if (target == kStacking)
return StackingEventHandler::HandleMessage(sender, link, reply);
return false;
}
bool
SATWindow::PropagateToGroup(SATGroup* group)
{
if (fWindowArea == NULL)
return false;
return fWindowArea->PropagateToGroup(group);
}
bool
SATWindow::AddedToGroup(SATGroup* group, WindowArea* area)
{
STRACE_SAT("SATWindow::AddedToGroup group: %p window %s\n", group,
fWindow->Title());
fWindowArea = area;
return true;
}
bool
SATWindow::RemovedFromGroup(SATGroup* group, bool stayBelowMouse)
{
STRACE_SAT("SATWindow::RemovedFromGroup group: %p window %s\n", group,
fWindow->Title());
_RestoreOriginalSize(stayBelowMouse);
if (group->CountItems() == 1)
group->WindowAt(0)->_RestoreOriginalSize(false);
return true;
}
bool
SATWindow::StackWindow(SATWindow* child)
{
SATGroup* group = GetGroup();
WindowArea* area = GetWindowArea();
if (!group || !area)
return false;
if (group->AddWindow(child, area, this) == false)
return false;
DoGroupLayout();
if (fWindow->AddWindowToStack(child->GetWindow()) == false) {
group->RemoveWindow(child);
DoGroupLayout();
return false;
}
return true;
}
void
SATWindow::RemovedFromArea(WindowArea* area)
{
SATDecorator* decorator = GetDecorator();
if (decorator != NULL)
fOldTabLocatiom = decorator->TabRect(fWindow->PositionInStack()).left;
fWindow->DetachFromWindowStack(true);
for (int i = 0; i < fSATSnappingBehaviourList.CountItems(); i++)
fSATSnappingBehaviourList.ItemAt(i)->RemovedFromArea(area);
fWindowArea = NULL;
}
void
SATWindow::WindowLookChanged(window_look look)
{
for (int i = 0; i < fSATSnappingBehaviourList.CountItems(); i++)
fSATSnappingBehaviourList.ItemAt(i)->WindowLookChanged(look);
}
void
SATWindow::FindSnappingCandidates()
{
fOngoingSnapping = NULL;
if (fWindow->Feel() != B_NORMAL_WINDOW_FEEL)
return;
GroupIterator groupIterator(fStackAndTile, GetWindow()->Desktop());
for (SATGroup* group = groupIterator.NextGroup(); group;
group = groupIterator.NextGroup()) {
if (group->CountItems() == 1
&& group->WindowAt(0)->GetWindow()->Feel() != B_NORMAL_WINDOW_FEEL)
continue;
for (int i = 0; i < fSATSnappingBehaviourList.CountItems(); i++) {
if (fSATSnappingBehaviourList.ItemAt(i)->FindSnappingCandidates(
group)) {
fOngoingSnapping = fSATSnappingBehaviourList.ItemAt(i);
return;
}
}
}
}
bool
SATWindow::JoinCandidates()
{
if (!fOngoingSnapping)
return false;
bool status = fOngoingSnapping->JoinCandidates();
fOngoingSnapping = NULL;
return status;
}
void
SATWindow::DoGroupLayout()
{
if (!PositionManagedBySAT())
return;
if (fWindowArea != NULL)
fWindowArea->DoGroupLayout();
}
void
SATWindow::AdjustSizeLimits(BRect targetFrame)
{
SATDecorator* decorator = GetDecorator();
if (decorator == NULL)
return;
targetFrame.right -= 2 * (int32)decorator->BorderWidth();
targetFrame.bottom -= 2 * (int32)decorator->BorderWidth()
+ (int32)decorator->TabHeight() + 1;
int32 minWidth, maxWidth;
int32 minHeight, maxHeight;
GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight);
if (maxWidth < targetFrame.Width())
maxWidth = targetFrame.IntegerWidth();
if (maxHeight < targetFrame.Height())
maxHeight = targetFrame.IntegerHeight();
fWindow->SetSizeLimits(minWidth, maxWidth, minHeight, maxHeight);
}
void
SATWindow::GetSizeLimits(int32* minWidth, int32* maxWidth, int32* minHeight,
int32* maxHeight) const
{
*minWidth = fOriginalMinWidth;
*minHeight = fOriginalMinHeight;
*maxWidth = fOriginalMaxWidth;
*maxHeight = fOriginalMaxHeight;
SATDecorator* decorator = GetDecorator();
if (decorator == NULL)
return;
int32 minDecorWidth = 1, maxDecorWidth = 1;
int32 minDecorHeight = 1, maxDecorHeight = 1;
decorator->GetSizeLimits(&minDecorWidth, &minDecorHeight,
&maxDecorWidth, &maxDecorHeight);
if (IsHResizeable() == false && fOriginalMinWidth <= minDecorWidth)
*minWidth = (int32)fOriginalWidth;
if (IsVResizeable() == false && fOriginalMinHeight <= minDecorHeight)
*minHeight = (int32)fOriginalHeight;
if (*minWidth > *maxWidth)
*maxWidth = *minWidth;
if (*minHeight > *maxHeight)
*maxHeight = *minHeight;
}
void
SATWindow::AddDecorator(int32* minWidth, int32* maxWidth, int32* minHeight,
int32* maxHeight)
{
SATDecorator* decorator = GetDecorator();
if (decorator == NULL)
return;
*minWidth += 2 * (int32)decorator->BorderWidth();
*minHeight += 2 * (int32)decorator->BorderWidth()
+ (int32)decorator->TabHeight() + 1;
*maxWidth += 2 * (int32)decorator->BorderWidth();
*maxHeight += 2 * (int32)decorator->BorderWidth()
+ (int32)decorator->TabHeight() + 1;
}
void
SATWindow::AddDecorator(BRect& frame)
{
SATDecorator* decorator = GetDecorator();
if (!decorator)
return;
frame.left -= decorator->BorderWidth();
frame.right += decorator->BorderWidth() + 1;
frame.top -= decorator->BorderWidth() + decorator->TabHeight() + 1;
frame.bottom += decorator->BorderWidth();
}
void
SATWindow::SetOriginalSizeLimits(int32 minWidth, int32 maxWidth,
int32 minHeight, int32 maxHeight)
{
fOriginalMinWidth = minWidth;
fOriginalMaxWidth = maxWidth;
fOriginalMinHeight = minHeight;
fOriginalMaxHeight = maxHeight;
if (fWindowArea != NULL)
fWindowArea->UpdateSizeLimits();
}
void
SATWindow::Resized()
{
bool hResizeable = IsHResizeable();
bool vResizeable = IsVResizeable();
if (hResizeable == false && vResizeable == false)
return;
BRect frame = fWindow->Frame();
if (hResizeable)
fOriginalWidth = frame.Width();
if (vResizeable)
fOriginalHeight = frame.Height();
if (fWindowArea != NULL)
fWindowArea->UpdateSizeConstaints(CompleteWindowFrame());
}
bool
SATWindow::IsHResizeable() const
{
if (fWindow->Look() == B_MODAL_WINDOW_LOOK
|| fWindow->Look() == B_BORDERED_WINDOW_LOOK
|| fWindow->Look() == B_NO_BORDER_WINDOW_LOOK
|| (fWindow->Flags() & B_NOT_RESIZABLE) != 0
|| (fWindow->Flags() & B_NOT_H_RESIZABLE) != 0)
return false;
return true;
}
bool
SATWindow::IsVResizeable() const
{
if (fWindow->Look() == B_MODAL_WINDOW_LOOK
|| fWindow->Look() == B_BORDERED_WINDOW_LOOK
|| fWindow->Look() == B_NO_BORDER_WINDOW_LOOK
|| (fWindow->Flags() & B_NOT_RESIZABLE) != 0
|| (fWindow->Flags() & B_NOT_V_RESIZABLE) != 0)
return false;
return true;
}
BRect
SATWindow::CompleteWindowFrame()
{
BRect frame = fWindow->Frame();
if (fDesktop && fWindow->IsVisible()
&& fDesktop->CurrentWorkspace() != fWindow->CurrentWorkspace()) {
window_anchor& anchor = fWindow->Anchor(fWindow->CurrentWorkspace());
if (anchor.position != kInvalidWindowPosition)
frame.OffsetTo(anchor.position);
} else if (fDesktop && !fWindow->IsVisible() && fWindow->PriorWorkspace() >= 0
&& fDesktop->CurrentWorkspace() != fWindow->PriorWorkspace()) {
window_anchor& anchor = fWindow->Anchor(fWindow->PriorWorkspace());
if (anchor.position != kInvalidWindowPosition)
frame.OffsetTo(anchor.position);
}
AddDecorator(frame);
return frame;
}
bool
SATWindow::PositionManagedBySAT()
{
if (fWindowArea == NULL || fWindowArea->Group()->CountItems() == 1)
return false;
return true;
}
bool
SATWindow::HighlightTab(bool active)
{
SATDecorator* decorator = GetDecorator();
if (!decorator)
return false;
int32 tabIndex = fWindow->PositionInStack();
BRegion dirty;
uint8 highlight = active ? SATDecorator::HIGHLIGHT_STACK_AND_TILE : 0;
decorator->SetRegionHighlight(Decorator::REGION_TAB, highlight, &dirty,
tabIndex);
decorator->SetRegionHighlight(Decorator::REGION_CLOSE_BUTTON, highlight,
&dirty, tabIndex);
decorator->SetRegionHighlight(Decorator::REGION_ZOOM_BUTTON, highlight,
&dirty, tabIndex);
fWindow->TopLayerStackWindow()->ProcessDirtyRegion(dirty);
return true;
}
bool
SATWindow::HighlightBorders(Decorator::Region region, bool active)
{
SATDecorator* decorator = GetDecorator();
if (!decorator)
return false;
BRegion dirty;
uint8 highlight = active ? SATDecorator::HIGHLIGHT_STACK_AND_TILE : 0;
decorator->SetRegionHighlight(region, highlight, &dirty);
fWindow->ProcessDirtyRegion(dirty);
return true;
}
uint64
SATWindow::Id()
{
return fId;
}
bool
SATWindow::SetSettings(const BMessage& message)
{
uint64 id;
if (message.FindInt64("window_id", (int64*)&id) != B_OK)
return false;
fId = id;
return true;
}
void
SATWindow::GetSettings(BMessage& message)
{
message.AddInt64("window_id", fId);
}
uint64
SATWindow::_GenerateId()
{
bigtime_t time = real_time_clock_usecs();
srand(time);
int16 randNumber = rand();
return (time & ~0xFFFF) | randNumber;
}
void
SATWindow::_RestoreOriginalSize(bool stayBelowMouse)
{
fWindow->SetSizeLimits(fOriginalMinWidth, fOriginalMaxWidth,
fOriginalMinHeight, fOriginalMaxHeight);
BRect frame = fWindow->Frame();
float x = 0, y = 0;
if (IsHResizeable() == false)
x = fOriginalWidth - frame.Width();
if (IsVResizeable() == false)
y = fOriginalHeight - frame.Height();
fDesktop->ResizeWindowBy(fWindow, x, y);
if (!stayBelowMouse)
return;
BPoint mousePosition;
int32 buttons;
fDesktop->GetLastMouseState(&mousePosition, &buttons);
SATDecorator* decorator = GetDecorator();
if (decorator == NULL)
return;
BRect tabRect = decorator->TitleBarRect();
if (mousePosition.y < tabRect.bottom && mousePosition.y > tabRect.top
&& mousePosition.x <= frame.right + decorator->BorderWidth() +1
&& mousePosition.x >= frame.left + decorator->BorderWidth()) {
float oldOffset = mousePosition.x - fOldTabLocatiom;
float deltaX = mousePosition.x - (tabRect.left + oldOffset);
fDesktop->MoveWindowBy(fWindow, deltaX, 0);
} else {
float deltaX = 0;
float deltaY = 0;
BRect newFrame = fWindow->Frame();
if (x != 0 && mousePosition.x > frame.left
&& mousePosition.x > newFrame.right) {
deltaX = mousePosition.x - newFrame.right;
if (mousePosition.x > frame.right)
deltaX -= mousePosition.x - frame.right;
}
if (y != 0 && mousePosition.y > frame.top
&& mousePosition.y > newFrame.bottom) {
deltaY = mousePosition.y - newFrame.bottom;
if (mousePosition.y > frame.bottom)
deltaY -= mousePosition.y - frame.bottom;
}
fDesktop->MoveWindowBy(fWindow, deltaX, deltaY);
}
}