From: Vadim Zeitlin Date: Sun, 18 Dec 2005 16:37:56 +0000 (+0000) Subject: 1. changed wxControl::GetLabel() to return the originally provided label and X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/b2ff89d648e5b73172dc2f4aa84d2aadbdc063ec 1. changed wxControl::GetLabel() to return the originally provided label and not the one stripped from mnemonics (this was inconsistent with the other ports and resulted in problems when using wxUpdateUIEvent::SetText()) 2. added wxControl::GTKConvertMnemonics(), GTKRemoveMnemonics() and also helper GTKSetLabelForLabel() and GTKSetLabelForFrame() wrappers 3. use them instead of duplicating their code in different derived controls git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@36435 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- diff --git a/include/wx/gtk/control.h b/include/wx/gtk/control.h index 6e51a1116f..42e48726bf 100644 --- a/include/wx/gtk/control.h +++ b/include/wx/gtk/control.h @@ -21,6 +21,9 @@ class WXDLLIMPEXP_CORE wxControl; +typedef struct _GtkLabel GtkLabel; +typedef struct _GtkFrame GtkFrame; + //----------------------------------------------------------------------------- // wxControl //----------------------------------------------------------------------------- @@ -51,20 +54,29 @@ public: const wxValidator& validator = wxDefaultValidator, const wxString& name = wxControlNameStr); - // this function will filter out '&' characters and will put the accelerator - // char (the one immediately after '&') into m_chAccel (TODO not yet) virtual void SetLabel( const wxString &label ); virtual wxString GetLabel() const; - + virtual wxVisualAttributes GetDefaultAttributes() const; protected: virtual wxSize DoGetBestSize() const; void PostCreation(const wxSize& size); -#ifdef __WXGTK20__ - wxString PrepareLabelMnemonics( const wxString &label ) const; -#endif + // sets the label to the given string and also sets it for the given widget + void GTKSetLabelForLabel(GtkLabel *w, const wxString& label); + + // as GTKSetLabelForLabel() but for a GtkFrame widget + void GTKSetLabelForFrame(GtkFrame *w, const wxString& label); + + // remove mnemonics ("&"s) from the label + static wxString GTKRemoveMnemonics(const wxString& label); + + // converts wx label to GTK+ label, i.e. basically replace "&"s with "_"s + // + // for GTK+ 1 (which doesn't support mnemonics) this is the same as + // GTKRemoveMnemonics() + static wxString GTKConvertMnemonics(const wxString &label); // These are used by GetDefaultAttributes static wxVisualAttributes @@ -89,8 +101,8 @@ protected: // override this and return true. virtual bool UseGTKStyleBase() const { return false; } - wxString m_label; - char m_chAccel; // enabled to avoid breaking binary compatibility later on + // this field contains the label in wx format, i.e. with "&" mnemonics + wxString m_label; private: DECLARE_DYNAMIC_CLASS(wxControl) diff --git a/include/wx/gtk1/control.h b/include/wx/gtk1/control.h index 6e51a1116f..42e48726bf 100644 --- a/include/wx/gtk1/control.h +++ b/include/wx/gtk1/control.h @@ -21,6 +21,9 @@ class WXDLLIMPEXP_CORE wxControl; +typedef struct _GtkLabel GtkLabel; +typedef struct _GtkFrame GtkFrame; + //----------------------------------------------------------------------------- // wxControl //----------------------------------------------------------------------------- @@ -51,20 +54,29 @@ public: const wxValidator& validator = wxDefaultValidator, const wxString& name = wxControlNameStr); - // this function will filter out '&' characters and will put the accelerator - // char (the one immediately after '&') into m_chAccel (TODO not yet) virtual void SetLabel( const wxString &label ); virtual wxString GetLabel() const; - + virtual wxVisualAttributes GetDefaultAttributes() const; protected: virtual wxSize DoGetBestSize() const; void PostCreation(const wxSize& size); -#ifdef __WXGTK20__ - wxString PrepareLabelMnemonics( const wxString &label ) const; -#endif + // sets the label to the given string and also sets it for the given widget + void GTKSetLabelForLabel(GtkLabel *w, const wxString& label); + + // as GTKSetLabelForLabel() but for a GtkFrame widget + void GTKSetLabelForFrame(GtkFrame *w, const wxString& label); + + // remove mnemonics ("&"s) from the label + static wxString GTKRemoveMnemonics(const wxString& label); + + // converts wx label to GTK+ label, i.e. basically replace "&"s with "_"s + // + // for GTK+ 1 (which doesn't support mnemonics) this is the same as + // GTKRemoveMnemonics() + static wxString GTKConvertMnemonics(const wxString &label); // These are used by GetDefaultAttributes static wxVisualAttributes @@ -89,8 +101,8 @@ protected: // override this and return true. virtual bool UseGTKStyleBase() const { return false; } - wxString m_label; - char m_chAccel; // enabled to avoid breaking binary compatibility later on + // this field contains the label in wx format, i.e. with "&" mnemonics + wxString m_label; private: DECLARE_DYNAMIC_CLASS(wxControl) diff --git a/src/gtk/button.cpp b/src/gtk/button.cpp index e6904b1c1a..ca57fcc514 100644 --- a/src/gtk/button.cpp +++ b/src/gtk/button.cpp @@ -67,12 +67,12 @@ gtk_button_style_set_callback( GtkWidget *m_widget, GtkStyle *WXUNUSED(style), w { if (g_isIdle) wxapp_install_idle_handler(); - + int left_border = 0; int right_border = 0; int top_border = 0; int bottom_border = 0; - + /* the default button has a border around it */ if (GTK_WIDGET_CAN_DEFAULT(m_widget)) { @@ -97,7 +97,7 @@ gtk_button_style_set_callback( GtkWidget *m_widget, GtkStyle *WXUNUSED(style), w win->m_y-left_border, win->m_width+left_border+right_border, win->m_height+top_border+bottom_border ); - } + } return FALSE; } @@ -130,28 +130,6 @@ bool wxButton::Create( wxWindow *parent, wxWindowID id, const wxString &label, return FALSE; } -/* - wxString label2( label ); - for (size_t i = 0; i < label2.Len(); i++) - { - if (label2.GetChar(i) == wxT('&')) - label2.SetChar(i,wxT('_')); - } - - GtkWidget *accel_label = gtk_accel_label_new( label2.mb_str() ); - gtk_widget_show( accel_label ); - - m_widget = gtk_button_new(); - gtk_container_add( GTK_CONTAINER(m_widget), accel_label ); - - gtk_accel_label_set_accel_widget( GTK_ACCEL_LABEL(accel_label), m_widget ); - - guint accel_key = gtk_label_parse_uline (GTK_LABEL(accel_label), label2.mb_str() ); - gtk_accel_label_refetch( GTK_ACCEL_LABEL(accel_label) ); - - wxControl::SetLabel( label ); -*/ - #ifdef __WXGTK20__ m_widget = gtk_button_new_with_mnemonic(""); #else @@ -193,14 +171,14 @@ bool wxButton::Create( wxWindow *parent, wxWindowID id, const wxString &label, gtk_signal_connect_after( GTK_OBJECT(m_widget), "style_set", GTK_SIGNAL_FUNC(gtk_button_style_set_callback), (gpointer*) this ); - + m_parent->DoAddChild( this ); PostCreation(size); return true; } - + void wxButton::SetDefault() { @@ -208,10 +186,10 @@ void wxButton::SetDefault() wxCHECK_RET( parent, _T("button without parent?") ); parent->SetDefaultItem(this); - + GTK_WIDGET_SET_FLAGS( m_widget, GTK_CAN_DEFAULT ); gtk_widget_grab_default( m_widget ); - + // resize for default border gtk_button_style_set_callback( m_widget, NULL, this ); } @@ -246,7 +224,7 @@ wxSize wxButtonBase::GetDefaultSize() size.x = wxMax(minwidth, req.width); size.y = wxMax(minheight, req.height); - + gtk_widget_destroy(wnd); } return size; @@ -266,6 +244,8 @@ void wxButton::SetLabel( const wxString &lbl ) wxControl::SetLabel(label); + const wxString labelGTK = GTKConvertMnemonics(label); + #ifdef __WXGTK20__ if (wxIsStockID(m_windowId) && wxIsStockLabel(m_windowId, label)) { @@ -278,15 +258,13 @@ void wxButton::SetLabel( const wxString &lbl ) } } - wxString label2 = PrepareLabelMnemonics(label); - gtk_button_set_label(GTK_BUTTON(m_widget), wxGTK_CONV(label2)); + gtk_button_set_label(GTK_BUTTON(m_widget), wxGTK_CONV(labelGTK)); gtk_button_set_use_stock(GTK_BUTTON(m_widget), FALSE); - + ApplyWidgetStyle( false ); - -#else - gtk_label_set(GTK_LABEL(BUTTON_CHILD(m_widget)), wxGTK_CONV(GetLabel())); -#endif +#else // GTK+ 1 + gtk_label_set(GTK_LABEL(BUTTON_CHILD(m_widget)), wxGTK_CONV(labelGTK)); +#endif // GTK+ 2/1 } bool wxButton::Enable( bool enable ) @@ -338,7 +316,7 @@ wxSize wxButton::DoGetBestSize() const #ifndef __WXGTK20__ ret.x += 10; // add a few pixels for sloppy (but common) themes #endif - + if (!HasFlag(wxBU_EXACTFIT)) { wxSize defaultSize = GetDefaultSize(); diff --git a/src/gtk/checkbox.cpp b/src/gtk/checkbox.cpp index a0acbe43a4..8b561f3929 100644 --- a/src/gtk/checkbox.cpp +++ b/src/gtk/checkbox.cpp @@ -229,14 +229,7 @@ void wxCheckBox::SetLabel( const wxString& label ) { wxCHECK_RET( m_widgetLabel != NULL, wxT("invalid checkbox") ); - wxControl::SetLabel( label ); - -#ifdef __WXGTK20__ - wxString label2 = PrepareLabelMnemonics( label ); - gtk_label_set_text_with_mnemonic( GTK_LABEL(m_widgetLabel), wxGTK_CONV( label2 ) ); -#else - gtk_label_set( GTK_LABEL(m_widgetLabel), wxGTK_CONV( GetLabel() ) ); -#endif + GTKSetLabelForLabel(GTK_LABEL(m_widgetLabel), label); } bool wxCheckBox::Enable( bool enable ) diff --git a/src/gtk/control.cpp b/src/gtk/control.cpp index afcd65c371..735fdd924f 100644 --- a/src/gtk/control.cpp +++ b/src/gtk/control.cpp @@ -17,8 +17,7 @@ #include "wx/control.h" #include "wx/fontutil.h" #include "wx/settings.h" - -#include +#include "wx/gtk/private.h" //----------------------------------------------------------------------------- // wxControl @@ -40,7 +39,7 @@ bool wxControl::Create( wxWindow *parent, const wxString &name ) { bool ret = wxWindow::Create(parent, id, pos, size, style, name); - + #if wxUSE_VALIDATORS SetValidator(validator); #endif @@ -48,29 +47,6 @@ bool wxControl::Create( wxWindow *parent, return ret; } -void wxControl::SetLabel( const wxString &label ) -{ - m_label.Empty(); - for ( const wxChar *pc = label; *pc != wxT('\0'); pc++ ) - { - if ( *pc == wxT('&') ) - { - pc++; // skip it -#if 0 // it would be unused anyhow for now - kbd interface not done yet - if ( *pc != wxT('&') ) m_chAccel = *pc; -#endif - } - m_label << *pc; - } - InvalidateBestSize(); -} - -wxString wxControl::GetLabel() const -{ - return m_label; -} - - wxSize wxControl::DoGetBestSize() const { // Do not return any arbitrary default value... @@ -98,56 +74,143 @@ void wxControl::PostCreation(const wxSize& size) // size. This call ensure that a style is available at the time // GetBestSize is called. gtk_widget_ensure_style(m_widget); - + ApplyWidgetStyle(); SetInitialBestSize(size); } +// ---------------------------------------------------------------------------- +// wxControl dealing with labels +// ---------------------------------------------------------------------------- + +void wxControl::SetLabel( const wxString &label ) +{ + // keep the original string internally to be able to return it later (for + // consistency with the other ports) + m_label = label; + + InvalidateBestSize(); +} + +wxString wxControl::GetLabel() const +{ + return m_label; +} + +void wxControl::GTKSetLabelForLabel(GtkLabel *w, const wxString& label) +{ + // don't call the virtual function which might call this one back again + wxControl::SetLabel(label); + + const wxString labelGTK = GTKConvertMnemonics(label); #ifdef __WXGTK20__ -wxString wxControl::PrepareLabelMnemonics( const wxString &label ) const + gtk_label_set_text_with_mnemonic(w, wxGTK_CONV(labelGTK)); +#else + gtk_label_set(w, wxGTK_CONV(labelGTK)); +#endif +} + +void wxControl::GTKSetLabelForFrame(GtkFrame *w, const wxString& label) +{ + wxControl::SetLabel(label); + + // frames don't support mnemonics even under GTK+ 2 + const wxString labelGTK = GTKRemoveMnemonics(label); + + gtk_frame_set_label(w, labelGTK.empty() ? (char *)NULL + : wxGTK_CONV(labelGTK)); +} + +// worker function implementing both GTKConvert/RemoveMnemonics() +// +// notice that under GTK+ 1 we only really need to support MNEMONICS_REMOVE as +// it doesn't support mnemonics anyhow but this would make the code so ugly +// that we do the same thing for GKT+ 1 and 2 +enum MnemonicsFlag { - //Format mnemonics properly for GTK2. This can be called from GTK1.x, but - //it's not very useful because mnemonics don't exist prior to GTK2. - wxString label2; - for (size_t i = 0; i < label.Len(); i++) + MNEMONICS_REMOVE, + MNEMONICS_CONVERT +}; + +static wxString GTKProcessMnemonics(const wxString& label, MnemonicsFlag flag) +{ + const size_t len = label.length(); + wxString labelGTK; + labelGTK.reserve(len); + for ( size_t i = 0; i < len; i++ ) { - if (label.GetChar(i) == wxT('&')) - { - //Mnemonic escape sequence "&&" is a literal "&" in the output. - if (label.GetChar(i + 1) == wxT('&')) - { - label2 << wxT('&'); - i++; - } - //Handle special case of "&_" (i.e. "_" is the mnemonic). - //FIXME - Is it possible to use "_" as a GTK mnemonic? Just use a - //dash for now. - else if (label.GetChar(i + 1) == wxT('_')) - { - label2 << wxT("_-"); - i++; - } - //Replace WX mnemonic indicator "&" with GTK indicator "_". - else - { - label2 << wxT('_'); - } - } - else if (label.GetChar(i) == wxT('_')) - { - //Escape any underlines in the string so GTK doesn't use them. - label2 << wxT("__"); - } - else + wxChar ch = label[i]; + + switch ( ch ) { - label2 << label.GetChar(i); + case wxT('&'): + if ( i == len - 1 ) + { + // "&" at the end of string is an error + wxLogDebug(wxT("Invalid label \"%s\"."), label.c_str()); + break; + } + + ch = label[++i]; // skip '&' itself + switch ( ch ) + { + case wxT('&'): + // special case: "&&" is not a mnemonic at all but just + // an escaped "&" + labelGTK += wxT('&'); + break; + + case wxT('_'): + if ( flag == MNEMONICS_CONVERT ) + { + // '_' can't be a GTK mnemonic apparently so + // replace it with something similar + labelGTK += wxT("_-"); + break; + } + //else: fall through + + default: + if ( flag == MNEMONICS_CONVERT ) + labelGTK += wxT('_'); + labelGTK += ch; + } + break; + + case wxT('_'): + if ( flag == MNEMONICS_CONVERT ) + { + // escape any existing underlines in the string so that + // they don't become mnemonics accidentally + labelGTK += wxT("__"); + break; + } + //else: fall through + + default: + labelGTK += ch; } } - return label2; + + return labelGTK; +} + +/* static */ +wxString wxControl::GTKRemoveMnemonics(const wxString& label) +{ + return GTKProcessMnemonics(label, MNEMONICS_REMOVE); +} + +/* static */ +wxString wxControl::GTKConvertMnemonics(const wxString& label) +{ + return GTKProcessMnemonics(label, MNEMONICS_CONVERT); } -#endif +// ---------------------------------------------------------------------------- +// wxControl styles (a.k.a. attributes) +// ---------------------------------------------------------------------------- wxVisualAttributes wxControl::GetDefaultAttributes() const { @@ -178,7 +241,7 @@ wxControl::GetDefaultAttributesFromGTKWidget(GtkWidget* widget, if (state == -1) state = GTK_STATE_NORMAL; - + // get the style's colours attr.colFg = wxColour(style->fg[state].red >> SHIFT, style->fg[state].green >> SHIFT, @@ -195,19 +258,19 @@ wxControl::GetDefaultAttributesFromGTKWidget(GtkWidget* widget, // get the style's font #ifdef __WXGTK20__ if ( !style->font_desc ) - style = gtk_widget_get_default_style(); + style = gtk_widget_get_default_style(); if ( style && style->font_desc ) - { - wxNativeFontInfo info; + { + wxNativeFontInfo info; info.description = pango_font_description_copy(style->font_desc); - attr.font = wxFont(info); - } - else - { + attr.font = wxFont(info); + } + else + { GtkSettings *settings = gtk_settings_get_default(); gchar *font_name = NULL; g_object_get ( settings, - "gtk-font-name", + "gtk-font-name", &font_name, NULL); if (!font_name) @@ -215,12 +278,12 @@ wxControl::GetDefaultAttributesFromGTKWidget(GtkWidget* widget, else attr.font = wxFont(wxString::FromAscii(font_name)); g_free (font_name); - } + } #else // TODO: isn't there a way to get a standard gtk 1.2 font? attr.font = wxFont( 12, wxSWISS, wxNORMAL, wxNORMAL ); #endif - + return attr; } diff --git a/src/gtk/radiobox.cpp b/src/gtk/radiobox.cpp index f4bc5fed50..abe3aed93d 100644 --- a/src/gtk/radiobox.cpp +++ b/src/gtk/radiobox.cpp @@ -198,7 +198,8 @@ bool wxRadioBox::Create( wxWindow *parent, wxWindowID id, const wxString& title, return false; } - m_widget = gtk_frame_new( wxGTK_CONV( title ) ); + m_widget = gtk_frame_new(NULL); + SetLabel(title); // majorDim may be 0 if all trailing parameters were omitted, so don't // assert here but just use the correct value for it @@ -273,8 +274,6 @@ bool wxRadioBox::Create( wxWindow *parent, wxWindowID id, const wxString& title, m_parent->DoAddChild( this ); - SetLabel( title ); - PostCreation(size); return true; @@ -399,9 +398,7 @@ void wxRadioBox::SetLabel( const wxString& label ) { wxCHECK_RET( m_widget != NULL, wxT("invalid radiobox") ); - wxControl::SetLabel( label ); - - gtk_frame_set_label( GTK_FRAME(m_widget), wxGTK_CONV( wxControl::GetLabel() ) ); + GTKSetLabelForFrame(GTK_FRAME(m_widget), label); } void wxRadioBox::SetString( int item, const wxString& label ) diff --git a/src/gtk/radiobut.cpp b/src/gtk/radiobut.cpp index 9d0f498fae..cc9f90751f 100644 --- a/src/gtk/radiobut.cpp +++ b/src/gtk/radiobut.cpp @@ -36,19 +36,19 @@ extern wxWindowGTK *g_delayedFocus; //----------------------------------------------------------------------------- extern "C" { -static +static void gtk_radiobutton_clicked_callback( GtkToggleButton *button, wxRadioButton *rb ) { if (g_isIdle) wxapp_install_idle_handler(); if (!rb->m_hasVMT) return; - + if (g_blockEventsOnDrag) return; - + if (!button->active) return; - + if (rb->m_blockEvent) return; - + wxCommandEvent event( wxEVT_COMMAND_RADIOBUTTON_SELECTED, rb->GetId()); event.SetInt( rb->GetValue() ); event.SetEventObject( rb ); @@ -61,7 +61,7 @@ void gtk_radiobutton_clicked_callback( GtkToggleButton *button, wxRadioButton *r //----------------------------------------------------------------------------- IMPLEMENT_DYNAMIC_CLASS(wxRadioButton,wxControl) - + bool wxRadioButton::Create( wxWindow *parent, wxWindowID id, const wxString& label, @@ -73,7 +73,7 @@ bool wxRadioButton::Create( wxWindow *parent, { m_acceptsFocus = TRUE; m_needParent = TRUE; - + m_blockEvent = FALSE; if (!PreCreation( parent, pos, size ) || @@ -108,14 +108,14 @@ bool wxRadioButton::Create( wxWindow *parent, } m_widget = gtk_radio_button_new_with_label( radioButtonGroup, wxGTK_CONV( label ) ); - + SetLabel(label); - gtk_signal_connect( GTK_OBJECT(m_widget), "clicked", + gtk_signal_connect( GTK_OBJECT(m_widget), "clicked", GTK_SIGNAL_FUNC(gtk_radiobutton_clicked_callback), (gpointer*)this ); - + m_parent->DoAddChild( this ); - + PostCreation(size); return TRUE; @@ -124,15 +124,8 @@ bool wxRadioButton::Create( wxWindow *parent, void wxRadioButton::SetLabel( const wxString& label ) { wxCHECK_RET( m_widget != NULL, wxT("invalid radiobutton") ); - - wxControl::SetLabel( label ); - GtkLabel *g_label = GTK_LABEL( BUTTON_CHILD(m_widget) ); -#ifdef __WXGTK20__ - wxString label2 = PrepareLabelMnemonics( label ); - gtk_label_set_text_with_mnemonic( g_label, wxGTK_CONV( label2 ) ); -#else - gtk_label_set( g_label, wxGTK_CONV( GetLabel() ) ); -#endif + + GTKSetLabelForLabel(GTK_LABEL(BUTTON_CHILD(m_widget)), label); } void wxRadioButton::SetValue( bool val ) @@ -161,7 +154,7 @@ void wxRadioButton::SetValue( bool val ) bool wxRadioButton::GetValue() const { wxCHECK_MSG( m_widget != NULL, FALSE, wxT("invalid radiobutton") ); - + return GTK_TOGGLE_BUTTON(m_widget)->active; } @@ -169,7 +162,7 @@ bool wxRadioButton::Enable( bool enable ) { if ( !wxControl::Enable( enable ) ) return FALSE; - + gtk_widget_set_sensitive( BUTTON_CHILD(m_widget), enable ); return TRUE; @@ -198,7 +191,7 @@ void wxRadioButton::OnInternalIdle() as setting the cursor in a parent window also effects the windows above so that checking for the current cursor is not possible. */ - + gdk_window_set_cursor( win, cursor.GetCursor() ); } diff --git a/src/gtk/statbox.cpp b/src/gtk/statbox.cpp index 1cdcca5073..c000c1306a 100644 --- a/src/gtk/statbox.cpp +++ b/src/gtk/statbox.cpp @@ -56,9 +56,8 @@ bool wxStaticBox::Create( wxWindow *parent, return FALSE; } - wxControl::SetLabel(label); - - m_widget = gtk_frame_new(m_label.empty() ? (char *)NULL : (const char*) wxGTK_CONV( m_label ) ); + m_widget = gtk_frame_new(NULL); + SetLabel(label); m_parent->DoAddChild( this ); @@ -79,12 +78,11 @@ bool wxStaticBox::Create( wxWindow *parent, return TRUE; } -void wxStaticBox::SetLabel( const wxString &label ) +void wxStaticBox::SetLabel( const wxString& label ) { - wxControl::SetLabel( label ); + wxCHECK_RET( m_widget != NULL, wxT("invalid staticbox") ); - gtk_frame_set_label( GTK_FRAME( m_widget ), - m_label.empty() ? (char *)NULL : (const char*) wxGTK_CONV( m_label ) ); + GTKSetLabelForFrame(GTK_FRAME(m_widget), label); } void wxStaticBox::DoApplyWidgetStyle(GtkRcStyle *style) diff --git a/src/gtk1/button.cpp b/src/gtk1/button.cpp index e6904b1c1a..ca57fcc514 100644 --- a/src/gtk1/button.cpp +++ b/src/gtk1/button.cpp @@ -67,12 +67,12 @@ gtk_button_style_set_callback( GtkWidget *m_widget, GtkStyle *WXUNUSED(style), w { if (g_isIdle) wxapp_install_idle_handler(); - + int left_border = 0; int right_border = 0; int top_border = 0; int bottom_border = 0; - + /* the default button has a border around it */ if (GTK_WIDGET_CAN_DEFAULT(m_widget)) { @@ -97,7 +97,7 @@ gtk_button_style_set_callback( GtkWidget *m_widget, GtkStyle *WXUNUSED(style), w win->m_y-left_border, win->m_width+left_border+right_border, win->m_height+top_border+bottom_border ); - } + } return FALSE; } @@ -130,28 +130,6 @@ bool wxButton::Create( wxWindow *parent, wxWindowID id, const wxString &label, return FALSE; } -/* - wxString label2( label ); - for (size_t i = 0; i < label2.Len(); i++) - { - if (label2.GetChar(i) == wxT('&')) - label2.SetChar(i,wxT('_')); - } - - GtkWidget *accel_label = gtk_accel_label_new( label2.mb_str() ); - gtk_widget_show( accel_label ); - - m_widget = gtk_button_new(); - gtk_container_add( GTK_CONTAINER(m_widget), accel_label ); - - gtk_accel_label_set_accel_widget( GTK_ACCEL_LABEL(accel_label), m_widget ); - - guint accel_key = gtk_label_parse_uline (GTK_LABEL(accel_label), label2.mb_str() ); - gtk_accel_label_refetch( GTK_ACCEL_LABEL(accel_label) ); - - wxControl::SetLabel( label ); -*/ - #ifdef __WXGTK20__ m_widget = gtk_button_new_with_mnemonic(""); #else @@ -193,14 +171,14 @@ bool wxButton::Create( wxWindow *parent, wxWindowID id, const wxString &label, gtk_signal_connect_after( GTK_OBJECT(m_widget), "style_set", GTK_SIGNAL_FUNC(gtk_button_style_set_callback), (gpointer*) this ); - + m_parent->DoAddChild( this ); PostCreation(size); return true; } - + void wxButton::SetDefault() { @@ -208,10 +186,10 @@ void wxButton::SetDefault() wxCHECK_RET( parent, _T("button without parent?") ); parent->SetDefaultItem(this); - + GTK_WIDGET_SET_FLAGS( m_widget, GTK_CAN_DEFAULT ); gtk_widget_grab_default( m_widget ); - + // resize for default border gtk_button_style_set_callback( m_widget, NULL, this ); } @@ -246,7 +224,7 @@ wxSize wxButtonBase::GetDefaultSize() size.x = wxMax(minwidth, req.width); size.y = wxMax(minheight, req.height); - + gtk_widget_destroy(wnd); } return size; @@ -266,6 +244,8 @@ void wxButton::SetLabel( const wxString &lbl ) wxControl::SetLabel(label); + const wxString labelGTK = GTKConvertMnemonics(label); + #ifdef __WXGTK20__ if (wxIsStockID(m_windowId) && wxIsStockLabel(m_windowId, label)) { @@ -278,15 +258,13 @@ void wxButton::SetLabel( const wxString &lbl ) } } - wxString label2 = PrepareLabelMnemonics(label); - gtk_button_set_label(GTK_BUTTON(m_widget), wxGTK_CONV(label2)); + gtk_button_set_label(GTK_BUTTON(m_widget), wxGTK_CONV(labelGTK)); gtk_button_set_use_stock(GTK_BUTTON(m_widget), FALSE); - + ApplyWidgetStyle( false ); - -#else - gtk_label_set(GTK_LABEL(BUTTON_CHILD(m_widget)), wxGTK_CONV(GetLabel())); -#endif +#else // GTK+ 1 + gtk_label_set(GTK_LABEL(BUTTON_CHILD(m_widget)), wxGTK_CONV(labelGTK)); +#endif // GTK+ 2/1 } bool wxButton::Enable( bool enable ) @@ -338,7 +316,7 @@ wxSize wxButton::DoGetBestSize() const #ifndef __WXGTK20__ ret.x += 10; // add a few pixels for sloppy (but common) themes #endif - + if (!HasFlag(wxBU_EXACTFIT)) { wxSize defaultSize = GetDefaultSize(); diff --git a/src/gtk1/checkbox.cpp b/src/gtk1/checkbox.cpp index a0acbe43a4..8b561f3929 100644 --- a/src/gtk1/checkbox.cpp +++ b/src/gtk1/checkbox.cpp @@ -229,14 +229,7 @@ void wxCheckBox::SetLabel( const wxString& label ) { wxCHECK_RET( m_widgetLabel != NULL, wxT("invalid checkbox") ); - wxControl::SetLabel( label ); - -#ifdef __WXGTK20__ - wxString label2 = PrepareLabelMnemonics( label ); - gtk_label_set_text_with_mnemonic( GTK_LABEL(m_widgetLabel), wxGTK_CONV( label2 ) ); -#else - gtk_label_set( GTK_LABEL(m_widgetLabel), wxGTK_CONV( GetLabel() ) ); -#endif + GTKSetLabelForLabel(GTK_LABEL(m_widgetLabel), label); } bool wxCheckBox::Enable( bool enable ) diff --git a/src/gtk1/control.cpp b/src/gtk1/control.cpp index afcd65c371..735fdd924f 100644 --- a/src/gtk1/control.cpp +++ b/src/gtk1/control.cpp @@ -17,8 +17,7 @@ #include "wx/control.h" #include "wx/fontutil.h" #include "wx/settings.h" - -#include +#include "wx/gtk/private.h" //----------------------------------------------------------------------------- // wxControl @@ -40,7 +39,7 @@ bool wxControl::Create( wxWindow *parent, const wxString &name ) { bool ret = wxWindow::Create(parent, id, pos, size, style, name); - + #if wxUSE_VALIDATORS SetValidator(validator); #endif @@ -48,29 +47,6 @@ bool wxControl::Create( wxWindow *parent, return ret; } -void wxControl::SetLabel( const wxString &label ) -{ - m_label.Empty(); - for ( const wxChar *pc = label; *pc != wxT('\0'); pc++ ) - { - if ( *pc == wxT('&') ) - { - pc++; // skip it -#if 0 // it would be unused anyhow for now - kbd interface not done yet - if ( *pc != wxT('&') ) m_chAccel = *pc; -#endif - } - m_label << *pc; - } - InvalidateBestSize(); -} - -wxString wxControl::GetLabel() const -{ - return m_label; -} - - wxSize wxControl::DoGetBestSize() const { // Do not return any arbitrary default value... @@ -98,56 +74,143 @@ void wxControl::PostCreation(const wxSize& size) // size. This call ensure that a style is available at the time // GetBestSize is called. gtk_widget_ensure_style(m_widget); - + ApplyWidgetStyle(); SetInitialBestSize(size); } +// ---------------------------------------------------------------------------- +// wxControl dealing with labels +// ---------------------------------------------------------------------------- + +void wxControl::SetLabel( const wxString &label ) +{ + // keep the original string internally to be able to return it later (for + // consistency with the other ports) + m_label = label; + + InvalidateBestSize(); +} + +wxString wxControl::GetLabel() const +{ + return m_label; +} + +void wxControl::GTKSetLabelForLabel(GtkLabel *w, const wxString& label) +{ + // don't call the virtual function which might call this one back again + wxControl::SetLabel(label); + + const wxString labelGTK = GTKConvertMnemonics(label); #ifdef __WXGTK20__ -wxString wxControl::PrepareLabelMnemonics( const wxString &label ) const + gtk_label_set_text_with_mnemonic(w, wxGTK_CONV(labelGTK)); +#else + gtk_label_set(w, wxGTK_CONV(labelGTK)); +#endif +} + +void wxControl::GTKSetLabelForFrame(GtkFrame *w, const wxString& label) +{ + wxControl::SetLabel(label); + + // frames don't support mnemonics even under GTK+ 2 + const wxString labelGTK = GTKRemoveMnemonics(label); + + gtk_frame_set_label(w, labelGTK.empty() ? (char *)NULL + : wxGTK_CONV(labelGTK)); +} + +// worker function implementing both GTKConvert/RemoveMnemonics() +// +// notice that under GTK+ 1 we only really need to support MNEMONICS_REMOVE as +// it doesn't support mnemonics anyhow but this would make the code so ugly +// that we do the same thing for GKT+ 1 and 2 +enum MnemonicsFlag { - //Format mnemonics properly for GTK2. This can be called from GTK1.x, but - //it's not very useful because mnemonics don't exist prior to GTK2. - wxString label2; - for (size_t i = 0; i < label.Len(); i++) + MNEMONICS_REMOVE, + MNEMONICS_CONVERT +}; + +static wxString GTKProcessMnemonics(const wxString& label, MnemonicsFlag flag) +{ + const size_t len = label.length(); + wxString labelGTK; + labelGTK.reserve(len); + for ( size_t i = 0; i < len; i++ ) { - if (label.GetChar(i) == wxT('&')) - { - //Mnemonic escape sequence "&&" is a literal "&" in the output. - if (label.GetChar(i + 1) == wxT('&')) - { - label2 << wxT('&'); - i++; - } - //Handle special case of "&_" (i.e. "_" is the mnemonic). - //FIXME - Is it possible to use "_" as a GTK mnemonic? Just use a - //dash for now. - else if (label.GetChar(i + 1) == wxT('_')) - { - label2 << wxT("_-"); - i++; - } - //Replace WX mnemonic indicator "&" with GTK indicator "_". - else - { - label2 << wxT('_'); - } - } - else if (label.GetChar(i) == wxT('_')) - { - //Escape any underlines in the string so GTK doesn't use them. - label2 << wxT("__"); - } - else + wxChar ch = label[i]; + + switch ( ch ) { - label2 << label.GetChar(i); + case wxT('&'): + if ( i == len - 1 ) + { + // "&" at the end of string is an error + wxLogDebug(wxT("Invalid label \"%s\"."), label.c_str()); + break; + } + + ch = label[++i]; // skip '&' itself + switch ( ch ) + { + case wxT('&'): + // special case: "&&" is not a mnemonic at all but just + // an escaped "&" + labelGTK += wxT('&'); + break; + + case wxT('_'): + if ( flag == MNEMONICS_CONVERT ) + { + // '_' can't be a GTK mnemonic apparently so + // replace it with something similar + labelGTK += wxT("_-"); + break; + } + //else: fall through + + default: + if ( flag == MNEMONICS_CONVERT ) + labelGTK += wxT('_'); + labelGTK += ch; + } + break; + + case wxT('_'): + if ( flag == MNEMONICS_CONVERT ) + { + // escape any existing underlines in the string so that + // they don't become mnemonics accidentally + labelGTK += wxT("__"); + break; + } + //else: fall through + + default: + labelGTK += ch; } } - return label2; + + return labelGTK; +} + +/* static */ +wxString wxControl::GTKRemoveMnemonics(const wxString& label) +{ + return GTKProcessMnemonics(label, MNEMONICS_REMOVE); +} + +/* static */ +wxString wxControl::GTKConvertMnemonics(const wxString& label) +{ + return GTKProcessMnemonics(label, MNEMONICS_CONVERT); } -#endif +// ---------------------------------------------------------------------------- +// wxControl styles (a.k.a. attributes) +// ---------------------------------------------------------------------------- wxVisualAttributes wxControl::GetDefaultAttributes() const { @@ -178,7 +241,7 @@ wxControl::GetDefaultAttributesFromGTKWidget(GtkWidget* widget, if (state == -1) state = GTK_STATE_NORMAL; - + // get the style's colours attr.colFg = wxColour(style->fg[state].red >> SHIFT, style->fg[state].green >> SHIFT, @@ -195,19 +258,19 @@ wxControl::GetDefaultAttributesFromGTKWidget(GtkWidget* widget, // get the style's font #ifdef __WXGTK20__ if ( !style->font_desc ) - style = gtk_widget_get_default_style(); + style = gtk_widget_get_default_style(); if ( style && style->font_desc ) - { - wxNativeFontInfo info; + { + wxNativeFontInfo info; info.description = pango_font_description_copy(style->font_desc); - attr.font = wxFont(info); - } - else - { + attr.font = wxFont(info); + } + else + { GtkSettings *settings = gtk_settings_get_default(); gchar *font_name = NULL; g_object_get ( settings, - "gtk-font-name", + "gtk-font-name", &font_name, NULL); if (!font_name) @@ -215,12 +278,12 @@ wxControl::GetDefaultAttributesFromGTKWidget(GtkWidget* widget, else attr.font = wxFont(wxString::FromAscii(font_name)); g_free (font_name); - } + } #else // TODO: isn't there a way to get a standard gtk 1.2 font? attr.font = wxFont( 12, wxSWISS, wxNORMAL, wxNORMAL ); #endif - + return attr; } diff --git a/src/gtk1/radiobox.cpp b/src/gtk1/radiobox.cpp index f4bc5fed50..abe3aed93d 100644 --- a/src/gtk1/radiobox.cpp +++ b/src/gtk1/radiobox.cpp @@ -198,7 +198,8 @@ bool wxRadioBox::Create( wxWindow *parent, wxWindowID id, const wxString& title, return false; } - m_widget = gtk_frame_new( wxGTK_CONV( title ) ); + m_widget = gtk_frame_new(NULL); + SetLabel(title); // majorDim may be 0 if all trailing parameters were omitted, so don't // assert here but just use the correct value for it @@ -273,8 +274,6 @@ bool wxRadioBox::Create( wxWindow *parent, wxWindowID id, const wxString& title, m_parent->DoAddChild( this ); - SetLabel( title ); - PostCreation(size); return true; @@ -399,9 +398,7 @@ void wxRadioBox::SetLabel( const wxString& label ) { wxCHECK_RET( m_widget != NULL, wxT("invalid radiobox") ); - wxControl::SetLabel( label ); - - gtk_frame_set_label( GTK_FRAME(m_widget), wxGTK_CONV( wxControl::GetLabel() ) ); + GTKSetLabelForFrame(GTK_FRAME(m_widget), label); } void wxRadioBox::SetString( int item, const wxString& label ) diff --git a/src/gtk1/radiobut.cpp b/src/gtk1/radiobut.cpp index 9d0f498fae..cc9f90751f 100644 --- a/src/gtk1/radiobut.cpp +++ b/src/gtk1/radiobut.cpp @@ -36,19 +36,19 @@ extern wxWindowGTK *g_delayedFocus; //----------------------------------------------------------------------------- extern "C" { -static +static void gtk_radiobutton_clicked_callback( GtkToggleButton *button, wxRadioButton *rb ) { if (g_isIdle) wxapp_install_idle_handler(); if (!rb->m_hasVMT) return; - + if (g_blockEventsOnDrag) return; - + if (!button->active) return; - + if (rb->m_blockEvent) return; - + wxCommandEvent event( wxEVT_COMMAND_RADIOBUTTON_SELECTED, rb->GetId()); event.SetInt( rb->GetValue() ); event.SetEventObject( rb ); @@ -61,7 +61,7 @@ void gtk_radiobutton_clicked_callback( GtkToggleButton *button, wxRadioButton *r //----------------------------------------------------------------------------- IMPLEMENT_DYNAMIC_CLASS(wxRadioButton,wxControl) - + bool wxRadioButton::Create( wxWindow *parent, wxWindowID id, const wxString& label, @@ -73,7 +73,7 @@ bool wxRadioButton::Create( wxWindow *parent, { m_acceptsFocus = TRUE; m_needParent = TRUE; - + m_blockEvent = FALSE; if (!PreCreation( parent, pos, size ) || @@ -108,14 +108,14 @@ bool wxRadioButton::Create( wxWindow *parent, } m_widget = gtk_radio_button_new_with_label( radioButtonGroup, wxGTK_CONV( label ) ); - + SetLabel(label); - gtk_signal_connect( GTK_OBJECT(m_widget), "clicked", + gtk_signal_connect( GTK_OBJECT(m_widget), "clicked", GTK_SIGNAL_FUNC(gtk_radiobutton_clicked_callback), (gpointer*)this ); - + m_parent->DoAddChild( this ); - + PostCreation(size); return TRUE; @@ -124,15 +124,8 @@ bool wxRadioButton::Create( wxWindow *parent, void wxRadioButton::SetLabel( const wxString& label ) { wxCHECK_RET( m_widget != NULL, wxT("invalid radiobutton") ); - - wxControl::SetLabel( label ); - GtkLabel *g_label = GTK_LABEL( BUTTON_CHILD(m_widget) ); -#ifdef __WXGTK20__ - wxString label2 = PrepareLabelMnemonics( label ); - gtk_label_set_text_with_mnemonic( g_label, wxGTK_CONV( label2 ) ); -#else - gtk_label_set( g_label, wxGTK_CONV( GetLabel() ) ); -#endif + + GTKSetLabelForLabel(GTK_LABEL(BUTTON_CHILD(m_widget)), label); } void wxRadioButton::SetValue( bool val ) @@ -161,7 +154,7 @@ void wxRadioButton::SetValue( bool val ) bool wxRadioButton::GetValue() const { wxCHECK_MSG( m_widget != NULL, FALSE, wxT("invalid radiobutton") ); - + return GTK_TOGGLE_BUTTON(m_widget)->active; } @@ -169,7 +162,7 @@ bool wxRadioButton::Enable( bool enable ) { if ( !wxControl::Enable( enable ) ) return FALSE; - + gtk_widget_set_sensitive( BUTTON_CHILD(m_widget), enable ); return TRUE; @@ -198,7 +191,7 @@ void wxRadioButton::OnInternalIdle() as setting the cursor in a parent window also effects the windows above so that checking for the current cursor is not possible. */ - + gdk_window_set_cursor( win, cursor.GetCursor() ); } diff --git a/src/gtk1/statbox.cpp b/src/gtk1/statbox.cpp index 1cdcca5073..c000c1306a 100644 --- a/src/gtk1/statbox.cpp +++ b/src/gtk1/statbox.cpp @@ -56,9 +56,8 @@ bool wxStaticBox::Create( wxWindow *parent, return FALSE; } - wxControl::SetLabel(label); - - m_widget = gtk_frame_new(m_label.empty() ? (char *)NULL : (const char*) wxGTK_CONV( m_label ) ); + m_widget = gtk_frame_new(NULL); + SetLabel(label); m_parent->DoAddChild( this ); @@ -79,12 +78,11 @@ bool wxStaticBox::Create( wxWindow *parent, return TRUE; } -void wxStaticBox::SetLabel( const wxString &label ) +void wxStaticBox::SetLabel( const wxString& label ) { - wxControl::SetLabel( label ); + wxCHECK_RET( m_widget != NULL, wxT("invalid staticbox") ); - gtk_frame_set_label( GTK_FRAME( m_widget ), - m_label.empty() ? (char *)NULL : (const char*) wxGTK_CONV( m_label ) ); + GTKSetLabelForFrame(GTK_FRAME(m_widget), label); } void wxStaticBox::DoApplyWidgetStyle(GtkRcStyle *style)