Files
haiku-beta6/src/kits/interface/View.cpp
T
Oliver Tappe 39fbf5509b bonefish + zooey:
* Fixed a general problem with respect to overriding of the reserved
  virtual function slots: instead of statically invoking the method
  that corresponds to the reserved slot on the class that contains the
  slot, we now invoke the virtual Perform() method. Perform() then dispatches 
  the method invocation to the "proper" class, i.e. the highest class in the 
  hierarchy that  actually implements the requested method.
  This fixes a crash in apps that use liblayout's MSlider class and
  should fix one or other spurious bug with old apps or libraries, too.
* added new header folder 'binary_compatibility' that contains files that
  define the method codes and data structures required by Perform()
* looked for and implemented all used reserved virtual slot functions to
  invoke Perform() where necessary or to pass on the method call statically
  (for slots that were already maintained by Be)


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@28124 a95241bf-73f2-0310-859d-f6bbb57e9c96
2008-10-15 09:29:06 +00:00

5605 lines
115 KiB
C++

/*
* Copyright 2001-2008, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Adrian Oanca <[email protected]>
* Axel Dörfler, [email protected]
* Stephan Aßmus <[email protected]>
* Ingo Weinhold <[email protected]>
*/
#include <math.h>
#include <stdio.h>
#include <new>
#include <Application.h>
#include <Bitmap.h>
#include <Button.h>
#include <Cursor.h>
#include <File.h>
#include <InterfaceDefs.h>
#include <Layout.h>
#include <LayoutContext.h>
#include <LayoutUtils.h>
#include <MenuBar.h>
#include <Message.h>
#include <MessageQueue.h>
#include <Picture.h>
#include <Point.h>
#include <Polygon.h>
#include <PropertyInfo.h>
#include <Region.h>
#include <ScrollBar.h>
#include <Shape.h>
#include <Shelf.h>
#include <String.h>
#include <View.h>
#include <Window.h>
#include <GradientLinear.h>
#include <GradientRadial.h>
#include <GradientRadialFocus.h>
#include <GradientDiamond.h>
#include <GradientConic.h>
#include <AppMisc.h>
#include <AppServerLink.h>
#include <binary_compatibility/Interface.h>
#include <MessagePrivate.h>
#include <MessageUtils.h>
#include <PortLink.h>
#include <ServerProtocol.h>
#include <ShapePrivate.h>
#include <TokenSpace.h>
#include <ViewPrivate.h>
using std::nothrow;
//#define DEBUG_BVIEW
#ifdef DEBUG_BVIEW
# include <stdio.h>
# define STRACE(x) printf x
# define BVTRACE _PrintToStream()
#else
# define STRACE(x) ;
# define BVTRACE ;
#endif
static property_info sViewPropInfo[] = {
{ "Frame", { B_GET_PROPERTY, 0 },
{ B_DIRECT_SPECIFIER, 0 }, "Returns the view's frame rectangle.", 0,
{ B_RECT_TYPE }
},
{ "Frame", { B_SET_PROPERTY, 0 },
{ B_DIRECT_SPECIFIER, 0 }, "Sets the view's frame rectangle.", 0,
{ B_RECT_TYPE }
},
{ "Hidden", { B_GET_PROPERTY, 0 },
{ B_DIRECT_SPECIFIER, 0 }, "Returns wether or not the view is hidden.",
0, { B_BOOL_TYPE }
},
{ "Hidden", { B_SET_PROPERTY, 0 },
{ B_DIRECT_SPECIFIER, 0 }, "Hides or shows the view.", 0,
{ B_BOOL_TYPE }
},
{ "Shelf", { 0 },
{ B_DIRECT_SPECIFIER, 0 }, "Directs the scripting message to the "
"shelf.", 0
},
{ "View", { B_COUNT_PROPERTIES, 0 },
{ B_DIRECT_SPECIFIER, 0 }, "Returns the number of child views.", 0,
{ B_INT32_TYPE }
},
{ "View", { 0 },
{ B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, B_NAME_SPECIFIER, 0 },
"Directs the scripting message to the specified view.", 0
},
{ 0, { 0 }, { 0 }, 0, 0 }
};
// #pragma mark -
static inline uint32
get_uint32_color(rgb_color color)
{
return B_BENDIAN_TO_HOST_INT32(*(uint32 *)&color);
// rgb_color is always in rgba format, no matter what endian;
// we always return the int32 value in host endian.
}
static inline rgb_color
get_rgb_color(uint32 value)
{
value = B_HOST_TO_BENDIAN_INT32(value);
return *(rgb_color *)&value;
}
// #pragma mark -
namespace BPrivate {
ViewState::ViewState()
{
pen_location.Set(0, 0);
pen_size = 1.0;
// NOTE: the clipping_region is empty
// on construction but it is not used yet,
// we avoid having to keep track of it via
// this flag
clipping_region_used = false;
high_color = (rgb_color){ 0, 0, 0, 255 };
low_color = (rgb_color){ 255, 255, 255, 255 };
view_color = low_color;
pattern = B_SOLID_HIGH;
drawing_mode = B_OP_COPY;
origin.Set(0, 0);
line_join = B_MITER_JOIN;
line_cap = B_BUTT_CAP;
miter_limit = B_DEFAULT_MITER_LIMIT;
alpha_source_mode = B_PIXEL_ALPHA;
alpha_function_mode = B_ALPHA_OVERLAY;
scale = 1.0;
font = *be_plain_font;
font_flags = font.Flags();
font_aliasing = false;
/*
INFO: We include(invalidate) only B_VIEW_CLIP_REGION_BIT flag
because we should get the clipping region from app_server.
The other flags do not need to be included because the data they
represent is already in sync with app_server - app_server uses the
same init(default) values.
*/
valid_flags = ~B_VIEW_CLIP_REGION_BIT;
archiving_flags = B_VIEW_FRAME_BIT | B_VIEW_RESIZE_BIT;
}
void
ViewState::UpdateServerFontState(BPrivate::PortLink &link)
{
link.StartMessage(AS_VIEW_SET_FONT_STATE);
link.Attach<uint16>(font_flags);
// always present
if (font_flags & B_FONT_FAMILY_AND_STYLE)
link.Attach<uint32>(font.FamilyAndStyle());
if (font_flags & B_FONT_SIZE)
link.Attach<float>(font.Size());
if (font_flags & B_FONT_SHEAR)
link.Attach<float>(font.Shear());
if (font_flags & B_FONT_ROTATION)
link.Attach<float>(font.Rotation());
if (font_flags & B_FONT_FALSE_BOLD_WIDTH)
link.Attach<float>(font.FalseBoldWidth());
if (font_flags & B_FONT_SPACING)
link.Attach<uint8>(font.Spacing());
if (font_flags & B_FONT_ENCODING)
link.Attach<uint8>(font.Encoding());
if (font_flags & B_FONT_FACE)
link.Attach<uint16>(font.Face());
if (font_flags & B_FONT_FLAGS)
link.Attach<uint32>(font.Flags());
}
void
ViewState::UpdateServerState(BPrivate::PortLink &link)
{
UpdateServerFontState(link);
link.StartMessage(AS_VIEW_SET_STATE);
link.Attach<BPoint>(pen_location);
link.Attach<float>(pen_size);
link.Attach<rgb_color>(high_color);
link.Attach<rgb_color>(low_color);
link.Attach< ::pattern>(pattern);
link.Attach<int8>((int8)drawing_mode);
link.Attach<BPoint>(origin);
link.Attach<int8>((int8)line_join);
link.Attach<int8>((int8)line_cap);
link.Attach<float>(miter_limit);
link.Attach<int8>((int8)alpha_source_mode);
link.Attach<int8>((int8)alpha_function_mode);
link.Attach<float>(scale);
link.Attach<bool>(font_aliasing);
// we send the 'local' clipping region... if we have one...
if (clipping_region_used) {
int32 count = clipping_region.CountRects();
link.Attach<int32>(count);
for (int32 i = 0; i < count; i++)
link.Attach<BRect>(clipping_region.RectAt(i));
} else {
// no clipping region
link.Attach<int32>(-1);
}
// Although we might have a 'local' clipping region, when we call
// BView::GetClippingRegion() we ask for the 'global' one and it
// is kept on server, so we must invalidate B_VIEW_CLIP_REGION_BIT flag
valid_flags = ~B_VIEW_CLIP_REGION_BIT;
}
void
ViewState::UpdateFrom(BPrivate::PortLink &link)
{
link.StartMessage(AS_VIEW_GET_STATE);
int32 code;
if (link.FlushWithReply(code) != B_OK
|| code != B_OK)
return;
uint32 fontID;
float size;
float shear;
float rotation;
float falseBoldeWidth;
uint8 spacing;
uint8 encoding;
uint16 face;
uint32 flags;
// read and set the font state
link.Read<int32>((int32 *)&fontID);
link.Read<float>(&size);
link.Read<float>(&shear);
link.Read<float>(&rotation);
link.Read<float>(&falseBoldeWidth);
link.Read<int8>((int8 *)&spacing);
link.Read<int8>((int8 *)&encoding);
link.Read<int16>((int16 *)&face);
link.Read<int32>((int32 *)&flags);
font_flags = B_FONT_ALL;
font.SetFamilyAndStyle(fontID);
font.SetSize(size);
font.SetShear(shear);
font.SetRotation(rotation);
font.SetFalseBoldWidth(falseBoldeWidth);
font.SetSpacing(spacing);
font.SetEncoding(encoding);
font.SetFace(face);
font.SetFlags(flags);
// read and set view's state
link.Read<BPoint>(&pen_location);
link.Read<float>(&pen_size);
link.Read<rgb_color>(&high_color);
link.Read<rgb_color>(&low_color);
link.Read< ::pattern>(&pattern);
link.Read<BPoint>(&origin);
int8 drawingMode;
link.Read<int8>((int8 *)&drawingMode);
drawing_mode = (::drawing_mode)drawingMode;
link.Read<int8>((int8 *)&line_cap);
link.Read<int8>((int8 *)&line_join);
link.Read<float>(&miter_limit);
link.Read<int8>((int8 *)&alpha_source_mode);
link.Read<int8>((int8 *)&alpha_function_mode);
link.Read<float>(&scale);
link.Read<bool>(&font_aliasing);
// read the user clipping
// (that's NOT the current View visible clipping but the additional
// user specified clipping!)
int32 clippingRectCount;
link.Read<int32>(&clippingRectCount);
if (clippingRectCount >= 0) {
clipping_region.MakeEmpty();
for (int32 i = 0; i < clippingRectCount; i++) {
BRect rect;
link.Read<BRect>(&rect);
clipping_region.Include(rect);
}
} else {
// no user clipping used
clipping_region_used = false;
}
valid_flags = ~B_VIEW_CLIP_REGION_BIT;
}
} // namespace BPrivate
// #pragma mark -
struct BView::LayoutData {
LayoutData()
: fMinSize(),
fMaxSize(),
fPreferredSize(),
fAlignment(),
fLayoutInvalidationDisabled(0),
fLayout(NULL),
fLayoutContext(NULL),
fLayoutValid(true), // <- TODO: Rethink this!
fLayoutInProgress(false),
fNeedsRelayout(true)
{
}
BSize fMinSize;
BSize fMaxSize;
BSize fPreferredSize;
BAlignment fAlignment;
int fLayoutInvalidationDisabled;
BLayout* fLayout;
BLayoutContext* fLayoutContext;
bool fLayoutValid;
bool fLayoutInProgress;
bool fNeedsRelayout;
};
BView::BView(const char* name, uint32 flags, BLayout* layout)
: BHandler(name)
{
_InitData(BRect(0, 0, 0, 0), name, B_FOLLOW_NONE,
flags | B_SUPPORTS_LAYOUT);
SetLayout(layout);
}
BView::BView(BRect frame, const char *name, uint32 resizingMode, uint32 flags)
: BHandler(name)
{
_InitData(frame, name, resizingMode, flags);
}
BView::BView(BMessage *archive)
: BHandler(archive)
{
BRect frame;
archive->FindRect("_frame", &frame);
uint32 resizingMode;
if (archive->FindInt32("_resize_mode", (int32*)&resizingMode) != B_OK)
resizingMode = 0;
uint32 flags;
if (archive->FindInt32("_flags", (int32*)&flags) != B_OK)
flags = 0;
_InitData(frame, Name(), resizingMode, flags);
font_family family;
font_style style;
if (archive->FindString("_fname", 0, (const char **)&family) == B_OK
&& archive->FindString("_fname", 1, (const char **)&style) == B_OK) {
BFont font;
font.SetFamilyAndStyle(family, style);
float size;
if (archive->FindFloat("_fflt", 0, &size) == B_OK)
font.SetSize(size);
float shear;
if (archive->FindFloat("_fflt", 1, &shear) == B_OK
&& shear >= 45.0 && shear <= 135.0)
font.SetShear(shear);
float rotation;
if (archive->FindFloat("_fflt", 2, &rotation) == B_OK
&& rotation >=0 && rotation <= 360)
font.SetRotation(rotation);
SetFont(&font, B_FONT_FAMILY_AND_STYLE | B_FONT_SIZE
| B_FONT_SHEAR | B_FONT_ROTATION);
}
int32 color;
if (archive->FindInt32("_color", 0, &color) == B_OK)
SetHighColor(get_rgb_color(color));
if (archive->FindInt32("_color", 1, &color) == B_OK)
SetLowColor(get_rgb_color(color));
if (archive->FindInt32("_color", 2, &color) == B_OK)
SetViewColor(get_rgb_color(color));
uint32 evMask;
uint32 options;
if (archive->FindInt32("_evmask", 0, (int32 *)&evMask) == B_OK
&& archive->FindInt32("_evmask", 1, (int32 *)&options) == B_OK)
SetEventMask(evMask, options);
BPoint origin;
if (archive->FindPoint("_origin", &origin) == B_OK)
SetOrigin(origin);
float penSize;
if (archive->FindFloat("_psize", &penSize) == B_OK)
SetPenSize(penSize);
BPoint penLocation;
if (archive->FindPoint("_ploc", &penLocation) == B_OK)
MovePenTo(penLocation);
int16 lineCap;
int16 lineJoin;
float lineMiter;
if (archive->FindInt16("_lmcapjoin", 0, &lineCap) == B_OK
&& archive->FindInt16("_lmcapjoin", 1, &lineJoin) == B_OK
&& archive->FindFloat("_lmmiter", &lineMiter) == B_OK)
SetLineMode((cap_mode)lineCap, (join_mode)lineJoin, lineMiter);
int16 alphaBlend;
int16 modeBlend;
if (archive->FindInt16("_blend", 0, &alphaBlend) == B_OK
&& archive->FindInt16("_blend", 1, &modeBlend) == B_OK)
SetBlendingMode( (source_alpha)alphaBlend, (alpha_function)modeBlend);
uint32 drawingMode;
if (archive->FindInt32("_dmod", (int32 *)&drawingMode) == B_OK)
SetDrawingMode((drawing_mode)drawingMode);
BMessage msg;
for (int32 i = 0; archive->FindMessage("_views", i, &msg) == B_OK; i++) {
BArchivable *object = instantiate_object(&msg);
if (BView *child = dynamic_cast<BView *>(object))
AddChild(child);
}
}
BArchivable *
BView::Instantiate(BMessage *data)
{
if (!validate_instantiation(data , "BView"))
return NULL;
return new BView(data);
}
status_t
BView::Archive(BMessage *data, bool deep) const
{
status_t ret = BHandler::Archive(data, deep);
if (ret != B_OK)
return ret;
if (fState->archiving_flags & B_VIEW_FRAME_BIT)
ret = data->AddRect("_frame", Bounds().OffsetToCopy(fParentOffset));
if (ret == B_OK)
ret = data->AddInt32("_resize_mode", ResizingMode());
if (ret == B_OK)
ret = data->AddInt32("_flags", Flags());
if (ret == B_OK && fState->archiving_flags & B_VIEW_EVENT_MASK_BIT) {
ret = data->AddInt32("_evmask", fEventMask);
if (ret == B_OK)
ret = data->AddInt32("_evmask", fEventOptions);
}
if (ret == B_OK && fState->archiving_flags & B_VIEW_FONT_BIT) {
BFont font;
GetFont(&font);
font_family family;
font_style style;
font.GetFamilyAndStyle(&family, &style);
ret = data->AddString("_fname", family);
if (ret == B_OK)
ret = data->AddString("_fname", style);
if (ret == B_OK)
ret = data->AddFloat("_fflt", font.Size());
if (ret == B_OK)
ret = data->AddFloat("_fflt", font.Shear());
if (ret == B_OK)
ret = data->AddFloat("_fflt", font.Rotation());
}
// colors
if (ret == B_OK)
ret = data->AddInt32("_color", get_uint32_color(HighColor()));
if (ret == B_OK)
ret = data->AddInt32("_color", get_uint32_color(LowColor()));
if (ret == B_OK)
ret = data->AddInt32("_color", get_uint32_color(ViewColor()));
// NOTE: we do not use this flag any more
// if ( 1 ){
// ret = data->AddInt32("_dbuf", 1);
// }
if (ret == B_OK && fState->archiving_flags & B_VIEW_ORIGIN_BIT)
ret = data->AddPoint("_origin", Origin());
if (ret == B_OK && fState->archiving_flags & B_VIEW_PEN_SIZE_BIT)
ret = data->AddFloat("_psize", PenSize());
if (ret == B_OK && fState->archiving_flags & B_VIEW_PEN_LOCATION_BIT)
ret = data->AddPoint("_ploc", PenLocation());
if (ret == B_OK && fState->archiving_flags & B_VIEW_LINE_MODES_BIT) {
ret = data->AddInt16("_lmcapjoin", (int16)LineCapMode());
if (ret == B_OK)
ret = data->AddInt16("_lmcapjoin", (int16)LineJoinMode());
if (ret == B_OK)
ret = data->AddFloat("_lmmiter", LineMiterLimit());
}
if (ret == B_OK && fState->archiving_flags & B_VIEW_BLENDING_BIT) {
source_alpha alphaSourceMode;
alpha_function alphaFunctionMode;
GetBlendingMode(&alphaSourceMode, &alphaFunctionMode);
ret = data->AddInt16("_blend", (int16)alphaSourceMode);
if (ret == B_OK)
ret = data->AddInt16("_blend", (int16)alphaFunctionMode);
}
if (ret == B_OK && fState->archiving_flags & B_VIEW_DRAWING_MODE_BIT)
ret = data->AddInt32("_dmod", DrawingMode());
if (deep) {
int32 i = 0;
BView *child;
while (ret == B_OK && (child = ChildAt(i++)) != NULL) {
BMessage childArchive;
ret = child->Archive(&childArchive, deep);
if (ret == B_OK)
ret = data->AddMessage("_views", &childArchive);
}
}
return ret;
}
BView::~BView()
{
STRACE(("BView(%s)::~BView()\n", this->Name()));
if (fOwner)
debugger("Trying to delete a view that belongs to a window. Call RemoveSelf first.");
RemoveSelf();
// TODO: see about BShelf! must I delete it here? is it deleted by the window?
// we also delete all our children
BView *child = fFirstChild;
while (child) {
BView *nextChild = child->fNextSibling;
delete child;
child = nextChild;
}
// delete the layout and the layout data
delete fLayoutData->fLayout;
delete fLayoutData;
if (fVerScroller)
fVerScroller->SetTarget((BView*)NULL);
if (fHorScroller)
fHorScroller->SetTarget((BView*)NULL);
SetName(NULL);
_RemoveCommArray();
delete fState;
}
BRect
BView::Bounds() const
{
_CheckLock();
if (fIsPrinting)
return fState->print_rect;
return fBounds;
}
void
BView::_ConvertToParent(BPoint *point, bool checkLock) const
{
if (!fParent)
return;
if (checkLock)
_CheckLock();
// - our scrolling offset
// + our bounds location within the parent
point->x += -fBounds.left + fParentOffset.x;
point->y += -fBounds.top + fParentOffset.y;
}
void
BView::ConvertToParent(BPoint *point) const
{
_ConvertToParent(point, true);
}
BPoint
BView::ConvertToParent(BPoint point) const
{
ConvertToParent(&point);
return point;
}
void
BView::_ConvertFromParent(BPoint *point, bool checkLock) const
{
if (!fParent)
return;
if (checkLock)
_CheckLock();
// - our bounds location within the parent
// + our scrolling offset
point->x += -fParentOffset.x + fBounds.left;
point->y += -fParentOffset.y + fBounds.top;
}
void
BView::ConvertFromParent(BPoint *point) const
{
_ConvertFromParent(point, true);
}
BPoint
BView::ConvertFromParent(BPoint point) const
{
ConvertFromParent(&point);
return point;
}
void
BView::ConvertToParent(BRect *rect) const
{
if (!fParent)
return;
_CheckLock();
// - our scrolling offset
// + our bounds location within the parent
rect->OffsetBy(-fBounds.left + fParentOffset.x,
-fBounds.top + fParentOffset.y);
}
BRect
BView::ConvertToParent(BRect rect) const
{
ConvertToParent(&rect);
return rect;
}
void
BView::ConvertFromParent(BRect *rect) const
{
if (!fParent)
return;
_CheckLock();
// - our bounds location within the parent
// + our scrolling offset
rect->OffsetBy(-fParentOffset.x + fBounds.left,
-fParentOffset.y + fBounds.top);
}
BRect
BView::ConvertFromParent(BRect rect) const
{
ConvertFromParent(&rect);
return rect;
}
void
BView::_ConvertToScreen(BPoint *pt, bool checkLock) const
{
if (!fParent) {
if (fOwner)
fOwner->ConvertToScreen(pt);
return;
}
if (checkLock)
_CheckOwnerLock();
_ConvertToParent(pt, false);
fParent->_ConvertToScreen(pt, false);
}
void
BView::ConvertToScreen(BPoint *pt) const
{
_ConvertToScreen(pt, true);
}
BPoint
BView::ConvertToScreen(BPoint pt) const
{
ConvertToScreen(&pt);
return pt;
}
void
BView::_ConvertFromScreen(BPoint *pt, bool checkLock) const
{
if (!fParent) {
if (fOwner)
fOwner->ConvertFromScreen(pt);
return;
}
if (checkLock)
_CheckOwnerLock();
_ConvertFromParent(pt, false);
fParent->_ConvertFromScreen(pt, false);
}
void
BView::ConvertFromScreen(BPoint *pt) const
{
_ConvertFromScreen(pt, true);
}
BPoint
BView::ConvertFromScreen(BPoint pt) const
{
ConvertFromScreen(&pt);
return pt;
}
void
BView::ConvertToScreen(BRect *rect) const
{
BPoint offset(0.0, 0.0);
ConvertToScreen(&offset);
rect->OffsetBy(offset);
}
BRect
BView::ConvertToScreen(BRect rect) const
{
ConvertToScreen(&rect);
return rect;
}
void
BView::ConvertFromScreen(BRect *rect) const
{
BPoint offset(0.0, 0.0);
ConvertFromScreen(&offset);
rect->OffsetBy(offset);
}
BRect
BView::ConvertFromScreen(BRect rect) const
{
ConvertFromScreen(&rect);
return rect;
}
uint32
BView::Flags() const
{
_CheckLock();
return fFlags & ~_RESIZE_MASK_;
}
void
BView::SetFlags(uint32 flags)
{
if (Flags() == flags)
return;
if (fOwner) {
if (flags & B_PULSE_NEEDED) {
_CheckLock();
if (fOwner->fPulseRunner == NULL)
fOwner->SetPulseRate(fOwner->PulseRate());
}
if (flags & (B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE
| B_FRAME_EVENTS | B_SUBPIXEL_PRECISE)) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_SET_FLAGS);
fOwner->fLink->Attach<uint32>(flags);
fOwner->fLink->Flush();
}
}
/* Some useful info:
fFlags is a unsigned long (32 bits)
* bits 1-16 are used for BView's flags
* bits 17-32 are used for BView' resize mask
* _RESIZE_MASK_ is used for that. Look into View.h to see how
it's defined
*/
fFlags = (flags & ~_RESIZE_MASK_) | (fFlags & _RESIZE_MASK_);
fState->archiving_flags |= B_VIEW_FLAGS_BIT;
}
BRect
BView::Frame() const
{
return Bounds().OffsetToCopy(fParentOffset.x, fParentOffset.y);
}
void
BView::Hide()
{
if (fOwner && fShowLevel == 0) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_HIDE);
fOwner->fLink->Flush();
}
fShowLevel++;
if (fShowLevel == 1 && fParent)
fParent->InvalidateLayout();
}
void
BView::Show()
{
fShowLevel--;
if (fOwner && fShowLevel == 0) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_SHOW);
fOwner->fLink->Flush();
}
if (fShowLevel == 0 && fParent)
fParent->InvalidateLayout();
}
bool
BView::IsFocus() const
{
if (fOwner) {
_CheckLock();
return fOwner->CurrentFocus() == this;
} else
return false;
}
bool
BView::IsHidden(const BView *lookingFrom) const
{
if (fShowLevel > 0)
return true;
// may we be egocentric?
if (lookingFrom == this)
return false;
// we have the same visibility state as our
// parent, if there is one
if (fParent)
return fParent->IsHidden(lookingFrom);
// if we're the top view, and we're interested
// in the "global" view, we're inheriting the
// state of the window's visibility
if (fOwner && lookingFrom == NULL)
return fOwner->IsHidden();
return false;
}
bool
BView::IsHidden() const
{
return IsHidden(NULL);
}
bool
BView::IsPrinting() const
{
return fIsPrinting;
}
BPoint
BView::LeftTop() const
{
return Bounds().LeftTop();
}
void
BView::SetResizingMode(uint32 mode)
{
if (fOwner) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_RESIZE_MODE);
fOwner->fLink->Attach<uint32>(mode);
}
// look at SetFlags() for more info on the below line
fFlags = (fFlags & ~_RESIZE_MASK_) | (mode & _RESIZE_MASK_);
}
uint32
BView::ResizingMode() const
{
return fFlags & _RESIZE_MASK_;
}
void
BView::SetViewCursor(const BCursor *cursor, bool sync)
{
if (cursor == NULL || fOwner == NULL)
return;
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_SET_CURSOR);
fOwner->fLink->Attach<int32>(cursor->fServerToken);
fOwner->fLink->Attach<bool>(sync);
if (!sync) {
cursor->fPendingViewCursor = true;
// this avoids a race condition in case the cursor is
// immediately deleted after this call, as the deletion
// is handled by the application, not the window
} else {
// make sure the server has processed the
// message and "acquired" the cursor in
// the window thread before returning from
// this function
int32 code;
fOwner->fLink->FlushWithReply(code);
}
}
void
BView::Flush() const
{
if (fOwner)
fOwner->Flush();
}
void
BView::Sync() const
{
_CheckOwnerLock();
if (fOwner)
fOwner->Sync();
}
BWindow *
BView::Window() const
{
return fOwner;
}
// #pragma mark - Hook Functions
void
BView::AttachedToWindow()
{
// Hook function
STRACE(("\tHOOK: BView(%s)::AttachedToWindow()\n", Name()));
}
void
BView::AllAttached()
{
// Hook function
STRACE(("\tHOOK: BView(%s)::AllAttached()\n", Name()));
}
void
BView::DetachedFromWindow()
{
// Hook function
STRACE(("\tHOOK: BView(%s)::DetachedFromWindow()\n", Name()));
}
void
BView::AllDetached()
{
// Hook function
STRACE(("\tHOOK: BView(%s)::AllDetached()\n", Name()));
}
void
BView::Draw(BRect updateRect)
{
// Hook function
STRACE(("\tHOOK: BView(%s)::Draw()\n", Name()));
}
void
BView::DrawAfterChildren(BRect r)
{
// Hook function
STRACE(("\tHOOK: BView(%s)::DrawAfterChildren()\n", Name()));
}
void
BView::FrameMoved(BPoint newPosition)
{
// Hook function
STRACE(("\tHOOK: BView(%s)::FrameMoved()\n", Name()));
}
void
BView::FrameResized(float newWidth, float newHeight)
{
// Hook function
STRACE(("\tHOOK: BView(%s)::FrameResized()\n", Name()));
}
void
BView::GetPreferredSize(float* _width, float* _height)
{
STRACE(("\tHOOK: BView(%s)::GetPreferredSize()\n", Name()));
if (_width != NULL)
*_width = fBounds.Width();
if (_height != NULL)
*_height = fBounds.Height();
}
void
BView::ResizeToPreferred()
{
STRACE(("\tHOOK: BView(%s)::ResizeToPreferred()\n", Name()));
float width;
float height;
GetPreferredSize(&width, &height);
ResizeTo(width, height);
}
void
BView::KeyDown(const char* bytes, int32 numBytes)
{
// Hook function
STRACE(("\tHOOK: BView(%s)::KeyDown()\n", Name()));
if (Window())
Window()->_KeyboardNavigation();
}
void
BView::KeyUp(const char* bytes, int32 numBytes)
{
// Hook function
STRACE(("\tHOOK: BView(%s)::KeyUp()\n", Name()));
}
void
BView::MouseDown(BPoint where)
{
// Hook function
STRACE(("\tHOOK: BView(%s)::MouseDown()\n", Name()));
}
void
BView::MouseUp(BPoint where)
{
// Hook function
STRACE(("\tHOOK: BView(%s)::MouseUp()\n", Name()));
}
void
BView::MouseMoved(BPoint where, uint32 code, const BMessage* a_message)
{
// Hook function
STRACE(("\tHOOK: BView(%s)::MouseMoved()\n", Name()));
}
void
BView::Pulse()
{
// Hook function
STRACE(("\tHOOK: BView(%s)::Pulse()\n", Name()));
}
void
BView::TargetedByScrollView(BScrollView* scroll_view)
{
// Hook function
STRACE(("\tHOOK: BView(%s)::TargetedByScrollView()\n", Name()));
}
void
BView::WindowActivated(bool state)
{
// Hook function
STRACE(("\tHOOK: BView(%s)::WindowActivated()\n", Name()));
}
// #pragma mark - Input Functions
void
BView::BeginRectTracking(BRect startRect, uint32 style)
{
if (_CheckOwnerLockAndSwitchCurrent()) {
fOwner->fLink->StartMessage(AS_VIEW_BEGIN_RECT_TRACK);
fOwner->fLink->Attach<BRect>(startRect);
fOwner->fLink->Attach<uint32>(style);
fOwner->fLink->Flush();
}
}
void
BView::EndRectTracking()
{
if (_CheckOwnerLockAndSwitchCurrent()) {
fOwner->fLink->StartMessage(AS_VIEW_END_RECT_TRACK);
fOwner->fLink->Flush();
}
}
void
BView::DragMessage(BMessage *message, BRect dragRect, BHandler *replyTo)
{
if (!message)
return;
_CheckOwnerLock();
// calculate the offset
BPoint offset;
uint32 buttons;
BMessage *current = fOwner->CurrentMessage();
if (!current || current->FindPoint("be:view_where", &offset) != B_OK)
GetMouse(&offset, &buttons, false);
offset -= dragRect.LeftTop();
if (!dragRect.IsValid()) {
DragMessage(message, NULL, B_OP_BLEND, offset, replyTo);
return;
}
// TODO: that's not really what should happen - the app_server should take the chance
// *NOT* to need to drag a whole bitmap around but just a frame.
// create a drag bitmap for the rect
BBitmap *bitmap = new BBitmap(dragRect, B_RGBA32);
uint32 *bits = (uint32*)bitmap->Bits();
uint32 bytesPerRow = bitmap->BytesPerRow();
uint32 width = dragRect.IntegerWidth() + 1;
uint32 height = dragRect.IntegerHeight() + 1;
uint32 lastRow = (height - 1) * width;
memset(bits, 0x00, height * bytesPerRow);
// top
for (uint32 i = 0; i < width; i += 2)
bits[i] = 0xff000000;
// bottom
for (uint32 i = (height % 2 == 0 ? 1 : 0); i < width; i += 2)
bits[lastRow + i] = 0xff000000;
// left
for (uint32 i = 0; i < lastRow; i += width * 2)
bits[i] = 0xff000000;
// right
for (uint32 i = (width % 2 == 0 ? width : 0); i < lastRow; i += width * 2)
bits[width - 1 + i] = 0xff000000;
DragMessage(message, bitmap, B_OP_BLEND, offset, replyTo);
}
void
BView::DragMessage(BMessage *message, BBitmap *image, BPoint offset,
BHandler *replyTo)
{
DragMessage(message, image, B_OP_COPY, offset, replyTo);
}
void
BView::DragMessage(BMessage *message, BBitmap *image,
drawing_mode dragMode, BPoint offset, BHandler *replyTo)
{
if (message == NULL)
return;
if (image == NULL) {
// TODO: workaround for drags without a bitmap - should not be necessary if
// we move the rectangle dragging into the app_server
image = new (nothrow) BBitmap(BRect(0, 0, 0, 0), B_RGBA32);
if (image == NULL)
return;
}
if (replyTo == NULL)
replyTo = this;
if (replyTo->Looper() == NULL)
debugger("DragMessage: warning - the Handler needs a looper");
_CheckOwnerLock();
if (!message->HasInt32("buttons")) {
BMessage *msg = fOwner->CurrentMessage();
uint32 buttons;
if (msg == NULL
|| msg->FindInt32("buttons", (int32 *)&buttons) != B_OK) {
BPoint point;
GetMouse(&point, &buttons, false);
}
message->AddInt32("buttons", buttons);
}
BMessage::Private privateMessage(message);
privateMessage.SetReply(BMessenger(replyTo, replyTo->Looper()));
// TODO: create area and flatten message into that area!
// send area info over port, not the actual message!
int32 bufferSize = privateMessage.NativeFlattenedSize();
char* buffer = new (nothrow) char[bufferSize];
if (buffer) {
privateMessage.NativeFlatten(buffer, bufferSize);
fOwner->fLink->StartMessage(AS_VIEW_DRAG_IMAGE);
fOwner->fLink->Attach<int32>(image->_ServerToken());
fOwner->fLink->Attach<int32>((int32)dragMode);
fOwner->fLink->Attach<BPoint>(offset);
fOwner->fLink->Attach<int32>(bufferSize);
fOwner->fLink->Attach(buffer, bufferSize);
// we need to wait for the server
// to actually process this message
// before we can delete the bitmap
int32 code;
fOwner->fLink->FlushWithReply(code);
delete [] buffer;
} else {
fprintf(stderr, "BView::DragMessage() - no memory to flatten drag "
"message\n");
}
delete image;
}
void
BView::GetMouse(BPoint *_location, uint32 *_buttons, bool checkMessageQueue)
{
if (_location == NULL && _buttons == NULL)
return;
_CheckOwnerLockAndSwitchCurrent();
uint32 eventOptions = fEventOptions | fMouseEventOptions;
bool noHistory = eventOptions & B_NO_POINTER_HISTORY;
bool fullHistory = eventOptions & B_FULL_POINTER_HISTORY;
if (checkMessageQueue && !noHistory) {
Window()->UpdateIfNeeded();
BMessageQueue *queue = Window()->MessageQueue();
queue->Lock();
// Look out for mouse update messages
BMessage *message;
for (int32 i = 0; (message = queue->FindMessage(i)) != NULL; i++) {
switch (message->what) {
case B_MOUSE_MOVED:
case B_MOUSE_UP:
case B_MOUSE_DOWN:
bool deleteMessage;
if (!Window()->_StealMouseMessage(message, deleteMessage))
continue;
if (!fullHistory && message->what == B_MOUSE_MOVED) {
// Check if the message is too old. Some applications
// check the message queue in such a way that mouse
// messages *must* pile up. This check makes them work
// as intended, although these applications could simply
// use the version of BView::GetMouse() that does not
// check the history. Also note that it isn't a problem
// to delete the message in case there is not a newer
// one. If we don't find a message in the queue, we will
// just fall back to asking the app_sever directly. So
// the imposed delay will not be a problem on slower
// computers. This check also prevents another problem,
// when the message that we use is *not* removed from
// the queue. Subsequent calls to GetMouse() would find
// this message over and over!
bigtime_t eventTime;
if (message->FindInt64("when", &eventTime) == B_OK
&& system_time() - eventTime > 10000) {
// just discard the message
if (deleteMessage)
delete message;
continue;
}
}
message->FindPoint("screen_where", _location);
message->FindInt32("buttons", (int32 *)_buttons);
queue->Unlock();
// we need to hold the queue lock until here, because
// the message might still be used for something else
if (_location != NULL)
ConvertFromScreen(_location);
if (deleteMessage)
delete message;
return;
}
}
queue->Unlock();
}
// If no mouse update message has been found in the message queue,
// we get the current mouse location and buttons from the app_server
fOwner->fLink->StartMessage(AS_GET_MOUSE);
int32 code;
if (fOwner->fLink->FlushWithReply(code) == B_OK
&& code == B_OK) {
BPoint location;
uint32 buttons;
fOwner->fLink->Read<BPoint>(&location);
fOwner->fLink->Read<uint32>(&buttons);
// TODO: ServerWindow replies with an int32 here
ConvertFromScreen(&location);
// TODO: in beos R5, location is already converted to the view
// local coordinate system, so if an app checks the window message
// queue by itself, it might not find what it expects.
// NOTE: the fact that we have mouse coords in screen space in our
// queue avoids the problem that messages already in the queue will
// be outdated as soon as a window or even the view moves. The
// second situation being quite common actually, also with regards
// to scrolling. An app reading these messages would have to know
// the locations of the window and view for each message...
// otherwise it is potentially broken anyways.
if (_location != NULL)
*_location = location;
if (_buttons != NULL)
*_buttons = buttons;
} else {
if (_location != NULL)
_location->Set(0, 0);
if (_buttons != NULL)
*_buttons = 0;
}
}
void
BView::MakeFocus(bool focusState)
{
if (fOwner) {
// TODO: If this view has focus and focusState==false,
// will there really be no other view with focus? No
// cycling to the next one?
BView *focus = fOwner->CurrentFocus();
if (focusState) {
// Unfocus a previous focus view
if (focus && focus != this)
focus->MakeFocus(false);
// if we want to make this view the current focus view
fOwner->_SetFocus(this, true);
} else {
// we want to unfocus this view, but only if it actually has focus
if (focus == this) {
fOwner->_SetFocus(NULL, true);
}
}
}
}
BScrollBar *
BView::ScrollBar(orientation posture) const
{
switch (posture) {
case B_VERTICAL:
return fVerScroller;
case B_HORIZONTAL:
return fHorScroller;
default:
return NULL;
}
}
void
BView::ScrollBy(float deltaX, float deltaY)
{
ScrollTo(BPoint(fBounds.left + deltaX, fBounds.top + deltaY));
}
void
BView::ScrollTo(BPoint where)
{
// scrolling by fractional values is not supported
where.x = roundf(where.x);
where.y = roundf(where.y);
// no reason to process this further if no scroll is intended.
if (where.x == fBounds.left && where.y == fBounds.top)
return;
// make sure scrolling is within valid bounds
if (fHorScroller) {
float min, max;
fHorScroller->GetRange(&min, &max);
if (where.x < min)
where.x = min;
else if (where.x > max)
where.x = max;
}
if (fVerScroller) {
float min, max;
fVerScroller->GetRange(&min, &max);
if (where.y < min)
where.y = min;
else if (where.y > max)
where.y = max;
}
_CheckLockAndSwitchCurrent();
float xDiff = where.x - fBounds.left;
float yDiff = where.y - fBounds.top;
// if we're attached to a window tell app_server about this change
if (fOwner) {
fOwner->fLink->StartMessage(AS_VIEW_SCROLL);
fOwner->fLink->Attach<float>(xDiff);
fOwner->fLink->Attach<float>(yDiff);
fOwner->fLink->Flush();
// fState->valid_flags &= ~B_VIEW_FRAME_BIT;
}
// we modify our bounds rectangle by deltaX/deltaY coord units hor/ver.
fBounds.OffsetTo(where.x, where.y);
// then set the new values of the scrollbars
if (fHorScroller && xDiff != 0.0)
fHorScroller->SetValue(fBounds.left);
if (fVerScroller && yDiff != 0.0)
fVerScroller->SetValue(fBounds.top);
}
status_t
BView::SetEventMask(uint32 mask, uint32 options)
{
if (fEventMask == mask && fEventOptions == options)
return B_OK;
fEventMask = mask | (fEventMask & 0xffff0000);
fEventOptions = options;
fState->archiving_flags |= B_VIEW_EVENT_MASK_BIT;
if (fOwner) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_SET_EVENT_MASK);
fOwner->fLink->Attach<uint32>(mask);
fOwner->fLink->Attach<uint32>(options);
fOwner->fLink->Flush();
}
return B_OK;
}
uint32
BView::EventMask()
{
return fEventMask;
}
status_t
BView::SetMouseEventMask(uint32 mask, uint32 options)
{
// Just don't do anything if the view is not yet attached
// or we were called outside of BView::MouseDown()
if (fOwner != NULL
&& fOwner->CurrentMessage() != NULL
&& fOwner->CurrentMessage()->what == B_MOUSE_DOWN) {
_CheckLockAndSwitchCurrent();
fMouseEventOptions = options;
fOwner->fLink->StartMessage(AS_VIEW_SET_MOUSE_EVENT_MASK);
fOwner->fLink->Attach<uint32>(mask);
fOwner->fLink->Attach<uint32>(options);
fOwner->fLink->Flush();
return B_OK;
}
return B_ERROR;
}
// #pragma mark - Graphic State Functions
void
BView::PushState()
{
_CheckOwnerLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_PUSH_STATE);
// initialize origin and scale
fState->valid_flags |= B_VIEW_SCALE_BIT | B_VIEW_ORIGIN_BIT;
fState->scale = 1.0f;
fState->origin.Set(0, 0);
}
void
BView::PopState()
{
_CheckOwnerLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_POP_STATE);
_FlushIfNotInTransaction();
// invalidate all flags (except those that are not part of pop/push)
fState->valid_flags = B_VIEW_VIEW_COLOR_BIT;
}
void
BView::SetOrigin(BPoint pt)
{
SetOrigin(pt.x, pt.y);
}
void
BView::SetOrigin(float x, float y)
{
if (fState->IsValid(B_VIEW_ORIGIN_BIT)
&& x == fState->origin.x && y == fState->origin.y)
return;
fState->origin.x = x;
fState->origin.y = y;
if (_CheckOwnerLockAndSwitchCurrent()) {
fOwner->fLink->StartMessage(AS_VIEW_SET_ORIGIN);
fOwner->fLink->Attach<float>(x);
fOwner->fLink->Attach<float>(y);
fState->valid_flags |= B_VIEW_ORIGIN_BIT;
}
// our local coord system origin has changed, so when archiving we'll add
// this too
fState->archiving_flags |= B_VIEW_ORIGIN_BIT;
}
BPoint
BView::Origin() const
{
if (!fState->IsValid(B_VIEW_ORIGIN_BIT)) {
// we don't keep graphics state information, therefor
// we need to ask the server for the origin after PopState()
_CheckOwnerLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_GET_ORIGIN);
int32 code;
if (fOwner->fLink->FlushWithReply(code) == B_OK
&& code == B_OK) {
fOwner->fLink->Read<BPoint>(&fState->origin);
fState->valid_flags |= B_VIEW_ORIGIN_BIT;
}
}
return fState->origin;
}
void
BView::SetScale(float scale) const
{
if (fState->IsValid(B_VIEW_SCALE_BIT) && scale == fState->scale)
return;
if (fOwner) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_SET_SCALE);
fOwner->fLink->Attach<float>(scale);
fState->valid_flags |= B_VIEW_SCALE_BIT;
}
fState->scale = scale;
fState->archiving_flags |= B_VIEW_SCALE_BIT;
}
float
BView::Scale() const
{
if (!fState->IsValid(B_VIEW_SCALE_BIT) && fOwner) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_GET_SCALE);
int32 code;
if (fOwner->fLink->FlushWithReply(code) == B_OK
&& code == B_OK)
fOwner->fLink->Read<float>(&fState->scale);
fState->valid_flags |= B_VIEW_SCALE_BIT;
}
return fState->scale;
}
void
BView::SetLineMode(cap_mode lineCap, join_mode lineJoin, float miterLimit)
{
if (fState->IsValid(B_VIEW_LINE_MODES_BIT)
&& lineCap == fState->line_cap && lineJoin == fState->line_join
&& miterLimit == fState->miter_limit)
return;
if (fOwner) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_SET_LINE_MODE);
fOwner->fLink->Attach<int8>((int8)lineCap);
fOwner->fLink->Attach<int8>((int8)lineJoin);
fOwner->fLink->Attach<float>(miterLimit);
fState->valid_flags |= B_VIEW_LINE_MODES_BIT;
}
fState->line_cap = lineCap;
fState->line_join = lineJoin;
fState->miter_limit = miterLimit;
fState->archiving_flags |= B_VIEW_LINE_MODES_BIT;
}
join_mode
BView::LineJoinMode() const
{
// This will update the current state, if necessary
if (!fState->IsValid(B_VIEW_LINE_MODES_BIT))
LineMiterLimit();
return fState->line_join;
}
cap_mode
BView::LineCapMode() const
{
// This will update the current state, if necessary
if (!fState->IsValid(B_VIEW_LINE_MODES_BIT))
LineMiterLimit();
return fState->line_cap;
}
float
BView::LineMiterLimit() const
{
if (!fState->IsValid(B_VIEW_LINE_MODES_BIT) && fOwner) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_GET_LINE_MODE);
int32 code;
if (fOwner->fLink->FlushWithReply(code) == B_OK
&& code == B_OK) {
int8 cap, join;
fOwner->fLink->Read<int8>((int8 *)&cap);
fOwner->fLink->Read<int8>((int8 *)&join);
fOwner->fLink->Read<float>(&fState->miter_limit);
fState->line_cap = (cap_mode)cap;
fState->line_join = (join_mode)join;
}
fState->valid_flags |= B_VIEW_LINE_MODES_BIT;
}
return fState->miter_limit;
}
void
BView::SetDrawingMode(drawing_mode mode)
{
if (fState->IsValid(B_VIEW_DRAWING_MODE_BIT)
&& mode == fState->drawing_mode)
return;
if (fOwner) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_SET_DRAWING_MODE);
fOwner->fLink->Attach<int8>((int8)mode);
fState->valid_flags |= B_VIEW_DRAWING_MODE_BIT;
}
fState->drawing_mode = mode;
fState->archiving_flags |= B_VIEW_DRAWING_MODE_BIT;
}
drawing_mode
BView::DrawingMode() const
{
if (!fState->IsValid(B_VIEW_DRAWING_MODE_BIT) && fOwner) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_GET_DRAWING_MODE);
int32 code;
if (fOwner->fLink->FlushWithReply(code) == B_OK
&& code == B_OK) {
int8 drawingMode;
fOwner->fLink->Read<int8>(&drawingMode);
fState->drawing_mode = (drawing_mode)drawingMode;
fState->valid_flags |= B_VIEW_DRAWING_MODE_BIT;
}
}
return fState->drawing_mode;
}
void
BView::SetBlendingMode(source_alpha sourceAlpha, alpha_function alphaFunction)
{
if (fState->IsValid(B_VIEW_BLENDING_BIT)
&& sourceAlpha == fState->alpha_source_mode
&& alphaFunction == fState->alpha_function_mode)
return;
if (fOwner) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_SET_BLENDING_MODE);
fOwner->fLink->Attach<int8>((int8)sourceAlpha);
fOwner->fLink->Attach<int8>((int8)alphaFunction);
fState->valid_flags |= B_VIEW_BLENDING_BIT;
}
fState->alpha_source_mode = sourceAlpha;
fState->alpha_function_mode = alphaFunction;
fState->archiving_flags |= B_VIEW_BLENDING_BIT;
}
void
BView::GetBlendingMode(source_alpha *_sourceAlpha,
alpha_function *_alphaFunction) const
{
if (!fState->IsValid(B_VIEW_BLENDING_BIT) && fOwner) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_GET_BLENDING_MODE);
int32 code;
if (fOwner->fLink->FlushWithReply(code) == B_OK && code == B_OK) {
int8 alphaSourceMode, alphaFunctionMode;
fOwner->fLink->Read<int8>(&alphaSourceMode);
fOwner->fLink->Read<int8>(&alphaFunctionMode);
fState->alpha_source_mode = (source_alpha)alphaSourceMode;
fState->alpha_function_mode = (alpha_function)alphaFunctionMode;
fState->valid_flags |= B_VIEW_BLENDING_BIT;
}
}
if (_sourceAlpha)
*_sourceAlpha = fState->alpha_source_mode;
if (_alphaFunction)
*_alphaFunction = fState->alpha_function_mode;
}
void
BView::MovePenTo(BPoint point)
{
MovePenTo(point.x, point.y);
}
void
BView::MovePenTo(float x, float y)
{
if (fState->IsValid(B_VIEW_PEN_LOCATION_BIT)
&& x == fState->pen_location.x && y == fState->pen_location.y)
return;
if (fOwner) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_SET_PEN_LOC);
fOwner->fLink->Attach<float>(x);
fOwner->fLink->Attach<float>(y);
fState->valid_flags |= B_VIEW_PEN_LOCATION_BIT;
}
fState->pen_location.x = x;
fState->pen_location.y = y;
fState->archiving_flags |= B_VIEW_PEN_LOCATION_BIT;
}
void
BView::MovePenBy(float x, float y)
{
// this will update the pen location if necessary
if (!fState->IsValid(B_VIEW_PEN_LOCATION_BIT))
PenLocation();
MovePenTo(fState->pen_location.x + x, fState->pen_location.y + y);
}
BPoint
BView::PenLocation() const
{
if (!fState->IsValid(B_VIEW_PEN_LOCATION_BIT) && fOwner) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_GET_PEN_LOC);
int32 code;
if (fOwner->fLink->FlushWithReply(code) == B_OK
&& code == B_OK) {
fOwner->fLink->Read<BPoint>(&fState->pen_location);
fState->valid_flags |= B_VIEW_PEN_LOCATION_BIT;
}
}
return fState->pen_location;
}
void
BView::SetPenSize(float size)
{
if (fState->IsValid(B_VIEW_PEN_SIZE_BIT) && size == fState->pen_size)
return;
if (fOwner) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_SET_PEN_SIZE);
fOwner->fLink->Attach<float>(size);
fState->valid_flags |= B_VIEW_PEN_SIZE_BIT;
}
fState->pen_size = size;
fState->archiving_flags |= B_VIEW_PEN_SIZE_BIT;
}
float
BView::PenSize() const
{
if (!fState->IsValid(B_VIEW_PEN_SIZE_BIT) && fOwner) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_GET_PEN_SIZE);
int32 code;
if (fOwner->fLink->FlushWithReply(code) == B_OK
&& code == B_OK) {
fOwner->fLink->Read<float>(&fState->pen_size);
fState->valid_flags |= B_VIEW_PEN_SIZE_BIT;
}
}
return fState->pen_size;
}
void
BView::SetHighColor(rgb_color color)
{
// are we up-to-date already?
if (fState->IsValid(B_VIEW_HIGH_COLOR_BIT)
&& fState->high_color == color)
return;
if (fOwner) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_SET_HIGH_COLOR);
fOwner->fLink->Attach<rgb_color>(color);
fState->valid_flags |= B_VIEW_HIGH_COLOR_BIT;
}
fState->high_color = color;
fState->archiving_flags |= B_VIEW_HIGH_COLOR_BIT;
}
rgb_color
BView::HighColor() const
{
if (!fState->IsValid(B_VIEW_HIGH_COLOR_BIT) && fOwner) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_GET_HIGH_COLOR);
int32 code;
if (fOwner->fLink->FlushWithReply(code) == B_OK
&& code == B_OK) {
fOwner->fLink->Read<rgb_color>(&fState->high_color);
fState->valid_flags |= B_VIEW_HIGH_COLOR_BIT;
}
}
return fState->high_color;
}
void
BView::SetLowColor(rgb_color color)
{
if (fState->IsValid(B_VIEW_LOW_COLOR_BIT)
&& fState->low_color == color)
return;
if (fOwner) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_SET_LOW_COLOR);
fOwner->fLink->Attach<rgb_color>(color);
fState->valid_flags |= B_VIEW_LOW_COLOR_BIT;
}
fState->low_color = color;
fState->archiving_flags |= B_VIEW_LOW_COLOR_BIT;
}
rgb_color
BView::LowColor() const
{
if (!fState->IsValid(B_VIEW_LOW_COLOR_BIT) && fOwner) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_GET_LOW_COLOR);
int32 code;
if (fOwner->fLink->FlushWithReply(code) == B_OK
&& code == B_OK) {
fOwner->fLink->Read<rgb_color>(&fState->low_color);
fState->valid_flags |= B_VIEW_LOW_COLOR_BIT;
}
}
return fState->low_color;
}
void
BView::SetViewColor(rgb_color color)
{
if (fState->IsValid(B_VIEW_VIEW_COLOR_BIT) && fState->view_color == color)
return;
if (fOwner) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_SET_VIEW_COLOR);
fOwner->fLink->Attach<rgb_color>(color);
fOwner->fLink->Flush();
fState->valid_flags |= B_VIEW_VIEW_COLOR_BIT;
}
fState->view_color = color;
fState->archiving_flags |= B_VIEW_VIEW_COLOR_BIT;
}
rgb_color
BView::ViewColor() const
{
if (!fState->IsValid(B_VIEW_VIEW_COLOR_BIT) && fOwner) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_GET_VIEW_COLOR);
int32 code;
if (fOwner->fLink->FlushWithReply(code) == B_OK
&& code == B_OK) {
fOwner->fLink->Read<rgb_color>(&fState->view_color);
fState->valid_flags |= B_VIEW_VIEW_COLOR_BIT;
}
}
return fState->view_color;
}
void
BView::ForceFontAliasing(bool enable)
{
if (fState->IsValid(B_VIEW_FONT_ALIASING_BIT)
&& enable == fState->font_aliasing)
return;
if (fOwner) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_PRINT_ALIASING);
fOwner->fLink->Attach<bool>(enable);
fState->valid_flags |= B_VIEW_FONT_ALIASING_BIT;
}
fState->font_aliasing = enable;
fState->archiving_flags |= B_VIEW_FONT_ALIASING_BIT;
}
void
BView::SetFont(const BFont* font, uint32 mask)
{
if (!font || mask == 0)
return;
if (mask == B_FONT_ALL) {
fState->font = *font;
} else {
// ToDo: move this into a BFont method
if (mask & B_FONT_FAMILY_AND_STYLE)
fState->font.SetFamilyAndStyle(font->FamilyAndStyle());
if (mask & B_FONT_SIZE)
fState->font.SetSize(font->Size());
if (mask & B_FONT_SHEAR)
fState->font.SetShear(font->Shear());
if (mask & B_FONT_ROTATION)
fState->font.SetRotation(font->Rotation());
if (mask & B_FONT_FALSE_BOLD_WIDTH)
fState->font.SetFalseBoldWidth(font->FalseBoldWidth());
if (mask & B_FONT_SPACING)
fState->font.SetSpacing(font->Spacing());
if (mask & B_FONT_ENCODING)
fState->font.SetEncoding(font->Encoding());
if (mask & B_FONT_FACE)
fState->font.SetFace(font->Face());
if (mask & B_FONT_FLAGS)
fState->font.SetFlags(font->Flags());
}
fState->font_flags |= mask;
if (fOwner) {
_CheckLockAndSwitchCurrent();
fState->UpdateServerFontState(*fOwner->fLink);
}
// TODO: InvalidateLayout() here for convenience?
}
void
BView::GetFont(BFont *font) const
{
*font = fState->font;
}
void
BView::GetFontHeight(font_height *height) const
{
fState->font.GetHeight(height);
}
void
BView::SetFontSize(float size)
{
BFont font;
font.SetSize(size);
SetFont(&font, B_FONT_SIZE);
}
float
BView::StringWidth(const char *string) const
{
return fState->font.StringWidth(string);
}
float
BView::StringWidth(const char* string, int32 length) const
{
return fState->font.StringWidth(string, length);
}
void
BView::GetStringWidths(char *stringArray[],int32 lengthArray[],
int32 numStrings, float widthArray[]) const
{
fState->font.GetStringWidths(const_cast<const char **>(stringArray),
const_cast<const int32 *>(lengthArray), numStrings, widthArray);
}
void
BView::TruncateString(BString *in_out, uint32 mode, float width) const
{
fState->font.TruncateString(in_out, mode, width);
}
void
BView::ClipToPicture(BPicture *picture, BPoint where, bool sync)
{
_ClipToPicture(picture, where, false, sync);
}
void
BView::ClipToInversePicture(BPicture *picture,
BPoint where, bool sync)
{
_ClipToPicture(picture, where, true, sync);
}
void
BView::GetClippingRegion(BRegion* region) const
{
if (!region)
return;
// NOTE: the client has no idea when the clipping in the server
// changed, so it is always read from the server
region->MakeEmpty();
if (fOwner) {
if (fIsPrinting && _CheckOwnerLock()) {
region->Set(fState->print_rect);
return;
}
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_GET_CLIP_REGION);
int32 code;
if (fOwner->fLink->FlushWithReply(code) == B_OK
&& code == B_OK) {
fOwner->fLink->ReadRegion(region);
fState->valid_flags |= B_VIEW_CLIP_REGION_BIT;
}
}
}
void
BView::ConstrainClippingRegion(BRegion* region)
{
if (_CheckOwnerLockAndSwitchCurrent()) {
fOwner->fLink->StartMessage(AS_VIEW_SET_CLIP_REGION);
if (region) {
int32 count = region->CountRects();
fOwner->fLink->Attach<int32>(count);
if (count > 0)
fOwner->fLink->AttachRegion(*region);
} else {
fOwner->fLink->Attach<int32>(-1);
// '-1' means that in the app_server, there won't be any 'local'
// clipping region (it will be NULL)
}
_FlushIfNotInTransaction();
fState->valid_flags &= ~B_VIEW_CLIP_REGION_BIT;
fState->archiving_flags |= B_VIEW_CLIP_REGION_BIT;
}
}
// #pragma mark - Drawing Functions
void
BView::DrawBitmapAsync(const BBitmap* bitmap, BRect bitmapRect, BRect viewRect,
uint32 options)
{
if (bitmap == NULL || fOwner == NULL
|| !bitmapRect.IsValid() || !viewRect.IsValid())
return;
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_DRAW_BITMAP);
fOwner->fLink->Attach<int32>(bitmap->_ServerToken());
fOwner->fLink->Attach<uint32>(options);
fOwner->fLink->Attach<BRect>(viewRect);
fOwner->fLink->Attach<BRect>(bitmapRect);
_FlushIfNotInTransaction();
}
void
BView::DrawBitmapAsync(const BBitmap* bitmap, BRect bitmapRect, BRect viewRect)
{
DrawBitmapAsync(bitmap, bitmapRect, viewRect, 0);
}
void
BView::DrawBitmapAsync(const BBitmap* bitmap, BRect viewRect)
{
if (bitmap && fOwner) {
DrawBitmapAsync(bitmap, bitmap->Bounds().OffsetToCopy(B_ORIGIN),
viewRect, 0);
}
}
void
BView::DrawBitmapAsync(const BBitmap* bitmap, BPoint where)
{
if (bitmap == NULL || fOwner == NULL)
return;
_CheckLockAndSwitchCurrent();
BRect bitmapRect = bitmap->Bounds().OffsetToCopy(B_ORIGIN);
BRect viewRect = bitmapRect.OffsetToCopy(where);
uint32 options = 0;
fOwner->fLink->StartMessage(AS_VIEW_DRAW_BITMAP);
fOwner->fLink->Attach<int32>(bitmap->_ServerToken());
fOwner->fLink->Attach<uint32>(options);
fOwner->fLink->Attach<BRect>(viewRect);
fOwner->fLink->Attach<BRect>(bitmapRect);
_FlushIfNotInTransaction();
}
void
BView::DrawBitmapAsync(const BBitmap* bitmap)
{
DrawBitmapAsync(bitmap, PenLocation());
}
void
BView::DrawBitmap(const BBitmap* bitmap, BRect bitmapRect, BRect viewRect,
uint32 options)
{
if (fOwner) {
DrawBitmapAsync(bitmap, bitmapRect, viewRect, options);
Sync();
}
}
void
BView::DrawBitmap(const BBitmap* bitmap, BRect bitmapRect, BRect viewRect)
{
if (fOwner) {
DrawBitmapAsync(bitmap, bitmapRect, viewRect, 0);
Sync();
}
}
void
BView::DrawBitmap(const BBitmap* bitmap, BRect viewRect)
{
if (bitmap && fOwner) {
DrawBitmap(bitmap, bitmap->Bounds().OffsetToCopy(B_ORIGIN), viewRect,
0);
}
}
void
BView::DrawBitmap(const BBitmap* bitmap, BPoint where)
{
if (fOwner) {
DrawBitmapAsync(bitmap, where);
Sync();
}
}
void
BView::DrawBitmap(const BBitmap* bitmap)
{
DrawBitmap(bitmap, PenLocation());
}
void
BView::DrawChar(char c)
{
DrawString(&c, 1, PenLocation());
}
void
BView::DrawChar(char c, BPoint location)
{
DrawString(&c, 1, location);
}
void
BView::DrawString(const char *string, escapement_delta *delta)
{
if (string == NULL)
return;
DrawString(string, strlen(string), PenLocation(), delta);
}
void
BView::DrawString(const char *string, BPoint location, escapement_delta *delta)
{
if (string == NULL)
return;
DrawString(string, strlen(string), location, delta);
}
void
BView::DrawString(const char *string, int32 length, escapement_delta *delta)
{
DrawString(string, length, PenLocation(), delta);
}
void
BView::DrawString(const char *string, int32 length, BPoint location,
escapement_delta *delta)
{
if (string == NULL || length < 1)
return;
if (fOwner) {
_CheckLockAndSwitchCurrent();
// quite often delta will be NULL
if (delta)
fOwner->fLink->StartMessage(AS_DRAW_STRING_WITH_DELTA);
else
fOwner->fLink->StartMessage(AS_DRAW_STRING);
fOwner->fLink->Attach<int32>(length);
fOwner->fLink->Attach<BPoint>(location);
if (delta)
fOwner->fLink->Attach<escapement_delta>(*delta);
fOwner->fLink->AttachString(string, length);
_FlushIfNotInTransaction();
// this modifies our pen location, so we invalidate the flag.
fState->valid_flags &= ~B_VIEW_PEN_LOCATION_BIT;
}
}
void
BView::StrokeEllipse(BPoint center, float xRadius, float yRadius,
::pattern pattern)
{
StrokeEllipse(BRect(center.x - xRadius, center.y - yRadius,
center.x + xRadius, center.y + yRadius), pattern);
}
void
BView::StrokeEllipse(BRect rect, ::pattern pattern)
{
if (fOwner == NULL)
return;
_CheckLockAndSwitchCurrent();
_UpdatePattern(pattern);
fOwner->fLink->StartMessage(AS_STROKE_ELLIPSE);
fOwner->fLink->Attach<BRect>(rect);
_FlushIfNotInTransaction();
}
void
BView::FillEllipse(BPoint center, float xRadius, float yRadius,
::pattern pattern)
{
FillEllipse(BRect(center.x - xRadius, center.y - yRadius,
center.x + xRadius, center.y + yRadius), pattern);
}
void
BView::FillEllipse(BPoint center, float xRadius, float yRadius,
const BGradient& gradient)
{
FillEllipse(BRect(center.x - xRadius, center.y - yRadius,
center.x + xRadius, center.y + yRadius), gradient);
}
void
BView::FillEllipse(BRect rect, ::pattern pattern)
{
if (fOwner == NULL)
return;
_CheckLockAndSwitchCurrent();
_UpdatePattern(pattern);
fOwner->fLink->StartMessage(AS_FILL_ELLIPSE);
fOwner->fLink->Attach<BRect>(rect);
_FlushIfNotInTransaction();
}
void
BView::FillEllipse(BRect rect, const BGradient& gradient)
{
if (fOwner == NULL)
return;
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_FILL_ELLIPSE_GRADIENT);
fOwner->fLink->Attach<BRect>(rect);
fOwner->fLink->Attach<gradient_type>(gradient.Type());
fOwner->fLink->AttachGradient(gradient);
_FlushIfNotInTransaction();
}
void
BView::StrokeArc(BPoint center, float xRadius, float yRadius, float startAngle,
float arcAngle, ::pattern pattern)
{
StrokeArc(BRect(center.x - xRadius, center.y - yRadius, center.x + xRadius,
center.y + yRadius), startAngle, arcAngle, pattern);
}
void
BView::StrokeArc(BRect rect, float startAngle, float arcAngle,
::pattern pattern)
{
if (fOwner == NULL)
return;
_CheckLockAndSwitchCurrent();
_UpdatePattern(pattern);
fOwner->fLink->StartMessage(AS_STROKE_ARC);
fOwner->fLink->Attach<BRect>(rect);
fOwner->fLink->Attach<float>(startAngle);
fOwner->fLink->Attach<float>(arcAngle);
_FlushIfNotInTransaction();
}
void
BView::FillArc(BPoint center,float xRadius, float yRadius, float startAngle,
float arcAngle, ::pattern pattern)
{
FillArc(BRect(center.x - xRadius, center.y - yRadius, center.x + xRadius,
center.y + yRadius), startAngle, arcAngle, pattern);
}
void
BView::FillArc(BPoint center,float xRadius, float yRadius, float startAngle,
float arcAngle, const BGradient& gradient)
{
FillArc(BRect(center.x - xRadius, center.y - yRadius, center.x + xRadius,
center.y + yRadius), startAngle, arcAngle, gradient);
}
void
BView::FillArc(BRect rect, float startAngle, float arcAngle,
::pattern pattern)
{
if (fOwner == NULL)
return;
_CheckLockAndSwitchCurrent();
_UpdatePattern(pattern);
fOwner->fLink->StartMessage(AS_FILL_ARC);
fOwner->fLink->Attach<BRect>(rect);
fOwner->fLink->Attach<float>(startAngle);
fOwner->fLink->Attach<float>(arcAngle);
_FlushIfNotInTransaction();
}
void
BView::FillArc(BRect rect, float startAngle, float arcAngle,
const BGradient& gradient)
{
if (fOwner == NULL)
return;
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_FILL_ARC_GRADIENT);
fOwner->fLink->Attach<BRect>(rect);
fOwner->fLink->Attach<float>(startAngle);
fOwner->fLink->Attach<float>(arcAngle);
fOwner->fLink->Attach<gradient_type>(gradient.Type());
fOwner->fLink->AttachGradient(gradient);
_FlushIfNotInTransaction();
}
void
BView::StrokeBezier(BPoint *controlPoints, ::pattern pattern)
{
if (fOwner == NULL)
return;
_CheckLockAndSwitchCurrent();
_UpdatePattern(pattern);
fOwner->fLink->StartMessage(AS_STROKE_BEZIER);
fOwner->fLink->Attach<BPoint>(controlPoints[0]);
fOwner->fLink->Attach<BPoint>(controlPoints[1]);
fOwner->fLink->Attach<BPoint>(controlPoints[2]);
fOwner->fLink->Attach<BPoint>(controlPoints[3]);
_FlushIfNotInTransaction();
}
void
BView::FillBezier(BPoint *controlPoints, ::pattern pattern)
{
if (fOwner == NULL)
return;
_CheckLockAndSwitchCurrent();
_UpdatePattern(pattern);
fOwner->fLink->StartMessage(AS_FILL_BEZIER);
fOwner->fLink->Attach<BPoint>(controlPoints[0]);
fOwner->fLink->Attach<BPoint>(controlPoints[1]);
fOwner->fLink->Attach<BPoint>(controlPoints[2]);
fOwner->fLink->Attach<BPoint>(controlPoints[3]);
_FlushIfNotInTransaction();
}
void
BView::FillBezier(BPoint *controlPoints, const BGradient& gradient)
{
if (fOwner == NULL)
return;
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_FILL_BEZIER_GRADIENT);
fOwner->fLink->Attach<BPoint>(controlPoints[0]);
fOwner->fLink->Attach<BPoint>(controlPoints[1]);
fOwner->fLink->Attach<BPoint>(controlPoints[2]);
fOwner->fLink->Attach<BPoint>(controlPoints[3]);
fOwner->fLink->Attach<gradient_type>(gradient.Type());
fOwner->fLink->AttachGradient(gradient);
_FlushIfNotInTransaction();
}
void
BView::StrokePolygon(const BPolygon *polygon, bool closed, ::pattern pattern)
{
if (!polygon)
return;
StrokePolygon(polygon->fPoints, polygon->fCount, polygon->Frame(), closed,
pattern);
}
void
BView::StrokePolygon(const BPoint* pointArray, int32 numPoints, bool closed,
::pattern pattern)
{
BPolygon polygon(pointArray, numPoints);
StrokePolygon(polygon.fPoints, polygon.fCount, polygon.Frame(), closed,
pattern);
}
void
BView::StrokePolygon(const BPoint *ptArray, int32 numPoints, BRect bounds,
bool closed, ::pattern pattern)
{
if (!ptArray
|| numPoints <= 1
|| fOwner == NULL)
return;
_CheckLockAndSwitchCurrent();
_UpdatePattern(pattern);
BPolygon polygon(ptArray, numPoints);
polygon.MapTo(polygon.Frame(), bounds);
if (fOwner->fLink->StartMessage(AS_STROKE_POLYGON,
polygon.fCount * sizeof(BPoint) + sizeof(BRect) + sizeof(bool)
+ sizeof(int32)) == B_OK) {
fOwner->fLink->Attach<BRect>(polygon.Frame());
fOwner->fLink->Attach<bool>(closed);
fOwner->fLink->Attach<int32>(polygon.fCount);
fOwner->fLink->Attach(polygon.fPoints, polygon.fCount * sizeof(BPoint));
_FlushIfNotInTransaction();
} else {
fprintf(stderr, "ERROR: Can't send polygon to app_server!\n");
}
}
void
BView::FillPolygon(const BPolygon *polygon, ::pattern pattern)
{
if (polygon == NULL
|| polygon->fCount <= 2
|| fOwner == NULL)
return;
_CheckLockAndSwitchCurrent();
_UpdatePattern(pattern);
if (fOwner->fLink->StartMessage(AS_FILL_POLYGON,
polygon->fCount * sizeof(BPoint) + sizeof(BRect) + sizeof(int32))
== B_OK) {
fOwner->fLink->Attach<BRect>(polygon->Frame());
fOwner->fLink->Attach<int32>(polygon->fCount);
fOwner->fLink->Attach(polygon->fPoints,
polygon->fCount * sizeof(BPoint));
_FlushIfNotInTransaction();
} else {
fprintf(stderr, "ERROR: Can't send polygon to app_server!\n");
}
}
void
BView::FillPolygon(const BPolygon *polygon, const BGradient& gradient)
{
if (polygon == NULL
|| polygon->fCount <= 2
|| fOwner == NULL)
return;
_CheckLockAndSwitchCurrent();
if (fOwner->fLink->StartMessage(AS_FILL_POLYGON_GRADIENT,
polygon->fCount * sizeof(BPoint)
+ sizeof(BRect) + sizeof(int32)) == B_OK) {
fOwner->fLink->Attach<BRect>(polygon->Frame());
fOwner->fLink->Attach<int32>(polygon->fCount);
fOwner->fLink->Attach(polygon->fPoints,
polygon->fCount * sizeof(BPoint));
fOwner->fLink->Attach<gradient_type>(gradient.Type());
fOwner->fLink->AttachGradient(gradient);
_FlushIfNotInTransaction();
} else {
fprintf(stderr, "ERROR: Can't send polygon to app_server!\n");
}
}
void
BView::FillPolygon(const BPoint *ptArray, int32 numPts, ::pattern pattern)
{
if (!ptArray)
return;
BPolygon polygon(ptArray, numPts);
FillPolygon(&polygon, pattern);
}
void
BView::FillPolygon(const BPoint *ptArray, int32 numPts,
const BGradient& gradient)
{
if (!ptArray)
return;
BPolygon polygon(ptArray, numPts);
FillPolygon(&polygon, gradient);
}
void
BView::FillPolygon(const BPoint *ptArray, int32 numPts, BRect bounds,
pattern p)
{
if (!ptArray)
return;
BPolygon polygon(ptArray, numPts);
polygon.MapTo(polygon.Frame(), bounds);
FillPolygon(&polygon, p);
}
void
BView::FillPolygon(const BPoint *ptArray, int32 numPts, BRect bounds,
const BGradient& gradient)
{
if (!ptArray)
return;
BPolygon polygon(ptArray, numPts);
polygon.MapTo(polygon.Frame(), bounds);
FillPolygon(&polygon, gradient);
}
void
BView::StrokeRect(BRect rect, ::pattern pattern)
{
if (fOwner == NULL)
return;
_CheckLockAndSwitchCurrent();
_UpdatePattern(pattern);
fOwner->fLink->StartMessage(AS_STROKE_RECT);
fOwner->fLink->Attach<BRect>(rect);
_FlushIfNotInTransaction();
}
void
BView::FillRect(BRect rect, ::pattern pattern)
{
if (fOwner == NULL)
return;
// NOTE: ensuring compatibility with R5,
// invalid rects are not filled, they are stroked though!
if (!rect.IsValid())
return;
_CheckLockAndSwitchCurrent();
_UpdatePattern(pattern);
fOwner->fLink->StartMessage(AS_FILL_RECT);
fOwner->fLink->Attach<BRect>(rect);
_FlushIfNotInTransaction();
}
void
BView::FillRect(BRect rect, const BGradient& gradient)
{
if (fOwner == NULL)
return;
// NOTE: ensuring compatibility with R5,
// invalid rects are not filled, they are stroked though!
if (!rect.IsValid())
return;
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_FILL_RECT_GRADIENT);
fOwner->fLink->Attach<BRect>(rect);
fOwner->fLink->Attach<gradient_type>(gradient.Type());
fOwner->fLink->AttachGradient(gradient);
_FlushIfNotInTransaction();
}
void
BView::StrokeRoundRect(BRect rect, float xRadius, float yRadius,
::pattern pattern)
{
if (fOwner == NULL)
return;
_CheckLockAndSwitchCurrent();
_UpdatePattern(pattern);
fOwner->fLink->StartMessage(AS_STROKE_ROUNDRECT);
fOwner->fLink->Attach<BRect>(rect);
fOwner->fLink->Attach<float>(xRadius);
fOwner->fLink->Attach<float>(yRadius);
_FlushIfNotInTransaction();
}
void
BView::FillRoundRect(BRect rect, float xRadius, float yRadius,
::pattern pattern)
{
if (fOwner == NULL)
return;
_CheckLockAndSwitchCurrent();
_UpdatePattern(pattern);
fOwner->fLink->StartMessage(AS_FILL_ROUNDRECT);
fOwner->fLink->Attach<BRect>(rect);
fOwner->fLink->Attach<float>(xRadius);
fOwner->fLink->Attach<float>(yRadius);
_FlushIfNotInTransaction();
}
void
BView::FillRoundRect(BRect rect, float xRadius, float yRadius,
const BGradient& gradient)
{
if (fOwner == NULL)
return;
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_FILL_ROUNDRECT_GRADIENT);
fOwner->fLink->Attach<BRect>(rect);
fOwner->fLink->Attach<float>(xRadius);
fOwner->fLink->Attach<float>(yRadius);
fOwner->fLink->Attach<gradient_type>(gradient.Type());
fOwner->fLink->AttachGradient(gradient);
_FlushIfNotInTransaction();
}
void
BView::FillRegion(BRegion *region, ::pattern pattern)
{
if (region == NULL || fOwner == NULL)
return;
_CheckLockAndSwitchCurrent();
_UpdatePattern(pattern);
fOwner->fLink->StartMessage(AS_FILL_REGION);
fOwner->fLink->AttachRegion(*region);
_FlushIfNotInTransaction();
}
void
BView::FillRegion(BRegion *region, const BGradient& gradient)
{
if (region == NULL || fOwner == NULL)
return;
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_FILL_REGION_GRADIENT);
fOwner->fLink->AttachRegion(*region);
fOwner->fLink->Attach<gradient_type>(gradient.Type());
fOwner->fLink->AttachGradient(gradient);
_FlushIfNotInTransaction();
}
void
BView::StrokeTriangle(BPoint pt1, BPoint pt2, BPoint pt3, BRect bounds,
::pattern pattern)
{
if (fOwner == NULL)
return;
_CheckLockAndSwitchCurrent();
_UpdatePattern(pattern);
fOwner->fLink->StartMessage(AS_STROKE_TRIANGLE);
fOwner->fLink->Attach<BPoint>(pt1);
fOwner->fLink->Attach<BPoint>(pt2);
fOwner->fLink->Attach<BPoint>(pt3);
fOwner->fLink->Attach<BRect>(bounds);
_FlushIfNotInTransaction();
}
void
BView::StrokeTriangle(BPoint pt1, BPoint pt2, BPoint pt3, pattern p)
{
if (fOwner) {
// we construct the smallest rectangle that contains the 3 points
// for the 1st point
BRect bounds(pt1, pt1);
// for the 2nd point
if (pt2.x < bounds.left)
bounds.left = pt2.x;
if (pt2.y < bounds.top)
bounds.top = pt2.y;
if (pt2.x > bounds.right)
bounds.right = pt2.x;
if (pt2.y > bounds.bottom)
bounds.bottom = pt2.y;
// for the 3rd point
if (pt3.x < bounds.left)
bounds.left = pt3.x;
if (pt3.y < bounds.top)
bounds.top = pt3.y;
if (pt3.x > bounds.right)
bounds.right = pt3.x;
if (pt3.y > bounds.bottom)
bounds.bottom = pt3.y;
StrokeTriangle(pt1, pt2, pt3, bounds, p);
}
}
void
BView::FillTriangle(BPoint pt1, BPoint pt2, BPoint pt3, pattern p)
{
if (fOwner) {
// we construct the smallest rectangle that contains the 3 points
// for the 1st point
BRect bounds(pt1, pt1);
// for the 2nd point
if (pt2.x < bounds.left)
bounds.left = pt2.x;
if (pt2.y < bounds.top)
bounds.top = pt2.y;
if (pt2.x > bounds.right)
bounds.right = pt2.x;
if (pt2.y > bounds.bottom)
bounds.bottom = pt2.y;
// for the 3rd point
if (pt3.x < bounds.left)
bounds.left = pt3.x;
if (pt3.y < bounds.top)
bounds.top = pt3.y;
if (pt3.x > bounds.right)
bounds.right = pt3.x;
if (pt3.y > bounds.bottom)
bounds.bottom = pt3.y;
FillTriangle(pt1, pt2, pt3, bounds, p);
}
}
void
BView::FillTriangle(BPoint pt1, BPoint pt2, BPoint pt3,
const BGradient& gradient)
{
if (fOwner) {
// we construct the smallest rectangle that contains the 3 points
// for the 1st point
BRect bounds(pt1, pt1);
// for the 2nd point
if (pt2.x < bounds.left)
bounds.left = pt2.x;
if (pt2.y < bounds.top)
bounds.top = pt2.y;
if (pt2.x > bounds.right)
bounds.right = pt2.x;
if (pt2.y > bounds.bottom)
bounds.bottom = pt2.y;
// for the 3rd point
if (pt3.x < bounds.left)
bounds.left = pt3.x;
if (pt3.y < bounds.top)
bounds.top = pt3.y;
if (pt3.x > bounds.right)
bounds.right = pt3.x;
if (pt3.y > bounds.bottom)
bounds.bottom = pt3.y;
FillTriangle(pt1, pt2, pt3, bounds, gradient);
}
}
void
BView::FillTriangle(BPoint pt1, BPoint pt2, BPoint pt3,
BRect bounds, ::pattern pattern)
{
if (fOwner == NULL)
return;
_CheckLockAndSwitchCurrent();
_UpdatePattern(pattern);
fOwner->fLink->StartMessage(AS_FILL_TRIANGLE);
fOwner->fLink->Attach<BPoint>(pt1);
fOwner->fLink->Attach<BPoint>(pt2);
fOwner->fLink->Attach<BPoint>(pt3);
fOwner->fLink->Attach<BRect>(bounds);
_FlushIfNotInTransaction();
}
void
BView::FillTriangle(BPoint pt1, BPoint pt2, BPoint pt3,
BRect bounds, const BGradient& gradient)
{
if (fOwner == NULL)
return;
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_FILL_TRIANGLE_GRADIENT);
fOwner->fLink->Attach<BPoint>(pt1);
fOwner->fLink->Attach<BPoint>(pt2);
fOwner->fLink->Attach<BPoint>(pt3);
fOwner->fLink->Attach<BRect>(bounds);
fOwner->fLink->Attach<gradient_type>(gradient.Type());
fOwner->fLink->AttachGradient(gradient);
_FlushIfNotInTransaction();
}
void
BView::StrokeLine(BPoint toPt, pattern p)
{
StrokeLine(PenLocation(), toPt, p);
}
void
BView::StrokeLine(BPoint pt0, BPoint pt1, ::pattern pattern)
{
if (fOwner == NULL)
return;
_CheckLockAndSwitchCurrent();
_UpdatePattern(pattern);
fOwner->fLink->StartMessage(AS_STROKE_LINE);
fOwner->fLink->Attach<BPoint>(pt0);
fOwner->fLink->Attach<BPoint>(pt1);
_FlushIfNotInTransaction();
// this modifies our pen location, so we invalidate the flag.
fState->valid_flags &= ~B_VIEW_PEN_LOCATION_BIT;
}
void
BView::StrokeShape(BShape *shape, ::pattern pattern)
{
if (shape == NULL || fOwner == NULL)
return;
shape_data *sd = (shape_data *)shape->fPrivateData;
if (sd->opCount == 0 || sd->ptCount == 0)
return;
_CheckLockAndSwitchCurrent();
_UpdatePattern(pattern);
fOwner->fLink->StartMessage(AS_STROKE_SHAPE);
fOwner->fLink->Attach<BRect>(shape->Bounds());
fOwner->fLink->Attach<int32>(sd->opCount);
fOwner->fLink->Attach<int32>(sd->ptCount);
fOwner->fLink->Attach(sd->opList, sd->opCount * sizeof(uint32));
fOwner->fLink->Attach(sd->ptList, sd->ptCount * sizeof(BPoint));
_FlushIfNotInTransaction();
}
void
BView::FillShape(BShape *shape, ::pattern pattern)
{
if (shape == NULL || fOwner == NULL)
return;
shape_data *sd = (shape_data *)(shape->fPrivateData);
if (sd->opCount == 0 || sd->ptCount == 0)
return;
_CheckLockAndSwitchCurrent();
_UpdatePattern(pattern);
fOwner->fLink->StartMessage(AS_FILL_SHAPE);
fOwner->fLink->Attach<BRect>(shape->Bounds());
fOwner->fLink->Attach<int32>(sd->opCount);
fOwner->fLink->Attach<int32>(sd->ptCount);
fOwner->fLink->Attach(sd->opList, sd->opCount * sizeof(int32));
fOwner->fLink->Attach(sd->ptList, sd->ptCount * sizeof(BPoint));
_FlushIfNotInTransaction();
}
void
BView::FillShape(BShape *shape, const BGradient& gradient)
{
if (shape == NULL || fOwner == NULL)
return;
shape_data *sd = (shape_data *)(shape->fPrivateData);
if (sd->opCount == 0 || sd->ptCount == 0)
return;
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_FILL_SHAPE_GRADIENT);
fOwner->fLink->Attach<BRect>(shape->Bounds());
fOwner->fLink->Attach<int32>(sd->opCount);
fOwner->fLink->Attach<int32>(sd->ptCount);
fOwner->fLink->Attach(sd->opList, sd->opCount * sizeof(int32));
fOwner->fLink->Attach(sd->ptList, sd->ptCount * sizeof(BPoint));
fOwner->fLink->Attach<gradient_type>(gradient.Type());
fOwner->fLink->AttachGradient(gradient);
_FlushIfNotInTransaction();
}
void
BView::BeginLineArray(int32 count)
{
if (fOwner == NULL)
return;
if (count <= 0)
debugger("Calling BeginLineArray with a count <= 0");
_CheckLock();
if (fCommArray) {
debugger("Can't nest BeginLineArray calls");
// not fatal, but it helps during
// development of your app and is in
// line with R5...
delete [] fCommArray->array;
delete fCommArray;
}
fCommArray = new _array_data_;
fCommArray->maxCount = count;
fCommArray->count = 0;
fCommArray->array = new _array_hdr_[count];
}
void
BView::AddLine(BPoint pt0, BPoint pt1, rgb_color col)
{
if (fOwner == NULL)
return;
if (!fCommArray)
debugger("BeginLineArray must be called before using AddLine");
_CheckLock();
const uint32 &arrayCount = fCommArray->count;
if (arrayCount < fCommArray->maxCount) {
fCommArray->array[arrayCount].startX = pt0.x;
fCommArray->array[arrayCount].startY = pt0.y;
fCommArray->array[arrayCount].endX = pt1.x;
fCommArray->array[arrayCount].endY = pt1.y;
fCommArray->array[arrayCount].color = col;
fCommArray->count++;
}
}
void
BView::EndLineArray()
{
if (fOwner == NULL)
return;
if (!fCommArray)
debugger("Can't call EndLineArray before BeginLineArray");
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_STROKE_LINEARRAY);
fOwner->fLink->Attach<int32>(fCommArray->count);
fOwner->fLink->Attach(fCommArray->array,
fCommArray->count * sizeof(_array_hdr_));
_FlushIfNotInTransaction();
_RemoveCommArray();
}
void
BView::SetDiskMode(char* filename, long offset)
{
// TODO: implement
// One BeBook version has this to say about SetDiskMode():
//
// "Begins recording a picture to the file with the given filename
// at the given offset. Subsequent drawing commands sent to the view
// will be written to the file until EndPicture() is called. The
// stored commands may be played from the file with DrawPicture()."
}
void
BView::BeginPicture(BPicture *picture)
{
if (_CheckOwnerLockAndSwitchCurrent()
&& picture && picture->fUsurped == NULL) {
picture->Usurp(fCurrentPicture);
fCurrentPicture = picture;
fOwner->fLink->StartMessage(AS_VIEW_BEGIN_PICTURE);
}
}
void
BView::AppendToPicture(BPicture *picture)
{
_CheckLockAndSwitchCurrent();
if (picture && picture->fUsurped == NULL) {
int32 token = picture->Token();
if (token == -1) {
BeginPicture(picture);
} else {
picture->SetToken(-1);
picture->Usurp(fCurrentPicture);
fCurrentPicture = picture;
fOwner->fLink->StartMessage(AS_VIEW_APPEND_TO_PICTURE);
fOwner->fLink->Attach<int32>(token);
}
}
}
BPicture *
BView::EndPicture()
{
if (_CheckOwnerLockAndSwitchCurrent() && fCurrentPicture) {
int32 token;
fOwner->fLink->StartMessage(AS_VIEW_END_PICTURE);
int32 code;
if (fOwner->fLink->FlushWithReply(code) == B_OK
&& code == B_OK
&& fOwner->fLink->Read<int32>(&token) == B_OK) {
BPicture *picture = fCurrentPicture;
fCurrentPicture = picture->StepDown();
picture->SetToken(token);
return picture;
}
}
return NULL;
}
void
BView::SetViewBitmap(const BBitmap *bitmap, BRect srcRect, BRect dstRect,
uint32 followFlags, uint32 options)
{
_SetViewBitmap(bitmap, srcRect, dstRect, followFlags, options);
}
void
BView::SetViewBitmap(const BBitmap *bitmap, uint32 followFlags, uint32 options)
{
BRect rect;
if (bitmap)
rect = bitmap->Bounds();
rect.OffsetTo(B_ORIGIN);
_SetViewBitmap(bitmap, rect, rect, followFlags, options);
}
void
BView::ClearViewBitmap()
{
_SetViewBitmap(NULL, BRect(), BRect(), 0, 0);
}
status_t
BView::SetViewOverlay(const BBitmap *overlay, BRect srcRect, BRect dstRect,
rgb_color *colorKey, uint32 followFlags, uint32 options)
{
if ((overlay->fFlags & B_BITMAP_WILL_OVERLAY) == 0)
return B_BAD_VALUE;
status_t status = _SetViewBitmap(overlay, srcRect, dstRect, followFlags,
options | AS_REQUEST_COLOR_KEY);
if (status == B_OK) {
// read the color that will be treated as transparent
fOwner->fLink->Read<rgb_color>(colorKey);
}
return status;
}
status_t
BView::SetViewOverlay(const BBitmap *overlay, rgb_color *colorKey,
uint32 followFlags, uint32 options)
{
BRect rect;
if (overlay != NULL) {
rect = overlay->Bounds();
rect.OffsetTo(B_ORIGIN);
}
return SetViewOverlay(overlay, rect, rect, colorKey, followFlags, options);
}
void
BView::ClearViewOverlay()
{
_SetViewBitmap(NULL, BRect(), BRect(), 0, 0);
}
void
BView::CopyBits(BRect src, BRect dst)
{
if (fOwner == NULL)
return;
if (!src.IsValid() || !dst.IsValid())
return;
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_COPY_BITS);
fOwner->fLink->Attach<BRect>(src);
fOwner->fLink->Attach<BRect>(dst);
_FlushIfNotInTransaction();
}
void
BView::DrawPicture(const BPicture *picture)
{
if (picture == NULL)
return;
DrawPictureAsync(picture, PenLocation());
Sync();
}
void
BView::DrawPicture(const BPicture *picture, BPoint where)
{
if (picture == NULL)
return;
DrawPictureAsync(picture, where);
Sync();
}
void
BView::DrawPicture(const char *filename, long offset, BPoint where)
{
if (!filename)
return;
DrawPictureAsync(filename, offset, where);
Sync();
}
void
BView::DrawPictureAsync(const BPicture *picture)
{
if (picture == NULL)
return;
DrawPictureAsync(picture, PenLocation());
}
void
BView::DrawPictureAsync(const BPicture *picture, BPoint where)
{
if (picture == NULL)
return;
if (_CheckOwnerLockAndSwitchCurrent() && picture->Token() > 0) {
fOwner->fLink->StartMessage(AS_VIEW_DRAW_PICTURE);
fOwner->fLink->Attach<int32>(picture->Token());
fOwner->fLink->Attach<BPoint>(where);
_FlushIfNotInTransaction();
}
}
void
BView::DrawPictureAsync(const char *filename, long offset, BPoint where)
{
if (!filename)
return;
// TODO: Test
BFile file(filename, B_READ_ONLY);
if (file.InitCheck() < B_OK)
return;
file.Seek(offset, SEEK_SET);
BPicture picture;
if (picture.Unflatten(&file) < B_OK)
return;
DrawPictureAsync(&picture, where);
}
void
BView::Invalidate(BRect invalRect)
{
if (fOwner == NULL)
return;
// NOTE: This rounding of the invalid rect is to stay compatible with BeOS.
// On the server side, the invalid rect will be converted to a BRegion,
// which rounds in a different manner, so that it really includes the
// fractional coordinates of a BRect (ie ceilf(rect.right) &
// ceilf(rect.bottom)), which is also what BeOS does. So we have to do the
// different rounding here to stay compatible in both ways.
invalRect.left = (int)invalRect.left;
invalRect.top = (int)invalRect.top;
invalRect.right = (int)invalRect.right;
invalRect.bottom = (int)invalRect.bottom;
if (!invalRect.IsValid())
return;
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_INVALIDATE_RECT);
fOwner->fLink->Attach<BRect>(invalRect);
// TODO: determine why this check isn't working correctly.
#if 0
if (!fOwner->fUpdateRequested) {
fOwner->fLink->Flush();
fOwner->fUpdateRequested = true;
}
#endif
fOwner->fLink->Flush();
}
void
BView::Invalidate(const BRegion* region)
{
if (region == NULL || fOwner == NULL)
return;
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_INVALIDATE_REGION);
fOwner->fLink->AttachRegion(*region);
if (!fOwner->fUpdateRequested) {
fOwner->fLink->Flush();
fOwner->fUpdateRequested = true;
}
}
void
BView::Invalidate()
{
Invalidate(Bounds());
}
void
BView::InvertRect(BRect rect)
{
if (fOwner) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_INVERT_RECT);
fOwner->fLink->Attach<BRect>(rect);
_FlushIfNotInTransaction();
}
}
// #pragma mark - View Hierarchy Functions
void
BView::AddChild(BView *child, BView *before)
{
STRACE(("BView(%s)::AddChild(child='%s' before='%s')\n",
this->Name() ? this->Name(): "NULL",
child && child->Name() ? child->Name(): "NULL",
before && before->Name() ? before->Name(): "NULL"));
if (!_AddChild(child, before))
return;
if (fLayoutData->fLayout)
fLayoutData->fLayout->AddView(child);
}
bool
BView::AddChild(BLayoutItem* child)
{
if (!fLayoutData->fLayout)
return false;
return fLayoutData->fLayout->AddItem(child);
}
bool
BView::_AddChild(BView *child, BView *before)
{
if (!child)
return false;
if (child->fParent != NULL) {
debugger("AddChild failed - the view already has a parent.");
return false;
}
bool lockedOwner = false;
if (fOwner && !fOwner->IsLocked()) {
fOwner->Lock();
lockedOwner = true;
}
if (!_AddChildToList(child, before)) {
debugger("AddChild failed!");
if (lockedOwner)
fOwner->Unlock();
return false;
}
if (fOwner) {
_CheckLockAndSwitchCurrent();
child->_SetOwner(fOwner);
child->_CreateSelf();
child->_Attach();
if (lockedOwner)
fOwner->Unlock();
}
InvalidateLayout();
return true;
}
bool
BView::RemoveChild(BView *child)
{
STRACE(("BView(%s)::RemoveChild(%s)\n", Name(), child->Name()));
if (!child)
return false;
if (child->fParent != this)
return false;
return child->RemoveSelf();
}
int32
BView::CountChildren() const
{
_CheckLock();
uint32 count = 0;
BView *child = fFirstChild;
while (child != NULL) {
count++;
child = child->fNextSibling;
}
return count;
}
BView *
BView::ChildAt(int32 index) const
{
_CheckLock();
BView *child = fFirstChild;
while (child != NULL && index-- > 0) {
child = child->fNextSibling;
}
return child;
}
BView *
BView::NextSibling() const
{
return fNextSibling;
}
BView *
BView::PreviousSibling() const
{
return fPreviousSibling;
}
bool
BView::RemoveSelf()
{
if (fParent && fParent->fLayoutData->fLayout)
return fParent->fLayoutData->fLayout->RemoveView(this);
else
return _RemoveSelf();
}
bool
BView::_RemoveSelf()
{
STRACE(("BView(%s)::RemoveSelf()...\n", Name()));
// Remove this child from its parent
BWindow* owner = fOwner;
_CheckLock();
if (owner != NULL) {
_UpdateStateForRemove();
_Detach();
}
BView* parent = fParent;
if (!parent || !parent->_RemoveChildFromList(this))
return false;
if (owner != NULL && !fTopLevelView) {
// the top level view is deleted by the app_server automatically
owner->fLink->StartMessage(AS_VIEW_DELETE);
owner->fLink->Attach<int32>(_get_object_token_(this));
}
parent->InvalidateLayout();
STRACE(("DONE: BView(%s)::RemoveSelf()\n", Name()));
return true;
}
BView *
BView::Parent() const
{
if (fParent && fParent->fTopLevelView)
return NULL;
return fParent;
}
BView *
BView::FindView(const char *name) const
{
if (name == NULL)
return NULL;
if (Name() != NULL && !strcmp(Name(), name))
return const_cast<BView *>(this);
BView *child = fFirstChild;
while (child != NULL) {
BView *view = child->FindView(name);
if (view != NULL)
return view;
child = child->fNextSibling;
}
return NULL;
}
void
BView::MoveBy(float deltaX, float deltaY)
{
MoveTo(fParentOffset.x + roundf(deltaX), fParentOffset.y + roundf(deltaY));
}
void
BView::MoveTo(BPoint where)
{
MoveTo(where.x, where.y);
}
void
BView::MoveTo(float x, float y)
{
if (x == fParentOffset.x && y == fParentOffset.y)
return;
// BeBook says we should do this. And it makes sense.
x = roundf(x);
y = roundf(y);
if (fOwner) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_MOVE_TO);
fOwner->fLink->Attach<float>(x);
fOwner->fLink->Attach<float>(y);
// fState->valid_flags |= B_VIEW_FRAME_BIT;
_FlushIfNotInTransaction();
}
_MoveTo((int32)x, (int32)y);
}
void
BView::ResizeBy(float deltaWidth, float deltaHeight)
{
// BeBook says we should do this. And it makes sense.
deltaWidth = roundf(deltaWidth);
deltaHeight = roundf(deltaHeight);
if (deltaWidth == 0 && deltaHeight == 0)
return;
if (fOwner) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_RESIZE_TO);
fOwner->fLink->Attach<float>(fBounds.Width() + deltaWidth);
fOwner->fLink->Attach<float>(fBounds.Height() + deltaHeight);
// fState->valid_flags |= B_VIEW_FRAME_BIT;
_FlushIfNotInTransaction();
}
_ResizeBy((int32)deltaWidth, (int32)deltaHeight);
}
void
BView::ResizeTo(float width, float height)
{
ResizeBy(width - fBounds.Width(), height - fBounds.Height());
}
void
BView::ResizeTo(BSize size)
{
ResizeBy(size.width - fBounds.Width(), size.height - fBounds.Height());
}
// #pragma mark - Inherited Methods (from BHandler)
status_t
BView::GetSupportedSuites(BMessage *data)
{
if (data == NULL)
return B_BAD_VALUE;
status_t status = data->AddString("suites", "suite/vnd.Be-view");
BPropertyInfo propertyInfo(sViewPropInfo);
if (status == B_OK)
status = data->AddFlat("messages", &propertyInfo);
if (status == B_OK)
return BHandler::GetSupportedSuites(data);
return status;
}
BHandler *
BView::ResolveSpecifier(BMessage *msg, int32 index, BMessage *specifier,
int32 what, const char *property)
{
if (msg->what == B_WINDOW_MOVE_BY
|| msg->what == B_WINDOW_MOVE_TO)
return this;
BPropertyInfo propertyInfo(sViewPropInfo);
status_t err = B_BAD_SCRIPT_SYNTAX;
BMessage replyMsg(B_REPLY);
switch (propertyInfo.FindMatch(msg, index, specifier, what, property)) {
case 0:
case 1:
case 2:
case 3:
case 5:
return this;
case 4:
if (fShelf) {
msg->PopSpecifier();
return fShelf;
}
err = B_NAME_NOT_FOUND;
replyMsg.AddString("message", "This window doesn't have a shelf");
break;
case 6:
{
if (!fFirstChild) {
err = B_NAME_NOT_FOUND;
replyMsg.AddString("message", "This window doesn't have children.");
break;
}
BView *child = NULL;
switch (what) {
case B_INDEX_SPECIFIER:
{
int32 index;
err = specifier->FindInt32("index", &index);
if (err == B_OK)
child = ChildAt(index);
break;
}
case B_REVERSE_INDEX_SPECIFIER:
{
int32 rindex;
err = specifier->FindInt32("index", &rindex);
if (err == B_OK)
child = ChildAt(CountChildren() - rindex);
break;
}
case B_NAME_SPECIFIER:
{
const char *name;
err = specifier->FindString("name", &name);
if (err == B_OK)
child = FindView(name);
break;
}
}
if (child != NULL) {
msg->PopSpecifier();
return child;
}
if (err == B_OK)
err = B_BAD_INDEX;
replyMsg.AddString("message",
"Cannot find view at/with specified index/name.");
break;
}
default:
return BHandler::ResolveSpecifier(msg, index, specifier, what,
property);
}
if (err < B_OK) {
replyMsg.what = B_MESSAGE_NOT_UNDERSTOOD;
if (err == B_BAD_SCRIPT_SYNTAX)
replyMsg.AddString("message", "Didn't understand the specifier(s)");
else
replyMsg.AddString("message", strerror(err));
}
replyMsg.AddInt32("error", err);
msg->SendReply(&replyMsg);
return NULL;
}
void
BView::MessageReceived(BMessage* msg)
{
if (!msg->HasSpecifiers()) {
switch (msg->what) {
case B_VIEW_RESIZED:
// By the time the message arrives, the bounds may have
// changed already, that's why we don't use the values
// in the message itself.
FrameResized(fBounds.Width(), fBounds.Height());
break;
case B_VIEW_MOVED:
FrameMoved(fParentOffset);
break;
case B_MOUSE_WHEEL_CHANGED:
{
float deltaX = 0.0f, deltaY = 0.0f;
BScrollBar *horizontal = ScrollBar(B_HORIZONTAL);
if (horizontal != NULL)
msg->FindFloat("be:wheel_delta_x", &deltaX);
BScrollBar *vertical = ScrollBar(B_VERTICAL);
if (vertical != NULL)
msg->FindFloat("be:wheel_delta_y", &deltaY);
if (deltaX == 0.0f && deltaY == 0.0f)
return;
float smallStep, largeStep;
if (horizontal != NULL) {
horizontal->GetSteps(&smallStep, &largeStep);
// pressing the option/command/control key scrolls faster
if (modifiers()
& (B_OPTION_KEY | B_COMMAND_KEY | B_CONTROL_KEY)) {
deltaX *= largeStep;
} else
deltaX *= smallStep * 3;
horizontal->SetValue(horizontal->Value() + deltaX);
}
if (vertical != NULL) {
vertical->GetSteps(&smallStep, &largeStep);
// pressing the option/command/control key scrolls faster
if (modifiers()
& (B_OPTION_KEY | B_COMMAND_KEY | B_CONTROL_KEY)) {
deltaY *= largeStep;
} else
deltaY *= smallStep * 3;
vertical->SetValue(vertical->Value() + deltaY);
}
break;
}
default:
return BHandler::MessageReceived(msg);
}
return;
}
// Scripting message
BMessage replyMsg(B_REPLY);
status_t err = B_BAD_SCRIPT_SYNTAX;
int32 index;
BMessage specifier;
int32 what;
const char *prop;
if (msg->GetCurrentSpecifier(&index, &specifier, &what, &prop) != B_OK)
return BHandler::MessageReceived(msg);
BPropertyInfo propertyInfo(sViewPropInfo);
switch (propertyInfo.FindMatch(msg, index, &specifier, what, prop)) {
case 0:
err = replyMsg.AddRect("result", Frame());
break;
case 1:
{
BRect newFrame;
err = msg->FindRect("data", &newFrame);
if (err == B_OK) {
MoveTo(newFrame.LeftTop());
ResizeTo(newFrame.right, newFrame.bottom);
}
break;
}
case 2:
err = replyMsg.AddBool( "result", IsHidden());
break;
case 3:
{
bool newHiddenState;
err = msg->FindBool("data", &newHiddenState);
if (err == B_OK) {
if (!IsHidden() && newHiddenState == true)
Hide();
else if (IsHidden() && newHiddenState == false)
Show();
}
}
case 5:
err = replyMsg.AddInt32("result", CountChildren());
break;
default:
return BHandler::MessageReceived(msg);
}
if (err < B_OK) {
replyMsg.what = B_MESSAGE_NOT_UNDERSTOOD;
if (err == B_BAD_SCRIPT_SYNTAX)
replyMsg.AddString("message", "Didn't understand the specifier(s)");
else
replyMsg.AddString("message", strerror(err));
}
replyMsg.AddInt32("error", err);
msg->SendReply(&replyMsg);
}
status_t
BView::Perform(perform_code code, void* _data)
{
switch (code) {
case PERFORM_CODE_MIN_SIZE:
((perform_data_min_size*)_data)->return_value
= BView::MinSize();
return B_OK;
case PERFORM_CODE_MAX_SIZE:
((perform_data_max_size*)_data)->return_value
= BView::MaxSize();
return B_OK;
case PERFORM_CODE_PREFERRED_SIZE:
((perform_data_preferred_size*)_data)->return_value
= BView::PreferredSize();
return B_OK;
case PERFORM_CODE_LAYOUT_ALIGNMENT:
((perform_data_layout_alignment*)_data)->return_value
= BView::LayoutAlignment();
return B_OK;
case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
((perform_data_has_height_for_width*)_data)->return_value
= BView::HasHeightForWidth();
return B_OK;
case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
{
perform_data_get_height_for_width* data
= (perform_data_get_height_for_width*)_data;
BView::GetHeightForWidth(data->width, &data->min, &data->max,
&data->preferred);
return B_OK;
}
case PERFORM_CODE_SET_LAYOUT:
{
perform_data_set_layout* data = (perform_data_set_layout*)_data;
BView::SetLayout(data->layout);
return B_OK;
}
case PERFORM_CODE_INVALIDATE_LAYOUT:
{
perform_data_invalidate_layout* data
= (perform_data_invalidate_layout*)_data;
BView::InvalidateLayout(data->descendants);
return B_OK;
}
case PERFORM_CODE_DO_LAYOUT:
{
BView::DoLayout();
return B_OK;
}
}
return BHandler::Perform(code, _data);
}
// #pragma mark - Layout Functions
BSize
BView::MinSize()
{
// TODO: make sure this works correctly when some methods are overridden
float width, height;
GetPreferredSize(&width, &height);
return BLayoutUtils::ComposeSize(fLayoutData->fMinSize,
(fLayoutData->fLayout ? fLayoutData->fLayout->MinSize()
: BSize(width, height)));
}
BSize
BView::MaxSize()
{
return BLayoutUtils::ComposeSize(fLayoutData->fMaxSize,
(fLayoutData->fLayout ? fLayoutData->fLayout->MaxSize()
: BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED)));
}
BSize
BView::PreferredSize()
{
// TODO: make sure this works correctly when some methods are overridden
float width, height;
GetPreferredSize(&width, &height);
return BLayoutUtils::ComposeSize(fLayoutData->fPreferredSize,
(fLayoutData->fLayout ? fLayoutData->fLayout->PreferredSize()
: BSize(width, height)));
}
BAlignment
BView::LayoutAlignment()
{
return BLayoutUtils::ComposeAlignment(fLayoutData->fAlignment,
(fLayoutData->fLayout ? fLayoutData->fLayout->Alignment()
: BAlignment(B_ALIGN_HORIZONTAL_CENTER, B_ALIGN_VERTICAL_CENTER)));
}
void
BView::SetExplicitMinSize(BSize size)
{
fLayoutData->fMinSize = size;
InvalidateLayout();
}
void
BView::SetExplicitMaxSize(BSize size)
{
fLayoutData->fMaxSize = size;
InvalidateLayout();
}
void
BView::SetExplicitPreferredSize(BSize size)
{
fLayoutData->fPreferredSize = size;
InvalidateLayout();
}
void
BView::SetExplicitAlignment(BAlignment alignment)
{
fLayoutData->fAlignment = alignment;
InvalidateLayout();
}
BSize
BView::ExplicitMinSize() const
{
return fLayoutData->fMinSize;
}
BSize
BView::ExplicitMaxSize() const
{
return fLayoutData->fMaxSize;
}
BSize
BView::ExplicitPreferredSize() const
{
return fLayoutData->fPreferredSize;
}
BAlignment
BView::ExplicitAlignment() const
{
return fLayoutData->fAlignment;
}
bool
BView::HasHeightForWidth()
{
return (fLayoutData->fLayout
? fLayoutData->fLayout->HasHeightForWidth() : false);
}
void
BView::GetHeightForWidth(float width, float* min, float* max, float* preferred)
{
if (fLayoutData->fLayout)
fLayoutData->fLayout->GetHeightForWidth(width, min, max, preferred);
}
void
BView::SetLayout(BLayout* layout)
{
if (layout == fLayoutData->fLayout)
return;
fFlags |= B_SUPPORTS_LAYOUT;
// unset and delete the old layout
if (fLayoutData->fLayout) {
fLayoutData->fLayout->SetView(NULL);
delete fLayoutData->fLayout;
}
fLayoutData->fLayout = layout;
if (fLayoutData->fLayout) {
fLayoutData->fLayout->SetView(this);
// add all children
int count = CountChildren();
for (int i = 0; i < count; i++)
fLayoutData->fLayout->AddView(ChildAt(i));
}
InvalidateLayout();
}
BLayout*
BView::GetLayout() const
{
return fLayoutData->fLayout;
}
void
BView::InvalidateLayout(bool descendants)
{
if (fLayoutData->fLayoutValid && !fLayoutData->fLayoutInProgress
&& fLayoutData->fLayoutInvalidationDisabled == 0) {
if (fParent && fParent->fLayoutData->fLayoutValid)
fParent->InvalidateLayout(false);
fLayoutData->fLayoutValid = false;
if (fLayoutData->fLayout)
fLayoutData->fLayout->InvalidateLayout();
if (descendants) {
int count = CountChildren();
for (int i = 0; i < count; i++)
ChildAt(i)->InvalidateLayout(descendants);
}
if (fTopLevelView) {
// trigger layout process
if (fOwner)
fOwner->PostMessage(B_LAYOUT_WINDOW);
}
}
}
void
BView::EnableLayoutInvalidation()
{
if (fLayoutData->fLayoutInvalidationDisabled > 0)
fLayoutData->fLayoutInvalidationDisabled--;
}
void
BView::DisableLayoutInvalidation()
{
fLayoutData->fLayoutInvalidationDisabled++;
}
bool
BView::IsLayoutValid() const
{
return fLayoutData->fLayoutValid;
}
BLayoutContext*
BView::LayoutContext() const
{
return fLayoutData->fLayoutContext;
}
void
BView::Layout(bool force)
{
BLayoutContext context;
_Layout(force, &context);
}
void
BView::Relayout()
{
if (fLayoutData->fLayoutValid && !fLayoutData->fLayoutInProgress) {
fLayoutData->fNeedsRelayout = true;
// Layout() is recursive, that is if the parent view is currently laid
// out, we don't call layout() on this view, but wait for the parent's
// Layout() to do that for us.
if (!fParent || !fParent->fLayoutData->fLayoutInProgress)
Layout(false);
}
}
void
BView::DoLayout()
{
if (fLayoutData->fLayout)
fLayoutData->fLayout->LayoutView();
}
void
BView::_Layout(bool force, BLayoutContext* context)
{
//printf("%p->BView::_Layout(%d, %p)\n", this, force, context);
//printf(" fNeedsRelayout: %d, fLayoutValid: %d, fLayoutInProgress: %d\n",
//fLayoutData->fNeedsRelayout, fLayoutData->fLayoutValid, fLayoutData->fLayoutInProgress);
if (fLayoutData->fNeedsRelayout || !fLayoutData->fLayoutValid || force) {
fLayoutData->fLayoutValid = false;
if (fLayoutData->fLayoutInProgress)
return;
BLayoutContext* oldContext = fLayoutData->fLayoutContext;
fLayoutData->fLayoutContext = context;
fLayoutData->fLayoutInProgress = true;
DoLayout();
fLayoutData->fLayoutInProgress = false;
fLayoutData->fLayoutValid = true;
fLayoutData->fNeedsRelayout = false;
// layout children
int32 childCount = CountChildren();
for (int32 i = 0; i < childCount; i++) {
BView* child = ChildAt(i);
if (!child->IsHidden(child))
child->_Layout(force, context);
}
fLayoutData->fLayoutContext = oldContext;
// invalidate the drawn content, if requested
if (fFlags & B_INVALIDATE_AFTER_LAYOUT)
Invalidate();
}
}
// #pragma mark - Private Functions
void
BView::_InitData(BRect frame, const char *name, uint32 resizingMode,
uint32 flags)
{
// Info: The name of the view is set by BHandler constructor
STRACE(("BView::InitData: enter\n"));
// initialize members
if ((resizingMode & ~_RESIZE_MASK_) || (flags & _RESIZE_MASK_))
printf("%s BView::InitData(): resizing mode or flags swapped\n", name);
// There are applications that swap the resize mask and the flags in the
// BView constructor. This does not cause problems under BeOS as it just
// ors the two fields to one 32bit flag.
// For now we do the same but print the above warning message.
// ToDo: this should be removed at some point and the original
// version restored:
// fFlags = (resizingMode & _RESIZE_MASK_) | (flags & ~_RESIZE_MASK_);
fFlags = resizingMode | flags;
// handle rounding
frame.left = roundf(frame.left);
frame.top = roundf(frame.top);
frame.right = roundf(frame.right);
frame.bottom = roundf(frame.bottom);
fParentOffset.Set(frame.left, frame.top);
fOwner = NULL;
fParent = NULL;
fNextSibling = NULL;
fPreviousSibling = NULL;
fFirstChild = NULL;
fShowLevel = 0;
fTopLevelView = false;
fCurrentPicture = NULL;
fCommArray = NULL;
fVerScroller = NULL;
fHorScroller = NULL;
fIsPrinting = false;
fAttached = false;
fState = new BPrivate::ViewState;
fBounds = frame.OffsetToCopy(B_ORIGIN);
fShelf = NULL;
fEventMask = 0;
fEventOptions = 0;
fMouseEventOptions = 0;
fLayoutData = new LayoutData;
}
void
BView::_RemoveCommArray()
{
if (fCommArray) {
delete [] fCommArray->array;
delete fCommArray;
fCommArray = NULL;
}
}
void
BView::_SetOwner(BWindow *newOwner)
{
if (!newOwner)
_RemoveCommArray();
if (fOwner != newOwner && fOwner) {
if (fOwner->fFocus == this)
MakeFocus(false);
if (fOwner->fLastMouseMovedView == this)
fOwner->fLastMouseMovedView = NULL;
fOwner->RemoveHandler(this);
if (fShelf)
fOwner->RemoveHandler(fShelf);
}
if (newOwner && newOwner != fOwner) {
newOwner->AddHandler(this);
if (fShelf)
newOwner->AddHandler(fShelf);
if (fTopLevelView)
SetNextHandler(newOwner);
else
SetNextHandler(fParent);
}
fOwner = newOwner;
for (BView *child = fFirstChild; child != NULL; child = child->fNextSibling)
child->_SetOwner(newOwner);
}
void
BView::_ClipToPicture(BPicture *picture, BPoint where,
bool invert, bool sync)
{
if (!picture)
return;
#if 1
// TODO: Move the implementation to the server!!!
// This implementation is pretty slow, since just creating an offscreen bitmap
// takes a lot of time. That's the main reason why it should be moved
// to the server.
// Here the idea is to get rid of the padding bytes in the bitmap,
// as padding complicates and slows down the iteration.
// TODO: Maybe it's not so nice as it assumes BBitmaps to be aligned
// to a 4 byte boundary.
BRect bounds(Bounds());
if ((bounds.IntegerWidth() + 1) % 32)
bounds.right = bounds.left + ((bounds.IntegerWidth() + 1) / 32 + 1) * 32 - 1;
// TODO: I used a RGBA32 bitmap because drawing on a GRAY8 doesn't work.
BBitmap *bitmap = new BBitmap(bounds, B_RGBA32, true);
if (bitmap && bitmap->InitCheck() == B_OK && bitmap->Lock()) {
BView *view = new BView(bounds, "drawing view", B_FOLLOW_NONE, 0);
bitmap->AddChild(view);
view->DrawPicture(picture, where);
view->Sync();
bitmap->Unlock();
}
BRegion region;
int32 width = bounds.IntegerWidth() + 1;
int32 height = bounds.IntegerHeight() + 1;
if (bitmap->LockBits() == B_OK) {
uint32 bit = 0;
uint32 *bits = (uint32 *)bitmap->Bits();
clipping_rect rect;
// TODO: A possible optimization would be adding "spans" instead
// of 1x1 rects. That would probably help with very complex
// BPictures
for (int32 y = 0; y < height; y++) {
for (int32 x = 0; x < width; x++) {
bit = *bits++;
if (bit != 0xFFFFFFFF) {
rect.left = x;
rect.right = rect.left;
rect.top = rect.bottom = y;
region.Include(rect);
}
}
}
bitmap->UnlockBits();
}
delete bitmap;
if (invert) {
BRegion inverseRegion;
inverseRegion.Include(Bounds());
inverseRegion.Exclude(&region);
ConstrainClippingRegion(&inverseRegion);
} else
ConstrainClippingRegion(&region);
#else
if (_CheckOwnerLockAndSwitchCurrent()) {
fOwner->fLink->StartMessage(AS_VIEW_CLIP_TO_PICTURE);
fOwner->fLink->Attach<int32>(picture->Token());
fOwner->fLink->Attach<BPoint>(where);
fOwner->fLink->Attach<bool>(invert);
// TODO: I think that "sync" means another thing here:
// the bebook, at least, says so.
if (sync)
fOwner->fLink->Flush();
fState->valid_flags &= ~B_VIEW_CLIP_REGION_BIT;
}
fState->archiving_flags |= B_VIEW_CLIP_REGION_BIT;
#endif
}
bool
BView::_RemoveChildFromList(BView* child)
{
if (child->fParent != this)
return false;
if (fFirstChild == child) {
// it's the first view in the list
fFirstChild = child->fNextSibling;
} else {
// there must be a previous sibling
child->fPreviousSibling->fNextSibling = child->fNextSibling;
}
if (child->fNextSibling)
child->fNextSibling->fPreviousSibling = child->fPreviousSibling;
child->fParent = NULL;
child->fNextSibling = NULL;
child->fPreviousSibling = NULL;
return true;
}
bool
BView::_AddChildToList(BView* child, BView* before)
{
if (!child)
return false;
if (child->fParent != NULL) {
debugger("View already belongs to someone else");
return false;
}
if (before != NULL && before->fParent != this) {
debugger("Invalid before view");
return false;
}
if (before != NULL) {
// add view before this one
child->fNextSibling = before;
child->fPreviousSibling = before->fPreviousSibling;
if (child->fPreviousSibling != NULL)
child->fPreviousSibling->fNextSibling = child;
before->fPreviousSibling = child;
if (fFirstChild == before)
fFirstChild = child;
} else {
// add view to the end of the list
BView *last = fFirstChild;
while (last != NULL && last->fNextSibling != NULL) {
last = last->fNextSibling;
}
if (last != NULL) {
last->fNextSibling = child;
child->fPreviousSibling = last;
} else {
fFirstChild = child;
child->fPreviousSibling = NULL;
}
child->fNextSibling = NULL;
}
child->fParent = this;
return true;
}
/*! \brief Creates the server counterpart of this view.
This is only done for views that are part of the view hierarchy, ie. when
they are attached to a window.
RemoveSelf() deletes the server object again.
*/
bool
BView::_CreateSelf()
{
// AS_VIEW_CREATE & AS_VIEW_CREATE_ROOT do not use the
// current view mechanism via _CheckLockAndSwitchCurrent() - the token
// of the view and its parent are both send to the server.
if (fTopLevelView)
fOwner->fLink->StartMessage(AS_VIEW_CREATE_ROOT);
else
fOwner->fLink->StartMessage(AS_VIEW_CREATE);
fOwner->fLink->Attach<int32>(_get_object_token_(this));
fOwner->fLink->AttachString(Name());
fOwner->fLink->Attach<BRect>(Frame());
fOwner->fLink->Attach<BPoint>(LeftTop());
fOwner->fLink->Attach<uint32>(ResizingMode());
fOwner->fLink->Attach<uint32>(fEventMask);
fOwner->fLink->Attach<uint32>(fEventOptions);
fOwner->fLink->Attach<uint32>(Flags());
fOwner->fLink->Attach<bool>(IsHidden(this));
fOwner->fLink->Attach<rgb_color>(fState->view_color);
if (fTopLevelView)
fOwner->fLink->Attach<int32>(B_NULL_TOKEN);
else
fOwner->fLink->Attach<int32>(_get_object_token_(fParent));
fOwner->fLink->Flush();
_CheckOwnerLockAndSwitchCurrent();
fState->UpdateServerState(*fOwner->fLink);
// we create all its children, too
for (BView *child = fFirstChild; child != NULL;
child = child->fNextSibling) {
child->_CreateSelf();
}
fOwner->fLink->Flush();
return true;
}
/*! Sets the new view position.
It doesn't contact the server, though - the only case where this
is called outside of MoveTo() is as reaction of moving a view
in the server (a.k.a. B_WINDOW_RESIZED).
It also calls the BView's FrameMoved() hook.
*/
void
BView::_MoveTo(int32 x, int32 y)
{
fParentOffset.Set(x, y);
if (Window() != NULL && fFlags & B_FRAME_EVENTS) {
BMessage moved(B_VIEW_MOVED);
moved.AddInt64("when", system_time());
moved.AddPoint("where", BPoint(x, y));
BMessenger target(this);
target.SendMessage(&moved);
}
}
/*! Computes the actual new frame size and recalculates the size of
the children as well.
It doesn't contact the server, though - the only case where this
is called outside of ResizeBy() is as reaction of resizing a view
in the server (a.k.a. B_WINDOW_RESIZED).
It also calls the BView's FrameResized() hook.
*/
void
BView::_ResizeBy(int32 deltaWidth, int32 deltaHeight)
{
fBounds.right += deltaWidth;
fBounds.bottom += deltaHeight;
if (Window() == NULL) {
// we're not supposed to exercise the resizing code in case
// we haven't been attached to a window yet
return;
}
// layout the children
if (fFlags & B_SUPPORTS_LAYOUT) {
Relayout();
} else {
for (BView* child = fFirstChild; child; child = child->fNextSibling)
child->_ParentResizedBy(deltaWidth, deltaHeight);
}
if (fFlags & B_FRAME_EVENTS) {
BMessage resized(B_VIEW_RESIZED);
resized.AddInt64("when", system_time());
resized.AddFloat("width", fBounds.Width());
resized.AddFloat("height", fBounds.Height());
BMessenger target(this);
target.SendMessage(&resized);
}
}
/*! Relayouts the view according to its resizing mode. */
void
BView::_ParentResizedBy(int32 x, int32 y)
{
uint32 resizingMode = fFlags & _RESIZE_MASK_;
BRect newFrame = Frame();
// follow with left side
if ((resizingMode & 0x0F00U) == _VIEW_RIGHT_ << 8)
newFrame.left += x;
else if ((resizingMode & 0x0F00U) == _VIEW_CENTER_ << 8)
newFrame.left += x / 2;
// follow with right side
if ((resizingMode & 0x000FU) == _VIEW_RIGHT_)
newFrame.right += x;
else if ((resizingMode & 0x000FU) == _VIEW_CENTER_)
newFrame.right += x / 2;
// follow with top side
if ((resizingMode & 0xF000U) == _VIEW_BOTTOM_ << 12)
newFrame.top += y;
else if ((resizingMode & 0xF000U) == _VIEW_CENTER_ << 12)
newFrame.top += y / 2;
// follow with bottom side
if ((resizingMode & 0x00F0U) == _VIEW_BOTTOM_ << 4)
newFrame.bottom += y;
else if ((resizingMode & 0x00F0U) == _VIEW_CENTER_ << 4)
newFrame.bottom += y / 2;
if (newFrame.LeftTop() != fParentOffset) {
// move view
_MoveTo((int32)roundf(newFrame.left), (int32)roundf(newFrame.top));
}
if (newFrame != Frame()) {
// resize view
int32 widthDiff = (int32)(newFrame.Width() - fBounds.Width());
int32 heightDiff = (int32)(newFrame.Height() - fBounds.Height());
_ResizeBy(widthDiff, heightDiff);
}
}
void
BView::_Activate(bool active)
{
WindowActivated(active);
for (BView *child = fFirstChild; child != NULL;
child = child->fNextSibling) {
child->_Activate(active);
}
}
void
BView::_Attach()
{
AttachedToWindow();
fAttached = true;
// after giving the view a chance to do this itself,
// check for the B_PULSE_NEEDED flag and make sure the
// window set's up the pulse messaging
if (fOwner) {
if (fFlags & B_PULSE_NEEDED) {
_CheckLock();
if (fOwner->fPulseRunner == NULL)
fOwner->SetPulseRate(fOwner->PulseRate());
}
if (!fOwner->IsHidden())
Invalidate();
}
for (BView* child = fFirstChild; child != NULL;
child = child->fNextSibling) {
// we need to check for fAttached as new views could have been
// added in AttachedToWindow() - and those are already attached
if (!child->fAttached)
child->_Attach();
}
AllAttached();
}
void
BView::_Detach()
{
DetachedFromWindow();
fAttached = false;
for (BView* child = fFirstChild; child != NULL;
child = child->fNextSibling) {
child->_Detach();
}
AllDetached();
if (fOwner) {
_CheckLock();
if (!fOwner->IsHidden())
Invalidate();
// make sure our owner doesn't need us anymore
if (fOwner->CurrentFocus() == this) {
MakeFocus(false);
// MakeFocus() is virtual and might not be
// passing through to the BView version,
// but we need to make sure at this point
// that we are not the focus view anymore.
if (fOwner->CurrentFocus() == this)
fOwner->_SetFocus(NULL, true);
}
if (fOwner->fDefaultButton == this)
fOwner->SetDefaultButton(NULL);
if (fOwner->fKeyMenuBar == this)
fOwner->fKeyMenuBar = NULL;
if (fOwner->fLastMouseMovedView == this)
fOwner->fLastMouseMovedView = NULL;
if (fOwner->fLastViewToken == _get_object_token_(this))
fOwner->fLastViewToken = B_NULL_TOKEN;
_SetOwner(NULL);
}
}
void
BView::_Draw(BRect updateRect)
{
if (IsHidden(this) || !(Flags() & B_WILL_DRAW))
return;
// NOTE: if ViewColor() == B_TRANSPARENT_COLOR and no B_WILL_DRAW
// -> View is simply not drawn at all
_SwitchServerCurrentView();
ConvertFromScreen(&updateRect);
// TODO: make states robust (the hook implementation could
// mess things up if it uses non-matching Push- and PopState(),
// we would not be guaranteed to still have the same state on
// the stack after having called Draw())
PushState();
Draw(updateRect);
PopState();
Flush();
}
void
BView::_DrawAfterChildren(BRect updateRect)
{
if (IsHidden(this) || !(Flags() & B_WILL_DRAW)
|| !(Flags() & B_DRAW_ON_CHILDREN))
return;
_SwitchServerCurrentView();
ConvertFromScreen(&updateRect);
// TODO: make states robust (see above)
PushState();
DrawAfterChildren(updateRect);
PopState();
Flush();
}
void
BView::_Pulse()
{
if (Flags() & B_PULSE_NEEDED)
Pulse();
for (BView *child = fFirstChild; child != NULL; child = child->fNextSibling) {
child->_Pulse();
}
}
void
BView::_UpdateStateForRemove()
{
// TODO: _CheckLockAndSwitchCurrent() would be good enough, no?
if (!_CheckOwnerLockAndSwitchCurrent())
return;
fState->UpdateFrom(*fOwner->fLink);
// if (!fState->IsValid(B_VIEW_FRAME_BIT)) {
// fOwner->fLink->StartMessage(AS_VIEW_GET_COORD);
//
// status_t code;
// if (fOwner->fLink->FlushWithReply(code) == B_OK
// && code == B_OK) {
// fOwner->fLink->Read<BPoint>(&fParentOffset);
// fOwner->fLink->Read<BRect>(&fBounds);
// fState->valid_flags |= B_VIEW_FRAME_BIT;
// }
// }
// update children as well
for (BView *child = fFirstChild; child != NULL; child = child->fNextSibling) {
if (child->fOwner)
child->_UpdateStateForRemove();
}
}
inline void
BView::_UpdatePattern(::pattern pattern)
{
if (fState->IsValid(B_VIEW_PATTERN_BIT) && pattern == fState->pattern)
return;
if (fOwner) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_SET_PATTERN);
fOwner->fLink->Attach< ::pattern>(pattern);
fState->valid_flags |= B_VIEW_PATTERN_BIT;
}
fState->pattern = pattern;
}
void
BView::_FlushIfNotInTransaction()
{
if (!fOwner->fInTransaction) {
fOwner->Flush();
}
}
BShelf *
BView::_Shelf() const
{
return fShelf;
}
void
BView::_SetShelf(BShelf *shelf)
{
if (fShelf != NULL && fOwner != NULL)
fOwner->RemoveHandler(fShelf);
fShelf = shelf;
if (fShelf != NULL && fOwner != NULL)
fOwner->AddHandler(fShelf);
}
status_t
BView::_SetViewBitmap(const BBitmap* bitmap, BRect srcRect, BRect dstRect,
uint32 followFlags, uint32 options)
{
if (!_CheckOwnerLockAndSwitchCurrent())
return B_ERROR;
int32 serverToken = bitmap ? bitmap->_ServerToken() : -1;
fOwner->fLink->StartMessage(AS_VIEW_SET_VIEW_BITMAP);
fOwner->fLink->Attach<int32>(serverToken);
fOwner->fLink->Attach<BRect>(srcRect);
fOwner->fLink->Attach<BRect>(dstRect);
fOwner->fLink->Attach<int32>(followFlags);
fOwner->fLink->Attach<int32>(options);
status_t status = B_ERROR;
fOwner->fLink->FlushWithReply(status);
return status;
}
bool
BView::_CheckOwnerLockAndSwitchCurrent() const
{
STRACE(("BView(%s)::_CheckOwnerLockAndSwitchCurrent()...", Name()));
if (fOwner == NULL) {
debugger("View method requires owner and doesn't have one.");
return false;
}
_CheckLockAndSwitchCurrent();
return true;
}
bool
BView::_CheckOwnerLock() const
{
if (fOwner) {
fOwner->check_lock();
return true;
} else {
debugger("View method requires owner and doesn't have one.");
return false;
}
}
void
BView::_CheckLockAndSwitchCurrent() const
{
STRACE(("BView(%s)::_CheckLockAndSwitchCurrent()...", Name() ? Name(): "NULL"));
if (!fOwner)
return;
fOwner->check_lock();
_SwitchServerCurrentView();
}
void
BView::_CheckLock() const
{
if (fOwner)
fOwner->check_lock();
}
void
BView::_SwitchServerCurrentView() const
{
int32 serverToken = _get_object_token_(this);
if (fOwner->fLastViewToken != serverToken) {
STRACE(("contacting app_server... sending token: %ld\n", serverToken));
fOwner->fLink->StartMessage(AS_SET_CURRENT_VIEW);
fOwner->fLink->Attach<int32>(serverToken);
fOwner->fLastViewToken = serverToken;
} else {
STRACE(("quiet2\n"));
}
}
extern "C" void
_ReservedView1__5BView(BView* view, BRect rect)
{
view->BView::DrawAfterChildren(rect);
}
extern "C" void
_ReservedView2__5BView(BView* view)
{
// MinSize()
perform_data_min_size data;
view->Perform(PERFORM_CODE_MIN_SIZE, &data);
}
extern "C" void
_ReservedView3__5BView(BView* view)
{
// MaxSize()
perform_data_max_size data;
view->Perform(PERFORM_CODE_MAX_SIZE, &data);
}
extern "C" BSize
_ReservedView4__5BView(BView* view)
{
// PreferredSize()
perform_data_preferred_size data;
view->Perform(PERFORM_CODE_PREFERRED_SIZE, &data);
return data.return_value;
}
extern "C" BAlignment
_ReservedView5__5BView(BView* view)
{
// LayoutAlignment()
perform_data_layout_alignment data;
view->Perform(PERFORM_CODE_LAYOUT_ALIGNMENT, &data);
return data.return_value;
}
extern "C" bool
_ReservedView6__5BView(BView* view)
{
// HasHeightForWidth()
perform_data_has_height_for_width data;
view->Perform(PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH, &data);
return data.return_value;
}
extern "C" void
_ReservedView7__5BView(BView* view, float width, float* min, float* max,
float* preferred)
{
// GetHeightForWidth()
perform_data_get_height_for_width data;
data.width = width;
view->Perform(PERFORM_CODE_GET_HEIGHT_FOR_WIDTH, &data);
if (min != NULL)
*min = data.min;
if (max != NULL)
*max = data.max;
if (preferred != NULL)
*preferred = data.preferred;
}
extern "C" void
_ReservedView8__5BView(BView* view, BLayout* layout)
{
// SetLayout()
perform_data_set_layout data;
data.layout = layout;
view->Perform(PERFORM_CODE_SET_LAYOUT, &data);
}
extern "C" void
_ReservedView9__5BView(BView* view, bool descendants)
{
// InvalidateLayout()
perform_data_invalidate_layout data;
data.descendants = descendants;
view->Perform(PERFORM_CODE_INVALIDATE_LAYOUT, &data);
}
extern "C" void
_ReservedView10__5BView(BView* view)
{
// DoLayout()
view->Perform(PERFORM_CODE_DO_LAYOUT, NULL);
}
void BView::_ReservedView11(){}
void BView::_ReservedView12(){}
void BView::_ReservedView13(){}
void BView::_ReservedView14(){}
void BView::_ReservedView15(){}
void BView::_ReservedView16(){}
BView::BView(const BView &other)
: BHandler()
{
// this is private and not functional, but exported
}
BView &
BView::operator=(const BView &other)
{
// this is private and not functional, but exported
return *this;
}
void
BView::_PrintToStream()
{
printf("BView::_PrintToStream()\n");
printf("\tName: %s\n"
"\tParent: %s\n"
"\tFirstChild: %s\n"
"\tNextSibling: %s\n"
"\tPrevSibling: %s\n"
"\tOwner(Window): %s\n"
"\tToken: %ld\n"
"\tFlags: %ld\n"
"\tView origin: (%f,%f)\n"
"\tView Bounds rectangle: (%f,%f,%f,%f)\n"
"\tShow level: %d\n"
"\tTopView?: %s\n"
"\tBPicture: %s\n"
"\tVertical Scrollbar %s\n"
"\tHorizontal Scrollbar %s\n"
"\tIs Printing?: %s\n"
"\tShelf?: %s\n"
"\tEventMask: %ld\n"
"\tEventOptions: %ld\n",
Name(),
fParent ? fParent->Name() : "NULL",
fFirstChild ? fFirstChild->Name() : "NULL",
fNextSibling ? fNextSibling->Name() : "NULL",
fPreviousSibling ? fPreviousSibling->Name() : "NULL",
fOwner ? fOwner->Name() : "NULL",
_get_object_token_(this),
fFlags,
fParentOffset.x, fParentOffset.y,
fBounds.left, fBounds.top, fBounds.right, fBounds.bottom,
fShowLevel,
fTopLevelView ? "YES" : "NO",
fCurrentPicture? "YES" : "NULL",
fVerScroller? "YES" : "NULL",
fHorScroller? "YES" : "NULL",
fIsPrinting? "YES" : "NO",
fShelf? "YES" : "NO",
fEventMask,
fEventOptions);
printf("\tState status:\n"
"\t\tLocalCoordianteSystem: (%f,%f)\n"
"\t\tPenLocation: (%f,%f)\n"
"\t\tPenSize: %f\n"
"\t\tHighColor: [%d,%d,%d,%d]\n"
"\t\tLowColor: [%d,%d,%d,%d]\n"
"\t\tViewColor: [%d,%d,%d,%d]\n"
"\t\tPattern: %llx\n"
"\t\tDrawingMode: %d\n"
"\t\tLineJoinMode: %d\n"
"\t\tLineCapMode: %d\n"
"\t\tMiterLimit: %f\n"
"\t\tAlphaSource: %d\n"
"\t\tAlphaFuntion: %d\n"
"\t\tScale: %f\n"
"\t\t(Print)FontAliasing: %s\n"
"\t\tFont Info:\n",
fState->origin.x, fState->origin.y,
fState->pen_location.x, fState->pen_location.y,
fState->pen_size,
fState->high_color.red, fState->high_color.blue, fState->high_color.green, fState->high_color.alpha,
fState->low_color.red, fState->low_color.blue, fState->low_color.green, fState->low_color.alpha,
fState->view_color.red, fState->view_color.blue, fState->view_color.green, fState->view_color.alpha,
*((uint64*)&(fState->pattern)),
fState->drawing_mode,
fState->line_join,
fState->line_cap,
fState->miter_limit,
fState->alpha_source_mode,
fState->alpha_function_mode,
fState->scale,
fState->font_aliasing? "YES" : "NO");
fState->font.PrintToStream();
// TODO: also print the line array.
}
void
BView::_PrintTree()
{
int32 spaces = 2;
BView *c = fFirstChild; //c = short for: current
printf( "'%s'\n", Name() );
if (c != NULL) {
while(true) {
// action block
{
for (int i = 0; i < spaces; i++)
printf(" ");
printf( "'%s'\n", c->Name() );
}
// go deep
if (c->fFirstChild) {
c = c->fFirstChild;
spaces += 2;
} else {
// go right
if (c->fNextSibling) {
c = c->fNextSibling;
} else {
// go up
while (!c->fParent->fNextSibling && c->fParent != this) {
c = c->fParent;
spaces -= 2;
}
// that enough! We've reached this view.
if (c->fParent == this)
break;
c = c->fParent->fNextSibling;
spaces -= 2;
}
}
}
}
}