summaryrefslogtreecommitdiff
path: root/shared/customButton.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'shared/customButton.cpp')
-rw-r--r--shared/customButton.cpp302
1 files changed, 302 insertions, 0 deletions
diff --git a/shared/customButton.cpp b/shared/customButton.cpp
new file mode 100644
index 00000000..4027d3ef
--- /dev/null
+++ b/shared/customButton.cpp
@@ -0,0 +1,302 @@
+#include "customButton.h"
+#include <wx/dcmemory.h>
+#include <wx/image.h>
+
+wxButtonWithImage::wxButtonWithImage(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 wxButtonWithImage::setBitmapFront(const wxBitmap& bitmap, unsigned spaceAfter)
+{
+ bitmapFront = bitmap;
+ m_spaceAfter = spaceAfter;
+ refreshButtonLabel();
+}
+
+
+void wxButtonWithImage::setTextLabel(const wxString& text)
+{
+ textLabel = text;
+ wxBitmapButton::SetLabel(text);
+ refreshButtonLabel();
+}
+
+
+void wxButtonWithImage::setBitmapBack(const wxBitmap& bitmap, unsigned spaceBefore)
+{
+ bitmapBack = bitmap;
+ m_spaceBefore = spaceBefore;
+ refreshButtonLabel();
+}
+
+
+void makeWhiteTransparent(const wxColor exceptColor, wxImage& image)
+{
+ unsigned char* alphaData = image.GetAlpha();
+ if (alphaData)
+ {
+ assert(exceptColor.Red() != 255);
+
+ unsigned char exCol = exceptColor.Red(); //alpha value can be extracted from any one of (red/green/blue)
+ unsigned char* imageStart = image.GetData();
+
+ unsigned char* j = alphaData;
+ const unsigned char* const rowEnd = j + image.GetWidth() * image.GetHeight();
+ while (j != rowEnd)
+ {
+ const unsigned char* imagePixel = imageStart + (j - alphaData) * 3; //each pixel consists of three chars
+ //exceptColor(red,green,blue) becomes fully opaque(255), while white(255,255,255) becomes transparent(0)
+ *(j++) = ((255 - imagePixel[0]) * wxIMAGE_ALPHA_OPAQUE) / (255 - exCol);
+ }
+ }
+}
+
+
+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);
+}
+
+/*
+inline
+void linearInterpolationHelper(const int shift, wxImage& img)
+{
+ unsigned char* const data = img.GetData();
+ const int width = img.GetWidth();
+ if (width < 2)
+ return;
+
+ const float intensity = 0.25;
+
+ for (int y = 1; y < img.GetHeight() - 1; ++y)
+ {
+ float back = 0;
+ float middle = 0;
+ float front = 0;
+
+ unsigned char* location = data + 3 * (y * width) + shift;
+ const unsigned char* const endPos = location + 3 * width;
+
+ middle = (*location + *(location - 3 * width) + *(location + 3 * width)) / 3;;
+ front = (*(location + 3) + *(location + 3 * (1 - width)) + *(location + 3 * (1 + width))) / 3;
+ *location += ((middle + front) / 2 - *location) * intensity;
+ location += 3;
+
+ while (location < endPos - 3)
+ {
+ back = middle;
+ middle = front;
+ front = (*(location + 3) + *(location + 3 * (1 - width)) + *(location + 3 * (1 + width))) / 3;
+ *location += ((back + middle + front) / 3 - *location) * intensity;
+ location += 3;
+ }
+
+ back = middle;
+ middle = front;
+ *location += ((back + middle) / 2 - *location) * intensity;
+ }
+}
+
+
+void linearInterpolation(wxImage& img)
+{
+ linearInterpolationHelper(0, img); //red channel
+ linearInterpolationHelper(1, img); //green channel
+ linearInterpolationHelper(2, img); //blue channel
+}
+*/
+
+wxBitmap wxButtonWithImage::createBitmapFromText(const wxString& text)
+{
+ 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 = accelPos;
+ }
+
+ dc.SetTextForeground(textColor);
+ 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);
+
+ //make white background transparent
+ makeWhiteTransparent(textColor, finalImage);
+
+ 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();
+ }
+ }
+ }
+}
+
+
+void wxButtonWithImage::refreshButtonLabel()
+{
+ wxBitmap bitmapText = createBitmapFromText(textLabel);
+
+ //calculate dimensions of new button
+ const int height = std::max(std::max(bitmapFront.GetHeight(), bitmapText.GetHeight()), bitmapBack.GetHeight());
+ const int width = bitmapFront.GetWidth() + m_spaceAfter + bitmapText.GetWidth() + m_spaceBefore + bitmapBack.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(wxImage(bitmapFront.ConvertToImage()),
+ wxPoint(0, (transparentImage.GetHeight() - bitmapFront.GetHeight()) / 2),
+ transparentImage);
+
+ if (bitmapText.IsOk())
+ writeToImage(wxImage(bitmapText.ConvertToImage()),
+ wxPoint(bitmapFront.GetWidth() + m_spaceAfter, (transparentImage.GetHeight() - bitmapText.GetHeight()) / 2),
+ transparentImage);
+
+ if (bitmapBack.IsOk())
+ writeToImage(wxImage(bitmapBack.ConvertToImage()),
+ wxPoint(bitmapFront.GetWidth() + m_spaceAfter + bitmapText.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));
+}
bgstack15