Merge branch 'gtk-text-changed-coalesce'

Harmonize wxEVT_TEXT events in wxGTK with other ports.

Also use IME for wxComboBox in wxGTK too.

See https://github.com/wxWidgets/wxWidgets/pull/1400
This commit is contained in:
Vadim Zeitlin
2019-07-16 19:41:32 +02:00
9 changed files with 283 additions and 91 deletions

View File

@@ -145,13 +145,16 @@ protected:
virtual GtkEntry *GetEntry() const wxOVERRIDE virtual GtkEntry *GetEntry() const wxOVERRIDE
{ return m_entry; } { return m_entry; }
virtual int GTKIMFilterKeypress(GdkEventKey* event) const wxOVERRIDE
{ return GTKEntryIMFilterKeypress(event); }
GtkEntry* m_entry; GtkEntry* m_entry;
private: private:
// From wxTextEntry: // From wxTextEntry:
virtual wxWindow *GetEditableWindow() wxOVERRIDE { return this; } virtual wxWindow *GetEditableWindow() wxOVERRIDE { return this; }
virtual GtkEditable *GetEditable() const wxOVERRIDE; virtual GtkEditable *GetEditable() const wxOVERRIDE;
virtual void EnableTextChangedEvents(bool enable) wxOVERRIDE;
void Init(); void Init();

View File

