/* This file is part of Clementine. Copyright 2011, David Sansome Clementine 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 3 of the License, or (at your option) any later version. Clementine 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 Clementine. If not, see . */ #ifndef CLOSURE_H #define CLOSURE_H #include #include #include #include #include #include #include #include namespace _detail { class ObjectHelper; // Interface for ObjectHelper to call on signal emission. class ClosureBase : boost::noncopyable { public: virtual ~ClosureBase(); virtual void Invoke() = 0; // Tests only. ObjectHelper* helper() const; protected: explicit ClosureBase(ObjectHelper*); ObjectHelper* helper_; }; // QObject helper as templated QObjects do not work. // Connects to the given signal and invokes the closure when called. // Deletes itself and the Closure after being invoked. class ObjectHelper : public QObject { Q_OBJECT public: ObjectHelper( QObject* parent, const char* signal, ClosureBase* closure); private slots: void Invoked(); private: boost::scoped_ptr closure_; Q_DISABLE_COPY(ObjectHelper); }; // Helper to unpack a tuple into a QList. template void Arg(const T& tail, QList* list) { typedef decltype(tail.get_head()) HeadType; list->append(Q_ARG(HeadType, tail.get_head())); Arg(tail.get_tail(), list); } // Specialisation for starting with an empty tuple, ie. no arguments. template<> void Arg>( const boost::tuples::tuple<>&, QList* list); // Specialisation for the end of a tuple, where get_tail() returns null_type. template<> void Arg( const boost::tuples::null_type&, QList* list); template class Closure : public ClosureBase { public: Closure( QObject* sender, const char* signal, QObject* receiver, const char* slot, const Args&... args) : ClosureBase(new ObjectHelper(sender, signal, this)), // boost::bind is the easiest way to store an argument list. function_(boost::bind(&Closure::Call, this, args...)), receiver_(receiver) { const QMetaObject* meta_receiver = receiver->metaObject(); QByteArray normalised_slot = QMetaObject::normalizedSignature(slot + 1); const int index = meta_receiver->indexOfSlot(normalised_slot.constData()); Q_ASSERT(index != -1); slot_ = meta_receiver->method(index); } virtual void Invoke() { function_(); } private: void Call(const Args&... args) { auto t = boost::make_tuple(args...); QList arg_list; Arg(t, &arg_list); slot_.invoke( receiver_, arg_list.size() > 0 ? arg_list[0] : QGenericArgument(), arg_list.size() > 1 ? arg_list[1] : QGenericArgument(), arg_list.size() > 2 ? arg_list[2] : QGenericArgument(), arg_list.size() > 3 ? arg_list[3] : QGenericArgument(), arg_list.size() > 4 ? arg_list[4] : QGenericArgument(), arg_list.size() > 5 ? arg_list[5] : QGenericArgument(), arg_list.size() > 6 ? arg_list[6] : QGenericArgument(), arg_list.size() > 7 ? arg_list[7] : QGenericArgument(), arg_list.size() > 8 ? arg_list[8] : QGenericArgument(), arg_list.size() > 9 ? arg_list[9] : QGenericArgument()); } boost::function function_; QObject* receiver_; QMetaMethod slot_; }; template class SharedClosure : public Closure { public: SharedClosure( QSharedPointer sender, const char* signal, QObject* receiver, const char* slot, const Args&... args) : Closure( sender.data(), signal, receiver, slot, args...), data_(sender) { } private: QSharedPointer data_; }; } // namespace _detail template _detail::ClosureBase* NewClosure( QObject* sender, const char* signal, QObject* receiver, const char* slot, const Args&... args) { return new _detail::Closure( sender, signal, receiver, slot, args...); } // QSharedPointer variant template _detail::ClosureBase* NewClosure( QSharedPointer sender, const char* signal, QObject* receiver, const char* slot, const Args&... args) { return new _detail::SharedClosure( sender, signal, receiver, slot, args...); } void DoAfter(QObject* receiver, const char* slot, int msec); void DoInAMinuteOrSo(QObject* receiver, const char* slot); #endif // CLOSURE_H