audacia/src/widgets/Grabber.cpp

315 lines
6.9 KiB
C++

/**********************************************************************
Audacity: A Digital Audio Editor
Grabber.cpp
Leland Lucius
*******************************************************************//**
\file Grabber.cpp
Implements Grabber
*//*******************************************************************//**
\class Grabber
\brief The widget to the left of a ToolBar that allows it to be dragged
around to NEW positions.
*//**********************************************************************/
#include "Grabber.h"
#include <wx/defs.h>
#include <wx/dcclient.h>
#include <wx/event.h>
#include <wx/intl.h>
#include <wx/window.h>
#include "../AColor.h"
#include "../AllThemeResources.h"
#include "Internat.h"
#include "../Theme.h"
////////////////////////////////////////////////////////////
/// Methods for Grabber
////////////////////////////////////////////////////////////
DEFINE_EVENT_TYPE(EVT_GRABBER_CLICKED)
BEGIN_EVENT_TABLE(Grabber, wxWindow)
EVT_ENTER_WINDOW(Grabber::OnEnter)
EVT_LEAVE_WINDOW(Grabber::OnLeave)
EVT_LEFT_DOWN(Grabber::OnLeftDown)
EVT_LEFT_UP(Grabber::OnLeftUp)
EVT_ERASE_BACKGROUND( Grabber::OnErase )
EVT_PAINT(Grabber::OnPaint)
EVT_KEY_DOWN(Grabber::OnKeyDown)
END_EVENT_TABLE()
//
// Constructor
//
Grabber::Grabber(wxWindow * parent, wxWindowID id)
: wxWindow(parent,
id,
wxDefaultPosition,
wxSize(grabberWidth, 27),
wxFULL_REPAINT_ON_RESIZE)
{
mOver = false;
mPressed = false;
mAsSpacer = false;
SetBackgroundColour( theTheme.Colour( clrMedium ) );
/* i18n-hint: A 'Grabber' is a region you can click and drag on
It's used to drag a track around (when in multi-tool mode) rather
than requiring that you use the drag tool. It's shown as a series
of horizontal bumps */
SetLabel(_("Grabber"));
SetName(_("Grabber"));
}
//
// Destructor
//
Grabber::~Grabber()
{
}
//
// Queue a drag event
//
void Grabber::SendEvent(wxEventType type, const wxPoint & pos, bool escaping)
{
wxWindow *parent = GetParent();
// Initialize event and convert mouse coordinates to screen space
GrabberEvent e(type, GetId(), parent->ClientToScreen(pos), escaping);
// Set the object of our desire
e.SetEventObject(parent);
// Queue the event
parent->GetEventHandler()->AddPendingEvent(e);
}
void Grabber::SetAsSpacer( bool bIsSpacer ) {
if( mAsSpacer != bIsSpacer ){
// HACK: Use a wider rectangle to also cover one pixel of space just to the right.
wxSize siz = GetSize();
siz.IncBy( bIsSpacer ? 1:-1, 0 );
SetSize( siz );
}
mAsSpacer = bIsSpacer;
};
void Grabber::SetToolTip(const TranslatableString &toolTip)
{
wxWindow::SetToolTip( toolTip.Stripped().Translation() );
}
//
// Draw the grabber
//
void Grabber::DrawGrabber( wxDC & dc )
{
wxRect r = GetRect();
// PaintDC positions are relative to the grabber, not the parent window.
// So use 0,0 as origin for draw, so that the grabber draws right if
// positioned in its parent at some non zero position.
r.SetPosition( wxPoint(0,0) );
int y, left, right, top, bottom;
AColor::Medium(&dc, mOver );
dc.DrawRectangle(r);
// HACK: We used a wider rectangle to also cover one pixel of space just to the right.
if( mAsSpacer )
r.width -= 1;
#ifndef __WXMAC__
// Add a box
r.width -= 1;
r.height -= 1;
AColor::Bevel(dc, !mPressed, r);
r.width += 1;
r.height += 1;
#endif
// No bumps in a spacer grabber.
if( mAsSpacer )
return;
// Calculate the bump rectangle
r.Deflate(3, 3);
if ((r.GetHeight() % 4) < 2) {
r.Offset(0, 1);
}
// Cache
left = r.GetLeft();
right = r.GetRight();
top = r.GetTop();
bottom = r.GetBottom();
// Draw the raised bumps
if (mPressed) {
AColor::Dark(&dc, false);
}
else {
AColor::Light(&dc, false);
}
for (y = top; y < bottom; y += 4) {
AColor::Line(dc, left, y, right, y);
}
// Draw the pushed bumps
if (mPressed) {
AColor::Light(&dc, false);
}
else {
AColor::Dark(&dc, false);
}
for (y = top + 1; y <= bottom; y += 4) {
AColor::Line(dc, left, y, right, y);
}
}
//
// Change the button state
//
void Grabber::PushButton(bool state )
{
if( mAsSpacer )
return;
if (!state)
mPressed = state;
wxRect r = GetRect();
mOver = r.Contains(ScreenToClient(wxGetMousePosition()));
// Redraw button
mPressed = state;
Refresh(false);
}
//
// Handle left button down events
//
void Grabber::OnLeftDown(wxMouseEvent & event)
{
// Button should be drawn pushed
PushButton(true);
// Notify parent
SendEvent(EVT_GRABBER_CLICKED, event.GetPosition(), false);
event.Skip();
}
//
// Handle left button up events
//
void Grabber::OnLeftUp(wxMouseEvent & event)
{
// Normally, "left up" events are handled by the ToolManager::OnMouse() method
// but, if the user double clicks a grabber, the "left up" event will come here
// instead, so just "unpush" the button.
PushButton(false);
event.Skip();
}
//
// Handle mouse enter events
//
void Grabber::OnEnter(wxMouseEvent & WXUNUSED(event))
{
#if defined(__WXMAC__)
// Bug 2416: On Mac, we can get Enter events from grabbers other
// than the one being dragged. So, ignore Enter events if another
// window has captured the mouse.
if (wxWindow::GetCapture() != nullptr)
{
return;
}
#endif
// Bug 1201: On Mac, unsetting and re-setting the tooltip may be needed
// to make it pop up when we want it.
const auto text = GetToolTipText();
UnsetToolTip();
wxWindow::SetToolTip(text);
if( mAsSpacer )
return;
// Redraw highlighted
mOver = true;
Refresh(false);
}
//
// Handle mouse leave events
//
void Grabber::OnLeave(wxMouseEvent & WXUNUSED(event))
{
#if defined(__WXMAC__)
// Bug 2416: On Mac, we can get Leave events from grabbers other
// than the one being dragged. So, ignore Leave events if another
// window has captured the mouse.
if (wxWindow::GetCapture() != nullptr)
{
return;
}
#endif
if (!GetCapture()) {
// Redraw plain
mOver = false;
Refresh(false);
}
}
void Grabber::OnErase( wxEraseEvent & WXUNUSED(event) )
{
// Ignore it to prevent flashing
}
//
// Handle the paint events
//
void Grabber::OnPaint(wxPaintEvent & WXUNUSED(event))
{
wxPaintDC dc(this);
// Redraw the grabber
DrawGrabber(dc);
}
void Grabber::OnKeyDown(wxKeyEvent &event)
{
event.Skip();
if(event.GetKeyCode() == WXK_ESCAPE) {
// We must not only skip this key event, but propagate it up the window
// hierarchy, so that ToolFrame detects it too.
event.ResumePropagation(wxEVENT_PROPAGATE_MAX);
SendEvent(EVT_GRABBER_CLICKED, wxPoint{ -1, -1 }, true);
}
}
// Piggy back in same source file as Grabber.
// Audacity Flicker-free StaticBitmap.
BEGIN_EVENT_TABLE(AStaticBitmap,wxStaticBitmap)
EVT_ERASE_BACKGROUND(AStaticBitmap::OnErase)
END_EVENT_TABLE()