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