3cb5a4c2064d4cf4d8cbdca75a84615a6c56fb7c from master; use j2c_uuid and pcm_uuid for...
[dcpomatic.git] / src / wx / content_widget.h
index 07abc7cbdf8d1de81140b691d03d112be4534689..6f406dadbc926e92c094b74d53366327b6d08093 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  src/wx/content_widget.h
+ *  @brief ContentWidget class.
+ */
+
 #ifndef DCPOMATIC_MULTIPLE_WIDGET_H
 #define DCPOMATIC_MULTIPLE_WIDGET_H
 
 #include <vector>
 #include <wx/wx.h>
 #include <wx/gbsizer.h>
+#include <wx/spinctrl.h>
 #include <boost/function.hpp>
 #include "wx_util.h"
 
-/** A widget which represents some Content state and which can be used
+/** @class ContentWidget
+ *  @brief A widget which represents some Content state and which can be used
  *  when multiple pieces of content are selected.
  *
  *  @param S Type containing the content being represented (e.g. VideoContent)
@@ -35,7 +41,7 @@
  *  @param V Data type of state as used by the view.
  */
 template <class S, class T, typename U, typename V>
-class ContentWidget
+class ContentWidget : public boost::noncopyable
 {
 public:
        /** @param parent Parent window.
@@ -50,7 +56,8 @@ public:
                int property,
                boost::function<U (S*)> model_getter,
                boost::function<void (S*, U)> model_setter,
-               boost::function<V (T*)> view_getter
+               boost::function<U (V)> view_to_model,
+               boost::function<V (U)> model_to_view
                )
                : _wrapped (wrapped)
                , _sizer (0)
@@ -58,7 +65,8 @@ public:
                , _property (property)
                , _model_getter (model_getter)
                , _model_setter (model_setter)
-               , _view_getter (view_getter)
+               , _view_to_model (view_to_model)
+               , _model_to_view (model_to_view)
                , _ignore_model_changes (false)
        {
                _button->SetToolTip (_("Click the button to set all selected content to the same value."));
@@ -66,12 +74,15 @@ public:
                _button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&ContentWidget::button_clicked, this));
        }
 
-       T* wrapped () const {
+       /** @return the widget that we are wrapping */
+       T* wrapped () const
+       {
                return _wrapped;
        }
 
        typedef std::vector<boost::shared_ptr<S> > List;
 
+       /** Set the content that this control is working on (i.e. the selected content) */
        void set_content (List content)
        {
                for (typename std::list<boost::signals2::connection>::iterator i = _connections.begin(); i != _connections.end(); ++i) {
@@ -91,13 +102,16 @@ public:
                }
        }
 
-       void add (wxGridBagSizer* sizer, wxGBPosition position)
+       /** Add this widget to a wxGridBagSizer */
+       void add (wxGridBagSizer* sizer, wxGBPosition position, wxGBSpan span = wxDefaultSpan)
        {
                _sizer = sizer;
                _position = position;
-               _sizer->Add (_wrapped, _position);
+               _span = span;
+               _sizer->Add (_wrapped, _position, _span);
        }
 
+       /** Update the view from the model */
        void update_from_model ()
        {
                if (_content.empty ()) {
@@ -113,7 +127,7 @@ public:
 
                if (i == _content.end ()) {
                        set_single ();
-                       checked_set (_wrapped, v);
+                       checked_set (_wrapped, _model_to_view (v));
                } else {
                        set_multiple ();
                }
@@ -121,11 +135,11 @@ public:
 
        void view_changed ()
        {
+               _ignore_model_changes = true;
                for (size_t i = 0; i < _content.size(); ++i) {
-                       /* Only update our view on the last time round this loop */
-                       _ignore_model_changes = i < (_content.size() - 1);
-                       boost::bind (_model_setter, _content[i].get(), static_cast<U> (boost::bind (_view_getter, _wrapped)()))();
+                       boost::bind (_model_setter, _content[i].get(), _view_to_model (wx_get (_wrapped))) ();
                }
+               _ignore_model_changes = false;
        }
        
 private:
@@ -138,7 +152,7 @@ private:
 
                _sizer->Detach (_button);
                _button->Hide ();
-               _sizer->Add (_wrapped, _position);
+               _sizer->Add (_wrapped, _position, _span);
                _wrapped->Show ();
                _sizer->Layout ();
        }
@@ -152,7 +166,7 @@ private:
                _wrapped->Hide ();
                _sizer->Detach (_wrapped);
                _button->Show ();
-               _sizer->Add (_button, _position);
+               _sizer->Add (_button, _position, _span);
                _sizer->Layout ();
        }
 
@@ -174,34 +188,94 @@ private:
        T* _wrapped;
        wxGridBagSizer* _sizer;
        wxGBPosition _position;
+       wxGBSpan _span;
        wxButton* _button;
        List _content;
        int _property;
        boost::function<U (S*)> _model_getter;
        boost::function<void (S*, U)> _model_setter;
-       boost::function<V (T*)> _view_getter;
+       boost::function<U (V)> _view_to_model;
+       boost::function<V (U)> _model_to_view;
        std::list<boost::signals2::connection> _connections;
        bool _ignore_model_changes;
 };
 
