X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/27dee9ae02f0afc054fe7a90bc3d077df1d7914a..db9febdf4171fdb57434e080f77dca8a02be1cca:/src/gtk/textctrl.cpp diff --git a/src/gtk/textctrl.cpp b/src/gtk/textctrl.cpp index 64a6796cb5..48dbbd95a2 100644 --- a/src/gtk/textctrl.cpp +++ b/src/gtk/textctrl.cpp @@ -16,7 +16,6 @@ #include "wx/intl.h" #include "wx/log.h" #include "wx/utils.h" - #include "wx/panel.h" #include "wx/settings.h" #include "wx/math.h" #endif @@ -29,13 +28,6 @@ #include #include "wx/gtk/private.h" -#include - -//----------------------------------------------------------------------------- -// data -//----------------------------------------------------------------------------- - -extern wxWindowGTK *g_delayedFocus; // ---------------------------------------------------------------------------- // helpers @@ -60,7 +52,8 @@ static void wxGtkOnRemoveTag(GtkTextBuffer *buffer, } extern "C" { -static void wxGtkTextApplyTagsFromAttr(GtkTextBuffer *text_buffer, +static void wxGtkTextApplyTagsFromAttr(GtkWidget *text, + GtkTextBuffer *text_buffer, const wxTextAttr& attr, GtkTextIter *start, GtkTextIter *end) @@ -75,10 +68,9 @@ static void wxGtkTextApplyTagsFromAttr(GtkTextBuffer *text_buffer, if (attr.HasFont()) { - char *font_string; PangoFontDescription *font_description = attr.GetFont().GetNativeFontInfo()->description; - font_string = pango_font_description_to_string(font_description); - g_snprintf(buf, sizeof(buf), "WXFONT %s", font_string); + wxGtkString font_string(pango_font_description_to_string(font_description)); + g_snprintf(buf, sizeof(buf), "WXFONT %s", font_string.c_str()); tag = gtk_text_tag_table_lookup( gtk_text_buffer_get_tag_table( text_buffer ), buf ); if (!tag) @@ -86,7 +78,6 @@ static void wxGtkTextApplyTagsFromAttr(GtkTextBuffer *text_buffer, "font-desc", font_description, NULL ); gtk_text_buffer_apply_tag (text_buffer, tag, start, end); - g_free (font_string); if (attr.GetFont().GetUnderlined()) { @@ -165,6 +156,109 @@ static void wxGtkTextApplyTagsFromAttr(GtkTextBuffer *text_buffer, "justification", align, NULL ); gtk_text_buffer_apply_tag( text_buffer, tag, ¶_start, ¶_end ); } + + if (attr.HasLeftIndent()) + { + // Indentation attribute + + // Clear old indentation tags + GtkTextIter para_start, para_end = *end; + gtk_text_buffer_get_iter_at_line( text_buffer, + ¶_start, + gtk_text_iter_get_line(start) ); + gtk_text_iter_forward_line(¶_end); + + remove_handler_id = g_signal_connect (text_buffer, "remove_tag", + G_CALLBACK(wxGtkOnRemoveTag), + gpointer("WXINDENT")); + gtk_text_buffer_remove_all_tags( text_buffer, ¶_start, ¶_end ); + g_signal_handler_disconnect (text_buffer, remove_handler_id); + + // Convert indent from 1/10th of a mm into pixels + float factor; +#if GTK_CHECK_VERSION(2,2,0) + if (!gtk_check_version(2,2,0)) + factor = (float)gdk_screen_get_width(gtk_widget_get_screen(text)) / + gdk_screen_get_width_mm(gtk_widget_get_screen(text)) / 10; + else +#endif + factor = (float)gdk_screen_width() / gdk_screen_width_mm() / 10; + + const int indent = (int)(factor * attr.GetLeftIndent()); + const int subIndent = (int)(factor * attr.GetLeftSubIndent()); + + gint gindent; + gint gsubindent; + + if (subIndent >= 0) + { + gindent = indent; + gsubindent = -subIndent; + } + else + { + gindent = -subIndent; + gsubindent = indent; + } + + g_snprintf(buf, sizeof(buf), "WXINDENT %d %d", gindent, gsubindent); + tag = gtk_text_tag_table_lookup( gtk_text_buffer_get_tag_table( text_buffer ), + buf ); + if (!tag) + tag = gtk_text_buffer_create_tag( text_buffer, buf, + "left-margin", gindent, "indent", gsubindent, NULL ); + gtk_text_buffer_apply_tag (text_buffer, tag, ¶_start, ¶_end); + } + + if (attr.HasTabs()) + { + // Set tab stops + + // Clear old tabs + GtkTextIter para_start, para_end = *end; + gtk_text_buffer_get_iter_at_line( text_buffer, + ¶_start, + gtk_text_iter_get_line(start) ); + gtk_text_iter_forward_line(¶_end); + + remove_handler_id = g_signal_connect (text_buffer, "remove_tag", + G_CALLBACK(wxGtkOnRemoveTag), + gpointer("WXTABS")); + gtk_text_buffer_remove_all_tags( text_buffer, ¶_start, ¶_end ); + g_signal_handler_disconnect (text_buffer, remove_handler_id); + + const wxArrayInt& tabs = attr.GetTabs(); + + wxString tagname = _T("WXTABS"); + g_snprintf(buf, sizeof(buf), "WXTABS"); + for (size_t i = 0; i < tabs.GetCount(); i++) + tagname += wxString::Format(_T(" %d"), tabs[i]); + + const wxWX2MBbuf buf = tagname.mb_str(wxConvUTF8); + + tag = gtk_text_tag_table_lookup( gtk_text_buffer_get_tag_table( text_buffer ), + buf ); + if (!tag) + { + // Factor to convert from 1/10th of a mm into pixels + float factor; +#if GTK_CHECK_VERSION(2,2,0) + if (!gtk_check_version(2,2,0)) + factor = (float)gdk_screen_get_width(gtk_widget_get_screen(text)) / + gdk_screen_get_width_mm(gtk_widget_get_screen(text)) / 10; + else +#endif + factor = (float)gdk_screen_width() / gdk_screen_width_mm() / 10; + + PangoTabArray* tabArray = pango_tab_array_new(tabs.GetCount(), TRUE); + for (size_t i = 0; i < tabs.GetCount(); i++) + pango_tab_array_set_tab(tabArray, i, PANGO_TAB_LEFT, (gint)(tabs[i] * factor)); + tag = gtk_text_buffer_create_tag( text_buffer, buf, + "tabs", tabArray, NULL ); + pango_tab_array_free(tabArray); + } + gtk_text_buffer_apply_tag (text_buffer, tag, ¶_start, ¶_end); + } } } @@ -185,7 +279,7 @@ static void wxGtkTextInsert(GtkWidget *text, gtk_text_buffer_get_iter_at_offset (text_buffer, &start, start_offset); - wxGtkTextApplyTagsFromAttr(text_buffer, attr, &start, &iter); + wxGtkTextApplyTagsFromAttr(text, text_buffer, attr, &start, &iter); } } @@ -456,9 +550,7 @@ gtk_text_changed_callback( GtkWidget *widget, wxTextCtrl *win ) if ( win->MarkDirtyOnChange() ) win->MarkDirty(); - wxCommandEvent event( wxEVT_COMMAND_TEXT_UPDATED, win->GetId() ); - event.SetEventObject( win ); - win->GetEventHandler()->ProcessEvent( event ); + win->SendTextUpdatedEvent(); } } @@ -552,9 +644,10 @@ END_EVENT_TABLE() void wxTextCtrl::Init() { m_dontMarkDirty = - m_ignoreNextUpdate = m_modified = false; + m_countUpdatesToIgnore = 0; + SetUpdateFont(false); m_text = NULL; @@ -793,13 +886,11 @@ wxString wxTextCtrl::GetValue() const gtk_text_buffer_get_start_iter( m_buffer, &start ); GtkTextIter end; gtk_text_buffer_get_end_iter( m_buffer, &end ); - gchar *text = gtk_text_buffer_get_text( m_buffer, &start, &end, TRUE ); + wxGtkString text(gtk_text_buffer_get_text(m_buffer, &start, &end, true)); const wxWxCharBuffer buf = wxGTK_CONV_BACK(text); if ( buf ) tmp = buf; - - g_free( text ); } else { @@ -830,7 +921,15 @@ wxFontEncoding wxTextCtrl::GetTextEncoding() const return enc; } -void wxTextCtrl::SetValue( const wxString &value ) +bool wxTextCtrl::IsEmpty() const +{ + if ( IsMultiLine() ) + return gtk_text_buffer_get_char_count(m_buffer) == 0; + + return wxTextCtrlBase::IsEmpty(); +} + +void wxTextCtrl::DoSetValue( const wxString &value, int flags ) { wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") ); @@ -841,32 +940,39 @@ void wxTextCtrl::SetValue( const wxString &value ) m_modified = false; DontMarkDirtyOnNextChange(); - if ( IsMultiLine() ) + const wxCharBuffer buffer(wxGTK_CONV_ENC(value, GetTextEncoding())); + if ( !buffer ) { - const wxCharBuffer buffer(wxGTK_CONV_ENC(value, GetTextEncoding())); - if ( !buffer ) - { - // see comment in WriteText() as to why we must warn the user about - // this - wxLogWarning(_("Failed to set text in the text control.")); - return; - } + // see comment in WriteText() as to why we must warn the user about + // this + wxLogWarning(_("Failed to set text in the text control.")); + return; + } + + // if the control is not empty, two "changed" signals are emitted, + // otherwise only one and we need to ignore either both or one of them + int ignore = flags & SetValue_SendEvent ? 0 : 1; + if ( !IsEmpty() ) + ignore++; - if (gtk_text_buffer_get_char_count(m_buffer) != 0) - IgnoreNextTextUpdate(); + if ( ignore ) + IgnoreNextTextUpdate(ignore); + if ( IsMultiLine() ) + { gtk_text_buffer_set_text( m_buffer, buffer, strlen(buffer) ); } else // single line { - // gtk_entry_set_text() emits two "changed" signals because internally - // it calls gtk_editable_delete_text() and gtk_editable_insert_text() - // but we want to have only one event - IgnoreNextTextUpdate(); - - gtk_entry_set_text( GTK_ENTRY(m_text), wxGTK_CONV(value) ); + gtk_entry_set_text( GTK_ENTRY(m_text), buffer ); } + // if, for whatever reason, the callback wasn't called the expected number + // of times, still reset the flags to the default values + m_dontMarkDirty = false; + m_countUpdatesToIgnore = 0; + + // GRG, Jun/2000: Changed this after a lot of discussion in // the lists. wxWidgets 2.2 will have a set of flags to // customize this behaviour. @@ -947,22 +1053,22 @@ void wxTextCtrl::AppendText( const wxString &text ) wxString wxTextCtrl::GetLineText( long lineNo ) const { + wxString result; if ( IsMultiLine() ) { GtkTextIter line; gtk_text_buffer_get_iter_at_line(m_buffer,&line,lineNo); GtkTextIter end = line; gtk_text_iter_forward_to_line_end(&end); - gchar *text = gtk_text_buffer_get_text(m_buffer,&line,&end,TRUE); - wxString result(wxGTK_CONV_BACK(text)); - g_free(text); - return result; + wxGtkString text(gtk_text_buffer_get_text(m_buffer, &line, &end, true)); + result = wxGTK_CONV_BACK(text); } else { - if (lineNo == 0) return GetValue(); - return wxEmptyString; + if (lineNo == 0) + result = GetValue(); } + return result; } void wxTextCtrl::OnDropFiles( wxDropFilesEvent &WXUNUSED(event) ) @@ -1047,31 +1153,7 @@ int wxTextCtrl::GetNumberOfLines() const { if ( IsMultiLine() ) { - GtkTextIter iter; - gtk_text_buffer_get_iter_at_offset( m_buffer, &iter, 0 ); - - // move forward by one display line until the end is reached - int lineCount = 1; - while ( gtk_text_view_forward_display_line(GTK_TEXT_VIEW(m_text), &iter) ) - { - lineCount++; - } - - // If the last character in the text buffer is a newline, - // gtk_text_view_forward_display_line() will return false without that - // line being counted. Must add one manually in that case. - GtkTextIter lastCharIter; - gtk_text_buffer_get_iter_at_offset - ( - m_buffer, - &lastCharIter, - gtk_text_buffer_get_char_count(m_buffer) - 1 - ); - gchar lastChar = gtk_text_iter_get_char( &lastCharIter ); - if ( lastChar == wxT('\n') ) - lineCount++; - - return lineCount; + return gtk_text_buffer_get_line_count( m_buffer ); } else // single line { @@ -1187,9 +1269,9 @@ void wxTextCtrl::DiscardEdits() bool wxTextCtrl::IgnoreTextUpdate() { - if ( m_ignoreNextUpdate ) + if ( m_countUpdatesToIgnore > 0 ) { - m_ignoreNextUpdate = false; + m_countUpdatesToIgnore--; return true; } @@ -1547,15 +1629,16 @@ GtkWidget* wxTextCtrl::GetConnectWidget() return GTK_WIDGET(m_text); } -bool wxTextCtrl::IsOwnGtkWindow( GdkWindow *window ) +GdkWindow *wxTextCtrl::GTKGetWindow(wxArrayGdkWindows& WXUNUSED(windows)) const { if ( IsMultiLine() ) { - return window == gtk_text_view_get_window( GTK_TEXT_VIEW( m_text ), GTK_TEXT_WINDOW_TEXT ); // pure guesswork + return gtk_text_view_get_window(GTK_TEXT_VIEW(m_text), + GTK_TEXT_WINDOW_TEXT ); } else { - return (window == GTK_ENTRY(m_text)->text_area); + return GTK_ENTRY(m_text)->text_area; } } @@ -1652,7 +1735,7 @@ bool wxTextCtrl::SetStyle( long start, long end, const wxTextAttr& style ) // colours for the others wxTextAttr attr = wxTextAttr::Combine(style, m_defaultStyle, this); - wxGtkTextApplyTagsFromAttr( m_buffer, attr, &starti, &endi ); + wxGtkTextApplyTagsFromAttr( m_widget, m_buffer, attr, &starti, &endi ); return true; } @@ -1717,24 +1800,6 @@ void wxTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event) event.Enable( CanRedo() ); } -void wxTextCtrl::OnInternalIdle() -{ - // Check if we have to show window now - if (GtkShowFromOnIdle()) return; - - if (g_delayedFocus == this) - { - if (GTK_WIDGET_REALIZED(m_widget)) - { - gtk_widget_grab_focus( m_widget ); - g_delayedFocus = NULL; - } - } - - if (wxUpdateUIEvent::CanUpdate(this)) - UpdateWindowUI(wxUPDATE_UI_FROMIDLE); -} - wxSize wxTextCtrl::DoGetBestSize() const { // FIXME should be different for multi-line controls... @@ -1761,7 +1826,10 @@ void wxTextCtrl::Freeze() G_CALLBACK (gtk_text_exposed_callback), this); gtk_widget_set_sensitive(m_widget, false); g_object_ref(m_buffer); - gtk_text_view_set_buffer(GTK_TEXT_VIEW(m_text), gtk_text_buffer_new(NULL)); + GtkTextBuffer* buf_new = gtk_text_buffer_new(NULL); + gtk_text_view_set_buffer(GTK_TEXT_VIEW(m_text), buf_new); + // FIXME: this leaks the new buffer, since gtk_text_view_set_buffer + // adds its own reference, but unrefing it here can cause a crash later } } } @@ -1839,6 +1907,18 @@ void wxTextCtrl::OnUrlMouseEvent(wxMouseEvent& event) GetEventHandler()->ProcessEvent(url_event); } +bool wxTextCtrl::GTKProcessEvent(wxEvent& event) const +{ + bool rc = wxTextCtrlBase::GTKProcessEvent(event); + + // GtkTextView starts a drag operation when left mouse button is pressed + // and ends it when it is released and if it doesn't get the release event + // the next click on a control results in an assertion failure inside + // gtk_text_view_start_selection_drag() which simply *kills* the program + // without anything we can do about it, so always let GTK+ have this event + return rc && (IsSingleLine() || event.GetEventType() != wxEVT_LEFT_UP); +} + // static wxVisualAttributes wxTextCtrl::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))