diff options
Diffstat (limited to 'wx+/button.cpp')
-rw-r--r-- | wx+/button.cpp | 318 |
1 files changed, 318 insertions, 0 deletions
diff --git a/wx+/button.cpp b/wx+/button.cpp new file mode 100644 index 00000000..7edfdea8 --- /dev/null +++ b/wx+/button.cpp @@ -0,0 +1,318 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * +// ************************************************************************** + +#include "button.h" +#include <algorithm> +#include <limits> +#include <cmath> +#include <wx/dcmemory.h> +#include <wx/image.h> +#include "image_tools.h" + +using namespace zen; + + +void zen::setBitmapLabel(wxBitmapButton& button, const wxBitmap& bmp) +{ + if (!isEqual(button.GetBitmapLabel(), bmp)) + button.SetBitmapLabel(bmp); +} + + +BitmapButton::BitmapButton(wxWindow* parent, + wxWindowID id, + const wxString& label, + const wxPoint& pos, + const wxSize& size, + long style, + const wxValidator& validator, + const wxString& name) : + wxBitmapButton(parent, id, wxNullBitmap, pos, size, style | wxBU_AUTODRAW, validator, name), + m_spaceAfter(0), + m_spaceBefore(0) +{ + setTextLabel(label); +} + + +void BitmapButton::setBitmapFront(const wxBitmap& bitmap, unsigned spaceAfter) +{ + if (!isEqual(bitmap, bitmapFront) || spaceAfter != m_spaceAfter) //avoid flicker + { + bitmapFront = bitmap; + m_spaceAfter = spaceAfter; + refreshButtonLabel(); + } +} + + +void BitmapButton::setTextLabel(const wxString& text) +{ + if (text != textLabel) //avoid flicker + { + textLabel = text; + wxBitmapButton::SetLabel(text); + refreshButtonLabel(); + } +} + + +void BitmapButton::setBitmapBack(const wxBitmap& bitmap, unsigned spaceBefore) +{ + if (!isEqual(bitmap, bitmapBack) || spaceBefore != m_spaceBefore) //avoid flicker + { + bitmapBack = bitmap; + m_spaceBefore = spaceBefore; + refreshButtonLabel(); + } +} + + +void makeWhiteTransparent(wxImage& image) //assume black text on white background +{ + unsigned char* alphaFirst = image.GetAlpha(); + if (alphaFirst) + { + unsigned char* alphaLast = alphaFirst + image.GetWidth() * image.GetHeight(); + + //dist(black, white) + double distBlackWhite = std::sqrt(3.0 * 255 * 255); + + const unsigned char* bytePos = image.GetData(); + + for (unsigned char* j = alphaFirst; j != alphaLast; ++j) + { + unsigned char r = *bytePos++; // + unsigned char g = *bytePos++; //each pixel consists of three chars + unsigned char b = *bytePos++; // + + //dist((r,g,b), white) + double distColWhite = std::sqrt((255.0 - r) * (255.0 - r) + (255.0 - g) * (255.0 - g) + (255.0 - b) * (255.0 - b)); + + //black(0,0,0) becomes fully opaque(255), while white(255,255,255) becomes transparent(0) + *j = distColWhite / distBlackWhite * wxIMAGE_ALPHA_OPAQUE; + } + } +} + + +wxSize getSizeNeeded(const wxString& text, wxFont& font) +{ + wxCoord width, height; + wxMemoryDC dc; + + wxString textFormatted = text; + textFormatted.Replace(wxT("&"), wxT(""), false); //remove accelerator + dc.GetMultiLineTextExtent(textFormatted, &width, &height , NULL, &font); + return wxSize(width, height); +} + + +wxBitmap BitmapButton::createBitmapFromText(const wxString& text) +{ + //wxDC::DrawLabel() doesn't respect alpha channel at all => apply workaround to calculate alpha values manually: + + if (text.empty()) + return wxBitmap(); + + wxFont currentFont = wxBitmapButton::GetFont(); + wxColor textColor = wxBitmapButton::GetForegroundColour(); + + wxSize sizeNeeded = getSizeNeeded(text, currentFont); + wxBitmap newBitmap(sizeNeeded.GetWidth(), sizeNeeded.GetHeight()); + + { + wxMemoryDC dc; + dc.SelectObject(newBitmap); + + //set up white background + dc.SetBackground(*wxWHITE_BRUSH); + dc.Clear(); + + //find position of accelerator + int indexAccel = -1; + size_t accelPos; + wxString textLabelFormatted = text; + if ((accelPos = text.find(wxT("&"))) != wxString::npos) + { + textLabelFormatted.Replace(wxT("&"), wxT(""), false); //remove accelerator + indexAccel = static_cast<int>(accelPos); + } + + dc.SetTextForeground(*wxBLACK); //for use in makeWhiteTransparent + dc.SetTextBackground(*wxWHITE); // + dc.SetFont(currentFont); + + dc.DrawLabel(textLabelFormatted, wxNullBitmap, wxRect(0, 0, newBitmap.GetWidth(), newBitmap.GetHeight()), wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, indexAccel); + + dc.SelectObject(wxNullBitmap); + } + + //add alpha channel to image + wxImage finalImage(newBitmap.ConvertToImage()); + finalImage.SetAlpha(); + + //linearInterpolation(finalImage); + + //calculate values for alpha channel + makeWhiteTransparent(finalImage); + + //now apply real text color + unsigned char* bytePos = finalImage.GetData(); + const int pixelCount = finalImage.GetWidth() * finalImage.GetHeight(); + for (int i = 0; i < pixelCount; ++ i) + { + *bytePos++ = textColor.Red(); + *bytePos++ = textColor.Green(); + *bytePos++ = textColor.Blue(); + } + + return wxBitmap(finalImage); +} + + +//copy one image into another, allowing arbitrary overlapping! (pos may contain negative numbers) +void writeToImage(const wxImage& source, const wxPoint pos, wxImage& target) +{ + //determine startpositions in source and target image, as well as width and height to be copied + wxPoint posSrc, posTrg; + int width, height; + + //X-axis + if (pos.x < 0) + { + posSrc.x = -pos.x; + posTrg.x = 0; + width = std::min(pos.x + source.GetWidth(), target.GetWidth()); + } + else + { + posSrc.x = 0; + posTrg.x = pos.x; + width = std::min(target.GetWidth() - pos.x, source.GetWidth()); + } + + //Y-axis + if (pos.y < 0) + { + posSrc.y = -pos.y; + posTrg.y = 0; + height = std::min(pos.y + source.GetHeight(), target.GetHeight()); + } + else + { + posSrc.y = 0; + posTrg.y = pos.y; + height = std::min(target.GetHeight() - pos.y, source.GetHeight()); + } + + + if (width > 0 && height > 0) + { + { + //copy source to target respecting overlapping parts + const unsigned char* sourcePtr = source.GetData() + 3 * (posSrc.x + posSrc.y * source.GetWidth()); + const unsigned char* const sourcePtrEnd = source.GetData() + 3 * (posSrc.x + (posSrc.y + height) * source.GetWidth()); + unsigned char* targetPtr = target.GetData() + 3 * (posTrg.x + posTrg.y * target.GetWidth()); + + while (sourcePtr < sourcePtrEnd) + { + memcpy(targetPtr, sourcePtr, 3 * width); + sourcePtr += 3 * source.GetWidth(); + targetPtr += 3 * target.GetWidth(); + } + } + + //handle different cases concerning alpha channel + if (source.HasAlpha()) + { + if (!target.HasAlpha()) + { + target.SetAlpha(); + unsigned char* alpha = target.GetAlpha(); + memset(alpha, wxIMAGE_ALPHA_OPAQUE, target.GetWidth() * target.GetHeight()); + } + + //copy alpha channel + const unsigned char* sourcePtr = source.GetAlpha() + (posSrc.x + posSrc.y * source.GetWidth()); + const unsigned char* const sourcePtrEnd = source.GetAlpha() + (posSrc.x + (posSrc.y + height) * source.GetWidth()); + unsigned char* targetPtr = target.GetAlpha() + (posTrg.x + posTrg.y * target.GetWidth()); + + while (sourcePtr < sourcePtrEnd) + { + memcpy(targetPtr, sourcePtr, width); + sourcePtr += source.GetWidth(); + targetPtr += target.GetWidth(); + } + } + else if (target.HasAlpha()) + { + unsigned char* targetPtr = target.GetAlpha() + (posTrg.x + posTrg.y * target.GetWidth()); + const unsigned char* const targetPtrEnd = target.GetAlpha() + (posTrg.x + (posTrg.y + height) * target.GetWidth()); + + while (targetPtr < targetPtrEnd) + { + memset(targetPtr, wxIMAGE_ALPHA_OPAQUE, width); + targetPtr += target.GetWidth(); + } + } + } +} + + +namespace +{ +inline +wxSize getSize(const wxBitmap& bmp) +{ + return bmp.IsOk() ? wxSize(bmp.GetWidth(), bmp.GetHeight()) : wxSize(0, 0); +} +} + + +void BitmapButton::refreshButtonLabel() +{ + wxBitmap bitmapText = createBitmapFromText(textLabel); + + wxSize szFront = getSize(bitmapFront); // + wxSize szText = getSize(bitmapText); //make sure to NOT access null-bitmaps! + wxSize szBack = getSize(bitmapBack); // + + //calculate dimensions of new button + const int height = std::max(std::max(szFront.GetHeight(), szText.GetHeight()), szBack.GetHeight()); + const int width = szFront.GetWidth() + m_spaceAfter + szText.GetWidth() + m_spaceBefore + szBack.GetWidth(); + + //create a transparent image + wxImage transparentImage(width, height, false); + transparentImage.SetAlpha(); + unsigned char* alpha = transparentImage.GetAlpha(); + ::memset(alpha, wxIMAGE_ALPHA_TRANSPARENT, width * height); + + //wxDC::DrawLabel() unfortunately isn't working for transparent images on Linux, so we need to use custom image-concatenation + if (bitmapFront.IsOk()) + writeToImage(bitmapFront.ConvertToImage(), + wxPoint(0, (transparentImage.GetHeight() - bitmapFront.GetHeight()) / 2), + transparentImage); + + if (bitmapText.IsOk()) + writeToImage(bitmapText.ConvertToImage(), + wxPoint(szFront.GetWidth() + m_spaceAfter, (transparentImage.GetHeight() - bitmapText.GetHeight()) / 2), + transparentImage); + + if (bitmapBack.IsOk()) + writeToImage(bitmapBack.ConvertToImage(), + wxPoint(szFront.GetWidth() + m_spaceAfter + szText.GetWidth() + m_spaceBefore, (transparentImage.GetHeight() - bitmapBack.GetHeight()) / 2), + transparentImage); + + //adjust button size + wxSize minSize = GetMinSize(); + + //SetMinSize() instead of SetSize() is needed here for wxWindows layout determination to work corretly + wxBitmapButton::SetMinSize(wxSize(std::max(width + 10, minSize.GetWidth()), std::max(height + 5, minSize.GetHeight()))); + + //finally set bitmap + wxBitmapButton::SetBitmapLabel(wxBitmap(transparentImage)); +} |