/* 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); }; // Helpers for unpacking a variadic template list. // Base case of no arguments. void Unpack(QList*); template void Unpack(QList* list, const Arg& arg) { list->append(Q_ARG(Arg, arg)); } template void Unpack(QList* list, const Head& head, const Tail&... tail) { Unpack(list, head); Unpack(list, tail...); } 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) { QList arg_list; Unpack(&arg_list, args...); 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_; }; class CallbackClosure : public ClosureBase { public: CallbackClosure( QObject* sender, const char* signal, std::tr1::function callback); virtual void Invoke(); private: std::tr1::function callback_; }; } // 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...); } _detail::ClosureBase* NewClosure( QObject* sender, const char* signal, std::tr1::function callback); template _detail::ClosureBase* NewClosure( QObject* sender, const char* signal, std::tr1::function callback, const Args&... args) { return NewClosure(sender, signal, boost::bind(callback, args...)); } template _detail::ClosureBase* NewClosure( QObject* sender, const char* signal, void (*callback)(Args...), const Args&... args) { return NewClosure(sender, signal, boost::bind(callback, args...)); } template _detail::ClosureBase* NewClosure( QObject* sender, const char* signal, T* receiver, Unused (T::*callback)(Args...), const Args&... args) { return NewClosure(sender, signal, boost::bind(callback, receiver, args...)); } void DoAfter(QObject* receiver, const char* slot, int msec); void DoInAMinuteOrSo(QObject* receiver, const char* slot); #endif // CLOSURE_H