From 9f1eeaee19576584fab930b6d407d0c5b307dacb Mon Sep 17 00:00:00 2001 From: Albrecht Schlosser Date: Sun, 8 Feb 2009 14:44:15 +0000 Subject: Added the helper class Fl_Watch to simplify safe handling of widget deletion in callbacks. This is used e.g. in Fl_Widget::do_callback() to prevent accessing widgets after deletion in the callback. Documentation adjusted, Fl_Menu_Button.cxx changed to use Fl_Watch instead of Fl::watch_widget_pointer. Fl::watch_widget_pointer() and Fl::release_widget_pointer() have been modified to use an array without "holes" (NULL pointers) for storing the widget pointers for faster access: Fl::release_widget_pointer() now shifts pointers to close gaps of freed pointers. git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@6651 ea41ed52-d2ee-0310-a9c1-e6b18d33e121 --- src/Fl.cxx | 114 +++++++++++++++++++++++++++++++++---------------- src/Fl_Menu_Button.cxx | 6 +-- src/Fl_Widget.cxx | 15 +++++++ 3 files changed, 94 insertions(+), 41 deletions(-) (limited to 'src') diff --git a/src/Fl.cxx b/src/Fl.cxx index 2b3ae89d1..0e5a1b251 100644 --- a/src/Fl.cxx +++ b/src/Fl.cxx @@ -1518,40 +1518,47 @@ static int max_widget_watch = 0; /** Adds a widget pointer to the widget watch list. + + \note Internal use only, please use class Fl_Watch instead. - This should be used, if it is possible that a widget might be deleted during - a callback or similar function. The widget pointer must be added to the - watch list before calling the callback. After the callback the widget - pointer can be queried, if it is NULL. \e If it is NULL, then the widget has been - deleted during the callback and must not be accessed anymore. If the widget - pointer is \e not NULL, then the widget has not been deleted and can be accessed - safely. + This can be used, if it is possible that a widget might be deleted during + a callback or similar function. The widget pointer must be added to the + watch list before calling the callback. After the callback the widget + pointer can be queried, if it is NULL. \e If it is NULL, then the widget has been + deleted during the callback and must not be accessed anymore. If the widget + pointer is \e not NULL, then the widget has not been deleted and can be accessed + safely. - After accessing the widget, the widget pointer should be released from the - watch list by calling Fl::release_widget_pointer(). + After accessing the widget, the widget pointer must be released from the + watch list by calling Fl::release_widget_pointer(). - Example for a button that is clicked (from its handle() method): - \code - Fl_Widget *wp = this; // save 'this' in a pointer variable - Fl::watch_widget_pointer(wp); // add the pointer to the watch list - set_changed(); // set the changed flag - do_callback(); // call the callback - if (!wp) { // the widget has been deleted + Example for a button that is clicked (from its handle() method): + \code + Fl_Widget *wp = this; // save 'this' in a pointer variable + Fl::watch_widget_pointer(wp); // add the pointer to the watch list + set_changed(); // set the changed flag + do_callback(); // call the callback + if (!wp) { // the widget has been deleted - // DO NOT ACCESS THE DELETED WIDGET ! + // DO NOT ACCESS THE DELETED WIDGET ! - } else { // the widget still exists - clear_changed(); // reset the changed flag - } + } else { // the widget still exists + clear_changed(); // reset the changed flag + } - Fl::release_widget_pointer(wp); // remove the pointer from the watch list - \endcode + Fl::release_widget_pointer(wp); // remove the pointer from the watch list + \endcode - This works, because all widgets call Fl::clear_widget_pointer() in their - destructors. - - \see Fl::release_widget_pointer() - \see Fl::clear_widget_pointer() + This works, because all widgets call Fl::clear_widget_pointer() in their + destructors. + + \see Fl::release_widget_pointer() + \see Fl::clear_widget_pointer() + + An easier and more convenient method to control widget deletion during + callbacks is to use the class Fl_Watch with a local (automatic) variable. + + \see class Fl_Watch */ void Fl::watch_widget_pointer(Fl_Widget *&w) { @@ -1560,17 +1567,16 @@ void Fl::watch_widget_pointer(Fl_Widget *&w) for (i=0; i %8p\n", + num_widget_watch,num_widget_watch,wp,*wp); + fflush(stdout); +#endif // DEBUG } /** @@ -1578,19 +1584,33 @@ void Fl::watch_widget_pointer(Fl_Widget *&w) This is used to remove a widget pointer that has been added to the watch list with Fl::watch_widget_pointer(), when it is not needed anymore. + + \note Internal use only, please use class Fl_Watch instead. \see Fl::watch_widget_pointer() */ void Fl::release_widget_pointer(Fl_Widget *&w) { Fl_Widget **wp = &w; - int i; + int i,j=0; for (i=0; i %8p\n", + i+1,num_widget_watch,wp,*wp); + } +#endif //DEBUG } + num_widget_watch = j; +#ifdef DEBUG + printf (" num_widget_watch = %d\n\n",num_widget_watch); + fflush(stdout); +#endif // DEBUG + return; } /** Clears a widget pointer \e in the watch list. @@ -1598,6 +1618,8 @@ void Fl::release_widget_pointer(Fl_Widget *&w) This is called when a widget is destroyed (by its destructor). You should never call this directly. + \note Internal use only, please use class Fl_Watch instead. + This method searches the widget watch list for pointers to the widget and clears all pointers that point to it. Widget pointers can be added to the widget watch list by calling Fl::watch_widget_pointer(). @@ -1615,6 +1637,24 @@ void Fl::clear_widget_pointer(Fl_Widget const *w) } } +// Helper class Fl_Watch + +/** + The constructor adds a widget to the watch list. +*/ +Fl_Watch::Fl_Watch(Fl_Widget *wi) { + + wp_ = wi; + Fl::watch_widget_pointer(wp_); // add pointer to watch list +} + +/** + The destructor removes a widget from the watch list. +*/ +Fl_Watch::~Fl_Watch() { + + Fl::release_widget_pointer(wp_); // remove pointer from watch list +} // // End of "$Id$". diff --git a/src/Fl_Menu_Button.cxx b/src/Fl_Menu_Button.cxx index 988a6e727..9389ca49f 100644 --- a/src/Fl_Menu_Button.cxx +++ b/src/Fl_Menu_Button.cxx @@ -58,8 +58,7 @@ const Fl_Menu_Item* Fl_Menu_Button::popup() { const Fl_Menu_Item* m; pressed_menu_button_ = this; redraw(); - Fl_Widget *mb = this; - Fl::watch_widget_pointer(mb); + Fl_Watch mb(this); if (!box() || type()) { m = menu()->popup(Fl::event_x(), Fl::event_y(), label(), mvalue(), this); } else { @@ -67,8 +66,7 @@ const Fl_Menu_Item* Fl_Menu_Button::popup() { } picked(m); pressed_menu_button_ = 0; - if (mb) mb->redraw(); - Fl::release_widget_pointer(mb); + if (mb.exists()) redraw(); return m; } diff --git a/src/Fl_Widget.cxx b/src/Fl_Widget.cxx index 550f1eb1c..740dad6fd 100644 --- a/src/Fl_Widget.cxx +++ b/src/Fl_Widget.cxx @@ -293,6 +293,21 @@ Fl_Widget::copy_label(const char *a) { redraw_label(); } +/** Calls the widget callback. + + Causes a widget to invoke its callback function with arbitrary arguments. + \param[in] o call the callback with \a o as the widget argument + \param[in] arg call the callback with \a arg as the user data argument + \see callback() +*/ +void +Fl_Widget::do_callback(Fl_Widget* o,void* arg) { + Fl_Watch wp(o); + callback_(o,arg); + if (wp.deleted()) return; + if (callback_ != default_callback) + clear_changed(); +} // // End of "$Id$". -- cgit v1.2.3