/* * Copyright 2001-2015, Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Stephan Aßmus * Axel Dörfler, axeld@pinc-software.de * Frans van Nispen (xlr8@tref.nl) * Ingo Weinhold */ //! BStringView draws a non-editable text string. #include #include #include #include #include #include #include #include #include #include #include static property_info sPropertyList[] = { { "Text", { B_GET_PROPERTY, B_SET_PROPERTY }, { B_DIRECT_SPECIFIER }, NULL, 0, { B_STRING_TYPE } }, { "Alignment", { B_GET_PROPERTY, B_SET_PROPERTY }, { B_DIRECT_SPECIFIER }, NULL, 0, { B_INT32_TYPE } }, { 0 } }; BStringView::BStringView(BRect frame, const char* name, const char* text, uint32 resizingMode, uint32 flags) : BView(frame, name, resizingMode, flags | B_FULL_UPDATE_ON_RESIZE), fText(text ? strdup(text) : NULL), fTruncation(B_NO_TRUNCATION), fAlign(B_ALIGN_LEFT), fPreferredSize(text ? _StringWidth(text) : 0.0, -1) { } BStringView::BStringView(const char* name, const char* text, uint32 flags) : BView(name, flags | B_FULL_UPDATE_ON_RESIZE), fText(text ? strdup(text) : NULL), fTruncation(B_NO_TRUNCATION), fAlign(B_ALIGN_LEFT), fPreferredSize(text ? _StringWidth(text) : 0.0, -1) { } BStringView::BStringView(BMessage* archive) : BView(archive), fText(NULL), fTruncation(B_NO_TRUNCATION), fPreferredSize(0, -1) { fAlign = (alignment)archive->GetInt32("_align", B_ALIGN_LEFT); fTruncation = (uint32)archive->GetInt32("_truncation", B_NO_TRUNCATION); const char* text = archive->GetString("_text", NULL); SetText(text); SetFlags(Flags() | B_FULL_UPDATE_ON_RESIZE); } BStringView::~BStringView() { free(fText); } // #pragma mark - Archiving methods BArchivable* BStringView::Instantiate(BMessage* data) { if (!validate_instantiation(data, "BStringView")) return NULL; return new BStringView(data); } status_t BStringView::Archive(BMessage* data, bool deep) const { status_t status = BView::Archive(data, deep); if (status == B_OK && fText) status = data->AddString("_text", fText); if (status == B_OK && fTruncation != B_NO_TRUNCATION) status = data->AddInt32("_truncation", fTruncation); if (status == B_OK) status = data->AddInt32("_align", fAlign); return status; } // #pragma mark - Hook methods void BStringView::AttachedToWindow() { if (HasDefaultColors()) SetHighUIColor(B_PANEL_TEXT_COLOR); BView* parent = Parent(); if (parent != NULL) { float tint = B_NO_TINT; color_which which = parent->ViewUIColor(&tint); if (which != B_NO_COLOR) { SetViewUIColor(which, tint); SetLowUIColor(which, tint); } else { SetViewColor(parent->ViewColor()); SetLowColor(ViewColor()); } } if (ViewColor() == B_TRANSPARENT_COLOR) AdoptSystemColors(); } void BStringView::DetachedFromWindow() { BView::DetachedFromWindow(); } void BStringView::AllAttached() { BView::AllAttached(); } void BStringView::AllDetached() { BView::AllDetached(); } // #pragma mark - Layout methods void BStringView::MakeFocus(bool focus) { BView::MakeFocus(focus); } void BStringView::GetPreferredSize(float* _width, float* _height) { _ValidatePreferredSize(); if (_width) *_width = fPreferredSize.width; if (_height) *_height = fPreferredSize.height; } BSize BStringView::MinSize() { return BLayoutUtils::ComposeSize(ExplicitMinSize(), _ValidatePreferredSize()); } BSize BStringView::MaxSize() { return BLayoutUtils::ComposeSize(ExplicitMaxSize(), _ValidatePreferredSize()); } BSize BStringView::PreferredSize() { return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), _ValidatePreferredSize()); } void BStringView::ResizeToPreferred() { float width, height; GetPreferredSize(&width, &height); // Resize the width only for B_ALIGN_LEFT (if its large enough already, that is) if (Bounds().Width() > width && Alignment() != B_ALIGN_LEFT) width = Bounds().Width(); BView::ResizeTo(width, height); } BAlignment BStringView::LayoutAlignment() { return BLayoutUtils::ComposeAlignment(ExplicitAlignment(), BAlignment(fAlign, B_ALIGN_MIDDLE)); } // #pragma mark - More hook methods void BStringView::FrameMoved(BPoint newPosition) { BView::FrameMoved(newPosition); } void BStringView::FrameResized(float newWidth, float newHeight) { BView::FrameResized(newWidth, newHeight); } void BStringView::Draw(BRect updateRect) { if (!fText) return; if (LowUIColor() == B_NO_COLOR) SetLowColor(ViewColor()); font_height fontHeight; GetFontHeight(&fontHeight); BRect bounds = Bounds(); BStringList lines; BString(fText).Split("\n", false, lines); for (int i = 0; i < lines.CountStrings(); i++) { const char* text = lines.StringAt(i).String(); float width = StringWidth(text); BString truncated; if (fTruncation != B_NO_TRUNCATION && width > bounds.Width()) { // The string needs to be truncated // TODO: we should cache this truncated = lines.StringAt(i); TruncateString(&truncated, fTruncation, bounds.Width()); text = truncated.String(); width = StringWidth(text); } float y = (bounds.top + bounds.bottom - ceilf(fontHeight.descent)) - ceilf(fontHeight.ascent + fontHeight.descent + fontHeight.leading) * (lines.CountStrings() - i - 1); float x; switch (fAlign) { case B_ALIGN_RIGHT: x = bounds.Width() - width; break; case B_ALIGN_CENTER: x = (bounds.Width() - width) / 2.0; break; default: x = 0.0; break; } DrawString(text, BPoint(x, y)); } } void BStringView::MessageReceived(BMessage* message) { if (message->what == B_GET_PROPERTY || message->what == B_SET_PROPERTY) { int32 index; BMessage specifier; int32 form; const char* property; if (message->GetCurrentSpecifier(&index, &specifier, &form, &property) != B_OK) { BView::MessageReceived(message); return; } BMessage reply(B_REPLY); bool handled = false; if (strcmp(property, "Text") == 0) { if (message->what == B_GET_PROPERTY) { reply.AddString("result", fText); handled = true; } else { const char* text; if (message->FindString("data", &text) == B_OK) { SetText(text); reply.AddInt32("error", B_OK); handled = true; } } } else if (strcmp(property, "Alignment") == 0) { if (message->what == B_GET_PROPERTY) { reply.AddInt32("result", (int32)fAlign); handled = true; } else { int32 align; if (message->FindInt32("data", &align) == B_OK) { SetAlignment((alignment)align); reply.AddInt32("error", B_OK); handled = true; } } } if (handled) { message->SendReply(&reply); return; } } BView::MessageReceived(message); } void BStringView::MouseDown(BPoint point) { BView::MouseDown(point); } void BStringView::MouseUp(BPoint point) { BView::MouseUp(point); } void BStringView::MouseMoved(BPoint point, uint32 transit, const BMessage* msg) { BView::MouseMoved(point, transit, msg); } // #pragma mark - void BStringView::SetText(const char* text) { if ((text && fText && !strcmp(text, fText)) || (!text && !fText)) return; free(fText); fText = text ? strdup(text) : NULL; float newStringWidth = _StringWidth(fText); if (fPreferredSize.width != newStringWidth) { fPreferredSize.width = newStringWidth; InvalidateLayout(); } Invalidate(); } const char* BStringView::Text() const { return fText; } void BStringView::SetAlignment(alignment flag) { fAlign = flag; Invalidate(); } alignment BStringView::Alignment() const { return fAlign; } void BStringView::SetTruncation(uint32 truncationMode) { if (fTruncation != truncationMode) { fTruncation = truncationMode; Invalidate(); } } uint32 BStringView::Truncation() const { return fTruncation; } BHandler* BStringView::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier, int32 form, const char* property) { BPropertyInfo propInfo(sPropertyList); if (propInfo.FindMatch(message, 0, specifier, form, property) >= B_OK) return this; return BView::ResolveSpecifier(message, index, specifier, form, property); } status_t BStringView::GetSupportedSuites(BMessage* data) { if (data == NULL) return B_BAD_VALUE; status_t status = data->AddString("suites", "suite/vnd.Be-string-view"); if (status != B_OK) return status; BPropertyInfo propertyInfo(sPropertyList); status = data->AddFlat("messages", &propertyInfo); if (status != B_OK) return status; return BView::GetSupportedSuites(data); } void BStringView::SetFont(const BFont* font, uint32 mask) { BView::SetFont(font, mask); fPreferredSize.width = _StringWidth(fText); Invalidate(); InvalidateLayout(); } void BStringView::LayoutInvalidated(bool descendants) { // invalidate cached preferred size fPreferredSize.height = -1; } // #pragma mark - Perform status_t BStringView::Perform(perform_code code, void* _data) { switch (code) { case PERFORM_CODE_MIN_SIZE: ((perform_data_min_size*)_data)->return_value = BStringView::MinSize(); return B_OK; case PERFORM_CODE_MAX_SIZE: ((perform_data_max_size*)_data)->return_value = BStringView::MaxSize(); return B_OK; case PERFORM_CODE_PREFERRED_SIZE: ((perform_data_preferred_size*)_data)->return_value = BStringView::PreferredSize(); return B_OK; case PERFORM_CODE_LAYOUT_ALIGNMENT: ((perform_data_layout_alignment*)_data)->return_value = BStringView::LayoutAlignment(); return B_OK; case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH: ((perform_data_has_height_for_width*)_data)->return_value = BStringView::HasHeightForWidth(); return B_OK; case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH: { perform_data_get_height_for_width* data = (perform_data_get_height_for_width*)_data; BStringView::GetHeightForWidth(data->width, &data->min, &data->max, &data->preferred); return B_OK; } case PERFORM_CODE_SET_LAYOUT: { perform_data_set_layout* data = (perform_data_set_layout*)_data; BStringView::SetLayout(data->layout); return B_OK; } case PERFORM_CODE_LAYOUT_INVALIDATED: { perform_data_layout_invalidated* data = (perform_data_layout_invalidated*)_data; BStringView::LayoutInvalidated(data->descendants); return B_OK; } case PERFORM_CODE_DO_LAYOUT: { BStringView::DoLayout(); return B_OK; } } return BView::Perform(code, _data); } // #pragma mark - FBC padding methods void BStringView::_ReservedStringView1() {} void BStringView::_ReservedStringView2() {} void BStringView::_ReservedStringView3() {} // #pragma mark - Private methods BStringView& BStringView::operator=(const BStringView&) { // Assignment not allowed (private) return *this; } BSize BStringView::_ValidatePreferredSize() { if (fPreferredSize.height < 0) { // height font_height fontHeight; GetFontHeight(&fontHeight); int32 lines = 1; char* temp = fText ? strchr(fText, '\n') : NULL; while (temp != NULL) { temp = strchr(temp + 1, '\n'); lines++; }; fPreferredSize.height = ceilf(fontHeight.ascent + fontHeight.descent + fontHeight.leading) * lines; ResetLayoutInvalidation(); } return fPreferredSize; } float BStringView::_StringWidth(const char* text) { if(text == NULL) return 0.0f; float maxWidth = 0.0f; BStringList lines; BString(fText).Split("\n", false, lines); for (int i = 0; i < lines.CountStrings(); i++) { float width = StringWidth(lines.StringAt(i)); if (maxWidth < width) maxWidth = width; } return maxWidth; } extern "C" void B_IF_GCC_2(InvalidateLayout__11BStringViewb, _ZN11BStringView16InvalidateLayoutEb)(BView* view, bool descendants) { perform_data_layout_invalidated data; data.descendants = descendants; view->Perform(PERFORM_CODE_LAYOUT_INVALIDATED, &data); }