Code

Node tool: special case node duplication for endnodes - select new endnode
[inkscape.git] / src / extension / timer.cpp
1 /*
2  * Here is where the extensions can get timed on when they load and
3  * unload.  All of the timing is done in here.
4  *
5  * Authors:
6  *   Ted Gould <ted@gould.cx>
7  *
8  * Copyright (C) 2004 Authors
9  *
10  * Released under GNU GPL, read the file 'COPYING' for more information
11  */
15 #include "extension.h"
16 #include "timer.h"
18 namespace Inkscape {
19 namespace Extension {
21 #define TIMER_SCALE_VALUE  20
23 ExpirationTimer * ExpirationTimer::timer_list = NULL;
24 ExpirationTimer * ExpirationTimer::idle_start = NULL;
25 long ExpirationTimer::timeout = 240;
26 bool ExpirationTimer::timer_started = false;
28 /** \brief  Create a new expiration timer
29     \param  in_extension  Which extension this timer is related to
31     This function creates the timer, and sets the time to the current
32     time, plus what ever the current timeout is.  Also, if this is
33     the first timer extension, the timer is kicked off.  This function
34     also sets up the circularly linked list of all the timers.
35 */
36 ExpirationTimer::ExpirationTimer (Extension * in_extension):
37     locked(0),
38     extension(in_extension)
39 {
40     /* Fix Me! */
41     if (timer_list == NULL) {
42         next = this;
43         timer_list = this;
44     } else {
45         next = timer_list->next;
46         timer_list->next = this;
47     }
49     expiration.assign_current_time();
50     expiration += timeout;
51     
52     if (!timer_started) {
53         Glib::signal_timeout().connect(sigc::ptr_fun(&timer_func), timeout * 1000 / TIMER_SCALE_VALUE);
54         timer_started = true;
55     }
57     return;
58 }
60 /** \brief  Deletes a \c ExpirationTimer
61     
62     The most complex thing that this function does is remove the timer
63     from the circularly linked list.  If this is the only entry in the
64     list that is easy, otherwise all the entries must be found, and this
65     one removed from the list.
66 */
67 ExpirationTimer::~ExpirationTimer(void)
68 {
69     if (this != next) {
70         /* This will remove this entry from the circularly linked
71            list. */
72         ExpirationTimer * prev;
73         for (prev = timer_list;
74                 prev->next != this;
75                 prev = prev->next){};
76         prev->next = next;
78         if (idle_start == this)
79             idle_start = next;
81         /* This may occur more than you think, just because the guy
82            doing most of the deletions is the idle function, who tracks
83            where it is looking using the \c timer_list variable. */
84         if (timer_list == this)
85             timer_list = next;
86     } else {
87         /* If we're the only entry in the list, the list needs to go
88            to NULL */
89         timer_list = NULL;
90         idle_start = NULL;
91     }
93     return;
94 }
96 /** \brief  Touches the timer to extend the length before it expires
98     Basically it adds more time to the timer.  One thing that is kinda
99     tricky is that it adds half the time remaining back into the timer.
100     This allows for some extensions that are used regularly to having
101     extended expiration times.  So, in the end, they stay loaded longer.
102     Extensions that are only used once will expire at a standard rate
103     set by \c timeout.
104 */
105 void
106 ExpirationTimer::touch (void)
108     Glib::TimeVal current;
109     current.assign_current_time();
111     long time_left = (long)(expiration.as_double() - current.as_double());
112     if (time_left < 0) time_left = 0;
113     time_left /= 2;
115     expiration = current + timeout + time_left;
116     return;
119 /** \brief  Check to see if the timer has expired
121     Checks the time against the current time.
122 */
123 bool
124 ExpirationTimer::expired (void) const
126     if (locked > 0) return false;
128     Glib::TimeVal current;
129     current.assign_current_time();
130     return expiration < current;
133 // int idle_cnt = 0;
135 /** \brief  This function goes in the idle loop to find expired extensions
136     \return Whether the function should be requeued or not
138     This function first insures that there is a timer list, and then checks
139     to see if the one on the top of the list has expired.  If it has
140     expired it unloads the module.  By unloading the module, the timer
141     gets deleted (happens in the unload function).  If the list is
142     no empty, the function returns that it should be dequeued and sets
143     the \c timer_started variable so that the timer will be reissued when
144     a timer is added.  If there is entries left, but the next one is
145     where this function started, then the timer is set up.  The timer
146     will then re-add the idle loop function when it runs.
147 */ 
148 bool
149 ExpirationTimer::idle_func (void)
151     // std::cout << "Idle func pass: " << idle_cnt++ << "  timer list: " << timer_list << std::endl;
153     /* see if this is the last */
154     if (timer_list == NULL) {
155         timer_started = false;
156         return false;
157     }
159     /* evalutate current */
160     if (timer_list->expired()) {
161         timer_list->extension->set_state(Extension::STATE_UNLOADED);
162     }
164     /* see if this is the last */
165     if (timer_list == NULL) {
166         timer_started = false;
167         return false;
168     }
170     if (timer_list->next == idle_start) {
171         /* if so, set up the timer and return FALSE */
172         /* Note: This may cause one to be missed on the evaluation if
173                  the one before it expires and it is last in the list.
174                  While this could be taken care of, it isn't worth the
175                  complexity for this lazy removal that we're doing.  It
176                  should get picked up next time */
177         Glib::signal_timeout().connect(sigc::ptr_fun(&timer_func), timeout * 1000 / TIMER_SCALE_VALUE);
178         return false;
179     }
181     /* If nothing else, continue on */
182     timer_list = timer_list->next;
183     return true;
186 /** \brief  A timer function to set up the idle function
187     \return Always false -- to disable the timer
189     This function sets up the idle loop when it runs.  The idle loop is
190     the one that unloads all the extensions.
191 */
192 bool
193 ExpirationTimer::timer_func (void)
195     // std::cout << "Timer func" << std::endl;
196     idle_start = timer_list;
197     // idle_cnt = 0;
198     Glib::signal_idle().connect(sigc::ptr_fun(&idle_func));
199     return false;
202 }; }; /* namespace Inkscape, Extension */
204 /*
205   Local Variables:
206   mode:c++
207   c-file-style:"stroustrup"
208   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
209   indent-tabs-mode:nil
210   fill-column:99
211   End:
212 */
213 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :