#include <stdio.h>
#include <List.h>
#include <View.h>
#include "Desktop.h"
#include "DrawingEngine.h"
#include "WindowLayer.h"
#include "ViewLayer.h"
extern BWindow* wind;
ViewLayer::ViewLayer(BRect frame, const char* name,
uint32 resizeMode, uint32 flags,
rgb_color viewColor)
: fName(name),
fFrame(frame),
fScrollingOffset(0.0, 0.0),
fViewColor(viewColor),
fResizeMode(resizeMode),
fFlags(flags),
fHidden(false),
fVisible(true),
fWindow(NULL),
fParent(NULL),
fFirstChild(NULL),
fPreviousSibling(NULL),
fNextSibling(NULL),
fLastChild(NULL),
fCurrentChild(NULL),
fLocalClipping(Bounds()),
fScreenClipping(),
fScreenClippingValid(false)
{
fFrame.left = float((int32)fFrame.left);
fFrame.top = float((int32)fFrame.top);
fFrame.right = float((int32)fFrame.right);
fFrame.bottom = float((int32)fFrame.bottom);
}
ViewLayer::~ViewLayer()
{
ViewLayer* layer = fFirstChild;
while (layer) {
ViewLayer* toast = layer;
layer = layer->fNextSibling;
delete toast;
}
}
BRect
ViewLayer::Bounds() const
{
BRect bounds(fScrollingOffset.x, fScrollingOffset.y,
fScrollingOffset.x + fFrame.Width(),
fScrollingOffset.y + fFrame.Height());
return bounds;
}
void
ViewLayer::ConvertToVisibleInTopView(BRect* bounds) const
{
*bounds = *bounds & Bounds();
ConvertToParent(bounds);
if (fParent)
fParent->ConvertToVisibleInTopView(bounds);
}
void
ViewLayer::AttachedToWindow(WindowLayer* window)
{
fWindow = window;
for (ViewLayer* child = FirstChild(); child; child = NextChild())
child->AttachedToWindow(window);
}
void
ViewLayer::DetachedFromWindow()
{
fWindow = NULL;
for (ViewLayer* child = FirstChild(); child; child = NextChild())
child->DetachedFromWindow();
}
void
ViewLayer::AddChild(ViewLayer* layer)
{
if (layer->fParent) {
printf("ViewLayer::AddChild() - ViewLayer already has a parent\n");
return;
}
layer->fParent = this;
if (!fLastChild) {
fFirstChild = layer;
} else {
fLastChild->fNextSibling = layer;
layer->fPreviousSibling = fLastChild;
}
fLastChild = layer;
if (layer->IsVisible())
RebuildClipping(false);
if (fWindow) {
layer->AttachedToWindow(fWindow);
if (fVisible && layer->IsVisible()) {
BRect clippedFrame = layer->Frame();
ConvertToVisibleInTopView(&clippedFrame);
BRegion dirty(clippedFrame);
fWindow->MarkContentDirty(&dirty);
}
}
}
bool
ViewLayer::RemoveChild(ViewLayer* layer)
{
if (layer->fParent != this) {
printf("ViewLayer::RemoveChild(%p - %s) - ViewLayer is not child of this (%p) layer!\n", layer, layer ? layer->Name() : NULL, this);
return false;
}
layer->fParent = NULL;
if (fLastChild == layer)
fLastChild = layer->fPreviousSibling;
if (fFirstChild == layer )
fFirstChild = layer->fNextSibling;
if (layer->fPreviousSibling)
layer->fPreviousSibling->fNextSibling = layer->fNextSibling;
if (layer->fNextSibling)
layer->fNextSibling->fPreviousSibling = layer->fPreviousSibling;
layer->fPreviousSibling = NULL;
layer->fNextSibling = NULL;
if (layer->IsVisible())
RebuildClipping(false);
if (fWindow) {
layer->DetachedFromWindow();
if (fVisible && layer->IsVisible()) {
BRect clippedFrame = layer->Frame();
ConvertToVisibleInTopView(&clippedFrame);
BRegion dirty(clippedFrame);
fWindow->MarkContentDirty(&dirty);
}
}
return true;
}
ViewLayer*
ViewLayer::FirstChild() const
{
fCurrentChild = fFirstChild;
return fCurrentChild;
}
ViewLayer*
ViewLayer::PreviousChild() const
{
fCurrentChild = fCurrentChild->fPreviousSibling;
return fCurrentChild;
}
ViewLayer*
ViewLayer::NextChild() const
{
fCurrentChild = fCurrentChild->fNextSibling;
return fCurrentChild;
}
ViewLayer*
ViewLayer::LastChild() const
{
fCurrentChild = fLastChild;
return fCurrentChild;
}
ViewLayer*
ViewLayer::TopLayer()
{
if (fParent)
return fParent->TopLayer();
return this;
}
uint32
ViewLayer::CountChildren(bool deep) const
{
uint32 count = 0;
for (ViewLayer* child = FirstChild(); child; child = NextChild()) {
count++;
if (deep) {
count += child->CountChildren(deep);
}
}
return count;
}
void
ViewLayer::CollectTokensForChildren(BList* tokenMap) const
{
for (ViewLayer* child = FirstChild(); child; child = NextChild()) {
tokenMap->AddItem((void*)child);
child->CollectTokensForChildren(tokenMap);
}
}
ViewLayer*
ViewLayer::ViewAt(const BPoint& where, BRegion* windowContentClipping)
{
if (!fVisible)
return NULL;
if (ScreenClipping(windowContentClipping).Contains(where))
return this;
for (ViewLayer* child = FirstChild(); child; child = NextChild()) {
ViewLayer* layer = child->ViewAt(where, windowContentClipping);
if (layer)
return layer;
}
return NULL;
}
void
ViewLayer::ConvertToParent(BPoint* point) const
{
point->x += fFrame.left - fScrollingOffset.x;
point->y += fFrame.top - fScrollingOffset.y;
}
void
ViewLayer::ConvertToParent(BRect* rect) const
{
rect->OffsetBy(fFrame.left - fScrollingOffset.x,
fFrame.top - fScrollingOffset.y);
}
void
ViewLayer::ConvertToParent(BRegion* region) const
{
region->OffsetBy(fFrame.left - fScrollingOffset.x,
fFrame.top - fScrollingOffset.y);
}
void
ViewLayer::ConvertFromParent(BPoint* point) const
{
point->x += fScrollingOffset.x - fFrame.left;
point->y += fScrollingOffset.y - fFrame.top;
}
void
ViewLayer::ConvertFromParent(BRect* rect) const
{
rect->OffsetBy(fScrollingOffset.x - fFrame.left,
fScrollingOffset.y - fFrame.top);
}
void
ViewLayer::ConvertFromParent(BRegion* region) const
{
region->OffsetBy(fScrollingOffset.x - fFrame.left,
fScrollingOffset.y - fFrame.top);
}
void
ViewLayer::ConvertToTop(BPoint* point) const
{
ConvertToParent(point);
if (fParent)
fParent->ConvertToTop(point);
}
void
ViewLayer::ConvertToTop(BRect* rect) const
{
ConvertToParent(rect);
if (fParent)
fParent->ConvertToTop(rect);
}
void
ViewLayer::ConvertToTop(BRegion* region) const
{
ConvertToParent(region);
if (fParent)
fParent->ConvertToTop(region);
}
void
ViewLayer::ConvertFromTop(BPoint* point) const
{
ConvertFromParent(point);
if (fParent)
fParent->ConvertFromTop(point);
}
void
ViewLayer::ConvertFromTop(BRect* rect) const
{
ConvertFromParent(rect);
if (fParent)
fParent->ConvertFromTop(rect);
}
void
ViewLayer::ConvertFromTop(BRegion* region) const
{
ConvertFromParent(region);
if (fParent)
fParent->ConvertFromTop(region);
}
void
ViewLayer::SetName(const char* string)
{
fName.SetTo(string);
}
void
ViewLayer::MoveBy(int32 x, int32 y, BRegion* dirtyRegion)
{
if (x == 0 && y == 0)
return;
fFrame.OffsetBy(x, y);
if (fVisible && fParent && dirtyRegion) {
#if 0
BRect newVisibleBounds = Bounds();
BRect oldVisibleBounds(Bounds());
oldVisibleBounds.OffsetBy(-x, -y);
ConvertToTop(&oldVisibleBounds);
ConvertToVisibleInTopView(&newVisibleBounds);
dirtyRegion->Include(oldVisibleBounds);
dirtyRegion->Include(newVisibleBounds);
#else
BRect oldVisibleBounds(Bounds());
oldVisibleBounds.OffsetBy(-x, -y);
ConvertToTop(&oldVisibleBounds);
BRect newVisibleBounds(Bounds());
ConvertToVisibleInTopView(&newVisibleBounds);
newVisibleBounds.OffsetBy(-x, -y);
BRegion copyRegion(oldVisibleBounds & newVisibleBounds);
fWindow->CopyContents(©Region, x, y);
BRegion dirty(oldVisibleBounds);
newVisibleBounds.OffsetBy(x, y);
dirty.Exclude(newVisibleBounds);
dirtyRegion->Include(&dirty);
#endif
}
if (!fParent) {
_MoveScreenClipping(x, y, true);
} else {
InvalidateScreenClipping(true);
}
}
void
ViewLayer::ResizeBy(int32 x, int32 y, BRegion* dirtyRegion)
{
if (x == 0 && y == 0)
return;
fFrame.right += x;
fFrame.bottom += y;
if (fVisible && dirtyRegion) {
BRect oldBounds(Bounds());
oldBounds.right -= x;
oldBounds.bottom -= y;
BRegion dirty(Bounds());
dirty.Include(oldBounds);
if (!(fFlags & B_FULL_UPDATE_ON_RESIZE)) {
dirty.Exclude(oldBounds & Bounds());
}
InvalidateScreenClipping(true);
if (dirty.CountRects() > 0) {
for (ViewLayer* child = FirstChild(); child; child = NextChild()) {
if (child->IsVisible()) {
BRect previousChildVisible(child->Frame() & oldBounds & Bounds());
if (dirty.Frame().Intersects(previousChildVisible)) {
dirty.Exclude(previousChildVisible);
}
}
}
ConvertToTop(&dirty);
dirtyRegion->Include(&dirty);
}
}
for (ViewLayer* child = FirstChild(); child; child = NextChild())
child->ParentResized(x, y, dirtyRegion);
RebuildClipping(false);
}
void
ViewLayer::ParentResized(int32 x, int32 y, BRegion* dirtyRegion)
{
uint16 rm = fResizeMode & 0x0000FFFF;
BRect newFrame = fFrame;
if ((rm & 0x0F00U) == _VIEW_RIGHT_ << 8)
newFrame.left += x;
else if ((rm & 0x0F00U) == _VIEW_CENTER_ << 8)
newFrame.left += x / 2;
if ((rm & 0x000FU) == _VIEW_RIGHT_)
newFrame.right += x;
else if ((rm & 0x000FU) == _VIEW_CENTER_)
newFrame.right += x / 2;
if ((rm & 0xF000U) == _VIEW_BOTTOM_ << 12)
newFrame.top += y;
else if ((rm & 0xF000U) == _VIEW_CENTER_ << 12)
newFrame.top += y / 2;
if ((rm & 0x00F0U) == _VIEW_BOTTOM_ << 4)
newFrame.bottom += y;
else if ((rm & 0x00F0U) == _VIEW_CENTER_ << 4)
newFrame.bottom += y / 2;
if (newFrame != fFrame) {
int32 widthDiff = (int32)(newFrame.Width() - fFrame.Width());
int32 heightDiff = (int32)(newFrame.Height() - fFrame.Height());
MoveBy(newFrame.left - fFrame.left,
newFrame.top - fFrame.top, dirtyRegion);
ResizeBy(widthDiff, heightDiff, dirtyRegion);
}
}
void
ViewLayer::ScrollBy(int32 x, int32 y, BRegion* dirtyRegion)
{
BRect oldBounds(Bounds());
BRect stillVisibleBounds(oldBounds);
stillVisibleBounds.OffsetBy(x, y);
ConvertToVisibleInTopView(&oldBounds);
ConvertToVisibleInTopView(&stillVisibleBounds);
fScrollingOffset.x += x;
fScrollingOffset.y += y;
BRegion copyRegion(stillVisibleBounds);
fWindow->CopyContents(©Region, -x, -y);
BRegion dirty(oldBounds);
stillVisibleBounds.OffsetBy(-x, -y);
dirty.Exclude(stillVisibleBounds);
dirtyRegion->Include(&dirty);
InvalidateScreenClipping(true);
RebuildClipping(false);
}
void
ViewLayer::Draw(DrawingEngine* drawingEngine, BRegion* effectiveClipping,
BRegion* windowContentClipping, bool deep)
{
BRegion redraw(ScreenClipping(windowContentClipping));
redraw.IntersectWith(effectiveClipping);
drawingEngine->FillRegion(&redraw, (rgb_color){ 255, 255, 255, 255 });
if (deep) {
effectiveClipping->Exclude(&ScreenClipping(windowContentClipping));
for (ViewLayer* child = FirstChild(); child; child = NextChild()) {
child->Draw(drawingEngine, effectiveClipping,
windowContentClipping, deep);
}
}
}
void
ViewLayer::ClientDraw(DrawingEngine* drawingEngine, BRegion* effectiveClipping)
{
BRect b(Bounds());
b.OffsetTo(0.0, 0.0);
ConvertToTop(&b);
if (drawingEngine->Lock()) {
drawingEngine->ConstrainClipping(effectiveClipping);
drawingEngine->StrokeRect(b, fViewColor);
b.InsetBy(1, 1);
drawingEngine->StrokeRect(b, fViewColor);
b.InsetBy(1, 1);
drawingEngine->StrokeRect(b, fViewColor);
b.InsetBy(1, 1);
drawingEngine->StrokeLine(b.LeftTop(), b.RightBottom(), fViewColor);
drawingEngine->Unlock();
}
}
void
ViewLayer::SetHidden(bool hidden)
{
if (fHidden != hidden) {
fHidden = hidden;
if (fParent) {
bool olfVisible = fVisible;
UpdateVisibleDeep(fParent->IsVisible());
if (olfVisible != fVisible) {
fParent->RebuildClipping(false);
if (fWindow) {
BRect clippedBounds = Bounds();
ConvertToVisibleInTopView(&clippedBounds);
BRegion dirty(clippedBounds);
fWindow->MarkContentDirty(&dirty);
}
}
} else {
UpdateVisibleDeep(true);
}
}
}
bool
ViewLayer::IsHidden() const
{
return fHidden;
}
void
ViewLayer::UpdateVisibleDeep(bool parentVisible)
{
fVisible = parentVisible && !fHidden;
for (ViewLayer* child = FirstChild(); child; child = NextChild())
child->UpdateVisibleDeep(fVisible);
}
void
ViewLayer::PrintToStream() const
{
}
void
ViewLayer::RebuildClipping(bool deep)
{
fLocalClipping.Set(Bounds());
for (ViewLayer* child = FirstChild(); child; child = NextChild()) {
if (child->IsVisible())
fLocalClipping.Exclude(child->Frame());
if (deep)
child->RebuildClipping(deep);
}
fScreenClippingValid = false;
}
BRegion&
ViewLayer::ScreenClipping(BRegion* windowContentClipping, bool force) const
{
if (!fScreenClippingValid || force) {
fScreenClipping = fLocalClipping;
ConvertToTop(&fScreenClipping);
BRect clippedBounds = Bounds();
ConvertToVisibleInTopView(&clippedBounds);
if (clippedBounds.Width() < fScreenClipping.Frame().Width() ||
clippedBounds.Height() < fScreenClipping.Frame().Height()) {
BRegion temp(clippedBounds);
fScreenClipping.IntersectWith(&temp);
}
fScreenClipping.IntersectWith(windowContentClipping);
fScreenClippingValid = true;
}
return fScreenClipping;
}
void
ViewLayer::InvalidateScreenClipping(bool deep)
{
fScreenClippingValid = false;
if (deep) {
for (ViewLayer* child = FirstChild(); child; child = NextChild()) {
child->InvalidateScreenClipping(deep);
}
}
}
void
ViewLayer::_MoveScreenClipping(int32 x, int32 y, bool deep)
{
if (fScreenClippingValid)
fScreenClipping.OffsetBy(x, y);
if (deep) {
for (ViewLayer* child = FirstChild(); child; child = NextChild()) {
child->_MoveScreenClipping(x, y, deep);
}
}
}