00001
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00033
00034 #ifndef GROOVX_VISX_EVENTRESPONSEHDLR_CC_UTC20050626084016_DEFINED
00035 #define GROOVX_VISX_EVENTRESPONSEHDLR_CC_UTC20050626084016_DEFINED
00036
00037 #include "visx/eventresponsehdlr.h"
00038
00039 #include "io/reader.h"
00040 #include "io/writer.h"
00041
00042 #include "nub/log.h"
00043 #include "nub/ref.h"
00044
00045 #include "tcl/makecmd.h"
00046 #include "tcl/eventloop.h"
00047 #include "tcl/interp.h"
00048
00049 #include "tcl-gfx/toglet.h"
00050 #include "tcl-io/tclprocwrapper.h"
00051
00052 #include "rutz/fileposition.h"
00053 #include "rutz/fstring.h"
00054 #include "rutz/shared_ptr.h"
00055 #include "rutz/scopedptr.h"
00056 #include "rutz/sfmt.h"
00057
00058 #include "visx/feedbackmap.h"
00059 #include "visx/sound.h"
00060 #include "visx/response.h"
00061 #include "visx/trial.h"
00062
00063 #define GVX_DYNAMIC_TRACE_EXPR EventResponseHdlr::tracer.status()
00064 #include "rutz/trace.h"
00065 #include "rutz/debug.h"
00066 GVX_DBG_REGISTER
00067
00068 using rutz::fstring;
00069
00071
00072
00073
00075
00076 namespace
00077 {
00078 const io::version_id ERH_SVID = 2;
00079
00080 fstring uniqCmdName(const char* stem)
00081 {
00082 static int cmdCounter = 0;
00083
00084 return rutz::sfmt("::__ERHPrivate::%s%d", stem, ++cmdCounter);
00085 }
00086 }
00087
00089
00090
00091
00093
00094 class EventResponseHdlr::Impl
00095 {
00096 private:
00097 Impl(const Impl&);
00098 Impl& operator=(const Impl&);
00099
00100 public:
00101 Impl(EventResponseHdlr* owner);
00102 ~Impl();
00103
00104 class ActiveState;
00105 friend class ActiveState;
00106
00107 class ActiveState
00108 {
00109 private:
00110 nub::soft_ref<Toglet> itsWidget;
00111 Trial& itsTrial;
00112 fstring itsEventSequence;
00113 fstring itsBindingScript;
00114 unsigned int itsResponseCount;
00115
00116 void attend()
00117 {
00118 if (itsWidget.is_valid())
00119 itsWidget->bind(itsEventSequence.c_str(), itsBindingScript.c_str());
00120 }
00121
00122 void ignore()
00123 {
00124 if (itsWidget.is_valid())
00125 itsWidget->bind(itsEventSequence.c_str(), "");
00126 }
00127
00128 public:
00129 ~ActiveState() { ignore(); }
00130
00131 ActiveState(const EventResponseHdlr::Impl* erh,
00132 nub::soft_ref<Toglet> widget, Trial& trial,
00133 const fstring& seq, const fstring& script) :
00134 itsWidget(widget),
00135 itsTrial(trial),
00136 itsEventSequence(seq),
00137 itsBindingScript(script),
00138 itsResponseCount(0)
00139 {
00140 GVX_PRECONDITION((erh != 0) && (widget.is_valid()) && (&trial != 0));
00141 attend();
00142 }
00143
00144 void rhAbortTrial()
00145 {
00146 ignore();
00147
00148 nub::ref<Sound> p = Sound::getErrSound();
00149 p->play();
00150 }
00151
00152 void rhEndTrial() { ignore(); }
00153
00154 void rhHaltExpt() { ignore(); }
00155
00156 void handleResponse(EventResponseHdlr::Impl* rep, const char* event_info)
00157 {
00158 Response theResponse;
00159
00160 theResponse.setMsec(int(itsTrial.trElapsedMsec() + 0.5));
00161
00162 nub::log( rutz::sfmt("event_info: %s", event_info) );
00163
00164 theResponse.setVal(Response::INVALID_VALUE);
00165
00166 if ( !rep->itsResponseProc->isNoop() )
00167 {
00168 try {
00169 theResponse.setVal(rep->itsResponseProc->call<int>(event_info));
00170 } catch (...) {}
00171 }
00172
00173 nub::log( rutz::sfmt("response val: %d", theResponse.val()) );
00174
00175 if (theResponse.shouldIgnore())
00176 return;
00177
00178 if (++itsResponseCount >= rep->itsMaxResponses)
00179 ignore();
00180
00181 if ( !theResponse.is_valid() )
00182 {
00183 if ( rep->itsAbortInvalidResponses )
00184 itsTrial.trAbortTrial();
00185 }
00186 else
00187 {
00188 itsTrial.trProcessResponse(theResponse);
00189 rep->itsFeedbackMap.giveFeedback(rep->itsInterp, theResponse.val());
00190 }
00191 }
00192 };
00193
00194 void becomeActive(nub::soft_ref<Toglet> widget, Trial& trial) const
00195 {
00196 nub::log( rutz::sfmt("binding to %s", itsCallbackName.c_str()) );
00197
00198 const fstring script =
00199 rutz::sfmt("%s %lu [list %s]",
00200 itsCallbackName.c_str(), itsOwner->id(),
00201 itsBindingSubstitution.c_str());
00202
00203 itsState.reset(new ActiveState(this, widget, trial,
00204 itsEventSequence, script));
00205 }
00206
00207 void becomeInactive() const { itsState.reset(0); }
00208
00209 bool isActive() const { return itsState.get() != 0; }
00210 bool isInactive() const { return itsState.get() == 0; }
00211
00212 static void handleResponseCallback(nub::ref<EventResponseHdlr> erh,
00213 const char* event_info)
00214 {
00215 EventResponseHdlr::Impl* rep = erh->rep;
00216 GVX_PRECONDITION( rep->isActive() );
00217
00218 rep->itsState->handleResponse(rep, event_info);
00219 }
00220
00221
00222
00223
00224
00225 EventResponseHdlr* itsOwner;
00226
00227 mutable rutz::scoped_ptr<ActiveState> itsState;
00228
00229 tcl::interpreter itsInterp;
00230
00231 fstring itsCallbackName;
00232
00233 FeedbackMap itsFeedbackMap;
00234
00235 fstring itsEventSequence;
00236 fstring itsBindingSubstitution;
00237
00238 bool itsAbortInvalidResponses;
00239
00240 nub::ref<tcl::ProcWrapper> itsResponseProc;
00241
00242 unsigned int itsMaxResponses;
00243 };
00244
00246
00247
00248
00250
00251 rutz::tracer EventResponseHdlr::tracer;
00252
00253
00255
00256
00257
00259
00260 EventResponseHdlr::Impl::Impl(EventResponseHdlr* owner) :
00261 itsOwner(owner),
00262 itsState(0),
00263 itsInterp(tcl::event_loop::interp()),
00264 itsCallbackName(uniqCmdName("handler")),
00265 itsFeedbackMap(),
00266 itsEventSequence("<KeyPress>"),
00267 itsBindingSubstitution("%K"),
00268 itsAbortInvalidResponses(true),
00269 itsResponseProc(new tcl::ProcWrapper(itsInterp, uniqCmdName("responseProc"))),
00270 itsMaxResponses(1)
00271 {
00272 GVX_TRACE("EventResponseHdlr::Impl::Impl");
00273
00274 tcl::make_command(itsInterp, &handleResponseCallback,
00275 itsCallbackName.c_str(), "<private>", SRC_POS);
00276 }
00277
00278 EventResponseHdlr::Impl::~Impl()
00279 {
00280 GVX_TRACE("EventResponseHdlr::Impl::~Impl");
00281
00282
00283
00284
00285 itsState.reset(0);
00286 }
00287
00288
00290
00291
00292
00294
00295 EventResponseHdlr* EventResponseHdlr::make()
00296 {
00297 GVX_TRACE("EventResponseHdlr::make");
00298 return new EventResponseHdlr;
00299 }
00300
00301 EventResponseHdlr::EventResponseHdlr() :
00302 ResponseHandler(),
00303 rep(new Impl(this))
00304 {}
00305
00306 EventResponseHdlr::~EventResponseHdlr() throw()
00307 { delete rep; }
00308
00309 io::version_id EventResponseHdlr::class_version_id() const
00310 { return ERH_SVID; }
00311
00312 void EventResponseHdlr::read_from(io::reader& reader)
00313 {
00314 GVX_TRACE("EventResponseHdlr::read_from");
00315
00316 reader.ensure_version_id("EventResponseHdlr", 2,
00317 "Try cvs tag xml_conversion_20040526",
00318 SRC_POS);
00319
00320 rep->becomeInactive();
00321
00322 fstring fmap;
00323 reader.read_value("feedbackMap", fmap);
00324 rep->itsFeedbackMap.set(fmap);
00325
00326 reader.read_value("useFeedback", rep->itsFeedbackMap.itsUseFeedback);
00327 reader.read_value("eventSequence", rep->itsEventSequence);
00328 reader.read_value("bindingSubstitution", rep->itsBindingSubstitution);
00329
00330 reader.read_owned_object("responseProc", rep->itsResponseProc);
00331 }
00332
00333 void EventResponseHdlr::write_to(io::writer& writer) const
00334 {
00335 GVX_TRACE("EventResponseHdlr::write_to");
00336
00337 writer.ensure_output_version_id("EventResponseHdlr", ERH_SVID, 2,
00338 "Try groovx0.8a7", SRC_POS);
00339
00340 writer.write_value("feedbackMap", rep->itsFeedbackMap.rep());
00341 writer.write_value("useFeedback", rep->itsFeedbackMap.itsUseFeedback);
00342 writer.write_value("eventSequence", rep->itsEventSequence);
00343 writer.write_value("bindingSubstitution", rep->itsBindingSubstitution);
00344
00345 writer.write_owned_object("responseProc", rep->itsResponseProc);
00346 }
00347
00348
00349 void EventResponseHdlr::setInputResponseMap(const fstring& s)
00350 {
00351 GVX_TRACE("EventResponseHdlr::setInputResponseMap");
00352
00353 const fstring args("inp");
00354
00355 const fstring body =
00356 rutz::sfmt("set map {%s}\n"
00357 "foreach pair $map {\n"
00358 " foreach {rx val} $pair {\n"
00359 " if [regexp $rx $inp] { return $val }\n"
00360 " }\n"
00361 "}\n"
00362 "return -1\n",
00363 s.c_str());
00364
00365 setResponseProc(args, body);
00366 }
00367
00368 bool EventResponseHdlr::getUseFeedback() const
00369 {
00370 GVX_TRACE("EventResponseHdlr::getUseFeedback");
00371 return rep->itsFeedbackMap.itsUseFeedback;
00372 }
00373
00374 void EventResponseHdlr::setUseFeedback(bool val)
00375 {
00376 GVX_TRACE("EventResponseHdlr::setUseFeedback");
00377 rep->itsFeedbackMap.itsUseFeedback = val;
00378 }
00379
00380 const char* EventResponseHdlr::getFeedbackMap() const
00381 {
00382 GVX_TRACE("EventResponseHdlr::getFeedbackMap");
00383 return rep->itsFeedbackMap.rep().c_str();
00384 }
00385
00386 void EventResponseHdlr::setFeedbackMap(const char* feedback_string)
00387 {
00388 GVX_TRACE("EventResponseHdlr::setFeedbackMap");
00389 rep->itsFeedbackMap.set(feedback_string);
00390 }
00391
00392 const fstring& EventResponseHdlr::getEventSequence() const
00393 { return rep->itsEventSequence; }
00394
00395 void EventResponseHdlr::setEventSequence(const fstring& seq)
00396 { rep->itsEventSequence = seq; }
00397
00398 const fstring& EventResponseHdlr::getBindingSubstitution() const
00399 { return rep->itsBindingSubstitution; }
00400
00401 void EventResponseHdlr::setBindingSubstitution(const fstring& sub)
00402 { rep->itsBindingSubstitution = sub; }
00403
00404 fstring EventResponseHdlr::getResponseProc() const
00405 {
00406 return rep->itsResponseProc->fullSpec();
00407 }
00408
00409 void EventResponseHdlr::setResponseProc(const fstring& args,
00410 const fstring& body)
00411 {
00412 rep->itsResponseProc->define(args, body);
00413 }
00414
00415 void EventResponseHdlr::abortInvalidResponses()
00416 { rep->itsAbortInvalidResponses = true; }
00417
00418 void EventResponseHdlr::ignoreInvalidResponses()
00419 { rep->itsAbortInvalidResponses = false; }
00420
00421 void EventResponseHdlr::setMaxResponses(unsigned int count)
00422 { rep->itsMaxResponses = count; }
00423
00424 unsigned int EventResponseHdlr::getMaxResponses() const
00425 { return rep->itsMaxResponses; }
00426
00427 void EventResponseHdlr::rhBeginTrial(nub::soft_ref<Toglet> widget,
00428 Trial& trial) const
00429 {
00430 GVX_PRECONDITION( rep->isInactive() );
00431
00432 rep->itsInterp.clear_event_queue();
00433
00434 rep->becomeActive(widget, trial);
00435
00436 GVX_POSTCONDITION( rep->isActive() );
00437 }
00438
00439 void EventResponseHdlr::rhAbortTrial() const
00440 {
00441 if ( rep->isActive() )
00442 rep->itsState->rhAbortTrial();
00443 }
00444
00445 void EventResponseHdlr::rhEndTrial() const
00446 {
00447 if ( rep->isActive() )
00448 {
00449 rep->itsState->rhEndTrial();
00450 rep->becomeInactive();
00451 }
00452
00453 GVX_POSTCONDITION( rep->isInactive() );
00454 }
00455
00456 void EventResponseHdlr::rhHaltExpt() const
00457 {
00458 if ( rep->isActive() )
00459 {
00460 rep->itsState->rhHaltExpt();
00461 rep->becomeInactive();
00462 }
00463
00464 GVX_POSTCONDITION( rep->isInactive() );
00465 }
00466
00467 void EventResponseHdlr::rhAllowResponses(nub::soft_ref<Toglet> widget,
00468 Trial& trial) const
00469 {
00470 rep->becomeActive(widget, trial);
00471
00472 GVX_POSTCONDITION( rep->isActive() );
00473 }
00474
00475 void EventResponseHdlr::rhDenyResponses() const
00476 {
00477 rep->becomeInactive();
00478
00479 GVX_POSTCONDITION( rep->isInactive() );
00480 }
00481
00482 static const char __attribute__((used)) vcid_groovx_visx_eventresponsehdlr_cc_utc20050626084016[] = "$Id: eventresponsehdlr.cc 10065 2007-04-12 05:54:56Z rjpeters $ $HeadURL: file:
00483 #endif // !GROOVX_VISX_EVENTRESPONSEHDLR_CC_UTC20050626084016_DEFINED