310 lines
8.8 KiB
C++
310 lines
8.8 KiB
C++
#include "StdAfx.h"
|
|
#include "OXHyperBarRender.h"
|
|
#include "OXHyperbar.h"
|
|
|
|
#include<cmath>
|
|
|
|
namespace
|
|
{
|
|
double ColumnDistanceFromDOI(int columnIndex, int columnCount, int barWidth, POINT * mousePos)
|
|
{
|
|
double mouseProportion = 0.5;
|
|
if (mousePos)
|
|
{
|
|
mouseProportion = mousePos->x / (double)barWidth;
|
|
}
|
|
double positionProportion = (double)(columnIndex + 0.5) / columnCount;
|
|
return mouseProportion - positionProportion;
|
|
}
|
|
|
|
double ValueAtX(double x, double variance)
|
|
{
|
|
double exponent = (x * x) / (2 * variance * variance);
|
|
return exp(-exponent);
|
|
}
|
|
}
|
|
|
|
double COXHyperBarRender::m_Focus = .2; // This represents the spacing of the toolbar, which also causes it to zoom
|
|
|
|
COXHyperBarRender::COXHyperBarRender(CToolBar * pToolbar)
|
|
{
|
|
m_nCmd = -1;
|
|
m_pToolbar = pToolbar;
|
|
m_buttonInfo = NULL;
|
|
m_buffer = NULL;
|
|
}
|
|
|
|
void COXHyperBarRender::Initialise(SIZE szBtn)
|
|
{
|
|
// Create array to contain button info, and populate it.
|
|
|
|
m_buttonInfo = new buttonInfo[m_pToolbar->GetToolBarCtrl().GetButtonCount()];
|
|
|
|
CImageList * const pImages = m_pToolbar->GetToolBarCtrl().GetImageList();
|
|
|
|
|
|
for(int i=0;i<m_pToolbar->GetToolBarCtrl().GetButtonCount();++i)
|
|
{
|
|
TBBUTTON btn;
|
|
|
|
m_pToolbar->GetToolBarCtrl().GetButton(i, &btn);
|
|
IMAGEINFO iif;
|
|
pImages->GetImageInfo( btn.iBitmap, &iif);
|
|
const int imgWidth = iif.rcImage.right - iif.rcImage.left;
|
|
const int imgHeight = iif.rcImage.bottom - iif.rcImage.top;
|
|
|
|
m_buttonInfo[i].defaultWidth = imgWidth;
|
|
m_buttonInfo[i].width = szBtn.cx;//imgWidth;
|
|
m_buttonInfo[i].defaultHeight = imgHeight;
|
|
m_buttonInfo[i].height = szBtn.cy;//imgHeight;
|
|
m_buttonInfo[i].bitmapIndex = btn.iBitmap;
|
|
m_buttonInfo[i].commandID = btn.idCommand;
|
|
}
|
|
}
|
|
|
|
COXHyperBarRender::~COXHyperBarRender(void)
|
|
{
|
|
if (m_buttonInfo)
|
|
{
|
|
delete [] m_buttonInfo;
|
|
m_buttonInfo = NULL;
|
|
}
|
|
|
|
if (m_buffer)
|
|
{
|
|
delete m_buffer;
|
|
m_buffer = NULL;
|
|
}
|
|
}
|
|
|
|
void COXHyperBarRender::RenderToolbar(CDC * pDC, RECT * pRect, SIZE szBtn, POINT * pMouse, CBitmap * pOverlay)
|
|
{
|
|
// The first time this is run after the toolbar is set up,
|
|
// call the method to fill the array of image data
|
|
if (!m_buttonInfo)
|
|
{
|
|
if(m_pToolbar->GetToolBarCtrl().m_hWnd && m_pToolbar->GetToolBarCtrl().GetButtonCount())
|
|
{
|
|
Initialise(szBtn);
|
|
}
|
|
else return;
|
|
}
|
|
|
|
CImageList * const pImages = m_pToolbar->GetToolBarCtrl().GetImageList();
|
|
|
|
const int width = pRect->right - pRect->left;
|
|
const int height = pRect->bottom - pRect->top;
|
|
|
|
// Create the bitmap used as a double buffer
|
|
if (!m_buffer)
|
|
{
|
|
m_buffer = new CBitmap();
|
|
// The buffer will be created when the mouse is not over the toolbar
|
|
m_buffer->CreateCompatibleBitmap(pDC, width, height+(COXHyperBar::OffsetY * 2));
|
|
}
|
|
|
|
CDC dc;
|
|
dc.CreateCompatibleDC(pDC);
|
|
CBitmap * pOld = dc.SelectObject(m_buffer);
|
|
|
|
// offset is the amount of space around each button
|
|
const int offset = 4;
|
|
const int buttonCount = m_pToolbar->GetToolBarCtrl().GetButtonCount();
|
|
|
|
RECT rc;
|
|
rc.right = width;
|
|
rc.bottom = height;
|
|
rc.top = rc.left = 0;
|
|
|
|
// Reset the command, it will be set again if our mouse is over a button in the code below
|
|
m_nCmd = -1;
|
|
|
|
// This variable is used to accumulate the current position as we draw the buttons
|
|
POINT ptDraw;
|
|
ptDraw.x = ptDraw.y = 0;
|
|
|
|
// We need to do this in two parts:
|
|
// 1 - create a bitmap which has the overlay as a background and then the buttons drawn in the right positions
|
|
// 2 - use the AlphaBlend function to blend this toolbar onto our device context.
|
|
// The reason for this is that we need to use the Image List to draw the buttons transparently first,
|
|
// then we need to do the alpha blend after that.
|
|
CDC imageListDC;
|
|
imageListDC.CreateCompatibleDC(pDC);
|
|
CBitmap imageListBitmap;
|
|
imageListBitmap.CreateCompatibleBitmap(pDC, width, height);
|
|
imageListDC.SetStretchBltMode(COLORONCOLOR);
|
|
dc.SetStretchBltMode(COLORONCOLOR);
|
|
CBitmap * pOldImageList = imageListDC.SelectObject(&imageListBitmap);
|
|
|
|
// If pOverlay != NULL, then draw the pOverlay onto both the buffer bitmap, and the bitmap that will be the source for alpha blending.
|
|
if (pOverlay)
|
|
{
|
|
CDC dcBmp;
|
|
dcBmp.CreateCompatibleDC(&dc);
|
|
CBitmap * pOld = dcBmp.SelectObject(pOverlay);
|
|
|
|
imageListDC.BitBlt(0, 0, width, height, &dcBmp, 0, 0, SRCCOPY);
|
|
dc.BitBlt(0, 0, width, height, &dcBmp, 0, 0, SRCCOPY);
|
|
|
|
dcBmp.SelectObject(&pOld);
|
|
}
|
|
|
|
// rcBar becomes the rectangle we use to know the position of the toolbar within our CDC
|
|
CRect rcBar = rc;
|
|
|
|
if (pOverlay)
|
|
{
|
|
rcBar.InflateRect(0, -(COXHyperBar::OffsetY + 2));
|
|
}
|
|
|
|
((COXHyperBar*)m_pToolbar)->RenderBackground(&dc, &rcBar);
|
|
((COXHyperBar*)m_pToolbar)->RenderBackground(&imageListDC, rcBar);
|
|
|
|
double * hypers = new double[buttonCount];
|
|
double hyperTotal = 0.0;
|
|
for(int j=0;j<buttonCount;++j)
|
|
{
|
|
// Skip seperators, which would otherwise kill the spacing, etc.
|
|
if (!m_buttonInfo[j].commandID)
|
|
{
|
|
hypers[j] = 0;
|
|
continue;
|
|
}
|
|
|
|
double distanceFromDOI = ColumnDistanceFromDOI(j, buttonCount, width, pMouse);
|
|
|
|
// Plug the distance from zero (the DOI itself is at zero) into the hyperbolic function.
|
|
// .05 -> .4 Focus
|
|
hypers[j] = ValueAtX(distanceFromDOI, m_Focus);
|
|
hyperTotal += hypers[j];
|
|
}
|
|
|
|
double factor = width/hyperTotal;
|
|
|
|
for(int n=0;n<buttonCount;++n)
|
|
{
|
|
double distanceFromDOI = ColumnDistanceFromDOI(n, buttonCount, width, pMouse);
|
|
|
|
double hyperbolic = hypers[n];
|
|
|
|
// commandID is 0 for non buttons.
|
|
if (!m_buttonInfo[n].commandID) continue;
|
|
|
|
int alpha = 255;
|
|
|
|
if (pMouse)
|
|
{
|
|
// The second param is always 1
|
|
alpha = (int) (ValueAtX(distanceFromDOI, m_Focus) * 255);
|
|
}
|
|
|
|
// If we're not drawing the overlay window, we nust draw at the default width
|
|
int imgWidth = (pMouse) ? (int)(factor * hyperbolic) - offset * 2 : m_buttonInfo[n].width ;
|
|
int imgHeight = (pMouse)?(int)(m_buttonInfo[n].height * factor/m_buttonInfo[n].width * hyperbolic) : m_buttonInfo[n].height ;
|
|
|
|
ptDraw.x += offset;
|
|
ptDraw.y = (height - imgHeight)/2;
|
|
|
|
POINT pt;
|
|
pt.x = pt.y = 0;
|
|
// Draw the whole toolbar image
|
|
SIZE szDraw;
|
|
szDraw.cx = szDraw.cy = 0;
|
|
|
|
CDC itemDC;
|
|
itemDC.CreateCompatibleDC(&dc);
|
|
CBitmap itemBitmap;
|
|
itemBitmap.CreateCompatibleBitmap(&dc, m_buttonInfo[n].defaultWidth, m_buttonInfo[n].defaultHeight);
|
|
CBitmap * pOldItemBitmap = itemDC.SelectObject(&itemBitmap);
|
|
|
|
itemDC.FillSolidRect(0, 0, m_buttonInfo[n].defaultWidth, m_buttonInfo[n].defaultHeight, RGB(255,0,255));
|
|
|
|
pImages->Draw(&itemDC, m_buttonInfo[n].bitmapIndex, pt, ILD_TRANSPARENT);
|
|
|
|
if (pMouse)
|
|
{
|
|
// Check if this is the active button
|
|
CRect rcDraw(ptDraw.x, ptDraw.y, imgWidth + ptDraw.x, ptDraw.y + imgHeight);
|
|
|
|
if (PtInRect(&rcDraw, *pMouse))
|
|
{
|
|
m_nCmd = m_buttonInfo[n].commandID;
|
|
}
|
|
}
|
|
|
|
// Draw the button transparently onto the image list render DC
|
|
TransparentBlt(imageListDC, ptDraw.x, ptDraw.y, imgWidth, imgHeight, itemDC, 0, 0, m_buttonInfo[n].defaultWidth, m_buttonInfo[n].defaultHeight, RGB(255, 0, 255));
|
|
|
|
// Now blend it back across the main DC
|
|
BLENDFUNCTION bf;
|
|
bf.AlphaFormat = 0;
|
|
bf.BlendFlags = 0;
|
|
bf.BlendOp = 0;
|
|
bf.SourceConstantAlpha = (BYTE)alpha;
|
|
AlphaBlend(dc, ptDraw.x, ptDraw.y, imgWidth, imgHeight, imageListDC, ptDraw.x, ptDraw.y, imgWidth, imgHeight, bf);
|
|
|
|
itemDC.SelectObject(pOldItemBitmap);
|
|
|
|
ptDraw.x += offset + imgWidth;
|
|
}
|
|
|
|
pDC->BitBlt(0, 0, width, height, &dc, 0, 0, SRCCOPY);
|
|
|
|
imageListDC.SelectObject(pOldImageList);
|
|
|
|
dc.SelectObject(pOld);
|
|
|
|
delete [] hypers;
|
|
|
|
// Once the paint has occurred, set the flag to say if we drew over the main toolbar, or the floating window.
|
|
((COXHyperBar*)m_pToolbar)->m_bHasRedrawn = (pMouse == NULL);
|
|
}
|
|
|
|
// This method is called whenever the mouse moves, it works out which item is under the mouse, and stores it's ID for use if
|
|
// the click occurs, in which case it's used to send the command to the main window.
|
|
void COXHyperBarRender::SetActiveItem(POINT pt)
|
|
{
|
|
CImageList * const pImages = m_pToolbar->GetToolBarCtrl().GetImageList();
|
|
|
|
RECT rect;
|
|
m_pToolbar->GetClientRect(&rect);
|
|
RECT * pRect = ▭
|
|
|
|
const int width = pRect->right - pRect->left;
|
|
const int height = pRect->bottom - pRect->top;
|
|
const int imageCount = pImages->GetImageCount();
|
|
|
|
const int buttonWidth = (int)floor(width/(double)imageCount);
|
|
const int buttonCount = m_pToolbar->GetToolBarCtrl().GetButtonCount();
|
|
|
|
// It's reset every time this method is called.
|
|
m_nCmd = -1;
|
|
|
|
for(int j=0;j<buttonCount;++j)
|
|
{
|
|
TBBUTTON btn;
|
|
|
|
m_pToolbar->GetToolBarCtrl().GetButton(j, &btn);
|
|
|
|
IMAGEINFO iif;
|
|
pImages->GetImageInfo(btn.iBitmap, &iif);
|
|
const int imgWidth = iif.rcImage.right - iif.rcImage.left;
|
|
const int imgHeight = iif.rcImage.bottom - iif.rcImage.top;
|
|
|
|
POINT ptDraw;
|
|
|
|
ptDraw.x = buttonWidth * btn.iBitmap + (buttonWidth - imgWidth)/2;
|
|
ptDraw.y = (height - imgHeight)/2;
|
|
|
|
CRect rcDraw(ptDraw.x, ptDraw.y, imgWidth + ptDraw.x, ptDraw.y + imgHeight + 20);
|
|
|
|
// We've found a match.
|
|
if (PtInRect(&rcDraw, pt))
|
|
{
|
|
m_nCmd = btn.idCommand;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|