Add RAII wrapper for GTKDisableEvents/GTKEnableEvents() calls

Ensure GTKEnableEvents() is called automatically on scope exit whenever
GTKDisableEvents() is called.

This fixes a couple of potential bugs where GTKEnableEvents() could be not
called if wxCHECK() condition failed and makes the code shorter and safer.
This commit is contained in:
Vadim Zeitlin
2016-02-06 19:10:11 +01:00
parent 72af0d4ca1
commit 7a8684a8bd
7 changed files with 65 additions and 39 deletions

View File

@@ -0,0 +1,41 @@
///////////////////////////////////////////////////////////////////////////////
// Name: wx/gtk/private/eventsdisabler.h
// Purpose: Helper for temporarily disabling events.
// Author: Vadim Zeitlin
// Created: 2016-02-06
// Copyright: (c) 2016 Vadim Zeitlin <vadim@wxwidgets.org>
// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
#ifndef _GTK_PRIVATE_EVENTSDISABLER_H_
#define _GTK_PRIVATE_EVENTSDISABLER_H_
// ----------------------------------------------------------------------------
// wxGtkEventsDisabler: calls GTKDisableEvents() and GTKEnableEvents() in dtor.
// ----------------------------------------------------------------------------
// Template parameter T must be a wxGTK class providing the required methods,
// e.g. wxCheckBox, wxChoice, ...
template <typename T>
class wxGtkEventsDisabler
{
public:
// Disable the events for the specified (non-NULL, having lifetime greater
// than ours) window for the lifetime of this object.
explicit wxGtkEventsDisabler(T* win) : m_win(win)
{
m_win->GTKDisableEvents();
}
~wxGtkEventsDisabler()
{
m_win->GTKEnableEvents();
}
private:
T* const m_win;
wxDECLARE_NO_COPY_TEMPLATE_CLASS(wxGtkEventsDisabler, T);
};
#endif // _GTK_PRIVATE_EVENTSDISABLER_H_

View File

@@ -54,10 +54,10 @@ public:
static wxVisualAttributes static wxVisualAttributes
GetClassDefaultAttributes(wxWindowVariant variant = wxWINDOW_VARIANT_NORMAL); GetClassDefaultAttributes(wxWindowVariant variant = wxWINDOW_VARIANT_NORMAL);
protected:
void GTKDisableEvents(); void GTKDisableEvents();
void GTKEnableEvents(); void GTKEnableEvents();
protected:
virtual wxSize DoGetBestSize() const wxOVERRIDE; virtual wxSize DoGetBestSize() const wxOVERRIDE;
virtual void DoApplyWidgetStyle(GtkRcStyle *style) wxOVERRIDE; virtual void DoApplyWidgetStyle(GtkRcStyle *style) wxOVERRIDE;

View File

@@ -15,6 +15,7 @@
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include "wx/gtk/private/gtk2-compat.h" #include "wx/gtk/private/gtk2-compat.h"
#include "wx/gtk/private/eventsdisabler.h"
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// data // data
@@ -45,7 +46,7 @@ static void gtk_checkbox_toggled_callback(GtkWidget *widget, wxCheckBox *cb)
bool active = gtk_toggle_button_get_active(toggle) != 0; bool active = gtk_toggle_button_get_active(toggle) != 0;
bool inconsistent = gtk_toggle_button_get_inconsistent(toggle) != 0; bool inconsistent = gtk_toggle_button_get_inconsistent(toggle) != 0;
cb->GTKDisableEvents(); wxGtkEventsDisabler<wxCheckBox> noEvents(cb);
if (!active && !inconsistent) if (!active && !inconsistent)
{ {
@@ -67,8 +68,6 @@ static void gtk_checkbox_toggled_callback(GtkWidget *widget, wxCheckBox *cb)
{ {
wxFAIL_MSG(wxT("3state wxCheckBox in unexpected state!")); wxFAIL_MSG(wxT("3state wxCheckBox in unexpected state!"));
} }
cb->GTKEnableEvents();
} }
else else
{ {
@@ -171,11 +170,8 @@ void wxCheckBox::SetValue( bool state )
if (state == GetValue()) if (state == GetValue())
return; return;
GTKDisableEvents(); wxGtkEventsDisabler<wxCheckBox> noEvents(this);
gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(m_widgetCheckbox), state ); gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(m_widgetCheckbox), state );
GTKEnableEvents();
} }
bool wxCheckBox::GetValue() const bool wxCheckBox::GetValue() const

View File

