* Copyright (c) 2001-2015, Haiku, Inc.
* Distributed under the terms of the MIT license.
*
* Authors:
* DarkWyrm <bpmagic@columbus.rr.com>
* Adi Oanca <adioanca@gmail.com>
* Axel Dörfler, axeld@pinc-software.de
* Stephan Aßmus <superstippi@gmx.de>
* Marcus Overhagen <marcus@overhagen.de>
* Adrien Destugues <pulkomandy@pulkomandy.tk
* Julian Harnath <julian.harnath@rwth-aachen.de>
* Joseph Groover <looncraz@looncraz.net>
*/
#include "View.h"
#include <new>
#include <stdio.h>
#include "AlphaMask.h"
#include "Desktop.h"
#include "DrawingEngine.h"
#include "DrawState.h"
#include "Layer.h"
#include "Overlay.h"
#include "ServerApp.h"
#include "ServerBitmap.h"
#include "ServerCursor.h"
#include "ServerPicture.h"
#include "ServerWindow.h"
#include "Window.h"
#include "BitmapHWInterface.h"
#include "drawing_support.h"
#include <List.h>
#include <Message.h>
#include <PortLink.h>
#include <View.h>
#include <WindowPrivate.h>
#include <GradientLinear.h>
#include <GradientRadial.h>
#include <GradientRadialFocus.h>
#include <GradientDiamond.h>
#include <GradientConic.h>
using std::nothrow;
void
resize_frame(IntRect& frame, uint32 resizingMode, int32 x, int32 y)
{
if ((resizingMode & 0x0F00U) == _VIEW_RIGHT_ << 8)
frame.left += x;
else if ((resizingMode & 0x0F00U) == _VIEW_CENTER_ << 8)
frame.left += x / 2;
if ((resizingMode & 0x000FU) == _VIEW_RIGHT_)
frame.right += x;
else if ((resizingMode & 0x000FU) == _VIEW_CENTER_)
frame.right += x / 2;
if ((resizingMode & 0xF000U) == _VIEW_BOTTOM_ << 12)
frame.top += y;
else if ((resizingMode & 0xF000U) == _VIEW_CENTER_ << 12)
frame.top += y / 2;
if ((resizingMode & 0x00F0U) == _VIEW_BOTTOM_ << 4)
frame.bottom += y;
else if ((resizingMode & 0x00F0U) == _VIEW_CENTER_ << 4)
frame.bottom += y / 2;
}
View::View(IntRect frame, IntPoint scrollingOffset, const char* name,
int32 token, uint32 resizeMode, uint32 flags)
:
fName(name),
fToken(token),
fFrame(frame),
fScrollingOffset(scrollingOffset),
fViewColor((rgb_color){ 255, 255, 255, 255 }),
fWhichViewColor(B_NO_COLOR),
fWhichViewColorTint(B_NO_TINT),
fViewBitmap(NULL),
fBitmapResizingMode(0),
fBitmapOptions(0),
fResizeMode(resizeMode),
fFlags(flags),
fHidden(false),
fVisible(true),
fBackgroundDirty(true),
fIsDesktopBackground(false),
fEventMask(0),
fEventOptions(0),
fWindow(NULL),
fParent(NULL),
fFirstChild(NULL),
fPreviousSibling(NULL),
fNextSibling(NULL),
fLastChild(NULL),
fCursor(NULL),
fPicture(NULL),
fLocalClipping((BRect)Bounds()),
fScreenClipping(),
fScreenClippingValid(false),
fUserClipping(NULL),
fScreenAndUserClipping(NULL)
{
if (fDrawState.IsSet())
fDrawState->SetSubPixelPrecise(fFlags & B_SUBPIXEL_PRECISE);
}
View::~View()
{
View* view = fFirstChild;
while (view) {
View* toast = view;
view = view->fNextSibling;
delete toast;
}
}
IntRect
View::Bounds() const
{
IntRect bounds(fScrollingOffset.x, fScrollingOffset.y,
fScrollingOffset.x + fFrame.Width(),
fScrollingOffset.y + fFrame.Height());
return bounds;
}
void
View::ConvertToVisibleInTopView(IntRect* bounds) const
{
*bounds = *bounds & Bounds();
bounds->OffsetBy(fFrame.left - fScrollingOffset.x,
fFrame.top - fScrollingOffset.y);
if (fParent)
fParent->ConvertToVisibleInTopView(bounds);
}
void
View::AttachedToWindow(::Window* window)
{
fWindow = window;
if (window->Feel() == kDesktopWindowFeel && Parent() == TopView())
fIsDesktopBackground = true;
if (fWindow != NULL) {
fWindow->ServerWindow()->App()->ViewTokens().SetToken(fToken,
B_HANDLER_TOKEN, this);
}
for (View* child = FirstChild(); child; child = child->NextSibling())
child->AttachedToWindow(window);
}
void
View::DetachedFromWindow()
{
if (fWindow != NULL && fWindow->ServerWindow()->App() != NULL)
fWindow->ServerWindow()->App()->ViewTokens().RemoveToken(fToken);
fWindow = NULL;
for (View* child = FirstChild(); child; child = child->NextSibling())
child->DetachedFromWindow();
}
DrawingEngine*
View::GetDrawingEngine() const
{
return Window()->GetDrawingEngine();
}
ServerPicture*
View::GetPicture(int32 token) const
{
return Window()->ServerWindow()->App()->GetPicture(token);
}
void
View::ResyncDrawState()
{
return Window()->ServerWindow()->ResyncDrawState();
}
void
View::UpdateCurrentDrawingRegion()
{
return Window()->ServerWindow()->UpdateCurrentDrawingRegion();
}
void
View::AddChild(View* view)
{
if (view->fParent) {
printf("View::AddChild() - View already has a parent\n");
return;
}
view->fParent = this;
if (!fLastChild) {
fFirstChild = view;
} else {
fLastChild->fNextSibling = view;
view->fPreviousSibling = fLastChild;
}
fLastChild = view;
view->UpdateVisibleDeep(fVisible);
if (view->IsVisible())
RebuildClipping(false);
if (fWindow) {
view->AttachedToWindow(fWindow);
if (view->IsVisible()) {
IntRect clippedFrame = view->Frame();
ConvertToVisibleInTopView(&clippedFrame);
BRegion dirty;
dirty.Set((clipping_rect)clippedFrame);
fWindow->MarkContentDirtyAsync(dirty);
}
}
}
bool
View::RemoveChild(View* view)
{
if (view == NULL || view->fParent != this) {
printf("View::RemoveChild(%p - %s) - View is not child of "
"this (%p) view!\n", view, view ? view->Name() : NULL, this);
return false;
}
view->fParent = NULL;
if (fLastChild == view)
fLastChild = view->fPreviousSibling;
if (fFirstChild == view )
fFirstChild = view->fNextSibling;
if (view->fPreviousSibling)
view->fPreviousSibling->fNextSibling = view->fNextSibling;
if (view->fNextSibling)
view->fNextSibling->fPreviousSibling = view->fPreviousSibling;
view->fPreviousSibling = NULL;
view->fNextSibling = NULL;
if (view->IsVisible()) {
Overlay* overlay = view->_Overlay();
if (overlay != NULL)
overlay->Hide();
RebuildClipping(false);
}
if (fWindow) {
view->DetachedFromWindow();
if (fVisible && view->IsVisible()) {
IntRect clippedFrame = view->Frame();
ConvertToVisibleInTopView(&clippedFrame);
BRegion dirty;
dirty.Set((clipping_rect)clippedFrame);
fWindow->MarkContentDirtyAsync(dirty);
}
}
return true;
}
View*
View::TopView()
{
if (fParent)
return fParent->TopView();
return this;
}
uint32
View::CountChildren(bool deep) const
{
uint32 count = 0;
for (View* child = FirstChild(); child; child = child->NextSibling()) {
count++;
if (deep) {
count += child->CountChildren(deep);
}
}
return count;
}
void
View::CollectTokensForChildren(BList* tokenMap) const
{
for (View* child = FirstChild(); child; child = child->NextSibling()) {
tokenMap->AddItem((void*)child);
child->CollectTokensForChildren(tokenMap);
}
}
#if 0
bool
View::MarkAt(DrawingEngine* engine, const BPoint& where, int32 level)
{
BRect rect(fFrame.left, fFrame.top, fFrame.right, fFrame.bottom);
if (Parent() != NULL) {
Parent()->ConvertToScreen(&rect);
if (!rect.Contains(where))
return false;
engine->StrokeRect(rect, (rgb_color){level * 30, level * 30, level * 30});
}
bool found = false;
for (View* child = FirstChild(); child; child = child->NextSibling()) {
found |= child->MarkAt(engine, where, level + 1);
}
if (!found) {
rgb_color color = {0};
switch (level % 2) {
case 0: color.green = rand() % 256; break;
case 1: color.blue = rand() % 256; break;
}
rect.InsetBy(1, 1);
engine->StrokeRect(rect, color);
rect.InsetBy(1, 1);
engine->StrokeRect(rect, color);
}
return true;
}
#endif
void
View::FindViews(uint32 flags, BObjectList<View>& list, int32& left)
{
if ((Flags() & flags) == flags) {
list.AddItem(this);
left--;
return;
}
for (View* child = FirstChild(); child; child = child->NextSibling()) {
child->FindViews(flags, list, left);
if (left == 0)
break;
}
}
bool
View::HasView(View* view)
{
if (view == this)
return true;
for (View* child = FirstChild(); child; child = child->NextSibling()) {
if (child->HasView(view))
return true;
}
return false;
}
View*
View::ViewAt(const BPoint& where)
{
if (!fVisible)
return NULL;
IntRect frame = Frame();
if (Parent() != NULL)
Parent()->LocalToScreenTransform().Apply(&frame);
if (!frame.Contains(where))
return NULL;
for (View* child = FirstChild(); child; child = child->NextSibling()) {
View* view = child->ViewAt(where);
if (view != NULL)
return view;
}
return this;
}
void
View::SetName(const char* string)
{
fName.SetTo(string);
}
void
View::SetFlags(uint32 flags)
{
uint32 oldFlags = fFlags;
fFlags = flags;
if (fParent != NULL
&& IsVisible()
&& (((oldFlags & B_TRANSPARENT_BACKGROUND) != 0)
!= ((fFlags & B_TRANSPARENT_BACKGROUND) != 0))) {
fParent->RebuildClipping(false);
}
fDrawState->SetSubPixelPrecise(fFlags & B_SUBPIXEL_PRECISE);
}
void
View::SetViewBitmap(ServerBitmap* bitmap, IntRect sourceRect,
IntRect destRect, int32 resizingMode, int32 options)
{
if (fViewBitmap != NULL) {
Overlay* overlay = _Overlay();
if (bitmap != NULL) {
Overlay* newOverlay = bitmap->Overlay();
if (overlay != NULL && newOverlay != NULL)
newOverlay->TakeOverToken(overlay);
} else if (overlay != NULL)
overlay->Hide();
}
fViewBitmap.SetTo(bitmap, false);
fBitmapSource = sourceRect;
fBitmapDestination = destRect;
fBitmapResizingMode = resizingMode;
fBitmapOptions = options;
_UpdateOverlayView();
}
::Overlay*
View::_Overlay() const
{
if (fViewBitmap == NULL)
return NULL;
return fViewBitmap->Overlay();
}
void
View::_UpdateOverlayView() const
{
Overlay* overlay = _Overlay();
if (overlay == NULL)
return;
IntRect destination = fBitmapDestination;
LocalToScreenTransform().Apply(&destination);
overlay->Configure(fBitmapSource, destination);
}
This method is called whenever the window is resized or moved - would
be nice to have a better solution for this, though.
*/
void
View::UpdateOverlay()
{
if (!IsVisible())
return;
if (_Overlay() != NULL) {
_UpdateOverlayView();
} else {
for (View* child = FirstChild(); child; child = child->NextSibling()) {
child->UpdateOverlay();
}
}
}
void
View::_LocalToScreenTransform(SimpleTransform& transform) const
{
const View* view = this;
int32 offsetX = 0;
int32 offsetY = 0;
do {
offsetX += view->fFrame.left - view->fScrollingOffset.x;
offsetY += view->fFrame.top - view->fScrollingOffset.y;
view = view->fParent;
} while (view != NULL);
transform.AddOffset(offsetX, offsetY);
}
void
View::_ScreenToLocalTransform(SimpleTransform& transform) const
{
const View* view = this;
int32 offsetX = 0;
int32 offsetY = 0;
do {
offsetX += view->fScrollingOffset.x - view->fFrame.left;
offsetY += view->fScrollingOffset.y - view->fFrame.top;
view = view->fParent;
} while (view != NULL);
transform.AddOffset(offsetX, offsetY);
}
void
View::MoveBy(int32 x, int32 y, BRegion* dirtyRegion)
{
if (x == 0 && y == 0)
return;
fFrame.OffsetBy(x, y);
if (fVisible && fParent && dirtyRegion) {
#if 1
IntRect newVisibleBounds(Bounds());
IntRect oldVisibleBounds(newVisibleBounds);
oldVisibleBounds.OffsetBy(-x, -y);
LocalToScreenTransform().Apply(&oldVisibleBounds);
ConvertToVisibleInTopView(&newVisibleBounds);
dirtyRegion->Include((clipping_rect)oldVisibleBounds);
dirtyRegion->Include((clipping_rect)newVisibleBounds);
#else
IntRect oldVisibleBounds(Bounds());
IntRect newVisibleBounds(oldVisibleBounds);
oldVisibleBounds.OffsetBy(-x, -y);
LocalToScreenTransform().Apply(&oldVisibleBounds);
ConvertToVisibleInTopView(&newVisibleBounds);
newVisibleBounds.OffsetBy(-x, -y);
BRegion* region = fWindow->GetRegion();
if (region) {
region->Set(oldVisibleBounds & newVisibleBounds);
fWindow->CopyContents(region, x, y);
region->Set(oldVisibleBounds);
newVisibleBounds.OffsetBy(x, y);
region->Exclude((clipping_rect)newVisibleBounds);
dirtyRegion->Include(dirty);
fWindow->RecycleRegion(region);
}
#endif
}
if (!fParent) {
_MoveScreenClipping(x, y, true);
} else {
InvalidateScreenClipping();
}
}
void
View::ResizeBy(int32 x, int32 y, BRegion* dirtyRegion)
{
if (x == 0 && y == 0)
return;
fFrame.right += x;
fFrame.bottom += y;
if (fVisible && dirtyRegion) {
IntRect oldBounds(Bounds());
oldBounds.right -= x;
oldBounds.bottom -= y;
BRegion* dirty = fWindow->GetRegion();
if (!dirty)
return;
dirty->Set((clipping_rect)Bounds());
dirty->Include((clipping_rect)oldBounds);
if (!(fFlags & B_FULL_UPDATE_ON_RESIZE)) {
dirty->Exclude((clipping_rect)(oldBounds & Bounds()));
}
InvalidateScreenClipping();
if (dirty->CountRects() > 0) {
if ((fFlags & B_DRAW_ON_CHILDREN) == 0) {
for (View* child = FirstChild(); child;
child = child->NextSibling()) {
if (!child->IsVisible()
|| (child->fFlags & B_TRANSPARENT_BACKGROUND) != 0) {
continue;
}
IntRect previousChildVisible(
child->Frame() & oldBounds & Bounds());
if (dirty->Frame().Intersects(previousChildVisible)) {
dirty->Exclude((clipping_rect)previousChildVisible);
}
}
}
LocalToScreenTransform().Apply(dirty);
dirtyRegion->Include(dirty);
}
fWindow->RecycleRegion(dirty);
}
for (View* child = FirstChild(); child; child = child->NextSibling())
child->ParentResized(x, y, dirtyRegion);
if (fViewBitmap != NULL)
resize_frame(fBitmapDestination, fBitmapResizingMode, x, y);
RebuildClipping(false);
}
void
View::ParentResized(int32 x, int32 y, BRegion* dirtyRegion)
{
IntRect newFrame = fFrame;
resize_frame(newFrame, fResizeMode & 0x0000ffff, x, y);
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);
} else {
InvalidateScreenClipping();
}
}
void
View::ScrollBy(int32 x, int32 y, BRegion* dirtyRegion)
{
if (!fVisible || !fWindow) {
fScrollingOffset.x += x;
fScrollingOffset.y += y;
return;
}
IntRect oldBounds(Bounds());
ConvertToVisibleInTopView(&oldBounds);
IntRect stillVisibleBounds(oldBounds);
stillVisibleBounds.OffsetBy(x, y);
stillVisibleBounds = stillVisibleBounds & oldBounds;
fScrollingOffset.x += x;
fScrollingOffset.y += y;
BRegion* copyRegion = fWindow->GetRegion();
if (!copyRegion)
return;
copyRegion->Set((clipping_rect)stillVisibleBounds);
fWindow->CopyContents(copyRegion, -x, -y);
BRegion* dirty = copyRegion;
dirty->Set((clipping_rect)oldBounds);
stillVisibleBounds.OffsetBy(-x, -y);
dirty->Exclude((clipping_rect)stillVisibleBounds);
dirtyRegion->Include(dirty);
fWindow->RecycleRegion(dirty);
InvalidateScreenClipping();
RebuildClipping(false);
}
void
View::CopyBits(IntRect src, IntRect dst, BRegion& windowContentClipping)
{
if (!fVisible || !fWindow)
return;
BAffineTransform transform = CurrentState()->CombinedTransform();
if (!transform.IsIdentity() && transform.IsDilation()) {
BPoint points[4] = { src.LeftTop(), src.RightBottom(),
dst.LeftTop(), dst.RightBottom() };
transform.Apply(&points[0], 4);
src.Set(points[0].x, points[0].y, points[1].x, points[1].y);
dst.Set(points[2].x, points[2].y, points[3].x, points[3].y);
}
int32 xOffset = dst.left - src.left;
int32 yOffset = dst.top - src.top;
IntRect visibleSrc(src);
ConvertToVisibleInTopView(&visibleSrc);
IntRect visibleSrcAtDest(src);
visibleSrcAtDest.OffsetBy(xOffset, yOffset);
ConvertToVisibleInTopView(&visibleSrcAtDest);
visibleSrcAtDest.OffsetBy(-xOffset, -yOffset);
visibleSrc = visibleSrc & visibleSrcAtDest;
BRegion* copyRegion = fWindow->GetRegion();
if (!copyRegion)
return;
visibleSrc.OffsetBy(xOffset, yOffset);
copyRegion->Set((clipping_rect)visibleSrc);
BRegion *screenAndUserClipping
= &ScreenAndUserClipping(&windowContentClipping);
copyRegion->IntersectWith(screenAndUserClipping);
copyRegion->OffsetBy(-xOffset, -yOffset);
copyRegion->IntersectWith(screenAndUserClipping);
fWindow->CopyContents(copyRegion, xOffset, yOffset);
IntRect dirtyDst(dst);
ConvertToVisibleInTopView(&dirtyDst);
BRegion* dirty = fWindow->GetRegion();
if (!dirty) {
fWindow->RecycleRegion(copyRegion);
return;
}
copyRegion->OffsetBy(xOffset, yOffset);
dirty->Set((clipping_rect)dirtyDst);
dirty->Exclude(copyRegion);
dirty->IntersectWith(screenAndUserClipping);
fWindow->MarkContentDirty(*dirty, *dirty);
fWindow->RecycleRegion(dirty);
fWindow->RecycleRegion(copyRegion);
}
void
View::ColorUpdated(color_which which, rgb_color color)
{
float tint = B_NO_TINT;
if (fWhichViewColor == which)
SetViewColor(tint_color(color, fWhichViewColorTint));
if (CurrentState()->HighUIColor(&tint) == which)
CurrentState()->SetHighColor(tint_color(color, tint));
if (CurrentState()->LowUIColor(&tint) == which)
CurrentState()->SetLowColor(tint_color(color, tint));
for (View* child = FirstChild(); child != NULL;
child = child->NextSibling()) {
child->ColorUpdated(which, color);
}
}
void
View::SetViewUIColor(color_which which, float tint)
{
if (which != B_NO_COLOR) {
DesktopSettings settings(fWindow->Desktop());
SetViewColor(tint_color(settings.UIColor(which), tint));
}
fWhichViewColor = which;
fWhichViewColorTint = tint;
}
color_which
View::ViewUIColor(float* tint)
{
if (tint != NULL)
*tint = fWhichViewColorTint;
return fWhichViewColor;
}
void
View::PushState()
{
DrawState* previousState = fDrawState.Detach();
DrawState* newState = previousState->PushState();
if (newState == NULL)
newState = previousState;
fDrawState.SetTo(newState);
fDrawState->SetSubPixelPrecise(fFlags & B_SUBPIXEL_PRECISE);
}
void
View::PopState()
{
if (fDrawState->PreviousState() == NULL) {
fprintf(stderr, "WARNING: User called BView(%s)::PopState(), "
"but there is NO state on stack!\n", Name());
return;
}
bool rebuildClipping = fDrawState->HasAdditionalClipping();
fDrawState.SetTo(fDrawState->PopState());
fDrawState->SetSubPixelPrecise(fFlags & B_SUBPIXEL_PRECISE);
if (rebuildClipping)
RebuildClipping(false);
}
void
View::SetEventMask(uint32 eventMask, uint32 options)
{
fEventMask = eventMask;
fEventOptions = options;
}
void
View::SetCursor(ServerCursor* cursor)
{
if (cursor == fCursor)
return;
fCursor.SetTo(cursor, false);
}
void
View::SetPicture(ServerPicture* picture)
{
if (picture == fPicture)
return;
fPicture.SetTo(picture, false);
}
void
View::BlendAllLayers()
{
if (fPicture == NULL)
return;
Layer* layer = dynamic_cast<Layer*>(fPicture.Get());
if (layer == NULL)
return;
BlendLayer(layer);
}
void
View::Draw(DrawingEngine* drawingEngine, const BRegion* effectiveClipping,
const BRegion* windowContentClipping, bool deep)
{
if (!fVisible) {
return;
}
if (fViewBitmap != NULL || fViewColor != B_TRANSPARENT_COLOR) {
BRegion* redraw;
if ((fFlags & B_DRAW_ON_CHILDREN) != 0) {
redraw = fWindow->GetRegion(
ScreenAndUserClipping(windowContentClipping));
} else {
redraw = fWindow->GetRegion(
_ScreenClipping(windowContentClipping));
}
if (!redraw)
return;
redraw->IntersectWith(effectiveClipping);
Overlay* overlayCookie = _Overlay();
if (fViewBitmap != NULL && overlayCookie == NULL) {
BRect rect = fBitmapDestination;
PenToScreenTransform().Apply(&rect);
align_rect_to_pixels(&rect);
if (fBitmapOptions & B_TILE_BITMAP_Y) {
while (rect.top > redraw->Frame().top)
rect.OffsetBy(0.0, -(rect.Height() + 1));
}
if (fBitmapOptions & B_TILE_BITMAP_X) {
while (rect.left > redraw->Frame().left)
rect.OffsetBy(-(rect.Width() + 1), 0.0);
}
if (rect.IsValid()) {
drawingEngine->ConstrainClippingRegion(redraw);
drawing_mode oldMode;
drawingEngine->SetDrawingMode(B_OP_COPY, oldMode);
if (fBitmapOptions & B_TILE_BITMAP) {
float start = rect.left;
while (rect.top < redraw->Frame().bottom) {
while (rect.left < redraw->Frame().right) {
drawingEngine->DrawBitmap(fViewBitmap,
fBitmapSource, rect, fBitmapOptions);
rect.OffsetBy(rect.Width() + 1, 0.0);
}
rect.OffsetBy(start - rect.left, rect.Height() + 1);
}
redraw->MakeEmpty();
} else if (fBitmapOptions & B_TILE_BITMAP_X) {
while (rect.left < redraw->Frame().right) {
drawingEngine->DrawBitmap(fViewBitmap, fBitmapSource,
rect, fBitmapOptions);
rect.OffsetBy(rect.Width() + 1, 0.0);
}
rect.left = redraw->Frame().left;
rect.right = redraw->Frame().right;
redraw->Exclude(rect);
} else if (fBitmapOptions & B_TILE_BITMAP_Y) {
while (rect.top < redraw->Frame().bottom) {
drawingEngine->DrawBitmap(fViewBitmap, fBitmapSource,
rect, fBitmapOptions);
rect.OffsetBy(0.0, rect.Height() + 1);
}
rect.top = redraw->Frame().top;
rect.bottom = redraw->Frame().bottom;
redraw->Exclude(rect);
} else {
drawingEngine->DrawBitmap(fViewBitmap, fBitmapSource,
rect, fBitmapOptions);
redraw->Exclude(rect);
}
drawingEngine->SetDrawingMode(oldMode);
}
}
if (fViewColor != B_TRANSPARENT_COLOR) {
drawingEngine->FillRegion(*redraw, overlayCookie != NULL
? overlayCookie->Color() : fViewColor);
}
fWindow->RecycleRegion(redraw);
}
fBackgroundDirty = false;
if (deep) {
for (View* child = FirstChild(); child; child = child->NextSibling()) {
child->Draw(drawingEngine, effectiveClipping,
windowContentClipping, deep);
}
}
}
void
View::MouseDown(BMessage* message, BPoint where)
{
}
void
View::MouseUp(BMessage* message, BPoint where)
{
}
void
View::MouseMoved(BMessage* message, BPoint where)
{
}
void
View::SetHidden(bool hidden)
{
if (fHidden != hidden) {
fHidden = hidden;
bool oldVisible = fVisible;
UpdateVisibleDeep(fParent ? fParent->IsVisible() : !fHidden);
if (oldVisible != fVisible) {
if (fParent)
fParent->RebuildClipping(fVisible);
else
RebuildClipping(fVisible);
if (fWindow) {
IntRect clippedBounds = Bounds();
ConvertToVisibleInTopView(&clippedBounds);
BRegion dirty, expose;
dirty.Set((clipping_rect)clippedBounds);
fWindow->MarkContentDirty(dirty, expose);
}
}
}
}
bool
View::IsHidden() const
{
return fHidden;
}
void
View::UpdateVisibleDeep(bool parentVisible)
{
bool wasVisible = fVisible;
fVisible = parentVisible && !fHidden;
for (View* child = FirstChild(); child; child = child->NextSibling())
child->UpdateVisibleDeep(fVisible);
Overlay* overlay = _Overlay();
if (overlay == NULL)
return;
if (fVisible && !wasVisible)
_UpdateOverlayView();
else if (!fVisible && wasVisible)
overlay->Hide();
}
void
View::MarkBackgroundDirty()
{
if (fBackgroundDirty)
return;
fBackgroundDirty = true;
for (View* child = FirstChild(); child; child = child->NextSibling())
child->MarkBackgroundDirty();
}
void
View::AddTokensForViewsInRegion(BPrivate::PortLink& link, BRegion& region,
BRegion* windowContentClipping)
{
if (!fVisible)
return;
{
IntRect screenBounds(Bounds());
LocalToScreenTransform().Apply(&screenBounds);
if (!region.Intersects((clipping_rect)screenBounds))
return;
BRegion localDirty = _ScreenClipping(windowContentClipping);
localDirty.IntersectWith(®ion);
if (localDirty.CountRects() > 0) {
link.Attach<int32>(fToken);
link.Attach<BRect>(localDirty.Frame());
}
}
for (View* child = FirstChild(); child; child = child->NextSibling())
child->AddTokensForViewsInRegion(link, region, windowContentClipping);
}
void
View::PrintToStream() const
{
printf("View: %s\n", Name());
printf(" fToken: %" B_PRId32 "\n", fToken);
printf(" fFrame: IntRect(%" B_PRId32 ", %" B_PRId32 ", %" B_PRId32 ", %" B_PRId32 ")\n",
fFrame.left, fFrame.top, fFrame.right, fFrame.bottom);
printf(" fScrollingOffset: IntPoint(%" B_PRId32 ", %" B_PRId32 ")\n",
fScrollingOffset.x, fScrollingOffset.y);
printf(" fHidden: %d\n", fHidden);
printf(" fVisible: %d\n", fVisible);
printf(" fWindow: %p\n", fWindow);
printf(" fParent: %p\n", fParent);
printf(" fLocalClipping:\n");
fLocalClipping.PrintToStream();
printf(" fScreenClipping:\n");
fScreenClipping.PrintToStream();
printf(" valid: %d\n", fScreenClippingValid);
printf(" fUserClipping:\n");
if (fUserClipping.IsSet())
fUserClipping->PrintToStream();
else
printf(" none\n");
printf(" fScreenAndUserClipping:\n");
if (fScreenAndUserClipping.IsSet())
fScreenAndUserClipping->PrintToStream();
else
printf(" invalid\n");
printf(" state:\n");
printf(" user clipping: %d\n", fDrawState->HasClipping());
BPoint origin = fDrawState->CombinedOrigin();
printf(" origin: BPoint(%.1f, %.1f)\n", origin.x, origin.y);
printf(" scale: %.2f\n", fDrawState->CombinedScale());
printf("\n");
}
void
View::RebuildClipping(bool deep)
{
fLocalClipping.Set((clipping_rect)Bounds());
if (View* child = FirstChild()) {
if ((fFlags & B_DRAW_ON_CHILDREN) == 0) {
BRegion* childrenRegion = fWindow->GetRegion();
if (!childrenRegion)
return;
for (; child; child = child->NextSibling()) {
if (child->IsVisible()
&& (child->fFlags & B_TRANSPARENT_BACKGROUND) == 0) {
childrenRegion->Include((clipping_rect)child->Frame());
}
}
fLocalClipping.Exclude(childrenRegion);
fWindow->RecycleRegion(childrenRegion);
}
if (deep) {
for (child = FirstChild(); child; child = child->NextSibling())
child->RebuildClipping(true);
}
}
if (fDrawState->HasClipping()) {
if (!fUserClipping.IsSet()) {
fUserClipping.SetTo(new (nothrow) BRegion);
if (!fUserClipping.IsSet())
return;
}
fDrawState->GetCombinedClippingRegion(fUserClipping.Get());
} else {
fUserClipping.SetTo(NULL);
}
fScreenAndUserClipping.SetTo(NULL);
fScreenClippingValid = false;
}
BRegion&
View::ScreenAndUserClipping(const BRegion* windowContentClipping, bool force) const
{
if (!fUserClipping.IsSet())
return _ScreenClipping(windowContentClipping, force);
if (fScreenAndUserClipping.IsSet())
return *fScreenAndUserClipping.Get();
fScreenAndUserClipping.SetTo(new (nothrow) BRegion(*fUserClipping.Get()));
if (!fScreenAndUserClipping.IsSet())
return fScreenClipping;
LocalToScreenTransform().Apply(fScreenAndUserClipping.Get());
fScreenAndUserClipping->IntersectWith(
&_ScreenClipping(windowContentClipping, force));
return *fScreenAndUserClipping.Get();
}
void
View::InvalidateScreenClipping()
{
fScreenAndUserClipping.SetTo(NULL);
fScreenClippingValid = false;
for (View* child = FirstChild(); child; child = child->NextSibling()) {
child->InvalidateScreenClipping();
}
}
BRegion&
View::_ScreenClipping(const BRegion* windowContentClipping, bool force) const
{
if (!fScreenClippingValid || force) {
fScreenClipping = fLocalClipping;
LocalToScreenTransform().Apply(&fScreenClipping);
IntRect clippedBounds = Bounds();
ConvertToVisibleInTopView(&clippedBounds);
if (clippedBounds.Width() < fScreenClipping.Frame().Width()
|| clippedBounds.Height() < fScreenClipping.Frame().Height()) {
BRegion temp;
temp.Set((clipping_rect)clippedBounds);
fScreenClipping.IntersectWith(&temp);
}
fScreenClipping.IntersectWith(windowContentClipping);
fScreenClippingValid = true;
}
return fScreenClipping;
}
void
View::_MoveScreenClipping(int32 x, int32 y, bool deep)
{
if (fScreenClippingValid) {
fScreenClipping.OffsetBy(x, y);
fScreenAndUserClipping.SetTo(NULL);
}
if (deep) {
for (View* child = FirstChild(); child; child = child->NextSibling()) {
child->_MoveScreenClipping(x, y, deep);
}
}
}