diff --git a/headers/os/interface/Control.h b/headers/os/interface/Control.h index 31cd84fd4b..0db157d8ad 100644 --- a/headers/os/interface/Control.h +++ b/headers/os/interface/Control.h @@ -1,5 +1,5 @@ /* - * Copyright 2001-2009, Haiku, Inc. All rights reserved. + * Copyright 2001-2013, Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. */ #ifndef _CONTROL_H @@ -16,10 +16,42 @@ enum { B_CONTROL_PARTIALLY_ON = 2 }; +class BBitmap; class BWindow; class BControl : public BView, public BInvoker { +public: + // Values for [Set]IconBitmap(). Not all types are applicable for + // all controls. + enum { + B_OFF_BITMAP = 0x00, + B_ON_BITMAP = 0x01, + B_PARTIALLY_ON_BITMAP = 0x02, + + // flag, can be combined with any of the above + B_DISABLED_BITMAP = 0x80, + // disabled version of the specified bitmap + }; + + // flags for SetIconBitmap() + enum { + B_KEEP_BITMAP = 0x0001, + // transfer bitmap ownership to BControl object + }; + + // flags for SetIcon() + enum { + B_TRIM_BITMAP = 0x0100, + // crop the bitmap to the not fully transparent area, may + // change the icon size + B_TRIM_BITMAP_KEEP_ASPECT = 0x0200, + // like B_TRIM_BITMAP, but keeps the aspect ratio + B_CREATE_ON_BITMAP = 0x0400, + B_CREATE_PARTIALLY_ON_BITMAP = 0x0800, + B_CREATE_DISABLED_BITMAPS = 0x1000, + }; + public: BControl(BRect frame, const char* name, const char* label, BMessage* message, @@ -70,6 +102,12 @@ public: virtual status_t Perform(perform_code d, void* arg); + virtual status_t SetIcon(const BBitmap* bitmap, + uint32 flags = 0); + status_t SetIconBitmap(const BBitmap* bitmap, + uint32 which, uint32 flags = 0); + const BBitmap* IconBitmap(uint32 which) const; + protected: bool IsFocusChanging() const; bool IsTracking() const; @@ -78,7 +116,16 @@ protected: void SetValueNoUpdate(int32 value); private: - virtual void _ReservedControl1(); + struct IconData; + +private: + static BBitmap* _ConvertToRGB32(const BBitmap* bitmap, + bool noAppServerLink = false); + static status_t _TrimBitmap(const BBitmap* bitmap, + bool keepAspect, BBitmap*& _trimmedBitmap); + status_t _MakeBitmaps(const BBitmap* bitmap, + uint32 flags); + virtual void _ReservedControl2(); virtual void _ReservedControl3(); virtual void _ReservedControl4(); @@ -87,14 +134,20 @@ private: void InitData(BMessage* data = NULL); +private: char* fLabel; int32 fValue; bool fEnabled; bool fFocusChanging; bool fTracking; bool fWantsNav; + IconData* fIconData; - uint32 _reserved[4]; +#ifdef B_HAIKU_64_BIT + uint32 _reserved[2]; +#else + uint32 _reserved[3]; +#endif }; #endif // _CONTROL_H diff --git a/headers/private/binary_compatibility/Global.h b/headers/private/binary_compatibility/Global.h index 6ba9693e68..94e468d30d 100644 --- a/headers/private/binary_compatibility/Global.h +++ b/headers/private/binary_compatibility/Global.h @@ -29,12 +29,12 @@ enum { PERFORM_CODE_LAYOUT_INVALIDATED = 1007, PERFORM_CODE_DO_LAYOUT = 1008, PERFORM_CODE_GET_TOOL_TIP_AT = 1009, + PERFORM_CODE_LAYOUT_CHANGED = 1010, + PERFORM_CODE_SET_ICON = 1011, // support kit - PERFORM_CODE_ALL_ARCHIVED = 1010, - PERFORM_CODE_ALL_UNARCHIVED = 1011, - - PERFORM_CODE_LAYOUT_CHANGED = 1012 + PERFORM_CODE_ALL_ARCHIVED = 2000, + PERFORM_CODE_ALL_UNARCHIVED = 2001, }; diff --git a/headers/private/binary_compatibility/Interface.h b/headers/private/binary_compatibility/Interface.h index 5653583549..6255b5e459 100644 --- a/headers/private/binary_compatibility/Interface.h +++ b/headers/private/binary_compatibility/Interface.h @@ -51,4 +51,10 @@ struct perform_data_get_tool_tip_at { bool return_value; }; +struct perform_data_set_icon { + const BBitmap* icon; + uint32 flags; +}; + + #endif /* _BINARY_COMPATIBILITY_INTERFACE_H_ */ diff --git a/src/kits/interface/Control.cpp b/src/kits/interface/Control.cpp index 8fd92bd50c..42dbc2f16c 100644 --- a/src/kits/interface/Control.cpp +++ b/src/kits/interface/Control.cpp @@ -1,9 +1,11 @@ /* - * Copyright 2001-2006, Haiku Inc. + * Copyright 2001-2013, Haiku, Inc. * Distributed under the terms of the MIT License. * * Authors: - * Marc Flerackers (mflerackers@androme.be) + * Marc Flerackers, mflerackers@androme.be + * Stephan Aßmus, superstippi@gmx.de + * Ingo Weinhold, ingo_weinhold@gmx.de */ /*! BControl is the base class for user-event handling objects. */ @@ -12,10 +14,13 @@ #include #include +#include #include +#include #include #include +#include #include @@ -45,6 +50,85 @@ static property_info sPropertyList[] = { }; +struct BControl::IconData { +public: + IconData() + : + fEnabledBitmaps(8, true), + fDisabledBitmaps(8, true) + { + } + + ~IconData() + { + DeleteBitmaps(); + } + + BBitmap* CreateBitmap(const BRect& bounds, color_space colorSpace, + uint32 which) + { + BBitmap* bitmap = new(std::nothrow) BBitmap(bounds, colorSpace); + if (bitmap == NULL || !bitmap->IsValid() || !SetBitmap(bitmap, which)) { + delete bitmap; + return NULL; + } + + return bitmap; + } + + BBitmap* CopyBitmap(const BBitmap& bitmapToClone, uint32 which) + { + BBitmap* bitmap = new(std::nothrow) BBitmap(bitmapToClone); + if (bitmap == NULL || !bitmap->IsValid() || !SetBitmap(bitmap, which)) { + delete bitmap; + return NULL; + } + + return bitmap; + } + + bool SetBitmap(BBitmap* bitmap, uint32 which) + { + IconList& list = (which & B_DISABLED_BITMAP) == 0 + ? fEnabledBitmaps : fDisabledBitmaps; + which &= ~uint32(B_DISABLED_BITMAP); + + int32 count = list.CountItems(); + if ((int32)which < count) { + list.ReplaceItem(which, bitmap); + return true; + } + + while (list.CountItems() < (int32)which) { + if (!list.AddItem((BBitmap*)NULL)) + return false; + } + + return list.AddItem(bitmap); + } + + BBitmap* Bitmap(uint32 which) const + { + const IconList& list = (which & B_DISABLED_BITMAP) == 0 + ? fEnabledBitmaps : fDisabledBitmaps; + return list.ItemAt(which & ~uint32(B_DISABLED_BITMAP)); + } + + void DeleteBitmaps() + { + fEnabledBitmaps.MakeEmpty(true); + fDisabledBitmaps.MakeEmpty(true); + } + +private: + typedef BObjectList IconList; + +private: + IconList fEnabledBitmaps; + IconList fDisabledBitmaps; +}; + + BControl::BControl(BRect frame, const char *name, const char *label, BMessage *message, uint32 resizingMode, uint32 flags) : BView(frame, name, resizingMode, flags) @@ -70,6 +154,7 @@ BControl::BControl(const char *name, const char *label, BMessage *message, BControl::~BControl() { free(fLabel); + delete fIconData; SetMessage(NULL); } @@ -509,12 +594,148 @@ BControl::Perform(perform_code code, void* _data) BControl::DoLayout(); return B_OK; } + case PERFORM_CODE_SET_ICON: + { + perform_data_set_icon* data = (perform_data_set_icon*)_data; + return BControl::SetIcon(data->icon, data->flags); + } } return BView::Perform(code, _data); } +status_t +BControl::SetIcon(const BBitmap* bitmap, uint32 flags) +{ + if (bitmap == NULL) { + delete fIconData; + fIconData = NULL; + return B_OK; + } + + if (!bitmap->IsValid()) + return B_BAD_VALUE; + + // check the color space + bool hasAlpha = false; + bool canUseForMakeBitmaps = false; + + switch (bitmap->ColorSpace()) { + case B_RGBA32: + case B_RGBA32_BIG: + hasAlpha = true; + // fall through + case B_RGB32: + case B_RGB32_BIG: + canUseForMakeBitmaps = true; + break; + + case B_UVLA32: + case B_LABA32: + case B_HSIA32: + case B_HSVA32: + case B_HLSA32: + case B_CMYA32: + case B_RGBA15: + case B_RGBA15_BIG: + case B_CMAP8: + hasAlpha = true; + break; + + default: + break; + } + + BBitmap* trimmedBitmap = NULL; + + // trim the bitmap, if requested and the bitmap actually has alpha + status_t error; + if ((flags & (B_TRIM_BITMAP | B_TRIM_BITMAP_KEEP_ASPECT)) != 0 + && hasAlpha) { + if (bitmap->ColorSpace() == B_RGBA32) { + error = _TrimBitmap(bitmap, + (flags & B_TRIM_BITMAP_KEEP_ASPECT) != 0, trimmedBitmap); + } else { + BBitmap* rgb32Bitmap = _ConvertToRGB32(bitmap, true); + if (rgb32Bitmap != NULL) { + error = _TrimBitmap(rgb32Bitmap, + (flags & B_TRIM_BITMAP_KEEP_ASPECT) != 0, trimmedBitmap); + delete rgb32Bitmap; + } else + error = B_NO_MEMORY; + } + + if (error != B_OK) + return error; + + bitmap = trimmedBitmap; + canUseForMakeBitmaps = true; + } + + // create the bitmaps + if (canUseForMakeBitmaps) { + error = _MakeBitmaps(bitmap, flags); + } else { + if (BBitmap* rgb32Bitmap = _ConvertToRGB32(bitmap, true)) { + error = _MakeBitmaps(rgb32Bitmap, flags); + delete rgb32Bitmap; + } else + error = B_NO_MEMORY; + } + + delete trimmedBitmap; + + InvalidateLayout(); + Invalidate(); + + return error; +} + + +status_t +BControl::SetIconBitmap(const BBitmap* bitmap, uint32 which, uint32 flags) +{ + if (fIconData == NULL) { + fIconData = new(std::nothrow) IconData; + if (fIconData == NULL) + return B_NO_MEMORY; + } + + BBitmap* ourBitmap = NULL; + if (bitmap != NULL) { + if (!bitmap->IsValid()) + return B_BAD_VALUE; + + if ((flags & B_KEEP_BITMAP) != 0) { + ourBitmap = const_cast(bitmap); + } else { + ourBitmap = _ConvertToRGB32(bitmap); + if (ourBitmap == NULL) + return B_NO_MEMORY; + } + } + + if (!fIconData->SetBitmap(ourBitmap, which)) { + if (ourBitmap != bitmap) + delete ourBitmap; + return B_NO_MEMORY; + } + + InvalidateLayout(); + Invalidate(); + + return B_OK; +} + + +const BBitmap* +BControl::IconBitmap(uint32 which) const +{ + return fIconData != NULL ? fIconData->Bitmap(which) : NULL; +} + + bool BControl::IsFocusChanging() const { @@ -536,7 +757,286 @@ BControl::SetTracking(bool state) } -void BControl::_ReservedControl1() {} +extern "C" status_t +B_IF_GCC_2(_ReservedControl1__8BControl, _ZN8BControl17_ReservedControl1Ev)( + BControl* control, const BBitmap* icon, uint32 flags) +{ + // SetIcon() + perform_data_set_icon data; + data.icon = icon; + data.flags = flags; + return control->Perform(PERFORM_CODE_SET_ICON, &data); +} + + +/*static*/ BBitmap* +BControl::_ConvertToRGB32(const BBitmap* bitmap, bool noAppServerLink) +{ + BBitmap* rgb32Bitmap = new(std::nothrow) BBitmap(bitmap->Bounds(), + noAppServerLink ? B_BITMAP_NO_SERVER_LINK : 0, B_RGBA32); + if (rgb32Bitmap == NULL) + return NULL; + + if (!rgb32Bitmap->IsValid() || rgb32Bitmap->ImportBits(bitmap)!= B_OK) { + delete rgb32Bitmap; + return NULL; + } + + return rgb32Bitmap; +} + + +/*static*/ status_t +BControl::_TrimBitmap(const BBitmap* bitmap, bool keepAspect, + BBitmap*& _trimmedBitmap) +{ + if (bitmap == NULL || !bitmap->IsValid() + || bitmap->ColorSpace() != B_RGBA32) { + return B_BAD_VALUE; + } + + uint8* bits = (uint8*)bitmap->Bits(); + uint32 bpr = bitmap->BytesPerRow(); + uint32 width = bitmap->Bounds().IntegerWidth() + 1; + uint32 height = bitmap->Bounds().IntegerHeight() + 1; + BRect trimmed(INT32_MAX, INT32_MAX, INT32_MIN, INT32_MIN); + + for (uint32 y = 0; y < height; y++) { + uint8* b = bits + 3; + bool rowHasAlpha = false; + for (uint32 x = 0; x < width; x++) { + if (*b) { + rowHasAlpha = true; + if (x < trimmed.left) + trimmed.left = x; + if (x > trimmed.right) + trimmed.right = x; + } + b += 4; + } + if (rowHasAlpha) { + if (y < trimmed.top) + trimmed.top = y; + if (y > trimmed.bottom) + trimmed.bottom = y; + } + bits += bpr; + } + + if (!trimmed.IsValid()) + return B_BAD_VALUE; + + if (keepAspect) { + float minInset = trimmed.left; + minInset = min_c(minInset, trimmed.top); + minInset = min_c(minInset, bitmap->Bounds().right - trimmed.right); + minInset = min_c(minInset, bitmap->Bounds().bottom - trimmed.bottom); + trimmed = bitmap->Bounds().InsetByCopy(minInset, minInset); + } + trimmed = trimmed & bitmap->Bounds(); + + BBitmap* trimmedBitmap = new(std::nothrow) BBitmap( + trimmed.OffsetToCopy(B_ORIGIN), B_BITMAP_NO_SERVER_LINK, B_RGBA32); + if (trimmedBitmap == NULL) + return B_NO_MEMORY; + + bits = (uint8*)bitmap->Bits(); + bits += 4 * (int32)trimmed.left + bpr * (int32)trimmed.top; + uint8* dst = (uint8*)trimmedBitmap->Bits(); + uint32 trimmedWidth = trimmedBitmap->Bounds().IntegerWidth() + 1; + uint32 trimmedHeight = trimmedBitmap->Bounds().IntegerHeight() + 1; + uint32 trimmedBPR = trimmedBitmap->BytesPerRow(); + for (uint32 y = 0; y < trimmedHeight; y++) { + memcpy(dst, bits, trimmedWidth * 4); + dst += trimmedBPR; + bits += bpr; + } + + _trimmedBitmap = trimmedBitmap; + return B_OK; +} + + +status_t +BControl::_MakeBitmaps(const BBitmap* bitmap, uint32 flags) +{ + // make our own versions of the bitmap + BRect b(bitmap->Bounds()); + + IconData* iconData = new(std::nothrow) IconData; + if (iconData == NULL) + return B_NO_MEMORY; + ObjectDeleter iconDataDeleter(iconData); + + color_space format = bitmap->ColorSpace(); + BBitmap* normalBitmap = iconData->CreateBitmap(b, format, B_OFF_BITMAP); + if (normalBitmap == NULL) + return B_NO_MEMORY; + + BBitmap* disabledBitmap = NULL; + if ((flags & B_CREATE_DISABLED_BITMAPS) != 0) { + disabledBitmap = iconData->CreateBitmap(b, format, + B_OFF_BITMAP | B_DISABLED_BITMAP); + if (disabledBitmap == NULL) + return B_NO_MEMORY; + } + + BBitmap* clickedBitmap = NULL; + if ((flags & (B_CREATE_ON_BITMAP | B_CREATE_PARTIALLY_ON_BITMAP)) != 0) { + clickedBitmap = iconData->CreateBitmap(b, format, B_ON_BITMAP); + if (clickedBitmap == NULL) + return B_NO_MEMORY; + } + + BBitmap* disabledClickedBitmap = NULL; + if (disabledBitmap != NULL && clickedBitmap != NULL) { + disabledClickedBitmap = iconData->CreateBitmap(b, format, + B_ON_BITMAP | B_DISABLED_BITMAP); + if (disabledClickedBitmap == NULL) + return B_NO_MEMORY; + } + + // copy bitmaps from file bitmap + uint8* nBits = normalBitmap != NULL ? (uint8*)normalBitmap->Bits() : NULL; + uint8* dBits = disabledBitmap != NULL + ? (uint8*)disabledBitmap->Bits() : NULL; + uint8* cBits = clickedBitmap != NULL ? (uint8*)clickedBitmap->Bits() : NULL; + uint8* dcBits = disabledClickedBitmap != NULL + ? (uint8*)disabledClickedBitmap->Bits() : NULL; + uint8* fBits = (uint8*)bitmap->Bits(); + int32 nbpr = normalBitmap->BytesPerRow(); + int32 fbpr = bitmap->BytesPerRow(); + int32 pixels = b.IntegerWidth() + 1; + int32 lines = b.IntegerHeight() + 1; + if (format == B_RGB32 || format == B_RGB32_BIG) { + // nontransparent version + + // iterate over color components + for (int32 y = 0; y < lines; y++) { + for (int32 x = 0; x < pixels; x++) { + int32 nOffset = 4 * x; + int32 fOffset = 4 * x; + nBits[nOffset + 0] = fBits[fOffset + 0]; + nBits[nOffset + 1] = fBits[fOffset + 1]; + nBits[nOffset + 2] = fBits[fOffset + 2]; + nBits[nOffset + 3] = 255; + + // clicked bits are darker (lame method...) + if (cBits != NULL) { + cBits[nOffset + 0] = uint8((float)nBits[nOffset + 0] * 0.8); + cBits[nOffset + 1] = uint8((float)nBits[nOffset + 1] * 0.8); + cBits[nOffset + 2] = uint8((float)nBits[nOffset + 2] * 0.8); + cBits[nOffset + 3] = 255; + } + + // disabled bits have less contrast (lame method...) + if (dBits != NULL) { + uint8 grey = 216; + float dist = (nBits[nOffset + 0] - grey) * 0.4; + dBits[nOffset + 0] = (uint8)(grey + dist); + dist = (nBits[nOffset + 1] - grey) * 0.4; + dBits[nOffset + 1] = (uint8)(grey + dist); + dist = (nBits[nOffset + 2] - grey) * 0.4; + dBits[nOffset + 2] = (uint8)(grey + dist); + dBits[nOffset + 3] = 255; + } + + // disabled bits have less contrast (lame method...) + if (dcBits != NULL) { + uint8 grey = 188; + float dist = (nBits[nOffset + 0] - grey) * 0.4; + dcBits[nOffset + 0] = (uint8)(grey + dist); + dist = (nBits[nOffset + 1] - grey) * 0.4; + dcBits[nOffset + 1] = (uint8)(grey + dist); + dist = (nBits[nOffset + 2] - grey) * 0.4; + dcBits[nOffset + 2] = (uint8)(grey + dist); + dcBits[nOffset + 3] = 255; + } + } + fBits += fbpr; + nBits += nbpr; + if (cBits != NULL) + cBits += nbpr; + if (dBits != NULL) + dBits += nbpr; + if (dcBits != NULL) + dcBits += nbpr; + } + } else if (format == B_RGBA32 || format == B_RGBA32_BIG) { + // transparent version + + // iterate over color components + for (int32 y = 0; y < lines; y++) { + for (int32 x = 0; x < pixels; x++) { + int32 nOffset = 4 * x; + int32 fOffset = 4 * x; + nBits[nOffset + 0] = fBits[fOffset + 0]; + nBits[nOffset + 1] = fBits[fOffset + 1]; + nBits[nOffset + 2] = fBits[fOffset + 2]; + nBits[nOffset + 3] = fBits[fOffset + 3]; + + // clicked bits are darker (lame method...) + if (cBits != NULL) { + cBits[nOffset + 0] = (uint8)(nBits[nOffset + 0] * 0.8); + cBits[nOffset + 1] = (uint8)(nBits[nOffset + 1] * 0.8); + cBits[nOffset + 2] = (uint8)(nBits[nOffset + 2] * 0.8); + cBits[nOffset + 3] = fBits[fOffset + 3]; + } + + // disabled bits have less opacity + if (dBits != NULL) { + uint8 grey = ((uint16)nBits[nOffset + 0] * 10 + + nBits[nOffset + 1] * 60 + + nBits[nOffset + 2] * 30) / 100; + float dist = (nBits[nOffset + 0] - grey) * 0.3; + dBits[nOffset + 0] = (uint8)(grey + dist); + dist = (nBits[nOffset + 1] - grey) * 0.3; + dBits[nOffset + 1] = (uint8)(grey + dist); + dist = (nBits[nOffset + 2] - grey) * 0.3; + dBits[nOffset + 2] = (uint8)(grey + dist); + dBits[nOffset + 3] = (uint8)(fBits[fOffset + 3] * 0.3); + } + + // disabled bits have less contrast (lame method...) + if (dcBits != NULL) { + dcBits[nOffset + 0] = (uint8)(dBits[nOffset + 0] * 0.8); + dcBits[nOffset + 1] = (uint8)(dBits[nOffset + 1] * 0.8); + dcBits[nOffset + 2] = (uint8)(dBits[nOffset + 2] * 0.8); + dcBits[nOffset + 3] = (uint8)(fBits[fOffset + 3] * 0.3); + } + } + fBits += fbpr; + nBits += nbpr; + if (cBits != NULL) + cBits += nbpr; + if (dBits != NULL) + dBits += nbpr; + if (dcBits != NULL) + dcBits += nbpr; + } + } else { + // unsupported format + return B_BAD_VALUE; + } + + // make the partially-on bitmaps a copy of the on bitmaps + if ((flags & B_CREATE_PARTIALLY_ON_BITMAP) != 0) { + if (iconData->CopyBitmap(clickedBitmap, B_PARTIALLY_ON_BITMAP) == NULL) + return B_NO_MEMORY; + if ((flags & B_CREATE_DISABLED_BITMAPS) != 0) { + if (iconData->CopyBitmap(disabledClickedBitmap, + B_PARTIALLY_ON_BITMAP | B_DISABLED_BITMAP) == NULL) { + return B_NO_MEMORY; + } + } + } + + delete fIconData; + fIconData = iconDataDeleter.Detach(); + return B_OK; +} + + void BControl::_ReservedControl2() {} void BControl::_ReservedControl3() {} void BControl::_ReservedControl4() {} @@ -559,6 +1059,7 @@ BControl::InitData(BMessage *data) fFocusChanging = false; fTracking = false; fWantsNav = Flags() & B_NAVIGABLE; + fIconData = NULL; if (data && data->HasString("_fname")) SetFont(be_plain_font, B_FONT_FAMILY_AND_STYLE);