X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Fwx%2Fcontent_widget.h;h=23b409a97287d61b7768500335e7583e93f0836b;hb=b9a1ad3df5f9d85fb7439efd93fede72b9b078af;hp=07abc7cbdf8d1de81140b691d03d112be4534689;hpb=1415214ff18e05e5b609aab8c4efb89e5b9e0cc0;p=dcpomatic.git diff --git a/src/wx/content_widget.h b/src/wx/content_widget.h index 07abc7cbd..23b409a97 100644 --- a/src/wx/content_widget.h +++ b/src/wx/content_widget.h @@ -1,35 +1,48 @@ /* - Copyright (C) 2013 Carl Hetherington + Copyright (C) 2013-2016 Carl Hetherington - This program is free software; you can redistribute it and/or modify + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - This program is distributed in the hope that it will be useful, + DCP-o-matic is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + along with DCP-o-matic. If not, see . */ -#ifndef DCPOMATIC_MULTIPLE_WIDGET_H -#define DCPOMATIC_MULTIPLE_WIDGET_H +/** @file src/wx/content_widget.h + * @brief ContentWidget class. + */ + + +#ifndef DCPOMATIC_CONTENT_WIDGET_H +#define DCPOMATIC_CONTENT_WIDGET_H + -#include -#include -#include -#include #include "wx_util.h" +#include "lib/content.h" +#include "lib/warnings.h" +DCPOMATIC_DISABLE_WARNINGS +#include +#include +#include +DCPOMATIC_ENABLE_WARNINGS +#include -/** 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) + * @param S Type of ContentPart being manipulated (e.g. VideoContent) * @param T Type of the widget (e.g. wxSpinCtrl) * @param U Data type of state as used by the model. * @param V Data type of state as used by the view. @@ -41,24 +54,34 @@ public: /** @param parent Parent window. * @param wrapped Control widget that we are wrapping. * @param property ContentProperty that the widget is handling. + * @param part Part of Content that the property is in (e.g. &Content::video) * @param model_getter Function on the Content to get the value. * @param model_setter Function on the Content to set the value. + * @param view_changed Function called when the view has changed; useful for linking controls. + * @param view_to_model Function to convert a view value to a model value. + * @param model_to_view Function to convert a model value to a view value. */ ContentWidget ( wxWindow* parent, T* wrapped, int property, - boost::function model_getter, - boost::function model_setter, - boost::function view_getter + std::function (Content*)> part, + std::function model_getter, + std::function model_setter, + std::function view_changed, + std::function view_to_model, + std::function model_to_view ) : _wrapped (wrapped) , _sizer (0) , _button (new wxButton (parent, wxID_ANY, _("Multiple values"))) , _property (property) + , _part (part) , _model_getter (model_getter) , _model_setter (model_setter) - , _view_getter (view_getter) + , _view_changed (view_changed) + , _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 +89,18 @@ public: _button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&ContentWidget::button_clicked, this)); } - T* wrapped () const { + ContentWidget (ContentWidget const&) = delete; + ContentWidget& operator= (ContentWidget const&) = delete; + + /** @return the widget that we are wrapping */ + T* wrapped () const + { return _wrapped; } - typedef std::vector > List; + typedef std::vector > List; + /** Set the content that this control is working on (i.e. the selected content) */ void set_content (List content) { for (typename std::list::iterator i = _connections.begin(); i != _connections.end(); ++i) { @@ -79,7 +108,7 @@ public: } _connections.clear (); - + _content = content; _wrapped->Enable (!_content.empty ()); @@ -87,17 +116,24 @@ public: update_from_model (); for (typename List::iterator i = _content.begin(); i != _content.end(); ++i) { - _connections.push_back ((*i)->Changed.connect (boost::bind (&ContentWidget::model_changed, this, _2))); +#if BOOST_VERSION >= 106100 + _connections.push_back ((*i)->Change.connect (boost::bind (&ContentWidget::model_changed, this, boost::placeholders::_1, boost::placeholders::_3))); +#else + _connections.push_back ((*i)->Change.connect (boost::bind (&ContentWidget::model_changed, this, _1, _3))); +#endif } } - void add (wxGridBagSizer* sizer, wxGBPosition position) + /** Add this widget to a wxGridBagSizer */ + void add (wxGridBagSizer* sizer, wxGBPosition position, wxGBSpan span = wxDefaultSpan, int flag = 0) { _sizer = sizer; _position = position; - _sizer->Add (_wrapped, _position); + _span = span; + _sizer->Add (_wrapped, _position, _span, flag); } + /** Update the view from the model */ void update_from_model () { if (_content.empty ()) { @@ -106,14 +142,14 @@ public: } typename List::iterator i = _content.begin (); - U const v = boost::bind (_model_getter, _content.front().get())(); - while (i != _content.end() && boost::bind (_model_getter, i->get())() == v) { + U const v = boost::bind (_model_getter, _part(_content.front().get()).get())(); + while (i != _content.end() && boost::bind (_model_getter, _part(i->get()).get())() == v) { ++i; } if (i == _content.end ()) { set_single (); - checked_set (_wrapped, v); + checked_set (_wrapped, _model_to_view (v)); } else { set_multiple (); } @@ -121,87 +157,169 @@ 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 (boost::bind (_view_getter, _wrapped)()))(); + boost::bind (_model_setter, _part (_content[i].get()).get(), _view_to_model (wx_get (_wrapped))) (); } + if (_view_changed) { + _view_changed (); + } + _ignore_model_changes = false; } - + + void show (bool s) + { + _wrapped->Show (s); + } + private: - + void set_single () { - if (_wrapped->IsShown ()) { + if (_wrapped->IsShown() || !_sizer) { return; } _sizer->Detach (_button); _button->Hide (); - _sizer->Add (_wrapped, _position); + _sizer->Add (_wrapped, _position, _span); _wrapped->Show (); _sizer->Layout (); } void set_multiple () { - if (_button->IsShown ()) { + if (_button->IsShown() || !_sizer) { return; } - + _wrapped->Hide (); _sizer->Detach (_wrapped); _button->Show (); - _sizer->Add (_button, _position); + _sizer->Add (_button, _position, _span); _sizer->Layout (); } void button_clicked () { - U const v = boost::bind (_model_getter, _content.front().get())(); - for (typename List::iterator i = _content.begin (); i != _content.end(); ++i) { - boost::bind (_model_setter, i->get(), v) (); + U const v = boost::bind (_model_getter, _part(_content.front().get()).get())(); + for (auto const& i: _content) { + boost::bind (_model_setter, _part(i.get()).get(), v)(); } } - void model_changed (int property) + void model_changed (ChangeType type, int property) { - if (property == _property && !_ignore_model_changes) { + if (type == ChangeType::DONE && property == _property && !_ignore_model_changes) { update_from_model (); } } - + T* _wrapped; wxGridBagSizer* _sizer; wxGBPosition _position; + wxGBSpan _span; wxButton* _button; List _content; int _property; - boost::function _model_getter; - boost::function _model_setter; - boost::function _view_getter; + std::function (Content *)> _part; + std::function _model_getter; + std::function _model_setter; + std::function _view_changed; + std::function _view_to_model; + std::function _model_to_view; std::list _connections; bool _ignore_model_changes; }; +template +V caster (U x) +{ + return static_cast (x); +} + template class ContentSpinCtrl : public ContentWidget { public: - ContentSpinCtrl (wxWindow* parent, wxSpinCtrl* wrapped, int property, boost::function getter, boost::function setter) - : ContentWidget (parent, wrapped, property, getter, setter, boost::mem_fn (&wxSpinCtrl::GetValue)) + ContentSpinCtrl ( + wxWindow* parent, + wxSpinCtrl* wrapped, + int property, + std::function (Content *)> part, + std::function getter, + std::function setter, + std::function view_changed = std::function() + ) + : ContentWidget ( + parent, + wrapped, + property, + part, + getter, setter, + view_changed, + &caster, + &caster + ) { wrapped->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&ContentWidget::view_changed, this)); } +}; +template +class ContentSpinCtrlDouble : public ContentWidget +{ +public: + ContentSpinCtrlDouble ( + wxWindow* parent, + wxSpinCtrlDouble* wrapped, + int property, + std::function (Content *)> part, + std::function getter, + std::function setter, + std::function view_changed = std::function() + ) + : ContentWidget ( + parent, + wrapped, + property, + part, + getter, setter, + view_changed, + &caster, + &caster + ) + { + wrapped->Bind (wxEVT_COMMAND_SPINCTRLDOUBLE_UPDATED, boost::bind (&ContentWidget::view_changed, this)); + } }; template class ContentChoice : public ContentWidget { public: - ContentChoice (wxWindow* parent, wxChoice* wrapped, int property, boost::function getter, boost::function setter) - : ContentWidget (parent, wrapped, property, getter, setter, boost::mem_fn (&wxChoice::GetSelection)) + ContentChoice ( + wxWindow* parent, + wxChoice* wrapped, + int property, + std::function (Content *)> part, + std::function getter, + std::function setter, + std::function view_to_model, + std::function model_to_view, + std::function view_changed = std::function() + ) + : ContentWidget ( + parent, + wrapped, + property, + part, + getter, + setter, + view_changed, + view_to_model, + model_to_view + ) { wrapped->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&ContentWidget::view_changed, this)); }