HTTPRequest/pkg::Install: do not replace callbacks.

Keep a list of callbacks to allow registering multiple callbacks
to the same event. This is consistent with eg. jQuery.Deferred
and is needed for example to open multiple dialogs showing the
progress of installing a package at the same time.
This commit is contained in:
Thomas Geymayer
2014-06-30 18:22:24 +02:00
parent dc4644bf3a
commit 0b60643053
8 changed files with 204 additions and 36 deletions

View File

@@ -39,7 +39,7 @@ Request* Request::done(const Callback& cb)
if( _ready_state == DONE )
cb(this);
else
_cb_done = cb;
_cb_done.push_back(cb);
return this;
}
@@ -50,7 +50,7 @@ Request* Request::fail(const Callback& cb)
if( _ready_state == FAILED )
cb(this);
else
_cb_fail = cb;
_cb_fail.push_back(cb);
return this;
}
@@ -61,7 +61,7 @@ Request* Request::always(const Callback& cb)
if( isComplete() )
cb(this);
else
_cb_always = cb;
_cb_always.push_back(cb);
return this;
}
@@ -321,22 +321,19 @@ void Request::setReadyState(ReadyState state)
onDone();
onAlways();
if( _cb_done )
_cb_done(this);
_cb_done(this);
}
else if( state == FAILED )
{
onFail();
onAlways();
if( _cb_fail )
_cb_fail(this);
_cb_fail(this);
}
else
return;
if( _cb_always )
_cb_always(this);
_cb_always(this);
}
//------------------------------------------------------------------------------

View File

@@ -3,13 +3,13 @@
#include <map>
#include <simgear/structure/function_list.hxx>
#include <simgear/structure/map.hxx>
#include <simgear/structure/SGReferenced.hxx>
#include <simgear/structure/SGSharedPtr.hxx>
#include <simgear/math/sg_types.hxx>
#include <boost/bind.hpp>
#include <boost/function.hpp>
class SGPropertyNode;
@@ -47,7 +47,7 @@ public:
{ return _request_headers.get(key); }
/**
* Set the handler to be called when the request successfully completes.
* Add a handler to be called when the request successfully completes.
*
* @note If the request is already complete, the handler is called
* immediately.
@@ -61,7 +61,7 @@ public:
}
/**
* Set the handler to be called when the request completes or aborts with an
* Add a handler to be called when the request completes or aborts with an
* error.
*
* @note If the request has already failed, the handler is called
@@ -76,8 +76,8 @@ public:
}
/**
* Set the handler to be called when the request either successfully
* completes or fails.
* Add a handler to be called when the request either successfully completes
* or fails.
*
* @note If the request is already complete or has already failed, the
* handler is called immediately.
@@ -224,9 +224,9 @@ private:
unsigned int _responseLength;
unsigned int _receivedBodyBytes;
Callback _cb_done,
_cb_fail,
_cb_always;
function_list<Callback> _cb_done,
_cb_fail,
_cb_always;
ReadyState _ready_state;
bool _willClose;

View File

@@ -325,7 +325,7 @@ Install* Install::done(const Callback& cb)
if( _status == Delegate::FAIL_SUCCESS )
cb(this);
else
_cb_done = cb;
_cb_done.push_back(cb);
return this;
}
@@ -337,7 +337,7 @@ Install* Install::fail(const Callback& cb)
&& _status != Delegate::FAIL_IN_PROGRESS )
cb(this);
else
_cb_fail = cb;
_cb_fail.push_back(cb);
return this;
}
@@ -348,7 +348,7 @@ Install* Install::always(const Callback& cb)
if( _status != Delegate::FAIL_IN_PROGRESS )
cb(this);
else
_cb_always = cb;
_cb_always.push_back(cb);
return this;
}
@@ -356,7 +356,7 @@ Install* Install::always(const Callback& cb)
//------------------------------------------------------------------------------
Install* Install::progress(const ProgressCallback& cb)
{
_cb_progress = cb;
_cb_progress.push_back(cb);
return this;
}
@@ -365,24 +365,20 @@ void Install::installResult(Delegate::FailureCode aReason)
{
if (aReason == Delegate::FAIL_SUCCESS) {
m_package->catalog()->root()->finishInstall(this);
if( _cb_done )
_cb_done(this);
_cb_done(this);
} else {
m_package->catalog()->root()->failedInstall(this, aReason);
if( _cb_fail )
_cb_fail(this);
_cb_fail(this);
}
if( _cb_always )
_cb_always(this);
_cb_always(this);
}
//------------------------------------------------------------------------------
void Install::installProgress(unsigned int aBytes, unsigned int aTotal)
{
m_package->catalog()->root()->installProgress(this, aBytes, aTotal);
if( _cb_progress )
_cb_progress(this, aBytes, aTotal);
m_package->catalog()->root()->installProgress(this, aBytes, aTotal);
_cb_progress(this, aBytes, aTotal);
}

View File

@@ -23,11 +23,11 @@
#include <simgear/misc/sg_path.hxx>
#include <simgear/package/Delegate.hxx>
#include <simgear/structure/function_list.hxx>
#include <simgear/structure/SGReferenced.hxx>
#include <simgear/structure/SGSharedPtr.hxx>
#include <boost/bind.hpp>
#include <boost/function.hpp>
namespace simgear
{
@@ -156,11 +156,10 @@ private:
Delegate::FailureCode _status;
Callback _cb_done,
_cb_fail,
_cb_always;
ProgressCallback _cb_progress;
function_list<Callback> _cb_done,
_cb_fail,
_cb_always;
function_list<ProgressCallback> _cb_progress;
};

View File

@@ -21,12 +21,17 @@ set(HEADERS
commands.hxx
event_mgr.hxx
exception.hxx
function_list.hxx
intern.hxx
map.hxx
subsystem_mgr.hxx
StateMachine.hxx
)
set(DETAIL_HEADERS
detail/function_list_template.hxx
)
set(SOURCES
SGAtomic.cxx
SGBinding.cxx
@@ -43,6 +48,7 @@ set(SOURCES
)
simgear_component(structure structure "${SOURCES}" "${HEADERS}")
simgear_component(structure/detail structure/detail "" "${DETAIL_HEADERS}")
if(ENABLE_TESTS)
@@ -56,6 +62,11 @@ add_test(expressions ${EXECUTABLE_OUTPUT_PATH}/test_expressions)
endif(ENABLE_TESTS)
add_boost_test(function_list
SOURCES function_list_test.cxx
LIBRARIES ${TEST_LIBS}
)
add_boost_test(shared_ptr
SOURCES shared_ptr_test.cpp
LIBRARIES ${TEST_LIBS}

View File

@@ -0,0 +1,37 @@
#ifndef SG_FUNCTION_LIST_HXX_
# error function_list - do not include this file!
#endif
#ifndef SG_DONT_DO_ANYTHING
#define n BOOST_PP_ITERATION()
#define SG_FUNC_TYPE boost::function<Ret (BOOST_PP_ENUM_PARAMS(n, A))>
#define SG_LIST_TYPE std::vector<SG_FUNC_TYPE >
template<class Ret BOOST_PP_ENUM_TRAILING_PARAMS(n, class A)>
class function_list<Ret (BOOST_PP_ENUM_PARAMS(n, A))>:
public SG_LIST_TYPE
{
public:
typedef SG_FUNC_TYPE function_type;
typedef typename SG_LIST_TYPE::iterator iterator;
typedef typename SG_LIST_TYPE::const_iterator const_iterator;
Ret operator()(BOOST_PP_ENUM_BINARY_PARAMS(n, A, a)) const
{
if( this->empty() )
return Ret();
const_iterator list_end = --this->end();
for(const_iterator f = this->begin(); f != list_end; ++f)
if( *f )
(*f)(BOOST_PP_ENUM_PARAMS(n, a));
return (*list_end) ? (*list_end)(BOOST_PP_ENUM_PARAMS(n, a)) : Ret();
}
};
#undef n
#undef SG_FUNC_TYPE
#undef SG_LIST_TYPE
#endif // SG_DONT_DO_ANYTHING

View File

@@ -0,0 +1,51 @@
///@file Handle a list of callbacks like a single boost::function
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef SG_FUNCTION_LIST_HXX_
#define SG_FUNCTION_LIST_HXX_
#include <boost/function.hpp>
#include <boost/preprocessor/iteration/iterate.hpp>
#include <boost/preprocessor/repetition/enum_binary_params.hpp>
#include <boost/preprocessor/repetition/enum_trailing_params.hpp>
#include <boost/utility/enable_if.hpp>
#include <vector>
namespace simgear
{
template<typename Sig> class function_list;
// Build dependency for CMake, gcc, etc.
# define SG_DONT_DO_ANYTHING
# include <simgear/structure/detail/function_list_template.hxx>
# undef SG_DONT_DO_ANYTHING
# define BOOST_PP_ITERATION_LIMITS (0, 3)
# define BOOST_PP_FILENAME_1 <simgear/structure/detail/function_list_template.hxx>
# include BOOST_PP_ITERATE()
template<typename Sig>
class function_list<boost::function<Sig> >:
public function_list<Sig>
{
};
} // namespace simgear
#endif /* SG_FUNCTION_LIST_HXX_ */

