Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/tutorial3/src/modules/notifications/NotificationQueue.cc @ 9527

Last change on this file since 9527 was 9253, checked in by landauf, 12 years ago

fixed crash in NotificationQueue - iterators are not guaranteed to be accessible after erasing them from the set

  • Property svn:eol-style set to native
File size: 16.8 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Damian 'Mozork' Frick
24 *   Co-authors:
25 *      ...
26 *
27 */
28
29/**
30    @file NotificationQueue.cc
31    @brief Implementation of the NotificationQueue class.
32*/
33
34#include "NotificationQueue.h"
35
36#include <map>
37#include <sstream>
38
39#include "core/CoreIncludes.h"
40#include "core/XMLPort.h"
41#include "util/SubString.h"
42
43namespace orxonox
44{
45
46    CreateFactory(NotificationQueue);
47
48    /**
49    @brief
50        Default constructor. Registers and initializes the object.
51    @param creator
52        The creator of the NotificationQueue.
53    */
54    NotificationQueue::NotificationQueue(BaseObject* creator) : BaseObject(creator), Synchronisable(creator), registered_(false)
55    {
56        RegisterObject(NotificationQueue);
57
58        this->size_ = 0;
59        this->tickTime_ = 0.0f;
60        this->maxSize_ = NotificationQueue::DEFAULT_SIZE;
61        this->displayTime_ = NotificationQueue::DEFAULT_DISPLAY_TIME;
62
63        this->creationTime_ = std::time(0);
64
65        this->registerVariables();
66    }
67
68    // TODO move to docu.
69    /**
70    @brief
71        Constructor. Registers and initializes the object.
72    @param creator
73        The creator of the NotificationQueue
74    @param name
75        The name of the new NotificationQueue. It needs to be unique
76    @param senders
77        The senders that are targets of this NotificationQueue, i.e. the names of senders whose Notifications this NotificationQueue displays.
78        The senders need to be seperated by commas.
79    @param size
80        The size (the maximum number of displayed Notifications) of this NotificationQueue.
81    @param displayTime
82        The time during which a Notification is (at most) displayed.
83    */
84
85    /**
86    @brief
87        Destructor.
88    */
89    NotificationQueue::~NotificationQueue()
90    {
91        this->targets_.clear();
92
93        if(this->isRegistered()) // If the NotificationQueue is registered.
94        {
95            this->clear(true);
96
97            // Unregister with the NotificationManager.
98            NotificationManager::getInstance().unregisterQueue(this);
99        }
100    }
101
102    /**
103    @brief
104        Is called when the name of the NotificationQueue has changed.
105        Clears and re-creates the NotificationQueue.
106    */
107    void NotificationQueue::changedName(void)
108    {
109        SUPER(NotificationQueue, changedName);
110
111        if(this->isRegistered())
112            this->clear();
113
114        this->create();
115
116        this->targetsChanged();
117        this->maxSizeChanged();
118        this->displayTimeChanged();
119    }
120
121    /**
122    @brief
123        Creates the NotificationQueue.
124    */
125    void NotificationQueue::create(void)
126    {
127        // Register the NotificationQueue with the NotificationManager.
128        bool queueRegistered = NotificationManager::getInstance().registerQueue(this);
129        this->registered_ = true;
130        if(!queueRegistered) // If the registration has failed.
131        {
132            this->registered_ = false;
133            orxout(internal_error, context::notifications) << "NotificationQueue '" << this->getName() << "' could not be registered." << endl;
134            return;
135        }
136
137        orxout(internal_info, context::notifications) << "NotificationQueue '" << this->getName() << "' created." << endl;
138    }
139
140    /**
141    @brief
142        Updates the queue from time to time.
143    @param dt
144        The time interval that has passed since the last tick.
145    */
146    void NotificationQueue::tick(float dt)
147    {
148        this->tickTime_ += dt; // Add the time interval that has passed to the time counter.
149        if(this->displayTime_ != INF && this->tickTime_ >= 1.0) // If the time counter is greater than 1 s all Notifications that have expired are removed, if it is smaller we wait to the next tick.
150        {
151            this->timeLimit_.time = std::time(0)-this->displayTime_; // Container containing the current time.
152
153            std::multiset<NotificationContainer*, NotificationContainerCompare>::iterator it = this->ordering_.begin();
154            // Iterate through all elements whose creation time is smaller than the current time minus the display time.
155            while(it != this->ordering_.upper_bound(&this->timeLimit_))
156            {
157                this->remove(it); // Remove the Notifications that have expired.
158                it = this->ordering_.begin();
159            }
160
161            this->tickTime_ = this->tickTime_ - (int)this->tickTime_; // Reset time counter.
162        }
163    }
164
165    void NotificationQueue::XMLPort(Element& xmlelement, XMLPort::Mode mode)
166    {
167        SUPER(NotificationQueue, XMLPort, xmlelement, mode);
168
169        XMLPortParam(NotificationQueue, "targets", setTargets, getTargets, xmlelement, mode).defaultValues(NotificationListener::ALL);
170        XMLPortParam(NotificationQueue, "size", setMaxSize, getMaxSize, xmlelement, mode);
171        XMLPortParam(NotificationQueue, "displayTime", setDisplayTime, getDisplayTime, xmlelement, mode);
172    }
173
174
175    /**
176    @brief
177        Registers Variables to be Synchronised.
178        Registers Variables which have to be synchronised to the network system.
179      */
180    void NotificationQueue::registerVariables()
181    {
182        registerVariable( this->name_, VariableDirection::ToClient, new NetworkCallback<NotificationQueue>(this, &NotificationQueue::changedName));
183        registerVariable( this->maxSize_, VariableDirection::ToClient, new NetworkCallback<NotificationQueue>(this, &NotificationQueue::maxSizeChanged));
184        registerVariable( this->targets_, VariableDirection::ToClient, new NetworkCallback<NotificationQueue>(this, &NotificationQueue::targetsChanged));
185        registerVariable( this->displayTime_, VariableDirection::ToClient, new NetworkCallback<NotificationQueue>(this, &NotificationQueue::displayTimeChanged));
186    }
187
188    /**
189    @brief
190        Updates the NotificationQueue.
191        Updates by clearing the queue and requesting all relevant Notifications from the NotificationManager and inserting them into the queue.
192        This is called by the NotificationManager when the Notifications have changed so much, that the NotificationQueue may have to re-initialize his operations.
193    */
194    void NotificationQueue::update(void)
195    {
196        this->clear();
197
198        std::multimap<std::time_t, Notification*>* notifications = new std::multimap<std::time_t, Notification*>;
199        // Get the Notifications sent in the interval from now to now minus the display time.
200        if(this->displayTime_ == INF)
201            NotificationManager::getInstance().getNewestNotifications(this, notifications, this->getMaxSize());
202        else
203            NotificationManager::getInstance().getNotifications(this, notifications, this->displayTime_);
204
205        if(!notifications->empty())
206        {
207            // Add all Notifications that have been created after this NotificationQueue was created.
208            for(std::multimap<std::time_t, Notification*>::iterator it = notifications->begin(); it != notifications->end(); it++)
209            {
210                if(it->first >= this->creationTime_)
211                    this->push(it->second, it->first);
212            }
213        }
214
215        delete notifications;
216
217        orxout(verbose, context::notifications) << "NotificationQueue '" << this->getName() << "' updated." << endl;
218    }
219
220    /**
221    @brief
222        Updates the NotificationQueue by adding an new Notification.
223    @param notification
224        Pointer to the Notification.
225    @param time
226        The time the Notification was sent.
227    */
228    void NotificationQueue::update(Notification* notification, const std::time_t & time)
229    {
230        assert(notification);
231
232        this->push(notification, time);
233
234        orxout(verbose, context::notifications) << "NotificationQueue '" << this->getName() << "' updated. A new Notification has been added." << endl;
235    }
236
237    /**
238    @brief
239        Adds (pushes) a Notification to the NotificationQueue.
240        It inserts it into the storage containers, creates a corresponding container and pushes the notification message to the GUI.
241    @param notification
242        The Notification to be pushed.
243    @param time
244        The time when the Notification has been sent.
245    */
246    void NotificationQueue::push(Notification* notification, const std::time_t & time)
247    {
248        assert(notification);
249
250        NotificationContainer* container = new NotificationContainer;
251        container->notification = notification;
252        container->time = time;
253
254        // If the maximum size of the NotificationQueue has been reached the last (least recently added) Notification is removed.
255        if(this->getSize() >= this->getMaxSize())
256            this->pop();
257
258        this->size_++;
259
260        this->ordering_.insert(container);
261        // Insert the Notification at the begin of the list (vector, actually).
262        this->notifications_.insert(this->notifications_.begin(), container);
263
264        // Inform that a Notification was pushed.
265        this->notificationPushed(notification);
266
267        orxout(verbose_more, context::notifications) << "Notification \"" << notification->getMessage() << "\" pushed to NotificationQueue '" << this->getName() << "'" << endl;
268        orxout(internal_info, context::notifications) << "NotificationQueue \"" << this->getName() << "\": " << notification->getMessage() << endl;
269    }
270
271    /**
272    @brief
273        Removes (pops) the least recently added Notification form the NotificationQueue.
274    */
275    void NotificationQueue::pop(void)
276    {
277        NotificationContainer* container = this->notifications_.back();
278        // Get all the NotificationContainers that were sent the same time the NotificationContainer we want to pop was sent.
279        std::pair<std::multiset<NotificationContainer*, NotificationContainerCompare>::iterator, std::multiset<NotificationContainer*, NotificationContainerCompare>::iterator> iterators = this->ordering_.equal_range(container);
280
281        // Iterate through all suspects and remove the container as soon as we find it.
282        for(std::multiset<NotificationContainer*, NotificationContainerCompare>::iterator it = iterators.first; it != iterators.second; it++)
283        {
284            if(container == *it)
285            {
286                orxout(verbose_more, context::notifications) << "Notification \"" << (*it)->notification->getMessage() << "\" popped from NotificationQueue '" << this->getName() << "'" << endl;
287                this->ordering_.erase(it);
288                break;
289            }
290        }
291        this->notifications_.pop_back();
292
293        this->size_--;
294
295        delete container;
296
297        // Inform that a Notification was popped.
298        this->notificationPopped();
299    }
300
301    /**
302    @brief
303        Removes the Notification that is stored in the input NotificationContainer.
304    @param containerIterator
305        An iterator to the NotificationContainer to be removed.
306    */
307    void NotificationQueue::remove(const std::multiset<NotificationContainer*, NotificationContainerCompare>::iterator& containerIterator)
308    {
309        std::vector<NotificationContainer*>::iterator it = std::find(this->notifications_.begin(), this->notifications_.end(), *containerIterator);
310        // Get the index at which the Notification is.
311        std::vector<NotificationContainer*>::difference_type index = it - this->notifications_.begin ();
312
313        orxout(verbose_more, context::notifications) << "Notification \"" << (*it)->notification->getMessage() << "\" removed from NotificationQueue '" << this->getName() << "'" << endl;
314
315        delete *containerIterator;
316
317        this->ordering_.erase(containerIterator);
318        this->notifications_.erase(it);
319
320        this->size_--;
321
322        // TODO: index automatically cast?
323        // Inform that a Notification was removed.
324        this->notificationRemoved(index);
325    }
326
327    /**
328    @brief
329        Clears the NotificationQueue by removing all NotificationContainers.
330    @param noGraphics
331        If this is set to true the GUI is not informed of the clearing of the NotificationQueue. This is needed only internally.
332    */
333    void NotificationQueue::clear(bool noGraphics)
334    {
335        orxout(verbose, context::notifications) << "Clearing NotificationQueue " << this->getName() << "." << endl;
336        this->ordering_.clear();
337        // Delete all NotificationContainers in the list.
338        for(std::vector<NotificationContainer*>::iterator it = this->notifications_.begin(); it != this->notifications_.end(); it++)
339            delete *it;
340
341        this->notifications_.clear();
342        this->size_ = 0;
343    }
344
345    /**
346    @brief
347        Sets the name of the NotificationQueue.
348    @param name
349        The name to be set.
350    */
351    void NotificationQueue::setName(const std::string& name)
352    {
353        this->name_ = name;
354    }
355
356    /**
357    @brief
358        Sets the maximum number of displayed Notifications.
359    @param size
360        The size to be set.
361    */
362    void NotificationQueue::setMaxSize(unsigned int size)
363    {
364        if(this->maxSize_ == size)
365            return;
366
367        if(size == 0)
368        {
369            orxout(internal_warning, context::notifications) << "Trying to set maximal size of NotificationQueue '" << this->getName() << "' to 0. Ignoring..." << endl;
370            return;
371        }
372
373        this->maxSize_ = size;
374        this->maxSizeChanged();
375    }
376
377    /**
378    @brief
379        Is called when the maximum number of displayed Notifications has changed.
380    */
381    void NotificationQueue::maxSizeChanged(void)
382    {
383        if(this->isRegistered())
384            this->update();
385    }
386
387    /**
388    @brief
389        Sets the maximum number of seconds a Notification is displayed.
390    @param time
391        The number of seconds a Notification is displayed.
392    */
393    void NotificationQueue::setDisplayTime(int time)
394    {
395        if(this->displayTime_ == time)
396            return;
397
398        if(time != NotificationQueue::INF && time <= 0)
399        {
400            orxout(internal_warning, context::notifications) << "Trying to set display time of NotificationQueue '" << this->getName() << "' to non-positive value. Ignoring..." << endl;
401        }
402
403        this->displayTime_ = time;
404        this->displayTimeChanged();
405    }
406
407    /**
408    @brief
409        Is called when the maximum number of seconds a Notification is displayed has changed.
410    */
411    void NotificationQueue::displayTimeChanged(void)
412    {
413        if(this->isRegistered())
414            this->update();
415    }
416
417    /**
418    @brief
419        Produces all targets of the NotificationQueue concatenated as string, with commas (',') as separators.
420    @return
421        Returns the targets as a string.
422    */
423    const std::string& NotificationQueue::getTargets(void) const
424    {
425        std::stringstream stream;
426        bool first = true;
427        // Iterate through the set of targets.
428        for(std::set<std::string>::const_iterator it = this->targets_.begin(); it != this->targets_.end(); it++)
429        {
430            if(!first)
431                stream << ", ";
432            else
433                first = false;
434            stream << *it;
435        }
436
437        return *(new std::string(stream.str()));
438    }
439
440    /**
441    @brief
442        Sets the targets of the NotificationQueue.
443        The targets are the senders whose Notifications are displayed in this queue.
444    @param targets
445        Accepts a string of targets, each separated by commas (','), spaces are ignored.
446    */
447    void NotificationQueue::setTargets(const std::string & targets)
448    {
449        this->targets_.clear();
450
451        SubString string = SubString(targets, ",", " ", false);
452        for(unsigned int i = 0; i < string.size(); i++)
453            this->targets_.insert(string[i]);
454
455        this->targetsChanged();
456    }
457
458    /**
459    @brief
460        Is called when the NotificationQueue's targets have changed.
461    */
462    void NotificationQueue::targetsChanged(void)
463    {
464        // TODO: Why?
465        if(this->isRegistered())
466        {
467            NotificationManager::getInstance().unregisterQueue(this);
468            NotificationManager::getInstance().registerQueue(this);
469        }
470    }
471
472    /**
473    @brief
474        Pops all Notifications from the NotificationQueue.
475    @return
476        Returns true if successful, false if not.
477    */
478    bool NotificationQueue::tidy(void)
479    {
480        while(this->size_ > 0)
481            this->pop();
482        return true;
483    }
484
485}
486
Note: See TracBrowser for help on using the repository browser.