Mercurial > MadButterfly
view src/observer.c @ 1517:31a21656c0a8
Describe designs of Scribboo
author | Thinker K.F. Li <thinker@codemud.net> |
---|---|
date | Sat, 14 May 2011 01:07:23 +0800 |
parents | e415c55b4a0d |
children |
line wrap: on
line source
// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*- // vim: sw=4:ts=8:sts=4 #include <stdio.h> #include "mb_redraw_man.h" #include "mb_observer.h" #include "mb_tools.h" #ifndef ASSERT #define ASSERT(x) #endif subject_t *subject_new(observer_factory_t *factory, void *obj, int obj_type) { subject_t *subject; subject = factory->subject_alloc(factory); if(subject == NULL) return NULL; subject->obj = obj; subject->obj_type = obj_type; subject->flags = 0; subject->monitor_sub = NULL; STAILQ_INIT(subject->observers); subject->factory = factory; return subject; } /*! * \todo Keep observer_factory following subject objects. */ void subject_free(subject_t *subject) { observer_factory_t *factory = subject->factory; observer_t *observer; monitor_event_t mevt; ASSERT(!(subject->flags & SUBF_FREE)); if(subject->flags & SUBF_BUSY) { /* Postpond the request until busy status been stoped. * SUBF_BUSY means in subject_notify(). */ subject->flags |= SUBF_FREE; return; } if(subject->monitor_sub) { mevt.event.type = EVT_MONITOR_FREE; mevt.subject = subject; mevt.observer = NULL; subject_notify(subject->monitor_sub, (event_t *)&mevt); } while((observer = STAILQ_HEAD(subject->observers))) { STAILQ_REMOVE(subject->observers, observer_t, next, observer); factory->observer_free(factory, observer); } factory->subject_free(factory, subject); } void subject_notify(subject_t *subject, event_t *evt) { observer_factory_t *factory = subject->factory; observer_t *observer; subject_t *old_subject; int stop_propagate = 0; int old_busy; evt->tgt = subject; evt->flags = 0; while(subject) { /*! * \note What is happend when the subject is freed by observer? * Postponding the request of free until notification * been finished. (\ref SUBF_BUSY / \ref SUBF_FREE) */ old_busy = subject->flags & SUBF_BUSY; subject->flags |= SUBF_BUSY; evt->cur_tgt = subject; for(observer = STAILQ_HEAD(subject->observers); observer != NULL; observer = STAILQ_NEXT(observer_t, next, observer)) { if (observer->type == EVT_ANY || observer->type == evt->type) { observer->hdr(evt, observer->arg); if(evt->flags & EVTF_STOP_NOTIFY) { stop_propagate = 1; break; } if(evt->flags & EVTF_STOP_PROPAGATE) stop_propagate = 1; } } if(!old_busy) subject->flags &= ~SUBF_BUSY; old_subject = subject; subject = factory->get_parent_subject(factory, subject); if(old_subject->flags & SUBF_STOP_PROPAGATE) stop_propagate = 1; if(old_subject->flags & SUBF_FREE) subject_free(old_subject); if(stop_propagate) break; } } /*! \brief Add an observer for specified type of events. */ observer_t *subject_add_event_observer(subject_t *subject, int type, evt_handler hdr, void *arg) { observer_factory_t *factory = subject->factory; observer_t *observer; monitor_event_t mevt; observer = factory->observer_alloc(factory); if(observer == NULL) return NULL; observer->hdr = hdr; observer->arg = arg; observer->type = type; STAILQ_INS_TAIL(subject->observers, observer_t, next, observer); if(subject->monitor_sub) { mevt.event.type = EVT_MONITOR_ADD; mevt.subject = subject; mevt.observer = observer; subject_notify(subject->monitor_sub, (event_t *)&mevt); } return observer; } /*! \brief Add an observer for specified type of events at head. */ observer_t *subject_add_event_observer_head(subject_t *subject, int type, evt_handler hdr, void *arg) { observer_factory_t *factory = subject->factory; observer_t *observer; monitor_event_t mevt; observer = factory->observer_alloc(factory); if(observer == NULL) return NULL; observer->hdr = hdr; observer->arg = arg; observer->type = type; STAILQ_INS(subject->observers, observer_t, next, observer); if(subject->monitor_sub) { mevt.event.type = EVT_MONITOR_ADD; mevt.subject = subject; mevt.observer = observer; subject_notify(subject->monitor_sub, (event_t *)&mevt); } return observer; } void subject_remove_observer(subject_t *subject, observer_t *observer) { observer_factory_t *factory = subject->factory; monitor_event_t mevt; STAILQ_REMOVE(subject->observers, observer_t, next, observer); if(subject->monitor_sub) { mevt.event.type = EVT_MONITOR_REMOVE; mevt.subject = subject; mevt.observer = observer; subject_notify(subject->monitor_sub, (event_t *)&mevt); } factory->observer_free(factory, observer); } #ifdef UNITTEST #include <CUnit/Basic.h> #include <stdlib.h> static subject_t *test_subject_alloc(observer_factory_t *factory) { subject_t *subject; subject = (subject_t *)malloc(sizeof(subject_t)); return subject; } static void test_subject_free(observer_factory_t *factory, subject_t *subject) { free(subject); } static observer_t *test_observer_alloc(observer_factory_t *factory) { observer_t *observer; observer = (observer_t *)malloc(sizeof(observer_t)); return observer; } static void test_observer_free(observer_factory_t *factory, observer_t *observer) { free(observer); } static subject_t *test_get_parent_subject(observer_factory_t *factory, subject_t *subject) { return NULL; } static observer_factory_t test_factory = { test_subject_alloc, test_subject_free, test_observer_alloc, test_observer_free, test_get_parent_subject }; static void handler(event_t *evt, void *arg) { int *cnt = (int *)arg; CU_ASSERT(evt->type == EVT_MOUSE_OUT); (*cnt)++; } static void test_observer(void) { subject_t *subject; observer_t *observer[2]; event_t evt; int cnt = 0; subject = subject_new(&test_factory, NULL, 0); subject->flags |= SUBF_STOP_PROPAGATE; observer[0] = subject_add_observer(subject, handler, &cnt); observer[1] = subject_add_observer(subject, handler, &cnt); evt.type = EVT_MOUSE_OUT; evt.tgt = NULL; evt.cur_tgt = NULL; subject_notify(subject, &evt); CU_ASSERT(cnt == 2); subject_remove_observer(subject, observer[0]); subject_remove_observer(subject, observer[1]); subject_free(subject); } static void monitor_handler(event_t *evt, void *arg) { int *cnt = (int *)arg; (*cnt)++; } static void test_monitor(void) { subject_t *subject, *monitor; observer_t *observer[2]; int cnt = 0; subject = subject_new(&test_factory, NULL, 0); monitor = subject_new(&test_factory, NULL, 0); subject_set_monitor(subject, monitor); observer[0] = subject_add_observer(monitor, monitor_handler, &cnt); observer[1] = subject_add_observer(subject, NULL, NULL); CU_ASSERT(cnt == 1); subject_remove_observer(subject, observer[1]); CU_ASSERT(cnt == 2); subject_free(subject); CU_ASSERT(cnt == 3); subject_remove_observer(monitor, observer[0]); subject_free(monitor); } CU_pSuite get_observer_suite(void) { CU_pSuite suite; suite = CU_add_suite("Suite_observer", NULL, NULL); CU_ADD_TEST(suite, test_observer); CU_ADD_TEST(suite, test_monitor); return suite; } #endif /* UNITTEST */