@@ -19,6 +19,7 @@
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include "wx/gtk/private.h" #include "wx/gtk/private.h"
#include "wx/gtk/private/gtk2-compat.h" #include "wx/gtk/private/gtk2-compat.h"
#include "wx/gtk/private/eventsdisabler.h"
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// GTK callbacks // GTK callbacks
@@ -155,7 +156,7 @@ void wxChoice::DoClear()
{ {
wxCHECK_RET( m_widget != NULL, wxT("invalid control") ); wxCHECK_RET( m_widget != NULL, wxT("invalid control") );
GTKDisableEvents(); wxGtkEventsDisabler<wxChoice> noEvents(this);
GtkComboBox* combobox = GTK_COMBO_BOX( m_widget ); GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
GtkTreeModel* model = gtk_combo_box_get_model( combobox ); GtkTreeModel* model = gtk_combo_box_get_model( combobox );
@@ -166,8 +167,6 @@ void wxChoice::DoClear()
if (m_strings) if (m_strings)
m_strings->Clear(); m_strings->Clear();
GTKEnableEvents();
InvalidateBestSize(); InvalidateBestSize();
} }
@@ -290,12 +289,10 @@ void wxChoice::SetSelection( int n )
{ {
wxCHECK_RET( m_widget != NULL, wxT("invalid control") ); wxCHECK_RET( m_widget != NULL, wxT("invalid control") );
GTKDisableEvents(); wxGtkEventsDisabler<wxChoice> noEvents(this);
GtkComboBox* combobox = GTK_COMBO_BOX( m_widget ); GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
gtk_combo_box_set_active( combobox, n ); gtk_combo_box_set_active( combobox, n );
GTKEnableEvents();
} }
void wxChoice::SetColumns(int n) void wxChoice::SetColumns(int n)

View File

@@ -30,6 +30,7 @@
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include "wx/gtk/private.h" #include "wx/gtk/private.h"
#include "wx/gtk/private/eventsdisabler.h"
#include "wx/gtk/private/gtk2-compat.h" #include "wx/gtk/private/gtk2-compat.h"
#include "wx/gtk/private/object.h" #include "wx/gtk/private/object.h"
#include "wx/gtk/private/treeentry_gtk.h" #include "wx/gtk/private/treeentry_gtk.h"
@@ -479,13 +480,13 @@ void wxListBox::DoClear()
{ {
wxCHECK_RET( m_treeview != NULL, wxT("invalid listbox") ); wxCHECK_RET( m_treeview != NULL, wxT("invalid listbox") );
GTKDisableEvents(); // just in case {
wxGtkEventsDisabler<wxListBox> noEvents(this);
InvalidateBestSize(); InvalidateBestSize();
gtk_list_store_clear( m_liststore ); /* well, THAT was easy :) */ gtk_list_store_clear( m_liststore ); /* well, THAT was easy :) */
}
GTKEnableEvents();
UpdateOldSelections(); UpdateOldSelections();
} }
@@ -496,7 +497,7 @@ void wxListBox::DoDeleteOneItem(unsigned int n)
InvalidateBestSize(); InvalidateBestSize();
GTKDisableEvents(); // just in case wxGtkEventsDisabler<wxListBox> noEvents(this);
GtkTreeIter iter; GtkTreeIter iter;
wxCHECK_RET( GTKGetIteratorFor(n, &iter), wxT("wrong listbox index") ); wxCHECK_RET( GTKGetIteratorFor(n, &iter), wxT("wrong listbox index") );
@@ -504,8 +505,6 @@ void wxListBox::DoDeleteOneItem(unsigned int n)
// this returns false if iter is invalid (e.g. deleting item at end) but // this returns false if iter is invalid (e.g. deleting item at end) but
// since we don't use iter, we ignore the return value // since we don't use iter, we ignore the return value
gtk_list_store_remove(m_liststore, &iter); gtk_list_store_remove(m_liststore, &iter);
GTKEnableEvents();
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -701,7 +700,7 @@ void wxListBox::DoSetSelection( int n, bool select )
{ {
wxCHECK_RET( m_treeview != NULL, wxT("invalid listbox") ); wxCHECK_RET( m_treeview != NULL, wxT("invalid listbox") );
GTKDisableEvents(); wxGtkEventsDisabler<wxListBox> noEvents(this);
GtkTreeSelection* selection = gtk_tree_view_get_selection(m_treeview); GtkTreeSelection* selection = gtk_tree_view_get_selection(m_treeview);
@@ -709,7 +708,6 @@ void wxListBox::DoSetSelection( int n, bool select )
if ( n == wxNOT_FOUND ) if ( n == wxNOT_FOUND )
{ {
gtk_tree_selection_unselect_all(selection); gtk_tree_selection_unselect_all(selection);
GTKEnableEvents();
return; return;
} }
@@ -728,8 +726,6 @@ void wxListBox::DoSetSelection( int n, bool select )
gtk_tree_model_get_path(GTK_TREE_MODEL(m_liststore), &iter)); gtk_tree_model_get_path(GTK_TREE_MODEL(m_liststore), &iter));
gtk_tree_view_scroll_to_cell(m_treeview, path, NULL, FALSE, 0.0f, 0.0f); gtk_tree_view_scroll_to_cell(m_treeview, path, NULL, FALSE, 0.0f, 0.0f);
GTKEnableEvents();
} }
void wxListBox::DoScrollToCell(int n, float alignY, float alignX) void wxListBox::DoScrollToCell(int n, float alignY, float alignX)