@@ -142,6 +142,8 @@ public:
static wxVisualAttributes static wxVisualAttributes
GetClassDefaultAttributes(wxWindowVariant variant = wxWINDOW_VARIANT_NORMAL); GetClassDefaultAttributes(wxWindowVariant variant = wxWINDOW_VARIANT_NORMAL);
void GTKOnTextChanged() wxOVERRIDE;
protected: protected:
// overridden wxWindow virtual methods // overridden wxWindow virtual methods
virtual wxSize DoGetBestSize() const wxOVERRIDE; virtual wxSize DoGetBestSize() const wxOVERRIDE;
@@ -182,7 +184,6 @@ private:
// overridden wxTextEntry virtual methods // overridden wxTextEntry virtual methods
virtual GtkEditable *GetEditable() const wxOVERRIDE; virtual GtkEditable *GetEditable() const wxOVERRIDE;
virtual GtkEntry *GetEntry() const wxOVERRIDE; virtual GtkEntry *GetEntry() const wxOVERRIDE;
virtual void EnableTextChangedEvents(bool enable) wxOVERRIDE;
// change the font for everything in this control // change the font for everything in this control
void ChangeFontGlobally(); void ChangeFontGlobally();
@@ -196,7 +197,7 @@ private:
// returns either m_text or m_buffer depending on whether the control is // returns either m_text or m_buffer depending on whether the control is
// single- or multi-line; convenient for the GTK+ functions which work with // single- or multi-line; convenient for the GTK+ functions which work with
// both // both
void *GetTextObject() const void *GetTextObject() const wxOVERRIDE
{ {
return IsMultiLine() ? static_cast<void *>(m_buffer) return IsMultiLine() ? static_cast<void *>(m_buffer)
: static_cast<void *>(m_text); : static_cast<void *>(m_text);

View File

@@ -15,6 +15,7 @@ typedef struct _GtkEditable GtkEditable;
typedef struct _GtkEntry GtkEntry; typedef struct _GtkEntry GtkEntry;
class wxTextAutoCompleteData; // private class used only by wxTextEntry itself class wxTextAutoCompleteData; // private class used only by wxTextEntry itself
class wxTextCoalesceData; // another private class
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// wxTextEntry: roughly corresponds to GtkEditable // wxTextEntry: roughly corresponds to GtkEditable
@@ -62,6 +63,16 @@ public:
bool GTKEntryOnInsertText(const char* text); bool GTKEntryOnInsertText(const char* text);
bool GTKIsUpperCase() const { return m_isUpperCase; } bool GTKIsUpperCase() const { return m_isUpperCase; }
// Called from "changed" signal handler (or, possibly, slightly later, when
// coalescing several "changed" signals into a single event) for GtkEntry.
//
// By default just generates a wxEVT_TEXT, but overridden to do more things
// in wxTextCtrl.
virtual void GTKOnTextChanged() { SendTextUpdatedEvent(); }
// Helper functions only used internally.
wxTextCoalesceData* GTKGetCoalesceData() const { return m_coalesceData; }
protected: protected:
// This method must be called from the derived class Create() to connect // This method must be called from the derived class Create() to connect
// the handlers for the clipboard (cut/copy/paste) events. // the handlers for the clipboard (cut/copy/paste) events.
@@ -70,6 +81,10 @@ protected:
// And this one to connect "insert-text" signal. // And this one to connect "insert-text" signal.
void GTKConnectInsertTextSignal(GtkEntry* entry); void GTKConnectInsertTextSignal(GtkEntry* entry);
// Finally this one connects to the "changed" signal on the object returned
// by GetTextObject().
void GTKConnectChangedSignal();
virtual void DoSetValue(const wxString& value, int flags) wxOVERRIDE; virtual void DoSetValue(const wxString& value, int flags) wxOVERRIDE;
virtual wxString DoGetValue() const wxOVERRIDE; virtual wxString DoGetValue() const wxOVERRIDE;
@@ -81,11 +96,24 @@ protected:
virtual bool DoAutoCompleteStrings(const wxArrayString& choices) wxOVERRIDE; virtual bool DoAutoCompleteStrings(const wxArrayString& choices) wxOVERRIDE;
virtual bool DoAutoCompleteCustom(wxTextCompleter *completer) wxOVERRIDE; virtual bool DoAutoCompleteCustom(wxTextCompleter *completer) wxOVERRIDE;
// Override the base class method to use GtkEntry IM context. // Call this from the overridden wxWindow::GTKIMFilterKeypress() to use
virtual int GTKIMFilterKeypress(GdkEventKey* event) const; // GtkEntry IM context.
int GTKEntryIMFilterKeypress(GdkEventKey* event) const;
// If GTKEntryIMFilterKeypress() is not called (as multiline wxTextCtrl
// uses its own IM), call this method instead to still notify wxTextEntry
// about the key press events in the given widget.
void GTKEntryOnKeypress(GtkWidget* widget) const;
static int GTKGetEntryTextLength(GtkEntry* entry); static int GTKGetEntryTextLength(GtkEntry* entry);
// Block/unblock the corresponding GTK signal.
//
// Note that we make it protected in wxGTK as it is called from wxComboBox
// currently.
virtual void EnableTextChangedEvents(bool enable) wxOVERRIDE;
private: private:
// implement this to return the associated GtkEntry or another widget // implement this to return the associated GtkEntry or another widget
// implementing GtkEditable // implementing GtkEditable
@@ -94,6 +122,12 @@ private:
// implement this to return the associated GtkEntry // implement this to return the associated GtkEntry
virtual GtkEntry *GetEntry() const = 0; virtual GtkEntry *GetEntry() const = 0;
// This one exists in order to be overridden by wxTextCtrl which uses
// either GtkEditable or GtkTextBuffer depending on whether it is single-
// or multi-line.
virtual void *GetTextObject() const { return GetEntry(); }
// Various auto-completion-related stuff, only used if any of AutoComplete() // Various auto-completion-related stuff, only used if any of AutoComplete()
// methods are called. // methods are called.
wxTextAutoCompleteData *m_autoCompleteData; wxTextAutoCompleteData *m_autoCompleteData;
@@ -101,6 +135,10 @@ private:
// It needs to call our GetEntry() method. // It needs to call our GetEntry() method.
friend class wxTextAutoCompleteData; friend class wxTextAutoCompleteData;
// Data used for coalescing "changed" events resulting from a single user
// action.
mutable wxTextCoalesceData* m_coalesceData;
bool m_isUpperCase; bool m_isUpperCase;
}; };

View File

