summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlbrecht Schlosser <albrechts.fltk@online.de>2009-02-08 14:44:15 +0000
committerAlbrecht Schlosser <albrechts.fltk@online.de>2009-02-08 14:44:15 +0000
commit9f1eeaee19576584fab930b6d407d0c5b307dacb (patch)
tree8c3df25fb2177c024cc7fcd50918748d90699409 /src
parentb30b9e170a0961493c1b256d9c53a1e8309f52b1 (diff)
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
Diffstat (limited to 'src')
-rw-r--r--src/Fl.cxx114
-rw-r--r--src/Fl_Menu_Button.cxx6
-rw-r--r--src/Fl_Widget.cxx15
3 files changed, 94 insertions, 41 deletions
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<num_widget_watch; ++i) {
if (widget_watch[i]==wp) return;
}
- for (i=0; i<num_widget_watch; ++i) {
- if (widget_watch[i]==0L) {
- widget_watch[i] = wp;
- return;
- }
- }
if (num_widget_watch==max_widget_watch) {
max_widget_watch += 8;
widget_watch = (Fl_Widget***)realloc(widget_watch, sizeof(Fl_Widget**)*max_widget_watch);
}
widget_watch[num_widget_watch++] = wp;
+#ifdef DEBUG
+ printf ("\nwatch_widget_pointer: (%d/%d) %8p => %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<num_widget_watch; ++i) {
- if (widget_watch[i]==wp) {
- widget_watch[i] = 0L;
- return;
+ if (widget_watch[i]!=wp) {
+ if (j<i) widget_watch[j] = widget_watch[i]; // fill gap
+ j++;
}
+#ifdef DEBUG
+ else { // found widget pointer
+ printf ("release_widget_pointer: (%d/%d) %8p => %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$".