View File

@@ -20,6 +20,7 @@
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include "wx/gtk/private/gtk2-compat.h" #include "wx/gtk/private/gtk2-compat.h"
#include "wx/gtk/private/eventsdisabler.h"
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// data // data
@@ -231,9 +232,8 @@ gtk_event_after(GtkRange* range, GdkEvent* event, wxSlider* win)
ProcessScrollEvent(win, wxEVT_SCROLL_THUMBRELEASE); ProcessScrollEvent(win, wxEVT_SCROLL_THUMBRELEASE);
} }
// Keep slider at an integral position // Keep slider at an integral position
win->GTKDisableEvents(); wxGtkEventsDisabler<wxSlider> noEvents(win);
gtk_range_set_value(GTK_RANGE (win->m_scale), win->GetValue()); gtk_range_set_value(GTK_RANGE (win->m_scale), win->GetValue());
win->GTKEnableEvents();
} }
} }
} }
@@ -422,21 +422,20 @@ void wxSlider::SetValue( int value )
void wxSlider::GTKSetValue(int value) void wxSlider::GTKSetValue(int value)
{ {
GTKDisableEvents(); wxGtkEventsDisabler<wxSlider> noEvents(this);
gtk_range_set_value(GTK_RANGE (m_scale), value); gtk_range_set_value(GTK_RANGE (m_scale), value);
// GTK only updates value label if handle moves at least 1 pixel // GTK only updates value label if handle moves at least 1 pixel
gtk_widget_queue_draw(m_scale); gtk_widget_queue_draw(m_scale);
GTKEnableEvents();
} }
void wxSlider::SetRange( int minValue, int maxValue ) void wxSlider::SetRange( int minValue, int maxValue )
{ {
GTKDisableEvents(); wxGtkEventsDisabler<wxSlider> noEvents(this);
if (minValue == maxValue) if (minValue == maxValue)
maxValue++; maxValue++;
gtk_range_set_range(GTK_RANGE (m_scale), minValue, maxValue); gtk_range_set_range(GTK_RANGE (m_scale), minValue, maxValue);
gtk_range_set_increments(GTK_RANGE (m_scale), 1, (maxValue - minValue + 9) / 10); gtk_range_set_increments(GTK_RANGE (m_scale), 1, (maxValue - minValue + 9) / 10);
GTKEnableEvents();
if (HasFlag(wxSL_MIN_MAX_LABELS)) if (HasFlag(wxSL_MIN_MAX_LABELS))
{ {
@@ -471,9 +470,8 @@ int wxSlider::GetMax() const
void wxSlider::SetPageSize( int pageSize ) void wxSlider::SetPageSize( int pageSize )
{ {
GTKDisableEvents(); wxGtkEventsDisabler<wxSlider> noEvents(this);
gtk_range_set_increments(GTK_RANGE (m_scale), GetLineSize(), pageSize); gtk_range_set_increments(GTK_RANGE (m_scale), GetLineSize(), pageSize);
GTKEnableEvents();
} }
int wxSlider::GetPageSize() const int wxSlider::GetPageSize() const
@@ -494,9 +492,8 @@ int wxSlider::GetThumbLength() const
void wxSlider::SetLineSize( int lineSize ) void wxSlider::SetLineSize( int lineSize )
{ {
GTKDisableEvents(); wxGtkEventsDisabler<wxSlider> noEvents(this);
gtk_range_set_increments(GTK_RANGE (m_scale), lineSize, GetPageSize()); gtk_range_set_increments(GTK_RANGE (m_scale), lineSize, GetPageSize());
GTKEnableEvents();
} }
int wxSlider::GetLineSize() const int wxSlider::GetLineSize() const

View File

@@ -22,6 +22,7 @@
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include "wx/gtk/private.h" #include "wx/gtk/private.h"
#include "wx/gtk/private/eventsdisabler.h"
#include "wx/gtk/private/list.h" #include "wx/gtk/private/list.h"
extern bool g_blockEventsOnDrag; extern bool g_blockEventsOnDrag;
@@ -145,11 +146,9 @@ void wxToggleButton::SetValue(bool state)
if (state == GetValue()) if (state == GetValue())
return; return;
GTKDisableEvents(); wxGtkEventsDisabler<wxToggleButton> noEvents(this);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_widget), state); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_widget), state);
GTKEnableEvents();
} }
// bool GetValue() const // bool GetValue() const