@@ -31,6 +31,7 @@
#include "wx/bitmap.h" #include "wx/bitmap.h"
#include "wx/button.h" #include "wx/button.h"
#include "wx/checkbox.h" #include "wx/checkbox.h"
#include "wx/dcclient.h"
#include "wx/radiobox.h" #include "wx/radiobox.h"
#include "wx/statbox.h" #include "wx/statbox.h"
#include "wx/stattext.h" #include "wx/stattext.h"
@@ -986,19 +987,21 @@ void TextWidgetsPage::OnUpdateUIResetButton(wxUpdateUIEvent& event)
(m_radioWrap->GetSelection() != DEFAULTS.wrapStyle) ); (m_radioWrap->GetSelection() != DEFAULTS.wrapStyle) );
} }
void TextWidgetsPage::OnText(wxCommandEvent& WXUNUSED(event)) void TextWidgetsPage::OnText(wxCommandEvent& event)
{ {
// small hack to suppress the very first message: by then the logging is if ( !IsUsingLogWindow() )
// not yet redirected and so initial setting of the text value results in
// an annoying message box
static bool s_firstTime = true;
if ( s_firstTime )
{
s_firstTime = false;
return; return;
}
wxLogMessage("Text ctrl value changed"); // Replace middle of long text with ellipsis just to avoid filling up the
// log control with too much unnecessary stuff.
wxLogMessage("Text control value changed (now '%s')",
wxControl::Ellipsize
(
event.GetString(),
wxClientDC(this),
wxELLIPSIZE_MIDDLE,
GetTextExtent('W').x*100
));
} }
void TextWidgetsPage::OnTextEnter(wxCommandEvent& event) void TextWidgetsPage::OnTextEnter(wxCommandEvent& event)

View File

