More-or-less finished reasonably cool wxToolBar class with tooltips.
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@899 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -14,11 +14,14 @@
|
||||
#endif
|
||||
|
||||
#include "wx/wx.h"
|
||||
#include "wx/app.h"
|
||||
#include "wx/timer.h"
|
||||
#include "wx/motif/toolbar.h"
|
||||
|
||||
#include <Xm/Xm.h>
|
||||
#include <Xm/PushBG.h>
|
||||
#include <Xm/PushB.h>
|
||||
#include <Xm/Label.h>
|
||||
#include <Xm/ToggleB.h>
|
||||
#include <Xm/ToggleBG.h>
|
||||
#include <Xm/Form.h>
|
||||
@@ -32,6 +35,30 @@ BEGIN_EVENT_TABLE(wxToolBar, wxToolBarBase)
|
||||
END_EVENT_TABLE()
|
||||
#endif
|
||||
|
||||
static void wxToolButtonCallback (Widget w, XtPointer clientData,
|
||||
XtPointer ptr);
|
||||
static void wxToolButtonPopupCallback (Widget w, XtPointer client_data,
|
||||
XEvent *event, Boolean *continue_to_dispatch);
|
||||
|
||||
wxBitmap wxCreateMaskedBitmap(wxBitmap& bitmap, wxColour& colour);
|
||||
|
||||
class wxToolBarTimer: public wxTimer
|
||||
{
|
||||
public:
|
||||
wxToolBarTimer() { }
|
||||
virtual void Notify();
|
||||
|
||||
static Widget help_popup;
|
||||
static Widget buttonWidget;
|
||||
static wxString helpString;
|
||||
};
|
||||
|
||||
static wxToolBarTimer* wxTheToolBarTimer = (wxToolBarTimer*) NULL;
|
||||
|
||||
Widget wxToolBarTimer::help_popup = (Widget) 0;
|
||||
Widget wxToolBarTimer::buttonWidget = (Widget) 0;
|
||||
wxString wxToolBarTimer::helpString = "";
|
||||
|
||||
wxToolBar::wxToolBar():
|
||||
m_widgets(wxKEY_INTEGER)
|
||||
{
|
||||
@@ -39,7 +66,6 @@ wxToolBar::wxToolBar():
|
||||
m_maxHeight = -1;
|
||||
m_defaultWidth = 24;
|
||||
m_defaultHeight = 22;
|
||||
// TODO
|
||||
}
|
||||
|
||||
bool wxToolBar::Create(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size,
|
||||
@@ -66,6 +92,10 @@ bool wxToolBar::Create(wxWindow *parent, wxWindowID id, const wxPoint& pos, cons
|
||||
XmNtraversalOn, False,
|
||||
XmNhorizontalSpacing, 0,
|
||||
XmNverticalSpacing, 0,
|
||||
XmNleftOffset, 0,
|
||||
XmNrightOffset, 0,
|
||||
XmNmarginWidth, 0,
|
||||
XmNmarginHeight, 0,
|
||||
NULL);
|
||||
|
||||
m_mainWidget = (WXWidget) toolbar;
|
||||
@@ -81,7 +111,10 @@ bool wxToolBar::Create(wxWindow *parent, wxWindowID id, const wxPoint& pos, cons
|
||||
|
||||
wxToolBar::~wxToolBar()
|
||||
{
|
||||
// TODO
|
||||
delete wxTheToolBarTimer;
|
||||
wxTheToolBarTimer = NULL;
|
||||
ClearTools();
|
||||
DestroyPixmaps();
|
||||
}
|
||||
|
||||
bool wxToolBar::CreateTools()
|
||||
@@ -89,13 +122,21 @@ bool wxToolBar::CreateTools()
|
||||
if (m_tools.Number() == 0)
|
||||
return FALSE;
|
||||
|
||||
// Separator spacing
|
||||
const int separatorSize = GetToolSeparation(); // 8;
|
||||
|
||||
int currentSpacing = 0;
|
||||
|
||||
m_widgets.Clear();
|
||||
Widget prevButton = (Widget) 0;
|
||||
wxNode* node = m_tools.First();
|
||||
while (node)
|
||||
{
|
||||
wxToolBarTool *tool = (wxToolBarTool *)node->Data();
|
||||
if ((tool->m_toolStyle != wxTOOL_STYLE_SEPARATOR) && tool->m_bitmap1.Ok())
|
||||
|
||||
if (tool->m_toolStyle == wxTOOL_STYLE_SEPARATOR)
|
||||
currentSpacing = separatorSize;
|
||||
else if (tool->m_bitmap1.Ok())
|
||||
{
|
||||
Widget button = (Widget) 0;
|
||||
|
||||
@@ -105,12 +146,14 @@ bool wxToolBar::CreateTools()
|
||||
xmToggleButtonWidgetClass, (Widget) m_mainWidget,
|
||||
XmNleftAttachment, (prevButton == (Widget) 0) ? XmATTACH_FORM : XmATTACH_WIDGET,
|
||||
XmNleftWidget, (prevButton == (Widget) 0) ? NULL : prevButton,
|
||||
XmNleftOffset, 0,
|
||||
XmNleftOffset, currentSpacing,
|
||||
XmNtopAttachment, XmATTACH_FORM,
|
||||
// XmNpushButtonEnabled, True,
|
||||
XmNmultiClick, XmMULTICLICK_KEEP,
|
||||
XmNlabelType, XmPIXMAP,
|
||||
NULL);
|
||||
XtAddCallback ((Widget) button, XmNvalueChangedCallback, (XtCallbackProc) wxToolButtonCallback,
|
||||
(XtPointer) this);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -118,23 +161,25 @@ bool wxToolBar::CreateTools()
|
||||
xmPushButtonWidgetClass, (Widget) m_mainWidget,
|
||||
XmNleftAttachment, (prevButton == (Widget) 0) ? XmATTACH_FORM : XmATTACH_WIDGET,
|
||||
XmNleftWidget, (prevButton == (Widget) 0) ? NULL : prevButton,
|
||||
XmNleftOffset, 0,
|
||||
XmNleftOffset, currentSpacing,
|
||||
XmNtopAttachment, XmATTACH_FORM,
|
||||
XmNpushButtonEnabled, True,
|
||||
XmNmultiClick, XmMULTICLICK_KEEP,
|
||||
XmNlabelType, XmPIXMAP,
|
||||
NULL);
|
||||
}
|
||||
XtAddCallback (button,
|
||||
XmNactivateCallback, (XtCallbackProc) wxToolButtonCallback,
|
||||
(XtPointer) this);
|
||||
}
|
||||
|
||||
// For each button, if there is a mask, we must create
|
||||
// a new wxBitmap that has the correct background colour
|
||||
// for the button. Otherwise the background will just be
|
||||
// e.g. black if a transparent XPM has been loaded.
|
||||
wxBitmap originalBitmap = tool->m_bitmap1;
|
||||
|
||||
if (tool->m_bitmap1.GetMask())
|
||||
{
|
||||
wxBitmap newBitmap(tool->m_bitmap1.GetWidth(),
|
||||
tool->m_bitmap1.GetHeight(),
|
||||
tool->m_bitmap1.GetDepth());
|
||||
int backgroundPixel;
|
||||
XtVaGetValues(button, XmNbackground, &backgroundPixel,
|
||||
NULL);
|
||||
@@ -143,50 +188,48 @@ bool wxToolBar::CreateTools()
|
||||
wxColour col;
|
||||
col.SetPixel(backgroundPixel);
|
||||
|
||||
wxMemoryDC destDC;
|
||||
wxMemoryDC srcDC;
|
||||
srcDC.SelectObject(tool->m_bitmap1);
|
||||
destDC.SelectObject(newBitmap);
|
||||
|
||||
wxBrush brush(col, wxSOLID);
|
||||
destDC.SetOptimization(FALSE);
|
||||
destDC.SetBackground(brush);
|
||||
destDC.Clear();
|
||||
destDC.Blit(0, 0, tool->m_bitmap1.GetWidth(), tool->m_bitmap1.GetHeight(), & srcDC, 0, 0, wxCOPY, TRUE);
|
||||
wxBitmap newBitmap = wxCreateMaskedBitmap(tool->m_bitmap1, col);
|
||||
|
||||
tool->m_bitmap1 = newBitmap;
|
||||
}
|
||||
|
||||
// Create a selected/toggled bitmap. If there isn't a m_bitmap2,
|
||||
// we need to create it (with a darker, selected background)
|
||||
int backgroundPixel;
|
||||
if (tool->m_isToggle)
|
||||
XtVaGetValues(button, XmNselectColor, &backgroundPixel,
|
||||
NULL);
|
||||
else
|
||||
XtVaGetValues(button, XmNarmColor, &backgroundPixel,
|
||||
NULL);
|
||||
|
||||
wxColour col;
|
||||
col.SetPixel(backgroundPixel);
|
||||
|
||||
if (tool->m_bitmap2.Ok() && tool->m_bitmap2.GetMask())
|
||||
{
|
||||
wxBitmap newBitmap(tool->m_bitmap2.GetWidth(),
|
||||
tool->m_bitmap2.GetHeight(),
|
||||
tool->m_bitmap2.GetDepth());
|
||||
int backgroundPixel;
|
||||
XtVaGetValues(button, XmNbackground, &backgroundPixel,
|
||||
NULL);
|
||||
|
||||
|
||||
wxColour col;
|
||||
col.SetPixel(backgroundPixel);
|
||||
|
||||
wxMemoryDC destDC;
|
||||
wxMemoryDC srcDC;
|
||||
srcDC.SelectObject(tool->m_bitmap2);
|
||||
destDC.SelectObject(newBitmap);
|
||||
|
||||
wxBrush brush(col, wxSOLID);
|
||||
destDC.SetOptimization(FALSE);
|
||||
destDC.SetBackground(brush);
|
||||
destDC.Clear();
|
||||
destDC.Blit(0, 0, tool->m_bitmap2.GetWidth(), tool->m_bitmap2.GetHeight(), & srcDC, 0, 0, wxCOPY, TRUE);
|
||||
|
||||
// Use what's there
|
||||
wxBitmap newBitmap = wxCreateMaskedBitmap(tool->m_bitmap2, col);
|
||||
tool->m_bitmap2 = newBitmap;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use unselected bitmap
|
||||
if (originalBitmap.GetMask())
|
||||
{
|
||||
wxBitmap newBitmap = wxCreateMaskedBitmap(originalBitmap, col);
|
||||
tool->m_bitmap2 = newBitmap;
|
||||
}
|
||||
else
|
||||
tool->m_bitmap2 = tool->m_bitmap1;
|
||||
}
|
||||
|
||||
Pixmap pixmap = (Pixmap) tool->m_bitmap1.GetPixmap();
|
||||
Pixmap insensPixmap = (Pixmap) tool->m_bitmap1.GetInsensPixmap();
|
||||
|
||||
if (tool->m_isToggle)
|
||||
{
|
||||
// Toggle button
|
||||
Pixmap pixmap2 = (Pixmap) 0;
|
||||
Pixmap insensPixmap2 = (Pixmap) 0;
|
||||
|
||||
@@ -200,34 +243,53 @@ bool wxToolBar::CreateTools()
|
||||
else
|
||||
{
|
||||
pixmap2 = (Pixmap) tool->m_bitmap1.GetArmPixmap(button);
|
||||
// This has to be both toggled and insensitive, but
|
||||
// wxBitmap doesn't yet have a member to store & destroy
|
||||
// it, so make it the same as pixmap2. Actually it's not
|
||||
// used!
|
||||
insensPixmap2 = pixmap2;
|
||||
insensPixmap2 = XCreateInsensitivePixmap((Display*) wxGetDisplay(), pixmap2);
|
||||
m_pixmaps.Append((wxObject*) insensPixmap2); // Store for later deletion
|
||||
}
|
||||
XtVaSetValues (button,
|
||||
XmNlabelPixmap, pixmap,
|
||||
XmNselectPixmap, pixmap,
|
||||
XmNlabelInsensitivePixmap, insensPixmap,
|
||||
XmNselectInsensitivePixmap, insensPixmap,
|
||||
XmNarmPixmap, pixmap2,
|
||||
XmNlabelType, XmPIXMAP,
|
||||
NULL);
|
||||
|
||||
}
|
||||
XmNindicatorOn, False,
|
||||
XmNshadowThickness, 2,
|
||||
// XmNborderWidth, 0,
|
||||
// XmNspacing, 0,
|
||||
XmNmarginWidth, 0,
|
||||
XmNmarginHeight, 0,
|
||||
XmNfillOnSelect, True,
|
||||
XmNlabelPixmap, pixmap,
|
||||
XmNselectPixmap, pixmap2,
|
||||
XmNlabelInsensitivePixmap, insensPixmap,
|
||||
XmNselectInsensitivePixmap, insensPixmap2,
|
||||
XmNlabelType, XmPIXMAP,
|
||||
NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
Pixmap pixmap2 = (Pixmap) 0;
|
||||
|
||||
// If there's a bitmap for the armed state, use it,
|
||||
// otherwise generate one.
|
||||
if (tool->m_bitmap2.Ok())
|
||||
{
|
||||
pixmap2 = (Pixmap) tool->m_bitmap2.GetPixmap();
|
||||
}
|
||||
else
|
||||
{
|
||||
pixmap2 = (Pixmap) tool->m_bitmap1.GetArmPixmap(button);
|
||||
|
||||
}
|
||||
// Normal button
|
||||
XtVaSetValues(button,
|
||||
XmNlabelPixmap, pixmap,
|
||||
XmNlabelInsensitivePixmap, insensPixmap,
|
||||
NULL);
|
||||
XmNlabelInsensitivePixmap, insensPixmap,
|
||||
XmNarmPixmap, pixmap2,
|
||||
NULL);
|
||||
}
|
||||
|
||||
XtAddEventHandler (button, EnterWindowMask | LeaveWindowMask,
|
||||
False, wxToolButtonPopupCallback, (XtPointer) this);
|
||||
m_widgets.Append(tool->m_index, (wxObject*) button);
|
||||
|
||||
prevButton = button;
|
||||
|
||||
currentSpacing = 0;
|
||||
}
|
||||
node = node->Next();
|
||||
}
|
||||
@@ -261,7 +323,12 @@ void wxToolBar::EnableTool(int toolIndex, bool enable)
|
||||
{
|
||||
wxToolBarTool *tool = (wxToolBarTool *)node->Data();
|
||||
tool->m_enabled = enable;
|
||||
// TODO enable button
|
||||
|
||||
WXWidget widget = FindWidgetForIndex(tool->m_index);
|
||||
if (widget == (WXWidget) 0)
|
||||
return;
|
||||
|
||||
XtSetSensitive((Widget) widget, (Boolean) enable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,7 +341,12 @@ void wxToolBar::ToggleTool(int toolIndex, bool toggle)
|
||||
if (tool->m_isToggle)
|
||||
{
|
||||
tool->m_toggleState = toggle;
|
||||
// TODO: set toggle state
|
||||
|
||||
WXWidget widget = FindWidgetForIndex(tool->m_index);
|
||||
if (widget == (WXWidget) 0)
|
||||
return;
|
||||
|
||||
XmToggleButtonSetState((Widget) widget, (Boolean) toggle, False);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -289,10 +361,23 @@ void wxToolBar::ClearTools()
|
||||
node = node->Next();
|
||||
}
|
||||
m_widgets.Clear();
|
||||
DestroyPixmaps();
|
||||
|
||||
wxToolBarBase::ClearTools();
|
||||
}
|
||||
|
||||
void wxToolBar::DestroyPixmaps()
|
||||
{
|
||||
wxNode* node = m_pixmaps.First();
|
||||
while (node)
|
||||
{
|
||||
Pixmap pixmap = (Pixmap) node->Data();
|
||||
XmDestroyPixmap (DefaultScreenOfDisplay ((Display*) GetXDisplay()), pixmap);
|
||||
node = node->Next();
|
||||
}
|
||||
m_pixmaps.Clear();
|
||||
}
|
||||
|
||||
// If pushedBitmap is NULL, a reversed version of bitmap is
|
||||
// created and used as the pushed/toggled image.
|
||||
// If toggle is TRUE, the button toggles between the two states.
|
||||
@@ -319,3 +404,180 @@ wxToolBarTool *wxToolBar::AddTool(int index, const wxBitmap& bitmap, const wxBit
|
||||
return tool;
|
||||
}
|
||||
|
||||
int wxToolBar::FindIndexForWidget(WXWidget w)
|
||||
{
|
||||
wxNode* node = m_widgets.First();
|
||||
while (node)
|
||||
{
|
||||
WXWidget widget = (WXWidget) node->Data();
|
||||
if (widget == w)
|
||||
return (int) node->key.integer;
|
||||
node = node->Next();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
WXWidget wxToolBar::FindWidgetForIndex(int index)
|
||||
{
|
||||
wxNode* node = m_widgets.Find((long) index);
|
||||
if (!node)
|
||||
return (WXWidget) 0;
|
||||
else
|
||||
return (WXWidget) node->Data();
|
||||
}
|
||||
|
||||
void wxToolButtonCallback (Widget w, XtPointer clientData,
|
||||
XtPointer ptr)
|
||||
{
|
||||
wxToolBar *toolBar = (wxToolBar *) clientData;
|
||||
int index = toolBar->FindIndexForWidget((WXWidget) w);
|
||||
|
||||
if (index != -1)
|
||||
{
|
||||
wxNode *node = toolBar->GetTools().Find((long)index);
|
||||
if (!node)
|
||||
return;
|
||||
wxToolBarTool *tool = (wxToolBarTool *)node->Data();
|
||||
if (tool->m_isToggle)
|
||||
tool->m_toggleState = toolBar->GetToolState(index);
|
||||
|
||||
(void) toolBar->OnLeftClick(index, tool->m_toggleState);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Creates a bitmap with transparent areas drawn in
|
||||
// the given colour.
|
||||
wxBitmap wxCreateMaskedBitmap(wxBitmap& bitmap, wxColour& colour)
|
||||
{
|
||||
wxBitmap newBitmap(bitmap.GetWidth(),
|
||||
bitmap.GetHeight(),
|
||||
bitmap.GetDepth());
|
||||
wxMemoryDC destDC;
|
||||
wxMemoryDC srcDC;
|
||||
srcDC.SelectObject(bitmap);
|
||||
destDC.SelectObject(newBitmap);
|
||||
|
||||
wxBrush brush(colour, wxSOLID);
|
||||
destDC.SetOptimization(FALSE);
|
||||
destDC.SetBackground(brush);
|
||||
destDC.Clear();
|
||||
destDC.Blit(0, 0, bitmap.GetWidth(), bitmap.GetHeight(), & srcDC, 0, 0, wxCOPY, TRUE);
|
||||
|
||||
return newBitmap;
|
||||
}
|
||||
|
||||
|
||||
static void wxToolButtonPopupCallback (Widget w, XtPointer client_data,
|
||||
XEvent *event, Boolean *continue_to_dispatch)
|
||||
{
|
||||
// TODO: retrieve delay before popping up tooltip from wxSystemSettings.
|
||||
int delayMilli = 800;
|
||||
wxToolBar* toolBar = (wxToolBar*) client_data;
|
||||
|
||||
int index = toolBar->FindIndexForWidget((WXWidget) w);
|
||||
|
||||
if (index != -1)
|
||||
{
|
||||
wxNode *node = toolBar->GetTools().Find((long)index);
|
||||
if (!node)
|
||||
return;
|
||||
wxToolBarTool *tool = (wxToolBarTool *)node->Data();
|
||||
wxString str(toolBar->GetToolShortHelp(index));
|
||||
if (str.IsNull() || str == "")
|
||||
return;
|
||||
|
||||
if (!wxTheToolBarTimer)
|
||||
wxTheToolBarTimer = new wxToolBarTimer;
|
||||
|
||||
wxToolBarTimer::buttonWidget = w;
|
||||
wxToolBarTimer::helpString = str;
|
||||
|
||||
|
||||
/************************************************************/
|
||||
/* Popup help label */
|
||||
/************************************************************/
|
||||
if (event->type == EnterNotify)
|
||||
{
|
||||
if (wxToolBarTimer::help_popup != (Widget) 0)
|
||||
{
|
||||
XtDestroyWidget (wxToolBarTimer::help_popup);
|
||||
XtPopdown (wxToolBarTimer::help_popup);
|
||||
}
|
||||
wxToolBarTimer::help_popup = (Widget) 0;
|
||||
|
||||
// One shot
|
||||
wxTheToolBarTimer->Start(delayMilli, TRUE);
|
||||
|
||||
}
|
||||
/************************************************************/
|
||||
/* Popdown help label */
|
||||
/************************************************************/
|
||||
else if (event->type == LeaveNotify)
|
||||
{
|
||||
if (wxTheToolBarTimer)
|
||||
wxTheToolBarTimer->Stop();
|
||||
if (wxToolBarTimer::help_popup != (Widget) 0)
|
||||
{
|
||||
XtDestroyWidget (wxToolBarTimer::help_popup);
|
||||
XtPopdown (wxToolBarTimer::help_popup);
|
||||
}
|
||||
wxToolBarTimer::help_popup = (Widget) 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wxToolBarTimer::Notify()
|
||||
{
|
||||
Position x, y;
|
||||
|
||||
/************************************************************/
|
||||
/* Create shell without window decorations */
|
||||
/************************************************************/
|
||||
help_popup = XtVaCreatePopupShell ("shell",
|
||||
overrideShellWidgetClass, (Widget) wxTheApp->GetTopLevelWidget(),
|
||||
NULL);
|
||||
|
||||
/************************************************************/
|
||||
/* Get absolute position on display of toolbar button */
|
||||
/************************************************************/
|
||||
XtTranslateCoords (buttonWidget,
|
||||
(Position) 0,
|
||||
(Position) 0,
|
||||
&x, &y);
|
||||
|
||||
// Move the tooltip more or less above the button
|
||||
int yOffset = 20; // TODO: What should be really?
|
||||
y -= yOffset;
|
||||
if (y < yOffset) y = 0;
|
||||
|
||||
/************************************************************/
|
||||
/* Set the position of the help popup */
|
||||
/************************************************************/
|
||||
XtVaSetValues (help_popup,
|
||||
XmNx, (Position) x,
|
||||
XmNy, (Position) y,
|
||||
NULL);
|
||||
|
||||
/************************************************************/
|
||||
/* Create help label */
|
||||
/************************************************************/
|
||||
XmString text = XmStringCreateSimple ((char*) (const char*) helpString);
|
||||
XtVaCreateManagedWidget ("help_label",
|
||||
xmLabelWidgetClass, help_popup,
|
||||
XmNlabelString, text,
|
||||
XtVaTypedArg,
|
||||
XmNforeground, XtRString, "black",
|
||||
strlen("black")+1,
|
||||
XtVaTypedArg,
|
||||
XmNbackground, XtRString, "LightGoldenrod",
|
||||
strlen("LightGoldenrod")+1,
|
||||
NULL);
|
||||
XmStringFree (text);
|
||||
|
||||
/************************************************************/
|
||||
/* Popup help label */
|
||||
/************************************************************/
|
||||
XtPopup (help_popup, XtGrabNone);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user