// Purpose: FancyRealCtrl (numeric wxTextCtrl + wxSlider + "lock" button)
// Copyright: (c) 2007 Marcin Wojdyr 
// Licence: wxWidgets licence 
// $Id: fancyrc.cpp 277 2007-04-01 23:09:41Z wojdyr $

/// In this file:
///  Fancy Real Control (FancyRealCtrl), which changes the value of 
///  parameter at the sidebar, and helpers

#include <wx/wxprec.h>
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif

#include "fancyrc.h"
#include "cmn.h" //KFTextCtrl

#include "img/lock.xpm"
#include "img/lock_open.xpm"

using namespace std;

static 
wxString double2wxstr(double v) { return wxString::Format(wxT("%g"), v); }

//===============================================================
//                      ValueChangingWidget
//===============================================================

/// small widget used to change value of associated wxTextCtrl with real number
class ValueChangingWidget : public wxSlider
{
public:
    ValueChangingWidget(wxWindow* parent, wxWindowID id, FancyRealCtrl* frc_)
        : wxSlider(parent, id, 0, -100, 100, wxDefaultPosition, wxSize(60, -1)),
          frc(frc_), timer(this, -1), button(0) {}

    void OnTimer(wxTimerEvent &event);
    void OnThumbTrack(wxScrollEvent&); 
    void OnMouse(wxMouseEvent &event);

private:
    FancyRealCtrl *frc;
    wxTimer timer;
    char button;
    
    DECLARE_EVENT_TABLE()
};

BEGIN_EVENT_TABLE(ValueChangingWidget, wxSlider)
    EVT_TIMER(-1, ValueChangingWidget::OnTimer)
    EVT_MOUSE_EVENTS(ValueChangingWidget::OnMouse)
    EVT_SCROLL_THUMBTRACK(ValueChangingWidget::OnThumbTrack)
END_EVENT_TABLE()

void ValueChangingWidget::OnTimer(wxTimerEvent&)
{
    if (button == 'l') {
        frc->ChangeValue(GetValue()*0.001);
    }
    else if (button == 'm') {
        frc->ChangeValue(GetValue()*0.0001);
    }
    else if (button == 'r') {
        frc->ChangeValue(GetValue()*0.00001);
    }
    else {
        assert (button == 0);
        timer.Stop();
        SetValue(0);
        frc->OnStopChanging();
    }
}

void ValueChangingWidget::OnThumbTrack(wxScrollEvent&) 
{ 
    if (!timer.IsRunning()) 
        timer.Start(100);
}

void ValueChangingWidget::OnMouse(wxMouseEvent &event)
{
    if (event.LeftIsDown())
        button = 'l';
    else if (event.RightIsDown())
        button = 'r';
    else if (event.MiddleIsDown())
        button = 'm';
    else
        button = 0;
    event.Skip();
}

//===============================================================
//                          FancyRealCtrl
//===============================================================

BEGIN_EVENT_TABLE(FancyRealCtrl, wxPanel)
    EVT_TEXT_ENTER(-1, FancyRealCtrl::OnTextEnter)
    EVT_BUTTON(-1, FancyRealCtrl::OnLockButton)
END_EVENT_TABLE()

FancyRealCtrl::FancyRealCtrl(wxWindow* parent, wxWindowID id, 
                  double value, wxString const& tip, bool locked_, 
                  V1Callback<FancyRealCtrl const*> const& changing_value_cb,
                  V1Callback<FancyRealCtrl const*> const& changed_value_cb,
                  V1Callback<FancyRealCtrl const*> const& toggled_lock_cb)
    : wxPanel(parent, id), initial_value(value), locked(locked_), 
      changing_value_callback(changing_value_cb),
      changed_value_callback(changed_value_cb), 
      toggled_lock_callback(toggled_lock_cb)
{
    wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
    // KFTextCtrl is a wxTextCtrl which sends wxEVT_COMMAND_TEXT_ENTER 
    // when loses the focus
    tc = new KFTextCtrl(this, -1, double2wxstr(value)); 
    tc->SetToolTip(tip);
    tc->SetEditable(!locked);
    sizer->Add(tc, 1, wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 1);
    lock_btn = new wxBitmapButton(this, -1, GetLockBitmap(),
                                  wxDefaultPosition, wxDefaultSize,
                                  wxNO_BORDER);
    sizer->Add(lock_btn, 0, wxALL|wxALIGN_CENTER_VERTICAL, 0);
    vch = new ValueChangingWidget(this, -1, this);
    vch->Enable(!locked);
    sizer->Add(vch, 0, wxALL|wxALIGN_CENTER_VERTICAL, 1);
    SetSizer(sizer);
}

void FancyRealCtrl::SetTemporaryValue(double value) 
{ 
    tc->SetValue(double2wxstr(value)); 
}

void FancyRealCtrl::AddValue(double term)
{
    double t;
    bool ok = tc->GetValue().ToDouble(&t);
    if (!ok)
        return;
    // changing_value_callback() is called for values different than
    // initial_value, before and after change, usually twice for every value
    if (t != initial_value)
        changing_value_callback(this);
    t += term;
    tc->SetValue(double2wxstr(t));
    if (t != initial_value)
        changing_value_callback(this);
}

double FancyRealCtrl::GetValue() const
{
    double t;
    bool ok = tc->GetValue().ToDouble(&t);
    return ok ? t : initial_value;
}

void FancyRealCtrl::OnStopChanging()
{
    if (tc->GetValue() != double2wxstr(initial_value)) {
        double t;
        bool ok = tc->GetValue().ToDouble(&t);
        if (ok) {
            initial_value = t;
            changed_value_callback(this);
        }
        else
            tc->SetValue(double2wxstr(initial_value));
    }
}

void FancyRealCtrl::ToggleLock()
{
    locked = !locked;
    lock_btn->SetBitmapLabel(GetLockBitmap());
    vch->Enable(!locked);
    tc->SetEditable(!locked);
}

void FancyRealCtrl::ConnectToOnKeyDown(wxObjectEventFunction function, 
                                         wxEvtHandler* sink)
{
    lock_btn->Connect(wxID_ANY, wxEVT_KEY_DOWN, function, 0, sink);
    vch->Connect(wxID_ANY, wxEVT_KEY_DOWN, function, 0, sink);
}

wxBitmap FancyRealCtrl::GetLockBitmap() const
{
    return wxBitmap(locked ? lock_xpm : lock_open_xpm);
}




syntax highlighted by Code2HTML, v. 0.9.1