#include <stdio.h>
#include <Application.h>
#include <Message.h>
#include <MessageQueue.h>
#include <Messenger.h>
#include <Window.h>
#include "DrawingEngine.h"
#include "DrawView.h"
#include "WindowLayer.h"
#include "Desktop.h"
Desktop::Desktop(DrawView* drawView, DrawingEngine* engine)
: BLooper("desktop", B_URGENT_DISPLAY_PRIORITY),
fTracking(false),
fLastMousePos(-1.0, -1.0),
fClickedWindow(NULL),
fScrollingView(NULL),
fResizing(false),
fIs2ndButton(false),
fClippingLock("clipping lock"),
fBackgroundRegion(),
fMasterClipping(),
fXOffset(0),
fYOffset(0),
fDrawView(drawView),
fDrawingEngine(engine),
fWindows(64),
fFocusFollowsMouse(true),
fFocusWindow(NULL)
{
fDrawView->SetDesktop(this);
BRegion stillAvailableOnScreen;
_RebuildClippingForAllWindows(&stillAvailableOnScreen);
_SetBackground(&stillAvailableOnScreen);
}
Desktop::~Desktop()
{
}
void
Desktop::MouseDown(BPoint where, uint32 buttons, int32 clicks)
{
fLastMousePos = where;
fClickedWindow = WindowAt(where);
fClickTime = system_time();
if (buttons == B_PRIMARY_MOUSE_BUTTON) {
fTracking = true;
if (fClickedWindow) {
if (modifiers() & B_SHIFT_KEY) {
fScrollingView = fClickedWindow->ViewAt(where);
} else if (clicks >= 2) {
HideWindow(fClickedWindow);
fClickedWindow = NULL;
} else {
BRect frame(fClickedWindow->Frame());
BRect resizeRect(frame.right - 10, frame.bottom - 10,
frame.right + 4, frame.bottom + 4);
fResizing = resizeRect.Contains(where);
}
}
} else if (buttons == B_SECONDARY_MOUSE_BUTTON) {
if (fClickedWindow)
SendToBack(fClickedWindow);
fIs2ndButton = true;
} else if (buttons == B_TERTIARY_MOUSE_BUTTON) {
fDrawView->Invalidate();
}
}
void
Desktop::MouseUp(BPoint where)
{
if (!fIs2ndButton && system_time() - fClickTime < 250000L && fClickedWindow) {
BringToFront(fClickedWindow);
}
fTracking = false;
fIs2ndButton = false;
fClickedWindow = NULL;
fScrollingView = NULL;
}
void
Desktop::MouseMoved(BPoint where, uint32 code, const BMessage* dragMessage)
{
WindowLayer* window;
if (!fTracking && fFocusFollowsMouse && (window = WindowAt(where))) {
SetFocusWindow(window);
}
if (fTracking) {
int32 dx = (int32)(where.x - fLastMousePos.x);
int32 dy = (int32)(where.y - fLastMousePos.y);
fLastMousePos = where;
if (dx != 0 || dy != 0) {
if (fClickedWindow) {
if (fScrollingView) {
if (LockClipping()) {
fClickedWindow->ScrollViewBy(fScrollingView, -dx, -dy);
UnlockClipping();
}
} else if (fResizing) {
ResizeWindowBy(fClickedWindow, dx, dy);
} else {
MoveWindowBy(fClickedWindow, dx, dy);
}
}
}
} else if (fIs2ndButton) {
fDrawingEngine->StrokeLine(fLastMousePos, where, (rgb_color){ 0, 0, 0, 255 });
fLastMousePos = where;
}
}
void
Desktop::MessageReceived(BMessage* message)
{
switch (message->what) {
case B_MOUSE_DOWN: {
BPoint where;
uint32 buttons;
int32 clicks;
if (message->FindPoint("where", &where) >= B_OK &&
message->FindInt32("buttons", (int32*)&buttons) >= B_OK &&
message->FindInt32("clicks", &clicks) >= B_OK) {
where.x += fXOffset;
where.y += fYOffset;
MouseDown(where, buttons, clicks);
}
break;
}
case B_MOUSE_UP: {
BPoint where;
if (message->FindPoint("where", &where) >= B_OK) {
where.x += fXOffset;
where.y += fYOffset;
MouseUp(where);
}
break;
}
case B_MOUSE_MOVED: {
if (!MessageQueue()->FindMessage(B_MOUSE_MOVED, 0)) {
BPoint where;
uint32 transit;
if (message->FindPoint("where", &where) >= B_OK &&
message->FindInt32("be:transit", (int32*)&transit) >= B_OK) {
where.x += fXOffset;
where.y += fYOffset;
MouseMoved(where, transit, NULL);
}
}
break;
}
case MSG_ADD_WINDOW: {
WindowLayer* window;
if (message->FindPointer("window", (void**)&window) >= B_OK)
AddWindow(window);
break;
}
case MSG_QUIT:
if (LockClipping()) {
int32 count = CountWindows();
for (int32 i = 0; i < count; i++)
WindowAtFast(i)->PostMessage(B_QUIT_REQUESTED);
UnlockClipping();
}
break;
default:
BLooper::MessageReceived(message);
}
}
void
Desktop::SetMasterClipping(BRegion* clipping)
{
BRegion update = *clipping;
update.Exclude(&fMasterClipping);
fMasterClipping = *clipping;
BRegion background;
_RebuildClippingForAllWindows(&background);
_SetBackground(&background);
fDrawingEngine->FillRegion(&fBackgroundRegion, (rgb_color){ 51, 102, 152, 255 });
update.Exclude(&fBackgroundRegion);
MarkDirty(&update);
}
void
Desktop::SetOffset(int32 x, int32 y)
{
fXOffset = x;
fYOffset = y;
}
bool
Desktop::AddWindow(WindowLayer* window)
{
bool success = false;
if (fWindows.AddItem((void*)window)) {
if (LockClipping()) {
BRegion background;
_RebuildClippingForAllWindows(&background);
fBackgroundRegion.Exclude(&window->VisibleRegion());
MarkDirty(&window->VisibleRegion());
_SetBackground(&background);
UnlockClipping();
}
SetFocusWindow(window);
success = true;
}
return success;
}
bool
Desktop::RemoveWindow(WindowLayer* window)
{
bool success = false;
if (fWindows.RemoveItem((void*)window)) {
if (LockClipping()) {
BRegion dirty = window->VisibleRegion();
BRegion background;
_RebuildClippingForAllWindows(&background);
MarkDirty(&dirty);
_SetBackground(&background);
UnlockClipping();
}
success = true;
}
return success;
}
int32
Desktop::IndexOf(WindowLayer* window) const
{
return fWindows.IndexOf((void*)window);
}
int32
Desktop::CountWindows() const
{
return fWindows.CountItems();
}
bool
Desktop::HasWindow(WindowLayer* window) const
{
return fWindows.HasItem((void*)window);
}
WindowLayer*
Desktop::WindowAt(int32 index) const
{
return (WindowLayer*)fWindows.ItemAt(index);
}
WindowLayer*
Desktop::WindowAtFast(int32 index) const
{
return (WindowLayer*)fWindows.ItemAtFast(index);
}
WindowLayer*
Desktop::WindowAt(const BPoint& where) const
{
int32 count = CountWindows();
for (int32 i = count - 1; i >= 0; i--) {
WindowLayer* window = WindowAtFast(i);
if (!window->IsHidden() && window->VisibleRegion().Contains(where))
return window;
}
return NULL;
}
WindowLayer*
Desktop::TopWindow() const
{
return (WindowLayer*)fWindows.LastItem();
}
WindowLayer*
Desktop::BottomWindow() const
{
return (WindowLayer*)fWindows.FirstItem();
}
#pragma mark -
void
Desktop::MoveWindowBy(WindowLayer* window, int32 x, int32 y)
{
if (!Lock())
return;
if (LockClipping()) {
BRegion newDirtyRegion(window->VisibleRegion());
window->MoveBy(x, y);
BRegion background;
_RebuildClippingForAllWindows(&background);
BRegion copyRegion(window->VisibleRegion());
copyRegion.OffsetBy(-x, -y);
copyRegion.IntersectWith(&newDirtyRegion);
newDirtyRegion.Include(&window->VisibleRegion());
fDrawingEngine->CopyRegion(©Region, x, y);
copyRegion.OffsetBy(x, y);
newDirtyRegion.Exclude(©Region);
MarkDirty(&newDirtyRegion);
_SetBackground(&background);
UnlockClipping();
}
Unlock();
}
void
Desktop::ResizeWindowBy(WindowLayer* window, int32 x, int32 y)
{
if (!Lock())
return;
if (LockClipping()) {
BRegion newDirtyRegion;
BRegion previouslyOccupiedRegion(window->VisibleRegion());
window->ResizeBy(x, y, &newDirtyRegion);
BRegion background;
_RebuildClippingForAllWindows(&background);
previouslyOccupiedRegion.Exclude(&window->VisibleRegion());
newDirtyRegion.IntersectWith(&window->VisibleRegion());
newDirtyRegion.Include(&previouslyOccupiedRegion);
MarkDirty(&newDirtyRegion);
_SetBackground(&background);
UnlockClipping();
}
Unlock();
}
void
Desktop::ShowWindow(WindowLayer* window)
{
SetWindowHidden(window, false);
}
void
Desktop::HideWindow(WindowLayer* window)
{
SetWindowHidden(window, true);
}
void
Desktop::SetWindowHidden(WindowLayer* window, bool hidden)
{
if (LockClipping()) {
if (window->IsHidden() != hidden) {
window->SetHidden(hidden);
BRegion dirty;
if (hidden) {
dirty = window->VisibleRegion();
}
BRegion background;
_RebuildClippingForAllWindows(&background);
_SetBackground(&background);
if (!hidden) {
dirty = window->VisibleRegion();
window->ProcessDirtyRegion(&dirty);
} else {
MarkDirty(&dirty);
}
}
UnlockClipping();
}
}
void
Desktop::BringToFront(WindowLayer* window)
{
if (window == TopWindow())
return;
if (LockClipping()) {
BRegion clean(window->VisibleRegion());
if (fWindows.RemoveItem((void*)window) &&
fWindows.AddItem((void*)window)) {
BRegion dummy;
_RebuildClippingForAllWindows(&dummy);
BRegion dirty(window->VisibleRegion());
dirty.Exclude(&clean);
MarkDirty(&dirty);
}
UnlockClipping();
}
if (!fFocusFollowsMouse)
SetFocusWindow(TopWindow());
}
void
Desktop::SendToBack(WindowLayer* window)
{
if (window == BottomWindow())
return;
if (LockClipping()) {
BRegion dirty(window->VisibleRegion());
if (fWindows.RemoveItem((void*)window) &&
fWindows.AddItem((void*)window, 0)) {
BRegion dummy;
_RebuildClippingForAllWindows(&dummy);
BRegion clean(window->VisibleRegion());
dirty.Exclude(&clean);
MarkDirty(&dirty);
}
UnlockClipping();
}
if (!fFocusFollowsMouse)
SetFocusWindow(TopWindow());
}
void
Desktop::SetFocusWindow(WindowLayer* window)
{
if (fFocusWindow == window)
return;
if (LockClipping()) {
if (fFocusWindow)
fFocusWindow->SetFocus(false);
fFocusWindow = window;
if (fFocusWindow)
fFocusWindow->SetFocus(true);
UnlockClipping();
}
}
void
Desktop::MarkDirty(BRegion* region)
{
if (region->CountRects() == 0)
return;
if (LockClipping()) {
_TriggerWindowRedrawing(region);
UnlockClipping();
}
}
void
Desktop::WindowDied(WindowLayer* window)
{
fWindows.RemoveItem(window);
if (fWindows.CountItems() == 0)
be_app->PostMessage(B_QUIT_REQUESTED);
}
void
Desktop::_RebuildClippingForAllWindows(BRegion* stillAvailableOnScreen)
{
*stillAvailableOnScreen = fMasterClipping;
int32 count = CountWindows();
for (int32 i = count - 1; i >= 0; i--) {
WindowLayer* window = WindowAtFast(i);
if (!window->IsHidden()) {
window->SetClipping(stillAvailableOnScreen);
stillAvailableOnScreen->Exclude(&window->VisibleRegion());
}
}
}
void
Desktop::_TriggerWindowRedrawing(BRegion* newDirtyRegion)
{
int32 count = CountWindows();
for (int32 i = count - 1; i >= 0; i--) {
WindowLayer* window = WindowAtFast(i);
if (!window->IsHidden() && newDirtyRegion->Intersects(window->VisibleRegion().Frame()))
window->ProcessDirtyRegion(newDirtyRegion);
}
}
void
Desktop::_SetBackground(BRegion* background)
{
BRegion dirtyBackground(*background);
dirtyBackground.Exclude(&fBackgroundRegion);
dirtyBackground.IntersectWith(background);
fBackgroundRegion = *background;
if (dirtyBackground.Frame().IsValid()) {
fDrawingEngine->FillRegion(&dirtyBackground, (rgb_color){ 51, 102, 152, 255 });
}
}