/*
* Copyright 2001-2017 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus, superstippi@gmx.de
* Axel Dörfler, axeld@pinc-software.de
* Adrian Oanca, adioanca@cotty.iren.ro
* Ingo Weinhold. ingo_weinhold@gmx.de
* Julian Harnath, julian.harnath@rwth-aachen.de
* Joseph Groover, looncraz@looncraz.net
*/
#include <View.h>
#include <algorithm>
#include <new>
#include <math.h>
#include <stdio.h>
#include <Application.h>
#include <Bitmap.h>
#include <Button.h>
#include <Cursor.h>
#include <File.h>
#include <GradientLinear.h>
#include <GradientRadial.h>
#include <GradientRadialFocus.h>
#include <GradientDiamond.h>
#include <GradientConic.h>
#include <InterfaceDefs.h>
#include <Layout.h>
#include <LayoutContext.h>
#include <LayoutUtils.h>
#include <MenuBar.h>
#include <Message.h>
#include <MessageQueue.h>
#include <ObjectList.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 <Window.h>
#include <AppMisc.h>
#include <AppServerLink.h>
#include <binary_compatibility/Interface.h>
#include <binary_compatibility/Support.h>
#include <MessagePrivate.h>
#include <MessageUtils.h>
#include <PortLink.h>
#include <ServerProtocol.h>
#include <ServerProtocolStructs.h>
#include <ShapePrivate.h>
#include <ToolTip.h>
#include <ToolTipManager.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, B_SET_PROPERTY },
{ B_DIRECT_SPECIFIER, 0 }, "The view's frame rectangle.", 0,
{ B_RECT_TYPE }
},
{ "Hidden", { B_GET_PROPERTY, B_SET_PROPERTY },
{ B_DIRECT_SPECIFIER, 0 }, "Whether or not the view is hidden.",
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 }
};
// #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;
which_view_color = B_NO_COLOR;
which_view_color_tint = B_NO_TINT;
which_high_color = B_NO_COLOR;
which_high_color_tint = B_NO_TINT;
which_low_color = B_NO_COLOR;
which_low_color_tint = B_NO_TINT;
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;
fill_rule = B_NONZERO;
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;
// We only keep the B_VIEW_CLIP_REGION_BIT flag invalidated,
// 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);
ViewSetStateInfo info;
info.penLocation = pen_location;
info.penSize = pen_size;
info.highColor = high_color;
info.lowColor = low_color;
info.whichHighColor = which_high_color;
info.whichLowColor = which_low_color;
info.whichHighColorTint = which_high_color_tint;
info.whichLowColorTint = which_low_color_tint;
info.pattern = pattern;
info.drawingMode = drawing_mode;
info.origin = origin;
info.scale = scale;
info.lineJoin = line_join;
info.lineCap = line_cap;
info.miterLimit = miter_limit;
info.fillRule = fill_rule;
info.alphaSourceMode = alpha_source_mode;
info.alphaFunctionMode = alpha_function_mode;
info.fontAntialiasing = font_aliasing;
link.Attach<ViewSetStateInfo>(info);
// BAffineTransform is transmitted as a double array
double _transform[6];
if (transform.Flatten(_transform, sizeof(_transform)) != B_OK)
return;
link.Attach<double[6]>(_transform);
// we send the 'local' clipping region... if we have one...
// TODO: Could be optimized, but is low prio, since most views won't
// have a custom clipping region.
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;
ViewGetStateInfo info;
link.Read<ViewGetStateInfo>(&info);
// set view's font state
font_flags = B_FONT_ALL;
font.SetFamilyAndStyle(info.fontID);
font.SetSize(info.fontSize);
font.SetShear(info.fontShear);
font.SetRotation(info.fontRotation);
font.SetFalseBoldWidth(info.fontFalseBoldWidth);
font.SetSpacing(info.fontSpacing);
font.SetEncoding(info.fontEncoding);
font.SetFace(info.fontFace);
font.SetFlags(info.fontFlags);
// set view's state
pen_location = info.viewStateInfo.penLocation;
pen_size = info.viewStateInfo.penSize;
high_color = info.viewStateInfo.highColor;
low_color = info.viewStateInfo.lowColor;
pattern = info.viewStateInfo.pattern;
drawing_mode = info.viewStateInfo.drawingMode;
origin = info.viewStateInfo.origin;
scale = info.viewStateInfo.scale;
line_join = info.viewStateInfo.lineJoin;
line_cap = info.viewStateInfo.lineCap;
miter_limit = info.viewStateInfo.miterLimit;
fill_rule = info.viewStateInfo.fillRule;
alpha_source_mode = info.viewStateInfo.alphaSourceMode;
alpha_function_mode = info.viewStateInfo.alphaFunctionMode;
font_aliasing = info.viewStateInfo.fontAntialiasing;
// BAffineTransform is transmitted as a double array
double _transform[6];
link.Read<double[6]>(&_transform);
if (transform.Unflatten(B_AFFINE_TRANSFORM_TYPE, _transform,
sizeof(_transform)) != B_OK) {
return;
}
// 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 -
// archiving constants
namespace {
const char* const kSizesField = "BView:sizes";
// kSizesField = {min, max, pref}
const char* const kAlignmentField = "BView:alignment";
const char* const kLayoutField = "BView:layout";
}
struct BView::LayoutData {
LayoutData()
:
fMinSize(),
fMaxSize(),
fPreferredSize(),
fAlignment(),
fLayoutInvalidationDisabled(0),
fLayout(NULL),
fLayoutContext(NULL),
fLayoutItems(5, false),
fLayoutValid(true), // TODO: Rethink these initial values!
fMinMaxValid(true), //
fLayoutInProgress(false),
fNeedsRelayout(true)
{
}
status_t
AddDataToArchive(BMessage* archive)
{
status_t err = archive->AddSize(kSizesField, fMinSize);
if (err == B_OK)
err = archive->AddSize(kSizesField, fMaxSize);
if (err == B_OK)
err = archive->AddSize(kSizesField, fPreferredSize);
if (err == B_OK)
err = archive->AddAlignment(kAlignmentField, fAlignment);
return err;
}
void
PopulateFromArchive(BMessage* archive)
{
archive->FindSize(kSizesField, 0, &fMinSize);
archive->FindSize(kSizesField, 1, &fMaxSize);
archive->FindSize(kSizesField, 2, &fPreferredSize);
archive->FindAlignment(kAlignmentField, &fAlignment);
}
BSize fMinSize;
BSize fMaxSize;
BSize fPreferredSize;
BAlignment fAlignment;
int fLayoutInvalidationDisabled;
BLayout* fLayout;
BLayoutContext* fLayoutContext;
BObjectList<BLayoutItem> fLayoutItems;
bool fLayoutValid;
bool fMinMaxValid;
bool fLayoutInProgress;
bool fNeedsRelayout;
};
BView::BView(const char* name, uint32 flags, BLayout* layout)
:
BHandler(name)
{
_InitData(BRect(0, 0, -1, -1), 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(BUnarchiver::PrepareArchive(archive))
{
BUnarchiver unarchiver(archive);
if (!archive)
debugger("BView cannot be constructed from a NULL 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 = 0;
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));
float tint = B_NO_TINT;
if (archive->FindInt32("_uicolor", 0, &color) == B_OK
&& color != B_NO_COLOR) {
if (archive->FindFloat("_uitint", 0, &tint) != B_OK)
tint = B_NO_TINT;
SetHighUIColor((color_which)color, tint);
}
if (archive->FindInt32("_uicolor", 1, &color) == B_OK
&& color != B_NO_COLOR) {
if (archive->FindFloat("_uitint", 1, &tint) != B_OK)
tint = B_NO_TINT;
SetLowUIColor((color_which)color, tint);
}
if (archive->FindInt32("_uicolor", 2, &color) == B_OK
&& color != B_NO_COLOR) {
if (archive->FindFloat("_uitint", 2, &tint) != B_OK)
tint = B_NO_TINT;
SetViewUIColor((color_which)color, tint);
}
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 scale;
if (archive->FindFloat("_scale", &scale) == B_OK)
SetScale(scale);
BAffineTransform transform;
if (archive->FindFlat("_transform", &transform) == B_OK)
SetTransform(transform);
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 fillRule;
if (archive->FindInt16("_fillrule", &fillRule) == B_OK)
SetFillRule(fillRule);
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);
fLayoutData->PopulateFromArchive(archive);
if (archive->FindInt16("_show", &fShowLevel) != B_OK)
fShowLevel = 0;
if (BUnarchiver::IsArchiveManaged(archive)) {
int32 i = 0;
while (unarchiver.EnsureUnarchived("_views", i++) == B_OK)
;
unarchiver.EnsureUnarchived(kLayoutField);
} else {
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(std::nothrow) BView(data);
}
status_t
BView::Archive(BMessage* data, bool deep) const
{
BArchiver archiver(data);
status_t ret = BHandler::Archive(data, deep);
if (ret != B_OK)
return ret;
if ((fState->archiving_flags & B_VIEW_FRAME_BIT) != 0)
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) != 0) {
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) != 0) {
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()));
if (ret == B_OK)
ret = data->AddInt32("_uicolor", (int32)HighUIColor());
if (ret == B_OK)
ret = data->AddInt32("_uicolor", (int32)LowUIColor());
if (ret == B_OK)
ret = data->AddInt32("_uicolor", (int32)ViewUIColor());
if (ret == B_OK)
ret = data->AddFloat("_uitint", fState->which_high_color_tint);
if (ret == B_OK)
ret = data->AddFloat("_uitint", fState->which_low_color_tint);
if (ret == B_OK)
ret = data->AddFloat("_uitint", fState->which_view_color_tint);
// 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) != 0)
ret = data->AddPoint("_origin", Origin());
if (ret == B_OK && (fState->archiving_flags & B_VIEW_SCALE_BIT) != 0)
ret = data->AddFloat("_scale", Scale());
if (ret == B_OK && (fState->archiving_flags & B_VIEW_TRANSFORM_BIT) != 0) {
BAffineTransform transform = Transform();
ret = data->AddFlat("_transform", &transform);
}
if (ret == B_OK && (fState->archiving_flags & B_VIEW_PEN_SIZE_BIT) != 0)
ret = data->AddFloat("_psize", PenSize());
if (ret == B_OK && (fState->archiving_flags & B_VIEW_PEN_LOCATION_BIT) != 0)
ret = data->AddPoint("_ploc", PenLocation());
if (ret == B_OK && (fState->archiving_flags & B_VIEW_LINE_MODES_BIT) != 0) {
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_FILL_RULE_BIT) != 0)
ret = data->AddInt16("_fillrule", (int16)FillRule());
if (ret == B_OK && (fState->archiving_flags & B_VIEW_BLENDING_BIT) != 0) {
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) != 0)
ret = data->AddInt32("_dmod", DrawingMode());
if (ret == B_OK)
ret = fLayoutData->AddDataToArchive(data);
if (ret == B_OK)
ret = data->AddInt16("_show", fShowLevel);
if (deep && ret == B_OK) {
for (BView* child = fFirstChild; child != NULL && ret == B_OK;
child = child->fNextSibling)
ret = archiver.AddArchivable("_views", child, deep);
if (ret == B_OK)
ret = archiver.AddArchivable(kLayoutField, GetLayout(), deep);
}
return archiver.Finish(ret);
}
status_t
BView::AllUnarchived(const BMessage* from)
{
BUnarchiver unarchiver(from);
status_t err = B_OK;
int32 count;
from->GetInfo("_views", NULL, &count);
for (int32 i = 0; err == B_OK && i < count; i++) {
BView* child;
err = unarchiver.FindObject<BView>("_views", i, child);
if (err == B_OK)
err = _AddChild(child, NULL) ? B_OK : B_ERROR;
}
if (err == B_OK) {
BLayout*& layout = fLayoutData->fLayout;
err = unarchiver.FindObject(kLayoutField, layout);
if (err == B_OK && layout) {
fFlags |= B_SUPPORTS_LAYOUT;
fLayoutData->fLayout->SetOwner(this);
}
}
return err;
}
status_t
BView::AllArchived(BMessage* into) const
{
return BHandler::AllArchived(into);
}
BView::~BView()
{
STRACE(("BView(%s)::~BView()\n", this->Name()));
if (fOwner != NULL) {
debugger("Trying to delete a view that belongs to a window. "
"Call RemoveSelf first.");
}
// we also delete all our children
BView* child = fFirstChild;
while (child) {
BView* nextChild = child->fNextSibling;
delete child;
child = nextChild;
}
SetLayout(NULL);
_RemoveLayoutItemsFromLayout(true);
delete fLayoutData;
_RemoveSelf();
if (fToolTip != NULL)
fToolTip->ReleaseReference();
if (fVerScroller != NULL)
fVerScroller->SetTarget((BView*)NULL);
if (fHorScroller != NULL)
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* point, bool checkLock) const
{
if (!fParent) {
if (fOwner)
fOwner->ConvertToScreen(point);
return;
}
if (checkLock)
_CheckOwnerLock();
_ConvertToParent(point, false);
fParent->_ConvertToScreen(point, false);
}
void
BView::ConvertToScreen(BPoint* point) const
{
_ConvertToScreen(point, true);
}
BPoint
BView::ConvertToScreen(BPoint point) const
{
ConvertToScreen(&point);
return point;
}
void
BView::_ConvertFromScreen(BPoint* point, bool checkLock) const
{
if (!fParent) {
if (fOwner)
fOwner->ConvertFromScreen(point);
return;
}
if (checkLock)
_CheckOwnerLock();
_ConvertFromParent(point, false);
fParent->_ConvertFromScreen(point, false);
}
void
BView::ConvertFromScreen(BPoint* point) const
{
_ConvertFromScreen(point, true);
}
BPoint
BView::ConvertFromScreen(BPoint point) const
{
ConvertFromScreen(&point);
return point;
}
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());
}
uint32 changesFlags = flags ^ fFlags;
if (changesFlags & (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)
_InvalidateParentLayout();
}
void
BView::Show()
{
fShowLevel--;
if (fOwner && fShowLevel == 0) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_SHOW);
fOwner->fLink->Flush();
}
if (fShowLevel == 0)
_InvalidateParentLayout();
}
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;
_CheckLock();
ViewSetViewCursorInfo info;
info.cursorToken = cursor->fServerToken;
info.viewToken = _get_object_token_(this);
info.sync = sync;
BPrivate::AppServerLink link;
link.StartMessage(AS_SET_VIEW_CURSOR);
link.Attach<ViewSetViewCursorInfo>(info);
if (sync) {
// Make sure the server has processed the message.
int32 code;
link.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 updateRect)
{
// 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* dragMessage)
{
// 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 active)
{
// 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(std::nothrow) BBitmap(dragRect, B_RGBA32);
if (bitmap == NULL)
return;
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(std::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()));
int32 bufferSize = message->FlattenedSize();
char* buffer = new(std::nothrow) char[bufferSize];
if (buffer != NULL) {
message->Flatten(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;
}
}
if (_location != NULL)
message->FindPoint("screen_where", _location);
if (_buttons != NULL)
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 focus)
{
if (fOwner == NULL)
return;
// TODO: If this view has focus and focus == false,
// will there really be no other view with focus? No
// cycling to the next one?
BView* focusView = fOwner->CurrentFocus();
if (focus) {
// Unfocus a previous focus view
if (focusView != NULL && focusView != this)
focusView->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 (focusView == this)
fOwner->_SetFocus(NULL, true);
}
}
BScrollBar*
BView::ScrollBar(orientation direction) const
{
switch (direction) {
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;
// don't change the mask if it's zero and we've got options
if (mask != 0 || options == 0)
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, scale and transform, new states start "clean".
fState->valid_flags |= B_VIEW_SCALE_BIT | B_VIEW_ORIGIN_BIT
| B_VIEW_TRANSFORM_BIT;
fState->scale = 1.0f;
fState->origin.Set(0, 0);
fState->transform.Reset();
}
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 where)
{
SetOrigin(where.x, where.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::SetTransform(BAffineTransform transform)
{
if (fState->IsValid(B_VIEW_TRANSFORM_BIT) && transform == fState->transform)
return;
if (fOwner != NULL) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_SET_TRANSFORM);
fOwner->fLink->Attach<BAffineTransform>(transform);
fState->valid_flags |= B_VIEW_TRANSFORM_BIT;
}
fState->transform = transform;
fState->archiving_flags |= B_VIEW_TRANSFORM_BIT;
}
BAffineTransform
BView::Transform() const
{
if (!fState->IsValid(B_VIEW_TRANSFORM_BIT) && fOwner != NULL) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_GET_TRANSFORM);
int32 code;
if (fOwner->fLink->FlushWithReply(code) == B_OK && code == B_OK)
fOwner->fLink->Read<BAffineTransform>(&fState->transform);
fState->valid_flags |= B_VIEW_TRANSFORM_BIT;
}
return fState->transform;
}
void
BView::TranslateBy(double x, double y)
{
if (fOwner != NULL) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_AFFINE_TRANSLATE);
fOwner->fLink->Attach<double>(x);
fOwner->fLink->Attach<double>(y);
fState->valid_flags &= ~B_VIEW_TRANSFORM_BIT;
}
fState->archiving_flags |= B_VIEW_TRANSFORM_BIT;
}
void
BView::ScaleBy(double x, double y)
{
if (fOwner != NULL) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_AFFINE_SCALE);
fOwner->fLink->Attach<double>(x);
fOwner->fLink->Attach<double>(y);
fState->valid_flags &= ~B_VIEW_TRANSFORM_BIT;
}
fState->archiving_flags |= B_VIEW_TRANSFORM_BIT;
}
void
BView::RotateBy(double angleRadians)
{
if (fOwner != NULL) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_AFFINE_ROTATE);
fOwner->fLink->Attach<double>(angleRadians);
fState->valid_flags &= ~B_VIEW_TRANSFORM_BIT;
}
fState->archiving_flags |= B_VIEW_TRANSFORM_BIT;
}
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();
ViewSetLineModeInfo info;
info.lineJoin = lineJoin;
info.lineCap = lineCap;
info.miterLimit = miterLimit;
fOwner->fLink->StartMessage(AS_VIEW_SET_LINE_MODE);
fOwner->fLink->Attach<ViewSetLineModeInfo>(info);
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) {
ViewSetLineModeInfo info;
fOwner->fLink->Read<ViewSetLineModeInfo>(&info);
fState->line_cap = info.lineCap;
fState->line_join = info.lineJoin;
fState->miter_limit = info.miterLimit;
}
fState->valid_flags |= B_VIEW_LINE_MODES_BIT;
}
return fState->miter_limit;
}
void
BView::SetFillRule(int32 fillRule)
{
if (fState->IsValid(B_VIEW_FILL_RULE_BIT) && fillRule == fState->fill_rule)
return;
if (fOwner) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_SET_FILL_RULE);
fOwner->fLink->Attach<int32>(fillRule);
fState->valid_flags |= B_VIEW_FILL_RULE_BIT;
}
fState->fill_rule = fillRule;
fState->archiving_flags |= B_VIEW_FILL_RULE_BIT;
}
int32
BView::FillRule() const
{
if (!fState->IsValid(B_VIEW_FILL_RULE_BIT) && fOwner) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_GET_FILL_RULE);
int32 code;
if (fOwner->fLink->FlushWithReply(code) == B_OK && code == B_OK) {
int32 fillRule;
fOwner->fLink->Read<int32>(&fillRule);
fState->fill_rule = fillRule;
}
fState->valid_flags |= B_VIEW_FILL_RULE_BIT;
}
return fState->fill_rule;
}
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();
ViewBlendingModeInfo info;
info.sourceAlpha = sourceAlpha;
info.alphaFunction = alphaFunction;
fOwner->fLink->StartMessage(AS_VIEW_SET_BLENDING_MODE);
fOwner->fLink->Attach<ViewBlendingModeInfo>(info);
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) {
ViewBlendingModeInfo info;
fOwner->fLink->Read<ViewBlendingModeInfo>(&info);
fState->alpha_source_mode = info.sourceAlpha;
fState->alpha_function_mode = info.alphaFunction;
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<BPoint>(BPoint(x, 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)
{
SetHighUIColor(B_NO_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::SetHighUIColor(color_which which, float tint)
{
if (fState->IsValid(B_VIEW_WHICH_HIGH_COLOR_BIT)
&& fState->which_high_color == which
&& fState->which_high_color_tint == tint)
return;
if (fOwner != NULL) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_SET_HIGH_UI_COLOR);
fOwner->fLink->Attach<color_which>(which);
fOwner->fLink->Attach<float>(tint);
fState->valid_flags |= B_VIEW_WHICH_HIGH_COLOR_BIT;
}
fState->which_high_color = which;
fState->which_high_color_tint = tint;
if (which != B_NO_COLOR) {
fState->archiving_flags |= B_VIEW_WHICH_HIGH_COLOR_BIT;
fState->archiving_flags &= ~B_VIEW_HIGH_COLOR_BIT;
fState->valid_flags |= B_VIEW_HIGH_COLOR_BIT;
fState->high_color = tint_color(ui_color(which), tint);
} else {
fState->valid_flags &= ~B_VIEW_HIGH_COLOR_BIT;
fState->archiving_flags &= ~B_VIEW_WHICH_HIGH_COLOR_BIT;
}
}
color_which
BView::HighUIColor(float* tint) const
{
if (!fState->IsValid(B_VIEW_WHICH_HIGH_COLOR_BIT)
&& fOwner != NULL) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_GET_HIGH_UI_COLOR);
int32 code;
if (fOwner->fLink->FlushWithReply(code) == B_OK
&& code == B_OK) {
fOwner->fLink->Read<color_which>(&fState->which_high_color);
fOwner->fLink->Read<float>(&fState->which_high_color_tint);
fOwner->fLink->Read<rgb_color>(&fState->high_color);
fState->valid_flags |= B_VIEW_WHICH_HIGH_COLOR_BIT;
fState->valid_flags |= B_VIEW_HIGH_COLOR_BIT;
}
}
if (tint != NULL)
*tint = fState->which_high_color_tint;
return fState->which_high_color;
}
void
BView::SetLowColor(rgb_color color)
{
SetLowUIColor(B_NO_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::SetLowUIColor(color_which which, float tint)
{
if (fState->IsValid(B_VIEW_WHICH_LOW_COLOR_BIT)
&& fState->which_low_color == which
&& fState->which_low_color_tint == tint)
return;
if (fOwner != NULL) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_SET_LOW_UI_COLOR);
fOwner->fLink->Attach<color_which>(which);
fOwner->fLink->Attach<float>(tint);
fState->valid_flags |= B_VIEW_WHICH_LOW_COLOR_BIT;
}
fState->which_low_color = which;
fState->which_low_color_tint = tint;
if (which != B_NO_COLOR) {
fState->archiving_flags |= B_VIEW_WHICH_LOW_COLOR_BIT;
fState->archiving_flags &= ~B_VIEW_LOW_COLOR_BIT;
fState->valid_flags |= B_VIEW_LOW_COLOR_BIT;
fState->low_color = tint_color(ui_color(which), tint);
} else {
fState->valid_flags &= ~B_VIEW_LOW_COLOR_BIT;
fState->archiving_flags &= ~B_VIEW_WHICH_LOW_COLOR_BIT;
}
}
color_which
BView::LowUIColor(float* tint) const
{
if (!fState->IsValid(B_VIEW_WHICH_LOW_COLOR_BIT)
&& fOwner != NULL) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_GET_LOW_UI_COLOR);
int32 code;
if (fOwner->fLink->FlushWithReply(code) == B_OK
&& code == B_OK) {
fOwner->fLink->Read<color_which>(&fState->which_low_color);
fOwner->fLink->Read<float>(&fState->which_low_color_tint);
fOwner->fLink->Read<rgb_color>(&fState->low_color);
fState->valid_flags |= B_VIEW_WHICH_LOW_COLOR_BIT;
fState->valid_flags |= B_VIEW_LOW_COLOR_BIT;
}
}
if (tint != NULL)
*tint = fState->which_low_color_tint;
return fState->which_low_color;
}
bool
BView::HasDefaultColors() const
{
// If we don't have any of these flags, then we have default colors
uint32 testMask = B_VIEW_VIEW_COLOR_BIT | B_VIEW_HIGH_COLOR_BIT
| B_VIEW_LOW_COLOR_BIT | B_VIEW_WHICH_VIEW_COLOR_BIT
| B_VIEW_WHICH_HIGH_COLOR_BIT | B_VIEW_WHICH_LOW_COLOR_BIT;
return (fState->archiving_flags & testMask) == 0;
}
bool
BView::HasSystemColors() const
{
return fState->which_view_color == B_PANEL_BACKGROUND_COLOR
&& fState->which_high_color == B_PANEL_TEXT_COLOR
&& fState->which_low_color == B_PANEL_BACKGROUND_COLOR
&& fState->which_view_color_tint == B_NO_TINT
&& fState->which_high_color_tint == B_NO_TINT
&& fState->which_low_color_tint == B_NO_TINT;
}
void
BView::AdoptParentColors()
{
AdoptViewColors(Parent());
}
void
BView::AdoptSystemColors()
{
SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
SetLowUIColor(B_PANEL_BACKGROUND_COLOR);
SetHighUIColor(B_PANEL_TEXT_COLOR);
}
void
BView::AdoptViewColors(BView* view)
{
if (view == NULL || (view->Window() != NULL && !view->LockLooper()))
return;
float tint = B_NO_TINT;
float viewTint = tint;
color_which viewWhich = view->ViewUIColor(&viewTint);
// View color
if (viewWhich != B_NO_COLOR)
SetViewUIColor(viewWhich, viewTint);
else
SetViewColor(view->ViewColor());
// Low color
color_which which = view->LowUIColor(&tint);
if (which != B_NO_COLOR)
SetLowUIColor(which, tint);
else if (viewWhich != B_NO_COLOR)
SetLowUIColor(viewWhich, viewTint);
else
SetLowColor(view->LowColor());
// High color
which = view->HighUIColor(&tint);
if (which != B_NO_COLOR)
SetHighUIColor(which, tint);
else
SetHighColor(view->HighColor());
if (view->Window() != NULL)
view->UnlockLooper();
}
void
BView::SetViewColor(rgb_color color)
{
SetViewUIColor(B_NO_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::SetViewUIColor(color_which which, float tint)
{
if (fState->IsValid(B_VIEW_WHICH_VIEW_COLOR_BIT)
&& fState->which_view_color == which
&& fState->which_view_color_tint == tint)
return;
if (fOwner != NULL) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_SET_VIEW_UI_COLOR);
fOwner->fLink->Attach<color_which>(which);
fOwner->fLink->Attach<float>(tint);
fState->valid_flags |= B_VIEW_WHICH_VIEW_COLOR_BIT;
}
fState->which_view_color = which;
fState->which_view_color_tint = tint;
if (which != B_NO_COLOR) {
fState->archiving_flags |= B_VIEW_WHICH_VIEW_COLOR_BIT;
fState->archiving_flags &= ~B_VIEW_VIEW_COLOR_BIT;
fState->valid_flags |= B_VIEW_VIEW_COLOR_BIT;
fState->view_color = tint_color(ui_color(which), tint);
} else {
fState->valid_flags &= ~B_VIEW_VIEW_COLOR_BIT;
fState->archiving_flags &= ~B_VIEW_WHICH_VIEW_COLOR_BIT;
}
if (!fState->IsValid(B_VIEW_WHICH_LOW_COLOR_BIT))
SetLowUIColor(which, tint);
}
color_which
BView::ViewUIColor(float* tint) const
{
if (!fState->IsValid(B_VIEW_WHICH_VIEW_COLOR_BIT)
&& fOwner != NULL) {
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_GET_VIEW_UI_COLOR);
int32 code;
if (fOwner->fLink->FlushWithReply(code) == B_OK
&& code == B_OK) {
fOwner->fLink->Read<color_which>(&fState->which_view_color);
fOwner->fLink->Read<float>(&fState->which_view_color_tint);
fOwner->fLink->Read<rgb_color>(&fState->view_color);
fState->valid_flags |= B_VIEW_WHICH_VIEW_COLOR_BIT;
fState->valid_flags |= B_VIEW_VIEW_COLOR_BIT;
}
}
if (tint != NULL)
*tint = fState->which_view_color_tint;
return fState->which_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);
fState->valid_flags |= B_VIEW_FONT_BIT;
}
fState->archiving_flags |= B_VIEW_FONT_BIT;
// TODO: InvalidateLayout() here for convenience?
}
void
BView::GetFont(BFont* font) const
{
if (!fState->IsValid(B_VIEW_FONT_BIT)) {
// we don't keep graphics state information, therefor
// we need to ask the server for the origin after PopState()
_CheckOwnerLockAndSwitchCurrent();
// TODO: add a font getter!
fState->UpdateFrom(*fOwner->fLink);
}
*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* string, uint32 mode, float width) const
{
fState->font.TruncateString(string, 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;
}
}
void
BView::ClipToRect(BRect rect)
{
_ClipToRect(rect, false);
}
void
BView::ClipToInverseRect(BRect rect)
{
_ClipToRect(rect, true);
}
void
BView::ClipToShape(BShape* shape)
{
_ClipToShape(shape, false);
}
void
BView::ClipToInverseShape(BShape* shape)
{
_ClipToShape(shape, true);
}
// #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();
ViewDrawBitmapInfo info;
info.bitmapToken = bitmap->_ServerToken();
info.options = options;
info.viewRect = viewRect;
info.bitmapRect = bitmapRect;
fOwner->fLink->StartMessage(AS_VIEW_DRAW_BITMAP);
fOwner->fLink->Attach<ViewDrawBitmapInfo>(info);
_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();
ViewDrawBitmapInfo info;
info.bitmapToken = bitmap->_ServerToken();
info.options = 0;
info.bitmapRect = bitmap->Bounds().OffsetToCopy(B_ORIGIN);
info.viewRect = info.bitmapRect.OffsetToCopy(where);
fOwner->fLink->StartMessage(AS_VIEW_DRAW_BITMAP);
fOwner->fLink->Attach<ViewDrawBitmapInfo>(info);
_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 (fOwner == NULL || string == NULL || length < 1)
return;
_CheckLockAndSwitchCurrent();
ViewDrawStringInfo info;
info.stringLength = length;
info.location = location;
if (delta != NULL)
info.delta = *delta;
// 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<ViewDrawStringInfo>(info);
fOwner->fLink->Attach(string, length);
_FlushIfNotInTransaction();
// this modifies our pen location, so we invalidate the flag.
fState->valid_flags &= ~B_VIEW_PEN_LOCATION_BIT;
}
void
BView::DrawString(const char* string, const BPoint* locations,
int32 locationCount)
{
if (string == NULL)
return;
DrawString(string, strlen(string), locations, locationCount);
}
void
BView::DrawString(const char* string, int32 length, const BPoint* locations,
int32 locationCount)
{
if (fOwner == NULL || string == NULL || length < 1 || locations == NULL)
return;
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_DRAW_STRING_WITH_OFFSETS);
fOwner->fLink->Attach<int32>(length);
fOwner->fLink->Attach<int32>(locationCount);
fOwner->fLink->Attach(string, length);
fOwner->fLink->Attach(locations, locationCount * sizeof(BPoint));
_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->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->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->AttachGradient(gradient);
_FlushIfNotInTransaction();
}
void
BView::StrokePolygon(const BPolygon* polygon, bool closed, ::pattern pattern)
{
if (polygon == NULL)
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* pointArray, int32 numPoints, BRect bounds,
bool closed, ::pattern pattern)
{
if (pointArray == NULL
|| numPoints <= 1
|| fOwner == NULL)
return;
_CheckLockAndSwitchCurrent();
_UpdatePattern(pattern);
BPolygon polygon(pointArray, 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->AttachGradient(gradient);
_FlushIfNotInTransaction();
} else {
fprintf(stderr, "ERROR: Can't send polygon to app_server!\n");
}
}
void
BView::FillPolygon(const BPoint* pointArray, int32 numPoints, ::pattern pattern)
{
if (pointArray == NULL)
return;
BPolygon polygon(pointArray, numPoints);
FillPolygon(&polygon, pattern);
}
void
BView::FillPolygon(const BPoint* pointArray, int32 numPoints,
const BGradient& gradient)
{
if (pointArray == NULL)
return;
BPolygon polygon(pointArray, numPoints);
FillPolygon(&polygon, gradient);
}
void
BView::FillPolygon(const BPoint* pointArray, int32 numPoints, BRect bounds,
::pattern pattern)
{
if (pointArray == NULL)
return;
BPolygon polygon(pointArray, numPoints);
polygon.MapTo(polygon.Frame(), bounds);
FillPolygon(&polygon, pattern);
}
void
BView::FillPolygon(const BPoint* pointArray, int32 numPoints, BRect bounds,
const BGradient& gradient)
{
if (pointArray == NULL)
return;
BPolygon polygon(pointArray, numPoints);
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->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->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->AttachGradient(gradient);
_FlushIfNotInTransaction();
}
void
BView::StrokeTriangle(BPoint point1, BPoint point2, BPoint point3, BRect bounds,
::pattern pattern)
{
if (fOwner == NULL)
return;
_CheckLockAndSwitchCurrent();
_UpdatePattern(pattern);
fOwner->fLink->StartMessage(AS_STROKE_TRIANGLE);
fOwner->fLink->Attach<BPoint>(point1);
fOwner->fLink->Attach<BPoint>(point2);
fOwner->fLink->Attach<BPoint>(point3);
fOwner->fLink->Attach<BRect>(bounds);
_FlushIfNotInTransaction();
}
void
BView::StrokeTriangle(BPoint point1, BPoint point2, BPoint point3,
::pattern pattern)
{
if (fOwner) {
// we construct the smallest rectangle that contains the 3 points
// for the 1st point
BRect bounds(point1, point1);
// for the 2nd point
if (point2.x < bounds.left)
bounds.left = point2.x;
if (point2.y < bounds.top)
bounds.top = point2.y;
if (point2.x > bounds.right)
bounds.right = point2.x;
if (point2.y > bounds.bottom)
bounds.bottom = point2.y;
// for the 3rd point
if (point3.x < bounds.left)
bounds.left = point3.x;
if (point3.y < bounds.top)
bounds.top = point3.y;
if (point3.x > bounds.right)
bounds.right = point3.x;
if (point3.y > bounds.bottom)
bounds.bottom = point3.y;
StrokeTriangle(point1, point2, point3, bounds, pattern);
}
}
void
BView::FillTriangle(BPoint point1, BPoint point2, BPoint point3,
::pattern pattern)
{
if (fOwner) {
// we construct the smallest rectangle that contains the 3 points
// for the 1st point
BRect bounds(point1, point1);
// for the 2nd point
if (point2.x < bounds.left)
bounds.left = point2.x;
if (point2.y < bounds.top)
bounds.top = point2.y;
if (point2.x > bounds.right)
bounds.right = point2.x;
if (point2.y > bounds.bottom)
bounds.bottom = point2.y;
// for the 3rd point
if (point3.x < bounds.left)
bounds.left = point3.x;
if (point3.y < bounds.top)
bounds.top = point3.y;
if (point3.x > bounds.right)
bounds.right = point3.x;
if (point3.y > bounds.bottom)
bounds.bottom = point3.y;
FillTriangle(point1, point2, point3, bounds, pattern);
}
}
void
BView::FillTriangle(BPoint point1, BPoint point2, BPoint point3,
const BGradient& gradient)
{
if (fOwner) {
// we construct the smallest rectangle that contains the 3 points
// for the 1st point
BRect bounds(point1, point1);
// for the 2nd point
if (point2.x < bounds.left)
bounds.left = point2.x;
if (point2.y < bounds.top)
bounds.top = point2.y;
if (point2.x > bounds.right)
bounds.right = point2.x;
if (point2.y > bounds.bottom)
bounds.bottom = point2.y;
// for the 3rd point
if (point3.x < bounds.left)
bounds.left = point3.x;
if (point3.y < bounds.top)
bounds.top = point3.y;
if (point3.x > bounds.right)
bounds.right = point3.x;
if (point3.y > bounds.bottom)
bounds.bottom = point3.y;
FillTriangle(point1, point2, point3, bounds, gradient);
}
}
void
BView::FillTriangle(BPoint point1, BPoint point2, BPoint point3,
BRect bounds, ::pattern pattern)
{
if (fOwner == NULL)
return;
_CheckLockAndSwitchCurrent();
_UpdatePattern(pattern);
fOwner->fLink->StartMessage(AS_FILL_TRIANGLE);
fOwner->fLink->Attach<BPoint>(point1);
fOwner->fLink->Attach<BPoint>(point2);
fOwner->fLink->Attach<BPoint>(point3);
fOwner->fLink->Attach<BRect>(bounds);
_FlushIfNotInTransaction();
}
void
BView::FillTriangle(BPoint point1, BPoint point2, BPoint point3, BRect bounds,
const BGradient& gradient)
{
if (fOwner == NULL)
return;
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_FILL_TRIANGLE_GRADIENT);
fOwner->fLink->Attach<BPoint>(point1);
fOwner->fLink->Attach<BPoint>(point2);
fOwner->fLink->Attach<BPoint>(point3);
fOwner->fLink->Attach<BRect>(bounds);
fOwner->fLink->AttachGradient(gradient);
_FlushIfNotInTransaction();
}
void
BView::StrokeLine(BPoint toPoint, ::pattern pattern)
{
StrokeLine(PenLocation(), toPoint, pattern);
}
void
BView::StrokeLine(BPoint start, BPoint end, ::pattern pattern)
{
if (fOwner == NULL)
return;
_CheckLockAndSwitchCurrent();
_UpdatePattern(pattern);
ViewStrokeLineInfo info;
info.startPoint = start;
info.endPoint = end;
fOwner->fLink->StartMessage(AS_STROKE_LINE);
fOwner->fLink->Attach<ViewStrokeLineInfo>(info);
_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->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;
}
// TODO: since this method cannot return failure, and further AddLine()
// calls with a NULL fCommArray would drop into the debugger anyway,
// we allow the possible std::bad_alloc exceptions here...
fCommArray = new _array_data_;
fCommArray->count = 0;
// Make sure the fCommArray is initialized to reasonable values in cases of
// bad_alloc. At least the exception can be caught and EndLineArray won't
// crash.
fCommArray->array = NULL;
fCommArray->maxCount = 0;
fCommArray->array = new ViewLineArrayInfo[count];
fCommArray->maxCount = count;
}
void
BView::AddLine(BPoint start, BPoint end, rgb_color color)
{
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].startPoint = start;
fCommArray->array[arrayCount].endPoint = end;
fCommArray->array[arrayCount].color = color;
fCommArray->count++;
}
}
void
BView::EndLineArray()
{
if (fOwner == NULL)
return;
if (fCommArray == NULL)
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(ViewLineArrayInfo));
_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);
// TODO do this more efficient e.g. use a shared area and let the
// client write into it
picture->_Download();
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 == NULL || (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)
{
if (overlay == NULL)
return B_BAD_VALUE;
BRect 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::BeginLayer(uint8 opacity)
{
if (_CheckOwnerLockAndSwitchCurrent()) {
fOwner->fLink->StartMessage(AS_VIEW_BEGIN_LAYER);
fOwner->fLink->Attach<uint8>(opacity);
_FlushIfNotInTransaction();
}
}
void
BView::EndLayer()
{
if (_CheckOwnerLockAndSwitchCurrent()) {
fOwner->fLink->StartMessage(AS_VIEW_END_LAYER);
_FlushIfNotInTransaction();
}
}
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;
}
#else
fOwner->fLink->Flush();
#endif
}
void
BView::Invalidate(const BRegion* region)
{
if (region == NULL || fOwner == NULL)
return;
_CheckLockAndSwitchCurrent();
fOwner->fLink->StartMessage(AS_VIEW_INVALIDATE_REGION);
fOwner->fLink->AttachRegion(*region);
// TODO: See above.
#if 0
if (!fOwner->fUpdateRequested) {
fOwner->fLink->Flush();
fOwner->fUpdateRequested = true;
}
#else
fOwner->fLink->Flush();
#endif
}
void
BView::Invalidate()
{
Invalidate(Bounds());
}
void
BView::DelayedInvalidate(bigtime_t delay)
{
DelayedInvalidate(delay, Bounds());
}
void
BView::DelayedInvalidate(bigtime_t delay, BRect invalRect)
{
if (fOwner == NULL)
return;
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_DELAYED_INVALIDATE_RECT);
fOwner->fLink->Attach<bigtime_t>(system_time() + delay);
fOwner->fLink->Attach<BRect>(invalRect);
fOwner->fLink->Flush();
}
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(),
child != NULL && child->Name() ? child->Name() : "NULL",
before != NULL && 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;
}
if (child == this) {
debugger("AddChild failed - cannot add a view to itself.");
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()
{
_RemoveLayoutItemsFromLayout(false);
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;
}
void
BView::_RemoveLayoutItemsFromLayout(bool deleteItems)
{
if (fParent == NULL || fParent->fLayoutData->fLayout == NULL)
return;
int32 index = fLayoutData->fLayoutItems.CountItems();
while (index-- > 0) {
BLayoutItem* item = fLayoutData->fLayoutItems.ItemAt(index);
item->RemoveSelf();
// Removes item from fLayoutItems list
if (deleteItems)
delete item;
}
}
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* message, int32 index, BMessage* specifier,
int32 what, const char* property)
{
if (message->what == B_WINDOW_MOVE_BY
|| message->what == B_WINDOW_MOVE_TO) {
return this;
}
BPropertyInfo propertyInfo(sViewPropInfo);
status_t err = B_BAD_SCRIPT_SYNTAX;
BMessage replyMsg(B_REPLY);
switch (propertyInfo.FindMatch(message, index, specifier, what, property)) {
case 0:
case 1:
case 3:
return this;
case 2:
if (fShelf) {
message->PopSpecifier();
return fShelf;
}
err = B_NAME_NOT_FOUND;
replyMsg.AddString("message", "This window doesn't have a shelf");
break;
case 4:
{
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) {
message->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(message, 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);
message->SendReply(&replyMsg);
return NULL;
}
void
BView::MessageReceived(BMessage* message)
{
if (!message->HasSpecifiers()) {
switch (message->what) {
case B_VIEW_RESIZED:
FrameResized(message->GetInt32("width", 0),
message->GetInt32("height", 0));
break;
case B_VIEW_MOVED:
FrameMoved(fParentOffset);
break;
case B_MOUSE_IDLE:
{
BPoint where;
if (message->FindPoint("be:view_where", &where) != B_OK)
break;
BToolTip* tip;
if (GetToolTipAt(where, &tip))
ShowToolTip(tip);
else
BHandler::MessageReceived(message);
break;
}
case B_MOUSE_WHEEL_CHANGED:
{
BScrollBar* horizontal = ScrollBar(B_HORIZONTAL);
BScrollBar* vertical = ScrollBar(B_VERTICAL);
if (horizontal == NULL && vertical == NULL) {
// Pass the message to the next handler
BHandler::MessageReceived(message);
break;
}
float deltaX = 0.0f;
float deltaY = 0.0f;
if (horizontal != NULL)
message->FindFloat("be:wheel_delta_x", &deltaX);
if (vertical != NULL)
message->FindFloat("be:wheel_delta_y", &deltaY);
if (deltaX == 0.0f && deltaY == 0.0f)
break;
if ((modifiers() & B_CONTROL_KEY) != 0)
std::swap(horizontal, vertical);
if (horizontal != NULL && deltaX != 0.0f)
ScrollWithMouseWheelDelta(horizontal, deltaX);
if (vertical != NULL && deltaY != 0.0f)
ScrollWithMouseWheelDelta(vertical, deltaY);
break;
}
// prevent message repeats
case B_COLORS_UPDATED:
case B_FONTS_UPDATED:
break;
case B_SCREEN_CHANGED:
{
// propegate message to child views
int32 childCount = CountChildren();
for (int32 i = 0; i < childCount; i++) {
BView* view = ChildAt(i);
if (view != NULL)
view->MessageReceived(message);
}
break;
}
default:
BHandler::MessageReceived(message);
break;
}
return;
}
// Scripting message
BMessage replyMsg(B_REPLY);
status_t err = B_BAD_SCRIPT_SYNTAX;
int32 index;
BMessage specifier;
int32 what;
const char* property;
if (message->GetCurrentSpecifier(&index, &specifier, &what, &property)
!= B_OK) {
return BHandler::MessageReceived(message);
}
BPropertyInfo propertyInfo(sViewPropInfo);
switch (propertyInfo.FindMatch(message, index, &specifier, what,
property)) {
case 0:
if (message->what == B_GET_PROPERTY) {
err = replyMsg.AddRect("result", Frame());
} else if (message->what == B_SET_PROPERTY) {
BRect newFrame;
err = message->FindRect("data", &newFrame);
if (err == B_OK) {
MoveTo(newFrame.LeftTop());
ResizeTo(newFrame.Width(), newFrame.Height());
}
}
break;
case 1:
if (message->what == B_GET_PROPERTY) {
err = replyMsg.AddBool("result", IsHidden());
} else if (message->what == B_SET_PROPERTY) {
bool newHiddenState;
err = message->FindBool("data", &newHiddenState);
if (err == B_OK) {
if (newHiddenState == true)
Hide();
else
Show();
}
}
break;
case 3:
err = replyMsg.AddInt32("result", CountChildren());
break;
default:
return BHandler::MessageReceived(message);
}
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);
}
message->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_LAYOUT_INVALIDATED:
{
perform_data_layout_invalidated* data
= (perform_data_layout_invalidated*)_data;
BView::LayoutInvalidated(data->descendants);
return B_OK;
}
case PERFORM_CODE_DO_LAYOUT:
{
BView::DoLayout();
return B_OK;
}
case PERFORM_CODE_LAYOUT_CHANGED:
{
BView::LayoutChanged();
return B_OK;
}
case PERFORM_CODE_GET_TOOL_TIP_AT:
{
perform_data_get_tool_tip_at* data
= (perform_data_get_tool_tip_at*)_data;
data->return_value
= BView::GetToolTipAt(data->point, data->tool_tip);
return B_OK;
}
case PERFORM_CODE_ALL_UNARCHIVED:
{
perform_data_all_unarchived* data =
(perform_data_all_unarchived*)_data;
data->return_value = BView::AllUnarchived(data->archive);
return B_OK;
}
case PERFORM_CODE_ALL_ARCHIVED:
{
perform_data_all_archived* data =
(perform_data_all_archived*)_data;
data->return_value = BView::AllArchived(data->archive);
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::SetExplicitSize(BSize size)
{
fLayoutData->fMinSize = size;
fLayoutData->fMaxSize = 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;
if (layout && layout->Layout())
debugger("BView::SetLayout() failed, layout is already in use.");
fFlags |= B_SUPPORTS_LAYOUT;
// unset and delete the old layout
if (fLayoutData->fLayout) {
fLayoutData->fLayout->RemoveSelf();
fLayoutData->fLayout->SetOwner(NULL);
delete fLayoutData->fLayout;
}
fLayoutData->fLayout = layout;
if (fLayoutData->fLayout) {
fLayoutData->fLayout->SetOwner(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)
{
// printf("BView(%p)::InvalidateLayout(%i), valid: %i, inProgress: %i\n",
// this, descendants, fLayoutData->fLayoutValid,
// fLayoutData->fLayoutInProgress);
if (!fLayoutData->fMinMaxValid || fLayoutData->fLayoutInProgress
|| fLayoutData->fLayoutInvalidationDisabled > 0) {
return;
}
fLayoutData->fLayoutValid = false;
fLayoutData->fMinMaxValid = false;
LayoutInvalidated(descendants);
if (descendants) {
for (BView* child = fFirstChild;
child; child = child->fNextSibling) {
child->InvalidateLayout(descendants);
}
}
if (fLayoutData->fLayout)
fLayoutData->fLayout->InvalidateLayout(descendants);
else
_InvalidateParentLayout();
if (fTopLevelView
&& fOwner != NULL)
fOwner->PostMessage(B_LAYOUT_WINDOW);
}
void
BView::EnableLayoutInvalidation()
{
if (fLayoutData->fLayoutInvalidationDisabled > 0)
fLayoutData->fLayoutInvalidationDisabled--;
}
void
BView::DisableLayoutInvalidation()
{
fLayoutData->fLayoutInvalidationDisabled++;
}
bool
BView::IsLayoutInvalidationDisabled()
{
if (fLayoutData->fLayoutInvalidationDisabled > 0)
return true;
return false;
}
bool
BView::IsLayoutValid() const
{
return fLayoutData->fLayoutValid;
}
void
BView::ResetLayoutInvalidation()
{
fLayoutData->fMinMaxValid = true;
}
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;
if (fLayoutData->fLayout)
fLayoutData->fLayout->RequireLayout();
// 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::LayoutInvalidated(bool descendants)
{
// hook method
}
void
BView::DoLayout()
{
if (fLayoutData->fLayout)
fLayoutData->fLayout->_LayoutWithinContext(false, LayoutContext());
}
void
BView::SetToolTip(const char* text)
{
if (text == NULL || text[0] == '\0') {
SetToolTip((BToolTip*)NULL);
return;
}
if (BTextToolTip* tip = dynamic_cast<BTextToolTip*>(fToolTip))
tip->SetText(text);
else
SetToolTip(new BTextToolTip(text));
}
void
BView::SetToolTip(BToolTip* tip)
{
if (fToolTip == tip)
return;
else if (tip == NULL)
HideToolTip();
if (fToolTip != NULL)
fToolTip->ReleaseReference();
fToolTip = tip;
if (fToolTip != NULL)
fToolTip->AcquireReference();
}
BToolTip*
BView::ToolTip() const
{
return fToolTip;
}
void
BView::ShowToolTip(BToolTip* tip)
{
if (tip == NULL)
return;
BPoint where;
GetMouse(&where, NULL, false);
BToolTipManager::Manager()->ShowTip(tip, ConvertToScreen(where), this);
}
void
BView::HideToolTip()
{
BToolTipManager::Manager()->HideTip();
}
bool
BView::GetToolTipAt(BPoint point, BToolTip** _tip)
{
if (fToolTip != NULL) {
*_tip = fToolTip;
return true;
}
*_tip = NULL;
return false;
}
void
BView::LayoutChanged()
{
// hook method
}
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->fMinMaxValid = true;
fLayoutData->fNeedsRelayout = false;
// layout children
for(BView* child = fFirstChild; child; child = child->fNextSibling) {
if (!child->IsHidden(child))
child->_Layout(force, context);
}
LayoutChanged();
fLayoutData->fLayoutContext = oldContext;
// invalidate the drawn content, if requested
if (fFlags & B_INVALIDATE_AFTER_LAYOUT)
Invalidate();
}
}
void
BView::_LayoutLeft(BLayout* deleted)
{
// If our layout is added to another layout (via BLayout::AddItem())
// then we share ownership of our layout. In the event that our layout gets
// deleted by the layout it has been added to, this method is called so
// that we don't double-delete our layout.
if (fLayoutData->fLayout == deleted)
fLayoutData->fLayout = NULL;
InvalidateLayout();
}
void
BView::_InvalidateParentLayout()
{
if (!fParent)
return;
BLayout* layout = fLayoutData->fLayout;
BLayout* layoutParent = layout ? layout->Layout() : NULL;
if (layoutParent) {
layoutParent->InvalidateLayout();
} else if (fLayoutData->fLayoutItems.CountItems() > 0) {
int32 count = fLayoutData->fLayoutItems.CountItems();
for (int32 i = 0; i < count; i++) {
fLayoutData->fLayoutItems.ItemAt(i)->Layout()->InvalidateLayout();
}
} else {
fParent->InvalidateLayout();
}
}
// #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;
// TODO: Since we cannot communicate failure, we don't use std::nothrow here
// TODO: Maybe we could auto-delete those views on AddChild() instead?
fState = new BPrivate::ViewState;
fBounds = frame.OffsetToCopy(B_ORIGIN);
fShelf = NULL;
fEventMask = 0;
fEventOptions = 0;
fMouseEventOptions = 0;
fLayoutData = new LayoutData;
fToolTip = NULL;
if ((flags & B_SUPPORTS_LAYOUT) != 0) {
SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
SetLowUIColor(ViewUIColor());
SetHighUIColor(B_PANEL_TEXT_COLOR);
}
}
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 (!_CheckOwnerLockAndSwitchCurrent())
return;
if (picture == NULL) {
fOwner->fLink->StartMessage(AS_VIEW_CLIP_TO_PICTURE);
fOwner->fLink->Attach<int32>(-1);
// NOTE: No need to sync here, since the -1 token cannot
// become invalid on the server.
} else {
fOwner->fLink->StartMessage(AS_VIEW_CLIP_TO_PICTURE);
fOwner->fLink->Attach<int32>(picture->Token());
fOwner->fLink->Attach<BPoint>(where);
fOwner->fLink->Attach<bool>(invert);
// NOTE: "sync" defaults to true in public methods. If you know what
// you are doing, i.e. if you know your BPicture stays valid, you
// can avoid the performance impact of syncing. In a use-case where
// the client creates BPictures on the stack, these BPictures may
// have issued a AS_DELETE_PICTURE command to the ServerApp when Draw()
// goes out of scope, and the command is processed earlier in the
// ServerApp thread than the AS_VIEW_CLIP_TO_PICTURE command in the
// ServerWindow thread, which will then have the result that no
// ServerPicture is found of the token.
if (sync)
Sync();
}
}
void
BView::_ClipToRect(BRect rect, bool inverse)
{
if (_CheckOwnerLockAndSwitchCurrent()) {
fOwner->fLink->StartMessage(AS_VIEW_CLIP_TO_RECT);
fOwner->fLink->Attach<bool>(inverse);
fOwner->fLink->Attach<BRect>(rect);
_FlushIfNotInTransaction();
}
}
void
BView::_ClipToShape(BShape* shape, bool inverse)
{
if (shape == NULL)
return;
shape_data* sd = (shape_data*)shape->fPrivateData;
if (sd->opCount == 0 || sd->ptCount == 0)
return;
if (_CheckOwnerLockAndSwitchCurrent()) {
fOwner->fLink->StartMessage(AS_VIEW_CLIP_TO_SHAPE);
fOwner->fLink->Attach<bool>(inverse);
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();
}
}
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) != 0) {
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.AddInt32("width", fBounds.IntegerWidth());
resized.AddInt32("height", fBounds.IntegerHeight());
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()
{
if (fOwner != NULL) {
// unmask state flags to force [re]syncing with the app_server
fState->valid_flags &= ~(B_VIEW_WHICH_VIEW_COLOR_BIT
| B_VIEW_WHICH_LOW_COLOR_BIT | B_VIEW_WHICH_HIGH_COLOR_BIT);
if (fState->which_view_color != B_NO_COLOR)
SetViewUIColor(fState->which_view_color,
fState->which_view_color_tint);
if (fState->which_high_color != B_NO_COLOR)
SetHighUIColor(fState->which_high_color,
fState->which_high_color_tint);
if (fState->which_low_color != B_NO_COLOR)
SetLowUIColor(fState->which_low_color,
fState->which_low_color_tint);
}
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::_ColorsUpdated(BMessage* message)
{
if (fTopLevelView
&& fLayoutData->fLayout != NULL
&& !fState->IsValid(B_VIEW_WHICH_VIEW_COLOR_BIT)) {
SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
SetHighUIColor(B_PANEL_TEXT_COLOR);
}
rgb_color color;
const char* colorName = ui_color_name(fState->which_view_color);
if (colorName != NULL && message->FindColor(colorName, &color) == B_OK) {
fState->view_color = tint_color(color, fState->which_view_color_tint);
fState->valid_flags |= B_VIEW_VIEW_COLOR_BIT;
}
colorName = ui_color_name(fState->which_low_color);
if (colorName != NULL && message->FindColor(colorName, &color) == B_OK) {
fState->low_color = tint_color(color, fState->which_low_color_tint);
fState->valid_flags |= B_VIEW_LOW_COLOR_BIT;
}
colorName = ui_color_name(fState->which_high_color);
if (colorName != NULL && message->FindColor(colorName, &color) == B_OK) {
fState->high_color = tint_color(color, fState->which_high_color_tint);
fState->valid_flags |= B_VIEW_HIGH_COLOR_BIT;
}
MessageReceived(message);
for (BView* child = fFirstChild; child != NULL;
child = child->fNextSibling)
child->_ColorsUpdated(message);
Invalidate();
}
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::_FontsUpdated(BMessage* message)
{
MessageReceived(message);
for (BView* child = fFirstChild; child != NULL;
child = child->fNextSibling) {
child->_FontsUpdated(message);
}
}
void
BView::_Pulse()
{
if ((Flags() & B_PULSE_NEEDED) != 0)
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()\n", 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()\n", Name()));
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: %" B_PRId32 "\n",
serverToken));
fOwner->fLink->StartMessage(AS_SET_CURRENT_VIEW);
fOwner->fLink->Attach<int32>(serverToken);
fOwner->fLastViewToken = serverToken;
}
}
status_t
BView::ScrollWithMouseWheelDelta(BScrollBar* scrollBar, float delta)
{
if (scrollBar == NULL || delta == 0.0f)
return B_BAD_VALUE;
float smallStep;
float largeStep;
scrollBar->GetSteps(&smallStep, &largeStep);
// pressing the shift key scrolls faster (following the pseudo-standard set
// by other desktop environments).
if ((modifiers() & B_SHIFT_KEY) != 0)
delta *= largeStep;
else
delta *= smallStep * 3;
scrollBar->SetValue(scrollBar->Value() + delta);
return B_OK;
}
#if __GNUC__ == 2
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)
{
// LayoutInvalidated()
perform_data_layout_invalidated data;
data.descendants = descendants;
view->Perform(PERFORM_CODE_LAYOUT_INVALIDATED, &data);
}
extern "C" void
_ReservedView10__5BView(BView* view)
{
// DoLayout()
view->Perform(PERFORM_CODE_DO_LAYOUT, NULL);
}
#endif // __GNUC__ == 2
extern "C" bool
B_IF_GCC_2(_ReservedView11__5BView, _ZN5BView15_ReservedView11Ev)(
BView* view, BPoint point, BToolTip** _toolTip)
{
// GetToolTipAt()
perform_data_get_tool_tip_at data;
data.point = point;
data.tool_tip = _toolTip;
view->Perform(PERFORM_CODE_GET_TOOL_TIP_AT, &data);
return data.return_value;
}
extern "C" void
B_IF_GCC_2(_ReservedView12__5BView, _ZN5BView15_ReservedView12Ev)(
BView* view)
{
// LayoutChanged();
view->Perform(PERFORM_CODE_LAYOUT_CHANGED, NULL);
}
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: %" B_PRId32 "\n"
"\tFlags: %" B_PRId32 "\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: %" B_PRId32 "\n"
"\tEventOptions: %" B_PRId32 "\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: %" B_PRIx64 "\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;
}
}
}
}
}
// #pragma mark -
BLayoutItem*
BView::Private::LayoutItemAt(int32 index)
{
return fView->fLayoutData->fLayoutItems.ItemAt(index);
}
int32
BView::Private::CountLayoutItems()
{
return fView->fLayoutData->fLayoutItems.CountItems();
}
void
BView::Private::RegisterLayoutItem(BLayoutItem* item)
{
fView->fLayoutData->fLayoutItems.AddItem(item);
}
void
BView::Private::DeregisterLayoutItem(BLayoutItem* item)
{
fView->fLayoutData->fLayoutItems.RemoveItem(item);
}
bool
BView::Private::MinMaxValid()
{
return fView->fLayoutData->fMinMaxValid;
}
bool
BView::Private::WillLayout()
{
BView::LayoutData* data = fView->fLayoutData;
if (data->fLayoutInProgress)
return false;
if (data->fNeedsRelayout || !data->fLayoutValid || !data->fMinMaxValid)
return true;
return false;
}
↑ V595 The 'fOwner' pointer was utilized before it was verified against nullptr. Check lines: 4535, 4539.