//------------------------------------------------------------------------------ // Copyright (c) 2001-2004, Haiku, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // // File Name: TextControl.cpp // Author: Frans van Nispen (xlr8@tref.nl) // Description: BTextControl displays text that can act like a control. //------------------------------------------------------------------------------ #include #include #include #include #include "TextInput.h" BTextControl::BTextControl(BRect frame, const char *name, const char *label, const char *text, BMessage *message, uint32 mask, uint32 flags) : BControl(frame, name, label, message, mask, flags | B_FRAME_EVENTS) { InitData(label, text); float lineHeight = fText->LineHeight(0); ResizeTo(Bounds().Width(), lineHeight + 8); fText->ResizeTo(fText->Bounds().Width(), lineHeight + 4); fText->MoveTo(fText->Frame().left, 2.0f); } BTextControl::~BTextControl() { SetModificationMessage(NULL); } BTextControl::BTextControl(BMessage *data) : BControl(data) { InitData(Label(), NULL, data); int32 _a_label = B_ALIGN_LEFT; int32 _a_text = B_ALIGN_LEFT; if (data->HasInt32("_a_label")) data->FindInt32("_a_label", &_a_label); if (data->HasInt32("_a_text")) data->FindInt32("_a_text", &_a_text); SetAlignment((alignment)_a_label, (alignment)_a_text); if (data->HasFloat("_divide")) data->FindFloat("_a_text", &fDivider); if (data->HasMessage("_mod_msg")) { BMessage *_mod_msg = new BMessage; data->FindMessage("_mod_msg", _mod_msg); SetModificationMessage(_mod_msg); } } BArchivable * BTextControl::Instantiate(BMessage *archive) { if (validate_instantiation(archive, "BTextControl")) return new BTextControl(archive); else return NULL; } status_t BTextControl::Archive(BMessage *data, bool deep) const { BView::Archive(data, deep); alignment labelAlignment, textAlignment; GetAlignment(&labelAlignment, &textAlignment); data->AddInt32("_a_label", labelAlignment); data->AddInt32("_a_text", textAlignment); data->AddFloat("_divide", Divider()); if (ModificationMessage()) data->AddMessage("_mod_msg", ModificationMessage()); return B_OK; } void BTextControl::SetText(const char *text) { if (InvokeKind() != B_CONTROL_INVOKED) return; fText->SetText(text); if (IsFocus()) fText->SetInitialText(); fText->Invalidate(); } const char * BTextControl::Text() const { return fText->Text(); } void BTextControl::SetValue(int32 value) { BControl::SetValue(value); } status_t BTextControl::Invoke(BMessage *message) { return BControl::Invoke(message); } BTextView * BTextControl::TextView() const { return fText; } void BTextControl::SetModificationMessage(BMessage *message) { delete fModificationMessage; fModificationMessage = message; } BMessage * BTextControl::ModificationMessage() const { return fModificationMessage; } void BTextControl::SetAlignment(alignment labelAlignment, alignment textAlignment) { fText->SetAlignment(textAlignment); fText->AlignTextRect(); if (fLabelAlign != labelAlignment) { fLabelAlign = labelAlignment; Invalidate(); } } void BTextControl::GetAlignment(alignment *label, alignment *text) const { if (label) *label = fLabelAlign; if (text) *text = fText->Alignment(); } void BTextControl::SetDivider(float dividingLine) { dividingLine = floorf(dividingLine + 0.5); float dx = fDivider - dividingLine; fDivider = dividingLine; fText->MoveBy(-dx, 0.0f); fText->ResizeBy(dx, 0.0f); if (Window()) { fText->Invalidate(); Invalidate(); } } float BTextControl::Divider() const { return fDivider; } void BTextControl::Draw(BRect updateRect) { rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR), lighten1 = tint_color(no_tint, B_LIGHTEN_1_TINT), lighten2 = tint_color(no_tint, B_LIGHTEN_2_TINT), lightenmax = tint_color(no_tint, B_LIGHTEN_MAX_TINT), darken1 = tint_color(no_tint, B_DARKEN_1_TINT), darken2 = tint_color(no_tint, B_DARKEN_2_TINT), darken4 = tint_color(no_tint, B_DARKEN_4_TINT), darkenmax = tint_color(no_tint, B_DARKEN_MAX_TINT), nav = ui_color(B_KEYBOARD_NAVIGATION_COLOR); bool enabled = IsEnabled(); bool active = false; if (fText->IsFocus() && Window()->IsActive()) active = true; BRect rect(fText->Frame()); rect.InsetBy(-1.0f, -1.0f); if (active) { SetHighColor(nav); StrokeRect(rect); } else { if (enabled) SetHighColor(darken4); else SetHighColor(darken2); StrokeLine(rect.LeftTop(), rect.LeftBottom()); StrokeLine(rect.LeftTop(), rect.RightTop()); SetHighColor(no_tint); StrokeLine(BPoint(rect.left + 1.0f, rect.bottom), rect.RightBottom()); StrokeLine(BPoint(rect.right, rect.top + 1.0f)); } rect.InsetBy(-1.0f, -1.0f); if (enabled) SetHighColor(darken1); else SetHighColor(no_tint); StrokeLine(rect.LeftBottom(), rect.LeftTop()); StrokeLine(rect.RightTop()); if (enabled) SetHighColor(lighten2); else SetHighColor(lighten1); StrokeLine(BPoint(rect.left + 1.0f, rect.bottom), rect.RightBottom()); StrokeLine(BPoint(rect.right, rect.top + 1.0f), rect.RightBottom()); if (Label()) { font_height fh; GetFontHeight(&fh); float y = (float)ceil(fh.ascent + fh.descent + fh.leading) + 2.0f; float x; switch (fLabelAlign) { case B_ALIGN_RIGHT: x = fDivider - StringWidth(Label()) - 3.0f; break; case B_ALIGN_CENTER: x = fDivider - StringWidth(Label()) / 2.0f; break; default: x = 3.0f; break; } SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), IsEnabled() ? B_DARKEN_MAX_TINT : B_DISABLED_LABEL_TINT)); DrawString(Label(), BPoint(x, y)); } } void BTextControl::MouseDown(BPoint where) { if (!fText->IsFocus()) { fText->MakeFocus(true); fText->SelectAll(); } } void BTextControl::AttachedToWindow() { BControl::AttachedToWindow(); bool enabled = IsEnabled(); rgb_color textColor; rgb_color color = HighColor(); BFont font; fText->GetFontAndColor(0, &font, &color); if (enabled) textColor = color; else textColor = tint_color(color, B_LIGHTEN_2_TINT); fText->SetFontAndColor(&font, B_FONT_ALL, &textColor); if (enabled) { color.red = 255; color.green = 255; color.blue = 255; } else color = tint_color(color, B_LIGHTEN_2_TINT); fText->SetViewColor(color); fText->SetLowColor(color); fText->MakeEditable(enabled); } void BTextControl::MakeFocus(bool state) { if (state != fText->IsFocus()) { fText->MakeFocus(state); if (state) fText->SelectAll(); } } void BTextControl::SetEnabled(bool state) { if (IsEnabled() == state) return; if (Window()) { fText->MakeEditable(state); rgb_color textColor; rgb_color color = {0, 0, 0, 255}; BFont font; fText->GetFontAndColor(0, &font, &color); if (state) textColor = color; else textColor = tint_color(color, B_DISABLED_LABEL_TINT); fText->SetFontAndColor(&font, B_FONT_ALL, &textColor); if (state) { color.red = 255; color.green = 255; color.blue = 255; } else color = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_LIGHTEN_2_TINT); fText->SetViewColor(color); fText->SetLowColor(color); fText->Invalidate(); Window()->UpdateIfNeeded(); } BControl::SetEnabled(state); } void BTextControl::GetPreferredSize(float *width, float *height) { if (height) *height = fText->LineHeight(0) + 8.0f; // TODO: this one I need to find out if (width) *width = 4.0f + ceilf(StringWidth(Label())) * 2.0f; } void BTextControl::ResizeToPreferred() { float width, height; GetPreferredSize(&width, &height); BView::ResizeTo(width, height); } void BTextControl::SetFlags(uint32 flags) { if (!fSkipSetFlags) { // If the textview is navigable, set it to not navigable if needed // Else if it is not navigable, set it to navigable if needed if (fText->Flags() & B_NAVIGABLE) { if (!(flags & B_NAVIGABLE)) fText->SetFlags(fText->Flags() & ~B_NAVIGABLE); } else { if (flags & B_NAVIGABLE) fText->SetFlags(fText->Flags() | B_NAVIGABLE); } // Don't make this one navigable flags &= ~B_NAVIGABLE; } BView::SetFlags(flags); } void BTextControl::MessageReceived(BMessage *msg) { switch(msg->what) { case B_SET_PROPERTY: case B_GET_PROPERTY: // TODO break; default: BControl::MessageReceived(msg); break; } } BHandler * BTextControl::ResolveSpecifier(BMessage *msg, int32 index, BMessage *specifier, int32 form, const char *property) { /* BPropertyInfo propInfo(prop_list); BHandler *target = NULL; if (propInfo.FindMatch(message, 0, specifier, what, property) < B_OK) return BControl::ResolveSpecifier(message, index, specifier, what, property); else return this; */ return BControl::ResolveSpecifier(msg, index, specifier, form, property); } status_t BTextControl::GetSupportedSuites(BMessage *data) { return BControl::GetSupportedSuites(data); } void BTextControl::MouseUp(BPoint pt) { BControl::MouseUp(pt); } void BTextControl::MouseMoved(BPoint pt, uint32 code, const BMessage *msg) { BControl::MouseMoved(pt, code, msg); } void BTextControl::DetachedFromWindow() { BControl::DetachedFromWindow(); } void BTextControl::AllAttached() { BControl::AllAttached(); } void BTextControl::AllDetached() { BControl::AllDetached(); } void BTextControl::FrameMoved(BPoint newPosition) { BControl::FrameMoved(newPosition); } void BTextControl::FrameResized(float newWidth, float newHeight) { BControl::FrameResized(newWidth, newHeight); } void BTextControl::WindowActivated(bool active) { if (fText->IsFocus()) { BRect rect(fText->Frame()); rect.InsetBy(-1.0, -1.0); Invalidate(rect); } } status_t BTextControl::Perform(perform_code d, void *arg) { return BControl::Perform(d, arg); } void BTextControl::_ReservedTextControl1() {} void BTextControl::_ReservedTextControl2() {} void BTextControl::_ReservedTextControl3() {} void BTextControl::_ReservedTextControl4() {} BTextControl & BTextControl::operator=(const BTextControl&) { return *this; } void BTextControl::CommitValue() { } void BTextControl::InitData(const char *label, const char *initial_text, BMessage *data) { BRect bounds(Bounds()); fText = NULL; fModificationMessage = NULL; fLabelAlign = B_ALIGN_LEFT; fDivider = 0.0f; fPrevWidth = 0; fPrevHeight = 0; fSkipSetFlags = false; int32 flags = 0; BFont font(be_plain_font); if (!data || !data->HasString("_fname")) flags |= B_FONT_FAMILY_AND_STYLE; if (!data || !data->HasFloat("_fflt")) flags |= B_FONT_SIZE; if (flags != 0) SetFont(&font, flags); if (label) fDivider = floorf(bounds.Width() / 2.0f); if (Flags() & B_NAVIGABLE) { fSkipSetFlags = true; SetFlags(Flags() & ~B_NAVIGABLE); fSkipSetFlags = false; } if (data) fText = static_cast<_BTextInput_ *>(FindView("_input_")); else { BRect frame(fDivider, bounds.top, bounds.right, bounds.bottom); // we are stroking the frame arround the text view, which // is 2 pixels wide frame.InsetBy(2.0, 2.0); BRect textRect(frame.OffsetToCopy(0.0f, 0.0f)); textRect.InsetBy(2.0, 2.0); fText = new _BTextInput_(frame, textRect, B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP, B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE); AddChild(fText); SetText(initial_text); fText->SetAlignment(B_ALIGN_LEFT); fText->AlignTextRect(); } }