+template <typename U, typename V>
+V caster (U x)
+{
+       return static_cast<V> (x);
+}
+
 template <class S>
 class ContentSpinCtrl : public ContentWidget<S, wxSpinCtrl, int, int>
 {
 public:
-       ContentSpinCtrl (wxWindow* parent, wxSpinCtrl* wrapped, int property, boost::function<int (S*)> getter, boost::function<void (S*, int)> setter)
-               : ContentWidget<S, wxSpinCtrl, int, int> (parent, wrapped, property, getter, setter, boost::mem_fn (&wxSpinCtrl::GetValue))
+       ContentSpinCtrl (
+               wxWindow* parent,
+               wxSpinCtrl* wrapped,
+               int property,
+               boost::function<int (S*)> getter,
+               boost::function<void (S*, int)> setter
+               )
+               : ContentWidget<S, wxSpinCtrl, int, int> (
+                       parent,
+                       wrapped,
+                       property,
+                       getter, setter,
+                       &caster<int, int>,
+                       &caster<int, int>
+                       )
        {
                wrapped->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&ContentWidget<S, wxSpinCtrl, int, int>::view_changed, this));
        }
+};
 
+template <class S>
+class ContentSpinCtrlDouble : public ContentWidget<S, wxSpinCtrlDouble, double, double>
+{
+public:
+       ContentSpinCtrlDouble (
+               wxWindow* parent,
+               wxSpinCtrlDouble* wrapped,
+               int property,
+               boost::function<double (S*)> getter,
+               boost::function<void (S*, double)> setter
+               )
+               : ContentWidget<S, wxSpinCtrlDouble, double, double> (
+                       parent,
+                       wrapped,
+                       property,
+                       getter, setter,
+                       &caster<double, double>,
+                       &caster<double, double>
+                       )
+       {
+               wrapped->Bind (wxEVT_COMMAND_SPINCTRLDOUBLE_UPDATED, boost::bind (&ContentWidget<S, wxSpinCtrlDouble, double, double>::view_changed, this));
+       }
 };
 
 template <class S, class U>
 class ContentChoice : public ContentWidget<S, wxChoice, U, int>
 {
 public:
-       ContentChoice (wxWindow* parent, wxChoice* wrapped, int property, boost::function<U (S*)> getter, boost::function<void (S*, U)> setter)
-               : ContentWidget<S, wxChoice, U, int> (parent, wrapped, property, getter, setter, boost::mem_fn (&wxChoice::GetSelection))
+       ContentChoice (
+               wxWindow* parent,
+               wxChoice* wrapped,
+               int property,
+               boost::function<U (S*)> getter,
+               boost::function<void (S*, U)> setter,
+               boost::function<U (int)> view_to_model,
+               boost::function<int (U)> model_to_view
+               )
+               : ContentWidget<S, wxChoice, U, int> (
+                       parent,
+                       wrapped,
+                       property,
+                       getter,
+                       setter,
+                       view_to_model,
+                       model_to_view
+                       )
        {
                wrapped->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&ContentWidget<S, wxChoice, U, int>::view_changed, this));
        }