View File

@@ -0,0 +1,77 @@
#define BOOST_TEST_MODULE structurce
#include <BoostTestTargetConfig.h>
#include "function_list.hxx"
static int func_called = 0,
func2_called = 0;
int func(int x)
{
++func_called;
return x;
}
int func2(int x)
{
++func2_called;
return 2 * x;
}
int func_add(int x, int y)
{
return func(x) + y;
}
int func2_add(int x, int y)
{
return func2(x) + y;
}
static int func_void_called = 0;
void func_void()
{
++func_void_called;
}
BOOST_AUTO_TEST_CASE( create_and_call )
{
using namespace simgear;
function_list<int (int)> func_list;
BOOST_REQUIRE(func_list.empty());
func_list.push_back(&func);
BOOST_REQUIRE_EQUAL(func_list(2), 2);
BOOST_REQUIRE_EQUAL(func_called, 1);
func_list.push_back(&func2);
BOOST_REQUIRE_EQUAL(func_list(2), 4);
BOOST_REQUIRE_EQUAL(func_called, 2);
BOOST_REQUIRE_EQUAL(func2_called, 1);
function_list<boost::function<int (int)> > func_list2;
func_list2.push_back(&func);
func_list2.push_back(&func2);
func_list2.push_back(&func2);
BOOST_REQUIRE_EQUAL(func_list2(2), 4);
BOOST_REQUIRE_EQUAL(func_called, 3);
BOOST_REQUIRE_EQUAL(func2_called, 3);
// two parameters
function_list<boost::function<int (int, int)> > func_list3;
func_list3.push_back(&func_add);
func_list3.push_back(&func2_add);
BOOST_REQUIRE_EQUAL(func_list3(2, 3), 7);
BOOST_REQUIRE_EQUAL(func_called, 4);
BOOST_REQUIRE_EQUAL(func2_called, 4);
// returning void/no params
function_list<void()> void_func_list;
void_func_list.push_back(&func_void);
void_func_list();
BOOST_REQUIRE_EQUAL(func_void_called, 1);
}