@@ -139,6 +139,13 @@ const wxChar *WidgetsCategories[MAX_PAGES] = {
class WidgetsApp : public wxApp class WidgetsApp : public wxApp
{ {
public: public:
WidgetsApp()
{
#if USE_LOG
m_logTarget = NULL;
#endif // USE_LOG
}
// override base class virtuals // override base class virtuals
// ---------------------------- // ----------------------------
@@ -146,8 +153,20 @@ public:
// initialization (doing it here and not in the ctor allows to have an error // initialization (doing it here and not in the ctor allows to have an error
// return: if OnInit() returns false, the application terminates) // return: if OnInit() returns false, the application terminates)
virtual bool OnInit() wxOVERRIDE; virtual bool OnInit() wxOVERRIDE;
// real implementation of WidgetsPage method with the same name
bool IsUsingLogWindow() const;
private:
#if USE_LOG
wxLog* m_logTarget;
#endif // USE_LOG
wxDECLARE_NO_COPY_CLASS(WidgetsApp);
}; };
wxDECLARE_APP(WidgetsApp); // This provides a convenient wxGetApp() accessor.
// Define a new frame type: this is going to be our main frame // Define a new frame type: this is going to be our main frame
class WidgetsFrame : public wxFrame class WidgetsFrame : public wxFrame
{ {
@@ -375,9 +394,22 @@ bool WidgetsApp::OnInit()
wxFrame *frame = new WidgetsFrame(title + " widgets demo"); wxFrame *frame = new WidgetsFrame(title + " widgets demo");
frame->Show(); frame->Show();
#if USE_LOG
m_logTarget = wxLog::GetActiveTarget();
#endif // USE_LOG
return true; return true;
} }
bool WidgetsApp::IsUsingLogWindow() const
{
#if USE_LOG
return wxLog::GetActiveTarget() == m_logTarget;
#else // !USE_LOG
return false;
#endif // USE_LOG
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// WidgetsFrame construction // WidgetsFrame construction
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -1200,8 +1232,13 @@ void WidgetsFrame::OnSetHint(wxCommandEvent& WXUNUSED(event))
void WidgetsFrame::OnWidgetFocus(wxFocusEvent& event) void WidgetsFrame::OnWidgetFocus(wxFocusEvent& event)
{ {
wxLogMessage("Widgets %s focus", // Don't show annoying message boxes when starting or closing the sample,
event.GetEventType() == wxEVT_SET_FOCUS ? "got" : "lost"); // only log these events in our own logger.
if ( wxGetApp().IsUsingLogWindow() )
{
wxLogMessage("Widgets %s focus",
event.GetEventType() == wxEVT_SET_FOCUS ? "got" : "lost");
}
event.Skip(); event.Skip();
} }
@@ -1384,3 +1421,9 @@ wxCheckBox *WidgetsPage::CreateCheckBoxAndAddToSizer(wxSizer *sizer,
return checkbox; return checkbox;
} }
/* static */
bool WidgetsPage::IsUsingLogWindow()
{
return wxGetApp().IsUsingLogWindow();
}

View File

@@ -155,6 +155,10 @@ public:
// the default attributes for the widget // the default attributes for the widget
static WidgetAttributes& GetAttrs(); static WidgetAttributes& GetAttrs();
// return true if we're showing logs in the log window (always the case
// except during startup and shutdown)
static bool IsUsingLogWindow();
protected: protected:
// several helper functions for page creation // several helper functions for page creation

View File

@@ -27,14 +27,6 @@
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
extern "C" { extern "C" {
static void
gtkcombobox_text_changed_callback( GtkWidget *WXUNUSED(widget), wxComboBox *combo )
{
wxCommandEvent event( wxEVT_TEXT, combo->GetId() );
event.SetString( combo->GetValue() );
event.SetEventObject( combo );
combo->HandleWindowEvent( event );
}
static void static void
gtkcombobox_changed_callback( GtkWidget *WXUNUSED(widget), wxComboBox *combo ) gtkcombobox_changed_callback( GtkWidget *WXUNUSED(widget), wxComboBox *combo )
@@ -185,9 +177,7 @@ bool wxComboBox::Create( wxWindow *parent, wxWindowID id, const wxString& value,
gtk_entry_set_text( entry, wxGTK_CONV(value) ); gtk_entry_set_text( entry, wxGTK_CONV(value) );
} }
g_signal_connect_after (entry, "changed", GTKConnectChangedSignal();
G_CALLBACK (gtkcombobox_text_changed_callback), this);
GTKConnectInsertTextSignal(entry); GTKConnectInsertTextSignal(entry);
GTKConnectClipboardSignals(GTK_WIDGET(entry)); GTKConnectClipboardSignals(GTK_WIDGET(entry));
} }
@@ -219,7 +209,7 @@ void wxComboBox::GTKCreateComboBoxWidget()
GtkEditable *wxComboBox::GetEditable() const GtkEditable *wxComboBox::GetEditable() const
{ {
return GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(m_widget))); return GTK_EDITABLE(m_entry);
} }
void wxComboBox::OnChar( wxKeyEvent &event ) void wxComboBox::OnChar( wxKeyEvent &event )
@@ -248,23 +238,6 @@ void wxComboBox::OnChar( wxKeyEvent &event )
event.Skip(); event.Skip();
} }
void wxComboBox::EnableTextChangedEvents(bool enable)
{
if ( !GetEntry() )
return;
if ( enable )
{
g_signal_handlers_unblock_by_func(gtk_bin_get_child(GTK_BIN(m_widget)),
(gpointer)gtkcombobox_text_changed_callback, this);
}
else // disable
{
g_signal_handlers_block_by_func(gtk_bin_get_child(GTK_BIN(m_widget)),
(gpointer)gtkcombobox_text_changed_callback, this);
}
}
void wxComboBox::GTKDisableEvents() void wxComboBox::GTKDisableEvents()
{ {
EnableTextChangedEvents(false); EnableTextChangedEvents(false);

View File

@@ -567,24 +567,6 @@ gtk_textctrl_populate_popup( GtkEntry *WXUNUSED(entry), GtkMenu *menu, wxTextCtr
} }
} }
//-----------------------------------------------------------------------------
// "changed"
//-----------------------------------------------------------------------------
extern "C" {
static void
gtk_text_changed_callback( GtkWidget *WXUNUSED(widget), wxTextCtrl *win )
{
if ( win->IgnoreTextUpdate() )
return;
if ( win->MarkDirtyOnChange() )
win->MarkDirty();
win->SendTextUpdatedEvent();
}
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// "mark_set" // "mark_set"
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@@ -774,16 +756,7 @@ bool wxTextCtrl::Create( wxWindow *parent,
} }
// We want to be notified about text changes. // We want to be notified about text changes.
if (multi_line) GTKConnectChangedSignal();
{
g_signal_connect (m_buffer, "changed",
G_CALLBACK (gtk_text_changed_callback), this);
}
else
{
g_signal_connect (m_text, "changed",
G_CALLBACK (gtk_text_changed_callback), this);
}
// Catch to disable focus out handling // Catch to disable focus out handling
g_signal_connect (m_text, "populate_popup", g_signal_connect (m_text, "populate_popup",
@@ -885,7 +858,11 @@ GtkEntry *wxTextCtrl::GetEntry() const
int wxTextCtrl::GTKIMFilterKeypress(GdkEventKey* event) const int wxTextCtrl::GTKIMFilterKeypress(GdkEventKey* event) const
{ {
if (IsSingleLine()) if (IsSingleLine())
return wxTextEntry::GTKIMFilterKeypress(event); return GTKEntryIMFilterKeypress(event);
// When not calling GTKEntryIMFilterKeypress(), we need to notify the code
// in wxTextEntry about the key presses explicitly.
GTKEntryOnKeypress(m_text);
int result = false; int result = false;
#if GTK_CHECK_VERSION(2, 22, 0) #if GTK_CHECK_VERSION(2, 22, 0)
@@ -1371,24 +1348,21 @@ void wxTextCtrl::DiscardEdits()
m_modified = false; m_modified = false;
} }
void wxTextCtrl::GTKOnTextChanged()
{
if ( IgnoreTextUpdate() )
return;
if ( MarkDirtyOnChange() )
MarkDirty();
SendTextUpdatedEvent();
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// event handling // event handling
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void wxTextCtrl::EnableTextChangedEvents(bool enable)
{
if ( enable )
{
g_signal_handlers_unblock_by_func(GetTextObject(),
(gpointer)gtk_text_changed_callback, this);
}
else // disable events
{
g_signal_handlers_block_by_func(GetTextObject(),
(gpointer)gtk_text_changed_callback, this);
}
}
bool wxTextCtrl::IgnoreTextUpdate() bool wxTextCtrl::IgnoreTextUpdate()
{ {
if ( m_countUpdatesToIgnore > 0 ) if ( m_countUpdatesToIgnore > 0 )

View File

@@ -37,6 +37,65 @@
#include "wx/gtk/private/object.h" #include "wx/gtk/private/object.h"
#include "wx/gtk/private/string.h" #include "wx/gtk/private/string.h"
// ----------------------------------------------------------------------------
// wxTextCoalesceData
// ----------------------------------------------------------------------------
class wxTextCoalesceData
{
public:
wxTextCoalesceData(GtkWidget* widget, gulong handlerAfterKeyPress)
: m_handlerAfterKeyPress(handlerAfterKeyPress)
{
m_inKeyPress = false;
m_pendingTextChanged = false;
// This signal handler is unblocked in StartHandlingKeyPress(), so
// we need to block it initially to compensate for this.
g_signal_handler_block(widget, m_handlerAfterKeyPress);
}
void StartHandlingKeyPress(GtkWidget* widget)
{
m_inKeyPress = true;
m_pendingTextChanged = false;
g_signal_handler_unblock(widget, m_handlerAfterKeyPress);
}
bool SetPendingIfInKeyPress()
{
if ( !m_inKeyPress )
return false;
m_pendingTextChanged = true;
return true;
}
bool EndHandlingKeyPressAndCheckIfPending(GtkWidget* widget)
{
g_signal_handler_block(widget, m_handlerAfterKeyPress);
wxASSERT( m_inKeyPress );
m_inKeyPress = false;
if ( !m_pendingTextChanged )
return false;
m_pendingTextChanged = false;
return true;
}
private:
bool m_inKeyPress;
bool m_pendingTextChanged;
const gulong m_handlerAfterKeyPress;
wxDECLARE_NO_COPY_CLASS(wxTextCoalesceData);
};
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// helper function to get the length of the text // helper function to get the length of the text
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@@ -57,8 +116,45 @@ static int GetEntryTextLength(GtkEntry* entry)
// signal handlers implementation // signal handlers implementation
// ============================================================================ // ============================================================================
// "insert_text" handler for GtkEntry
extern "C" { extern "C" {
// "event-after" handler is only connected when we get a "key-press-event", so
// it's effectively called after the end of processing of this event and used
// to send a single wxEVT_TEXT even if we received several (typically two, when
// the selected text in the control is replaced by new text) "changed" signals.
static gboolean
wx_gtk_text_after_key_press(GtkWidget* widget,
GdkEventKey* WXUNUSED(gdk_event),
wxTextEntry* entry)
{
wxTextCoalesceData* const data = entry->GTKGetCoalesceData();
wxCHECK_MSG( data, FALSE, "must be non-null if this handler is called" );
if ( data->EndHandlingKeyPressAndCheckIfPending(widget) )
{
entry->GTKOnTextChanged();
}
return FALSE;
}
// "changed" handler for GtkEntry
static void
wx_gtk_text_changed_callback(GtkWidget* WXUNUSED(widget), wxTextEntry* entry)
{
if ( wxTextCoalesceData* const data = entry->GTKGetCoalesceData() )
{
if ( data->SetPendingIfInKeyPress() )
{
// Don't send the event right now as more might be coming.
return;
}
}
entry->GTKOnTextChanged();
}
// "insert_text" handler for GtkEntry
static void static void
wx_gtk_insert_text_callback(GtkEditable *editable, wx_gtk_insert_text_callback(GtkEditable *editable,
const gchar * new_text, const gchar * new_text,
@@ -510,11 +606,13 @@ wx_gtk_entry_parent_grab_notify (GtkWidget *widget,
wxTextEntry::wxTextEntry() wxTextEntry::wxTextEntry()
{ {
m_autoCompleteData = NULL; m_autoCompleteData = NULL;
m_coalesceData = NULL;
m_isUpperCase = false; m_isUpperCase = false;
} }
wxTextEntry::~wxTextEntry() wxTextEntry::~wxTextEntry()
{ {
delete m_coalesceData;
delete m_autoCompleteData; delete m_autoCompleteData;
} }
@@ -854,8 +952,38 @@ void wxTextEntry::ForceUpper()
// IM handling // IM handling
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
int wxTextEntry::GTKIMFilterKeypress(GdkEventKey* event) const void wxTextEntry::GTKEntryOnKeypress(GtkWidget* widget) const
{ {
// We coalesce possibly multiple events resulting from a single key press
// (this always happens when there is a selection, as we always get a
// "changed" event when the selection is removed and another one when the
// new text is inserted) into a single wxEVT_TEXT and to do this we need
// this extra handler.
if ( !m_coalesceData )
{
// We can't use g_signal_connect_after("key-press-event") because the
// emission of this signal is stopped by GtkEntry own key-press-event
// handler, so we have to use the generic "event-after" instead to be
// notified about the end of handling of this key press and to send any
// pending events a.s.a.p.
const gulong handler = g_signal_connect
(
widget,
"event-after",
G_CALLBACK(wx_gtk_text_after_key_press),
const_cast<wxTextEntry*>(this)
);
m_coalesceData = new wxTextCoalesceData(widget, handler);
}
m_coalesceData->StartHandlingKeyPress(widget);
}
int wxTextEntry::GTKEntryIMFilterKeypress(GdkEventKey* event) const
{
GTKEntryOnKeypress(GTK_WIDGET(GetEntry()));
int result = false; int result = false;
#if GTK_CHECK_VERSION(2, 22, 0) #if GTK_CHECK_VERSION(2, 22, 0)
if (wx_is_at_least_gtk2(22)) if (wx_is_at_least_gtk2(22))
@@ -869,6 +997,31 @@ int wxTextEntry::GTKIMFilterKeypress(GdkEventKey* event) const
return result; return result;
} }
// ----------------------------------------------------------------------------
// signals and events
// ----------------------------------------------------------------------------
void wxTextEntry::EnableTextChangedEvents(bool enable)
{
if ( enable )
{
g_signal_handlers_unblock_by_func(GetTextObject(),
(gpointer)wx_gtk_text_changed_callback, this);
}
else // disable events
{
g_signal_handlers_block_by_func(GetTextObject(),
(gpointer)wx_gtk_text_changed_callback, this);
}
}
void wxTextEntry::GTKConnectChangedSignal()
{
g_signal_connect(GetTextObject(), "changed",
G_CALLBACK(wx_gtk_text_changed_callback), this);
}
void wxTextEntry::GTKConnectInsertTextSignal(GtkEntry* entry) void wxTextEntry::GTKConnectInsertTextSignal(GtkEntry* entry)
{ {
g_signal_connect(entry, "insert_text", g_signal_connect(entry, "insert_text",