Files
wxWidgets/src/motif/toolbar.cpp

793 lines
26 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: src/motif/toolbar.cpp
// Purpose: wxToolBar
// Author: Julian Smart
// Modified by: 13.12.99 by VZ during toolbar classes reorganization
// Created: 04/01/98
// RCS-ID: $Id$
// Copyright: (c) Julian Smart
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// ============================================================================
// declarations
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifdef __VMS
#define XtDisplay XTDISPLAY
#endif
#include "wx/toolbar.h"
#ifndef WX_PRECOMP
#include "wx/app.h"
#include "wx/frame.h"
#endif
#include "wx/settings.h"
#include "wx/timer.h"
#ifdef __VMS__
#pragma message disable nosimpint
#endif
#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>
#ifdef __VMS__
#pragma message enable nosimpint
#endif
#include "wx/motif/private.h"
#include "wx/motif/bmpmotif.h"
// ----------------------------------------------------------------------------
// wxWin macros
// ----------------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS(wxToolBar, wxControl)
// ----------------------------------------------------------------------------
// private functions
// ----------------------------------------------------------------------------
static void wxToolButtonCallback (Widget w, XtPointer clientData,
XtPointer ptr);
static void wxToolButtonPopupCallback (Widget w, XtPointer client_data,
XEvent *event, Boolean *continue_to_dispatch);
// ----------------------------------------------------------------------------
// private classes
// ----------------------------------------------------------------------------
class wxToolBarTimer : public wxTimer
{
public:
virtual void Notify();
static Widget help_popup;
static Widget buttonWidget;
static wxString helpString;
};
class wxToolBarTool : public wxToolBarToolBase
{
public:
wxToolBarTool(wxToolBar *tbar,
int id,
const wxString& label,
const wxBitmap& bmpNormal,
const wxBitmap& bmpToggled,
wxItemKind kind,
wxObject *clientData,
const wxString& shortHelp,
const wxString& longHelp)
: wxToolBarToolBase(tbar, id, label, bmpNormal, bmpToggled, kind,
clientData, shortHelp, longHelp)
{
Init();
}
wxToolBarTool(wxToolBar *tbar, wxControl *control)
: wxToolBarToolBase(tbar, control)
{
Init();
}
virtual ~wxToolBarTool();
// accessors
void SetWidget(Widget widget) { m_widget = widget; }
Widget GetButtonWidget() const { return m_widget; }
Pixmap GetArmPixmap()
{
m_bitmapCache.SetBitmap( GetNormalBitmap() );
return (Pixmap)m_bitmapCache.GetArmPixmap( (WXWidget)m_widget );
}
Pixmap GetInsensPixmap()
{
m_bitmapCache.SetBitmap( GetNormalBitmap() );
return (Pixmap)m_bitmapCache.GetInsensPixmap( (WXWidget)m_widget );
}
protected:
void Init();
Widget m_widget;
wxBitmapCache m_bitmapCache;
};
// ----------------------------------------------------------------------------
// globals
// ----------------------------------------------------------------------------
static wxToolBarTimer* wxTheToolBarTimer = (wxToolBarTimer*) NULL;
Widget wxToolBarTimer::help_popup = (Widget) 0;
Widget wxToolBarTimer::buttonWidget = (Widget) 0;
wxString wxToolBarTimer::helpString;
// ============================================================================
// implementation
// ============================================================================
// ----------------------------------------------------------------------------
// wxToolBarTool
// ----------------------------------------------------------------------------
wxToolBarToolBase *wxToolBar::CreateTool(int id,
const wxString& label,
const wxBitmap& bmpNormal,
const wxBitmap& bmpToggled,
wxItemKind kind,
wxObject *clientData,
const wxString& shortHelp,
const wxString& longHelp)
{
return new wxToolBarTool(this, id, label, bmpNormal, bmpToggled, kind,
clientData, shortHelp, longHelp);
}
wxToolBarToolBase *wxToolBar::CreateTool(wxControl *control)
{
return new wxToolBarTool(this, control);
}
void wxToolBarTool::Init()
{
m_widget = (Widget)0;
}
wxToolBarTool::~wxToolBarTool()
{
if ( m_widget )
XtDestroyWidget(m_widget);
}
// ----------------------------------------------------------------------------
// wxToolBar construction
// ----------------------------------------------------------------------------
void wxToolBar::Init()
{
m_maxWidth = -1;
m_maxHeight = -1;
m_defaultWidth = 24;
m_defaultHeight = 22;
m_toolPacking = 2;
m_toolSeparation = 8;
m_xMargin = 2;
m_yMargin = 2;
m_maxRows = 100;
m_maxCols = 100;
}
bool wxToolBar::Create(wxWindow *parent,
wxWindowID id,
const wxPoint& pos,
const wxSize& size,
long style,
const wxString& name)
{
if( !wxControl::CreateControl( parent, id, pos, size, style,
wxDefaultValidator, name ) )
return false;
m_backgroundColour = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
Widget parentWidget = (Widget) parent->GetClientWidget();
Widget toolbar = XtVaCreateManagedWidget("toolbar",
xmBulletinBoardWidgetClass, (Widget) parentWidget,
XmNmarginWidth, 0,
XmNmarginHeight, 0,
XmNresizePolicy, XmRESIZE_NONE,
NULL);
/*
Widget toolbar = XtVaCreateManagedWidget("toolbar",
xmFormWidgetClass, (Widget) m_clientWidget,
XmNtraversalOn, False,
XmNhorizontalSpacing, 0,
XmNverticalSpacing, 0,
XmNleftOffset, 0,
XmNrightOffset, 0,
XmNmarginWidth, 0,
XmNmarginHeight, 0,
NULL);
*/
m_mainWidget = (WXWidget) toolbar;
ChangeFont(false);
wxPoint rPos = pos;
wxSize rSize = size;
if( rPos.x == -1 ) rPos.x = 0;
if( rPos.y == -1 ) rPos.y = 0;
if( rSize.x == -1 && GetParent() )
rSize.x = GetParent()->GetSize().x;
AttachWidget (parent, m_mainWidget, (WXWidget) NULL,
rPos.x, rPos.y, rSize.x, rSize.y);
ChangeBackgroundColour();
return true;
}
wxToolBar::~wxToolBar()
{
delete wxTheToolBarTimer;
wxTheToolBarTimer = NULL;
}
bool wxToolBar::Realize()
{
if ( m_tools.GetCount() == 0 )
{
// nothing to do
return true;
}
bool isVertical = GetWindowStyle() & wxTB_VERTICAL;
// Separator spacing
const int separatorSize = GetToolSeparation(); // 8;
wxSize margins = GetToolMargins();
int packing = GetToolPacking();
int marginX = margins.x;
int marginY = margins.y;
int currentX = marginX;
int currentY = marginY;
int buttonHeight = 0, buttonWidth = 0;
Widget button;
Pixmap pixmap, insensPixmap;
wxBitmap bmp, insensBmp;
wxToolBarToolsList::compatibility_iterator node = m_tools.GetFirst();
while ( node )
{
wxToolBarTool *tool = (wxToolBarTool *)node->GetData();
switch ( tool->GetStyle() )
{
case wxTOOL_STYLE_CONTROL:
{
wxControl* control = tool->GetControl();
wxSize sz = control->GetSize();
wxPoint pos = control->GetPosition();
// Allow a control to specify a y[x]-offset by setting
// its initial position, but still don't allow it to
// position itself above the top[left] margin.
int controlY = (pos.y > 0) ? pos.y : currentY;
int controlX = (pos.x > 0) ? pos.x : currentX;
control->Move( isVertical ? controlX : currentX,
isVertical ? currentY : controlY );
if ( isVertical )
currentY += sz.y + packing;
else
currentX += sz.x + packing;
break;
}
case wxTOOL_STYLE_SEPARATOR:
// skip separators for vertical toolbars
if( !isVertical )
{
currentX += separatorSize;
}
break;
case wxTOOL_STYLE_BUTTON:
button = (Widget) 0;
if ( tool->CanBeToggled() && !tool->GetButtonWidget() )
{
button = XtVaCreateWidget("toggleButton",
xmToggleButtonWidgetClass, (Widget) m_mainWidget,
XmNx, currentX, XmNy, currentY,
XmNindicatorOn, False,
XmNshadowThickness, 2,
XmNborderWidth, 0,
XmNspacing, 0,
XmNmarginWidth, 0,
XmNmarginHeight, 0,
XmNmultiClick, XmMULTICLICK_KEEP,
XmNlabelType, XmPIXMAP,
NULL);
XtAddCallback ((Widget) button,
XmNvalueChangedCallback,
(XtCallbackProc) wxToolButtonCallback,
(XtPointer) this);
XtVaSetValues ((Widget) button,
XmNselectColor,
m_backgroundColour.AllocColour
(XtDisplay((Widget) button)),
NULL);
}
else if( !tool->GetButtonWidget() )
{
button = XtVaCreateWidget("button",
xmPushButtonWidgetClass, (Widget) m_mainWidget,
XmNx, currentX, XmNy, currentY,
XmNpushButtonEnabled, True,
XmNmultiClick, XmMULTICLICK_KEEP,
XmNlabelType, XmPIXMAP,
NULL);
XtAddCallback (button,
XmNactivateCallback,
(XtCallbackProc) wxToolButtonCallback,
(XtPointer) this);
}
if( !tool->GetButtonWidget() )
{
wxDoChangeBackgroundColour((WXWidget) button,
m_backgroundColour, true);
tool->SetWidget(button);
}
else
{
button = (Widget)tool->GetButtonWidget();
XtVaSetValues( button,
XmNx, currentX, XmNy, currentY,
NULL );
}
// 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.
bmp = tool->GetNormalBitmap();
insensBmp = tool->GetDisabledBitmap();
if ( bmp.GetMask() || insensBmp.GetMask() )
{
int backgroundPixel;
XtVaGetValues(button, XmNbackground, &backgroundPixel,
NULL);
wxColour col;
col.SetPixel(backgroundPixel);
if( bmp.Ok() && bmp.GetMask() )
{
bmp = wxCreateMaskedBitmap(bmp, col);
tool->SetNormalBitmap(bmp);
}
if( insensBmp.Ok() && insensBmp.GetMask() )
{
insensBmp = wxCreateMaskedBitmap(insensBmp, col);
tool->SetDisabledBitmap(insensBmp);
}
}
// Create a selected/toggled bitmap. If there isn't a 2nd
// bitmap, we need to create it (with a darker, selected
// background)
int backgroundPixel;
if ( tool->CanBeToggled() )
XtVaGetValues(button, XmNselectColor, &backgroundPixel,
NULL);
else
XtVaGetValues(button, XmNarmColor, &backgroundPixel,
NULL);
wxColour col;
col.SetPixel(backgroundPixel);
pixmap = (Pixmap) bmp.GetDrawable();
{
wxBitmap tmp = tool->GetDisabledBitmap();
insensPixmap = tmp.Ok() ?
(Pixmap)tmp.GetDrawable() :
tool->GetInsensPixmap();
}
if (tool->CanBeToggled())
{
// Toggle button
Pixmap pixmap2 = tool->GetArmPixmap();
Pixmap insensPixmap2 = tool->GetInsensPixmap();
XtVaSetValues (button,
XmNfillOnSelect, True,
XmNlabelPixmap, pixmap,
XmNselectPixmap, pixmap2,
XmNlabelInsensitivePixmap, insensPixmap,
XmNselectInsensitivePixmap, insensPixmap2,
XmNlabelType, XmPIXMAP,
NULL);
}
else
{
Pixmap pixmap2 = tool->GetArmPixmap();
// Normal button
XtVaSetValues(button,
XmNlabelPixmap, pixmap,
XmNlabelInsensitivePixmap, insensPixmap,
XmNarmPixmap, pixmap2,
NULL);
}
XtManageChild(button);
{
Dimension width, height;
XtVaGetValues(button,
XmNwidth, &width,
XmNheight, & height,
NULL);
if ( isVertical )
currentY += height + packing;
else
currentX += width + packing;
buttonHeight = wxMax(buttonHeight, height);
buttonWidth = wxMax(buttonWidth, width);
}
XtAddEventHandler (button, EnterWindowMask | LeaveWindowMask,
False, wxToolButtonPopupCallback, (XtPointer) this);
break;
}
node = node->GetNext();
}
SetSize( -1, -1,
isVertical ? buttonWidth + 2 * marginX : -1,
isVertical ? -1 : buttonHeight + 2*marginY );
return true;
}
wxToolBarToolBase *wxToolBar::FindToolForPosition(wxCoord WXUNUSED(x),
wxCoord WXUNUSED(y)) const
{
wxFAIL_MSG( _T("TODO") );
return (wxToolBarToolBase *)NULL;
}
bool wxToolBar::DoInsertTool(size_t WXUNUSED(pos), wxToolBarToolBase *tool)
{
tool->Attach(this);
return true;
}
bool wxToolBar::DoDeleteTool(size_t WXUNUSED(pos), wxToolBarToolBase *tool)
{
tool->Detach();
bool isVertical = GetWindowStyle() & wxTB_VERTICAL;
const int separatorSize = GetToolSeparation(); // 8;
int packing = GetToolPacking();
int offset = 0;
for( wxToolBarToolsList::compatibility_iterator node = m_tools.GetFirst();
node; node = node->GetNext() )
{
wxToolBarTool *t = (wxToolBarTool*)node->GetData();
if( t == tool )
{
switch ( t->GetStyle() )
{
case wxTOOL_STYLE_CONTROL:
{
wxSize size = t->GetControl()->GetSize();
offset = isVertical ? size.y : size.x;
offset += packing;
break;
}
case wxTOOL_STYLE_SEPARATOR:
offset = isVertical ? 0 : separatorSize;
break;
case wxTOOL_STYLE_BUTTON:
{
Widget w = t->GetButtonWidget();
Dimension width, height;
XtVaGetValues( w,
XmNwidth, &width,
XmNheight, &height,
NULL );
offset = isVertical ? height : width;
offset += packing;
break;
}
}
}
else if( offset )
{
switch ( t->GetStyle() )
{
case wxTOOL_STYLE_CONTROL:
{
wxPoint location = t->GetControl()->GetPosition();
if( isVertical )
location.y -= offset;
else
location.x -= offset;
t->GetControl()->Move( location );
break;
}
case wxTOOL_STYLE_SEPARATOR:
break;
case wxTOOL_STYLE_BUTTON:
{
Dimension x, y;
XtVaGetValues( t->GetButtonWidget(),
XmNx, &x,
XmNy, &y,
NULL );
if( isVertical )
y = (Dimension)(y - offset);
else
x = (Dimension)(x - offset);
XtVaSetValues( t->GetButtonWidget(),
XmNx, x,
XmNy, y,
NULL );
break;
}
}
}
}
return true;
}
void wxToolBar::DoEnableTool(wxToolBarToolBase *toolBase, bool enable)
{
wxToolBarTool *tool = (wxToolBarTool *)toolBase;
if (tool->GetButtonWidget()){
XtSetSensitive(tool->GetButtonWidget(), (Boolean) enable);
} else if (wxTOOL_STYLE_CONTROL == tool->GetStyle()){
// Controls (such as wxChoice) do not have button widgets
tool->GetControl()->Enable(enable);
}
}
void wxToolBar::DoToggleTool(wxToolBarToolBase *toolBase, bool toggle)
{
wxToolBarTool *tool = (wxToolBarTool *)toolBase;
XmToggleButtonSetState(tool->GetButtonWidget(), (Boolean) toggle, False);
}
void wxToolBar::DoSetToggle(wxToolBarToolBase * WXUNUSED(tool),
bool WXUNUSED(toggle))
{
// nothing to do
}
void wxToolBar::DoSetSize(int x, int y, int width, int height, int sizeFlags)
{
int old_width, old_height;
GetSize(&old_width, &old_height);
// Correct width and height if needed.
if ( width == -1 || height == -1 )
{
wxSize defaultSize = GetSize();
if ( width == -1 )
width = defaultSize.x;
if ( height == -1 )
height = defaultSize.y;
}
wxToolBarBase::DoSetSize(x, y, width, height, sizeFlags);
// We must refresh the frame size when the toolbar changes size
// otherwise the toolbar can be shown incorrectly
if ( old_width != width || old_height != height )
{
// But before we send the size event check it
// we have a frame that is not being deleted.
wxFrame *frame = wxDynamicCast(GetParent(), wxFrame);
if ( frame && !frame->IsBeingDeleted() )
{
frame->SendSizeEvent();
}
}
}
// ----------------------------------------------------------------------------
// Motif callbacks
// ----------------------------------------------------------------------------
wxToolBarToolBase *wxToolBar::FindToolByWidget(WXWidget w) const
{
wxToolBarToolsList::compatibility_iterator node = m_tools.GetFirst();
while ( node )
{
wxToolBarTool *tool = (wxToolBarTool *)node->GetData();
if ( tool->GetButtonWidget() == w)
{
return tool;
}
node = node->GetNext();
}
return (wxToolBarToolBase *)NULL;
}
static void wxToolButtonCallback(Widget w,
XtPointer clientData,
XtPointer WXUNUSED(ptr))
{
wxToolBar *toolBar = (wxToolBar *) clientData;
wxToolBarToolBase *tool = toolBar->FindToolByWidget((WXWidget) w);
if ( !tool )
return;
if ( tool->CanBeToggled() )
tool->Toggle();
if ( !toolBar->OnLeftClick(tool->GetId(), tool->IsToggled()) )
{
// revert
tool->Toggle();
}
}
static void wxToolButtonPopupCallback(Widget w,
XtPointer client_data,
XEvent *event,
Boolean *WXUNUSED(continue_to_dispatch))
{
// TODO: retrieve delay before popping up tooltip from wxSystemSettings.
static const int delayMilli = 800;
wxToolBar* toolBar = (wxToolBar*) client_data;
wxToolBarToolBase *tool = toolBar->FindToolByWidget((WXWidget) w);
if ( !tool )
return;
wxString tooltip = tool->GetShortHelp();
if ( !tooltip )
return;
if (!wxTheToolBarTimer)
wxTheToolBarTimer = new wxToolBarTimer;
wxToolBarTimer::buttonWidget = w;
wxToolBarTimer::helpString = tooltip;
/************************************************************/
/* 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 = (Position)(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);
}