X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/e4161a2a08e622ba55f66610e26892f5fda37e6f..cb98e78b1e363e70d07360728c49b6dd21f36f7c:/src/gtk/textctrl.cpp diff --git a/src/gtk/textctrl.cpp b/src/gtk/textctrl.cpp index a1bdfc5414..8275b82b16 100644 --- a/src/gtk/textctrl.cpp +++ b/src/gtk/textctrl.cpp @@ -22,6 +22,7 @@ #include "wx/math.h" #endif +#include "wx/scopeguard.h" #include "wx/strconv.h" #include "wx/fontutil.h" // for wxNativeFontInfo (GetNativeFontInfo()) @@ -149,7 +150,7 @@ static void wxGtkTextApplyTagsFromAttr(GtkWidget *text, gtk_text_iter_get_line(start) ); gtk_text_iter_forward_line(¶_end); - wxGtkTextRemoveTagsWithPrefix(text_buffer, "WXALIGNMENT", start, end); + wxGtkTextRemoveTagsWithPrefix(text_buffer, "WXALIGNMENT", ¶_start, ¶_end); GtkJustification align; switch (attr.GetAlignment()) @@ -195,17 +196,12 @@ static void wxGtkTextApplyTagsFromAttr(GtkWidget *text, gtk_text_iter_get_line(start) ); gtk_text_iter_forward_line(¶_end); - wxGtkTextRemoveTagsWithPrefix(text_buffer, "WXINDENT", start, end); + wxGtkTextRemoveTagsWithPrefix(text_buffer, "WXINDENT", ¶_start, ¶_end); // 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)) / + float 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()); @@ -244,14 +240,14 @@ static void wxGtkTextApplyTagsFromAttr(GtkWidget *text, gtk_text_iter_get_line(start) ); gtk_text_iter_forward_line(¶_end); - wxGtkTextRemoveTagsWithPrefix(text_buffer, "WXTABS", start, end); + wxGtkTextRemoveTagsWithPrefix(text_buffer, "WXTABS", ¶_start, ¶_end); const wxArrayInt& tabs = attr.GetTabs(); - wxString tagname = _T("WXTABS"); + wxString tagname = wxT("WXTABS"); g_snprintf(buf, sizeof(buf), "WXTABS"); for (size_t i = 0; i < tabs.GetCount(); i++) - tagname += wxString::Format(_T(" %d"), tabs[i]); + tagname += wxString::Format(wxT(" %d"), tabs[i]); const wxWX2MBbuf buftag = tagname.utf8_str(); @@ -260,14 +256,9 @@ static void wxGtkTextApplyTagsFromAttr(GtkWidget *text, 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)) / + float 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++) @@ -360,7 +351,7 @@ extern "C" { static void au_check_word( GtkTextIter *s, GtkTextIter *e ) { - static const char *URIPrefixes[] = + static const char *const URIPrefixes[] = { "http://", "ftp://", @@ -504,6 +495,27 @@ au_delete_range_callback(GtkTextBuffer * WXUNUSED(buffer), } } +//----------------------------------------------------------------------------- +// "populate_popup" from text control and "unmap" from its poup menu +//----------------------------------------------------------------------------- + +extern "C" { +static void +gtk_textctrl_popup_unmap( GtkMenu *WXUNUSED(menu), wxTextCtrl* win ) +{ + win->GTKEnableFocusOutEvent(); +} +} + +extern "C" { +static void +gtk_textctrl_populate_popup( GtkEntry *WXUNUSED(entry), GtkMenu *menu, wxTextCtrl *win ) +{ + win->GTKDisableFocusOutEvent(); + + g_signal_connect (menu, "unmap", G_CALLBACK (gtk_textctrl_popup_unmap), win ); +} +} //----------------------------------------------------------------------------- // "changed" @@ -511,7 +523,7 @@ au_delete_range_callback(GtkTextBuffer * WXUNUSED(buffer), extern "C" { static void -gtk_text_changed_callback( GtkWidget * WXUNUSED(widget), wxTextCtrl *win ) +gtk_text_changed_callback( GtkWidget *WXUNUSED(widget), wxTextCtrl *win ) { if ( win->IgnoreTextUpdate() ) return; @@ -536,7 +548,7 @@ handle_text_clipboard_callback( GtkWidget *widget, wxTextCtrl *win, { wxClipboardTextEvent event( eventType, win->GetId() ); event.SetEventObject( win ); - if ( win->GetEventHandler()->ProcessEvent( event ) ) + if ( win->HandleWindowEvent( event ) ) { // don't let the default processing to take place if we did something // ourselves in the event handler @@ -567,21 +579,6 @@ gtk_paste_clipboard_callback( GtkWidget *widget, wxTextCtrl *win ) } } -//----------------------------------------------------------------------------- -// "expose_event" from scrolled window and textview -//----------------------------------------------------------------------------- - -extern "C" { -static gboolean -gtk_text_exposed_callback( GtkWidget * WXUNUSED(widget), - GdkEventExpose * WXUNUSED(event), - wxTextCtrl * WXUNUSED(win) ) -{ - return TRUE; -} -} - - //----------------------------------------------------------------------------- // wxTextCtrl //----------------------------------------------------------------------------- @@ -624,18 +621,11 @@ void wxTextCtrl::Init() SetUpdateFont(false); m_text = NULL; - m_freezeCount = 0; m_showPositionOnThaw = NULL; - m_gdkHandCursor = NULL; - m_gdkXTermCursor = NULL; } wxTextCtrl::~wxTextCtrl() { - if(m_gdkHandCursor) - gdk_cursor_unref(m_gdkHandCursor); - if(m_gdkXTermCursor) - gdk_cursor_unref(m_gdkXTermCursor); } wxTextCtrl::wxTextCtrl( wxWindow *parent, @@ -697,7 +687,7 @@ bool wxTextCtrl::Create( wxWindow *parent, GTKSetWrapMode(); - GtkScrolledWindowSetBorder(m_widget, style); + GTKScrolledWindowSetBorder(m_widget, style); gtk_widget_add_events( GTK_WIDGET(m_text), GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK ); @@ -708,10 +698,15 @@ bool wxTextCtrl::Create( wxWindow *parent, // a single-line text control: no need for scrollbars m_widget = m_text = gtk_entry_new(); + // work around probable bug in GTK+ 2.18 when calling WriteText on a + // new, empty control, see http://trac.wxwidgets.org/ticket/11409 + gtk_entry_get_text((GtkEntry*)m_text); if (style & wxNO_BORDER) g_object_set (m_text, "has-frame", FALSE, NULL); + } + g_object_ref(m_widget); m_parent->DoAddChild( this ); @@ -736,6 +731,11 @@ bool wxTextCtrl::Create( wxWindow *parent, G_CALLBACK (gtk_text_changed_callback), this); } + // Catch to disable focus out handling + g_signal_connect (m_text, "populate_popup", + G_CALLBACK (gtk_textctrl_populate_popup), + this); + if (!value.empty()) { SetValue( value ); @@ -757,8 +757,6 @@ bool wxTextCtrl::Create( wxWindow *parent, if (style & wxTE_AUTO_URL) { GtkTextIter start, end; - m_gdkHandCursor = gdk_cursor_new(GDK_HAND2); - m_gdkXTermCursor = gdk_cursor_new(GDK_XTERM); // We create our wxUrl tag here for slight efficiency gain - we // don't have to check for the tag existance in callbacks, @@ -789,6 +787,13 @@ bool wxTextCtrl::Create( wxWindow *parent, au_check_range(&start, &end); } } + else // single line + { + // do the right thing with Enter presses depending on whether we have + // wxTE_PROCESS_ENTER or not + GTKSetActivatesDefault(); + } + g_signal_connect (m_text, "copy-clipboard", G_CALLBACK (gtk_copy_clipboard_callback), this); @@ -809,6 +814,11 @@ GtkEditable *wxTextCtrl::GetEditable() const return GTK_EDITABLE(m_text); } +GtkEntry *wxTextCtrl::GetEntry() const +{ + return GTK_ENTRY(m_text); +} + // ---------------------------------------------------------------------------- // flags handling // ---------------------------------------------------------------------------- @@ -824,9 +834,19 @@ void wxTextCtrl::GTKSetEditable() void wxTextCtrl::GTKSetVisibility() { - // VZ: shouldn't we assert if wxTE_PASSWORD is set for multiline control? - if ( IsSingleLine() ) - gtk_entry_set_visibility(GTK_ENTRY(m_text), !HasFlag(wxTE_PASSWORD)); + wxCHECK_RET( IsSingleLine(), + "wxTE_PASSWORD is for single line text controls only" ); + + gtk_entry_set_visibility(GTK_ENTRY(m_text), !HasFlag(wxTE_PASSWORD)); +} + +void wxTextCtrl::GTKSetActivatesDefault() +{ + wxCHECK_RET( IsSingleLine(), + "wxTE_PROCESS_ENTER is for single line text controls only" ); + + gtk_entry_set_activates_default(GTK_ENTRY(m_text), + !HasFlag(wxTE_PROCESS_ENTER)); } void wxTextCtrl::GTKSetWrapMode() @@ -844,17 +864,7 @@ void wxTextCtrl::GTKSetWrapMode() else if ( HasFlag( wxTE_WORDWRAP ) ) wrap = GTK_WRAP_WORD; else // HasFlag(wxTE_BESTWRAP) always true as wxTE_BESTWRAP == 0 - { - // GTK_WRAP_WORD_CHAR seems to be new in GTK+ 2.4 -#ifdef __WXGTK24__ - if ( !gtk_check_version(2,4,0) ) - { - wrap = GTK_WRAP_WORD_CHAR; - } - else -#endif // __WXGTK24__ - wrap = GTK_WRAP_WORD; - } + wrap = GTK_WRAP_WORD_CHAR; gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW( m_text ), wrap ); } @@ -875,23 +885,16 @@ void wxTextCtrl::GTKSetJustification() } else // single line { -#ifdef __WXGTK24__ - // gtk_entry_set_alignment was introduced in gtk+-2.3.5 - if (!gtk_check_version(2,4,0)) - { - gfloat align; - if ( HasFlag(wxTE_RIGHT) ) - align = 1.0; - else if ( HasFlag(wxTE_CENTRE) ) - align = 0.5; - else // single line - align = 0.0; - - gtk_entry_set_alignment(GTK_ENTRY(m_text), align); - } -#endif // __WXGTK24__ - } + gfloat align; + if ( HasFlag(wxTE_RIGHT) ) + align = 1.0; + else if ( HasFlag(wxTE_CENTRE) ) + align = 0.5; + else // single line + align = 0.0; + gtk_entry_set_alignment(GTK_ENTRY(m_text), align); + } } void wxTextCtrl::SetWindowStyleFlag(long style) @@ -906,6 +909,9 @@ void wxTextCtrl::SetWindowStyleFlag(long style) if ( (style & wxTE_PASSWORD) != (styleOld & wxTE_PASSWORD) ) GTKSetVisibility(); + if ( (style & wxTE_PROCESS_ENTER) != (styleOld & wxTE_PROCESS_ENTER) ) + GTKSetActivatesDefault(); + static const long flagsWrap = wxTE_WORDWRAP | wxTE_CHARWRAP | wxTE_DONTWRAP; if ( (style & flagsWrap) != (styleOld & flagsWrap) ) GTKSetWrapMode(); @@ -977,6 +983,22 @@ void wxTextCtrl::DoSetValue( const wxString &value, int flags ) return; } + if (value.IsEmpty()) + { + if ( !(flags & SetValue_SendEvent) ) + EnableTextChangedEvents(false); + + gtk_text_buffer_set_text( m_buffer, "", 0 ); + + if ( !(flags & SetValue_SendEvent) ) + EnableTextChangedEvents(true); + + return; + } + +#if wxUSE_UNICODE + const wxCharBuffer buffer(value.utf8_str()); +#else wxFontEncoding enc = m_defaultStyle.HasFont() ? m_defaultStyle.GetFont().GetEncoding() : wxFONTENCODING_SYSTEM; @@ -991,6 +1013,7 @@ void wxTextCtrl::DoSetValue( const wxString &value, int flags ) wxLogWarning(_("Failed to set text in the text control.")); return; } +#endif if ( !(flags & SetValue_SendEvent) ) { @@ -1011,9 +1034,6 @@ void wxTextCtrl::DoSetValue( const wxString &value, int flags ) { EnableTextChangedEvents(true); } - - // This was added after discussion on the list - SetInsertionPoint(0); } void wxTextCtrl::WriteText( const wxString &text ) @@ -1029,6 +1049,9 @@ void wxTextCtrl::WriteText( const wxString &text ) return; } +#if wxUSE_UNICODE + const wxCharBuffer buffer(text.utf8_str()); +#else // check if we have a specific style for the current position wxFontEncoding enc = wxFONTENCODING_SYSTEM; wxTextAttr style; @@ -1049,6 +1072,7 @@ void wxTextCtrl::WriteText( const wxString &text ) wxLogWarning(_("Failed to insert text in the control.")); return; } +#endif // First remove the selection if there is one // TODO: Is there an easier GTK specific way to do this? @@ -1229,6 +1253,7 @@ bool wxTextCtrl::Enable( bool enable ) } gtk_widget_set_sensitive( m_text, enable ); + SetCursor(enable ? wxCursor(wxCURSOR_IBEAM) : wxCursor()); return true; } @@ -1309,20 +1334,19 @@ void wxTextCtrl::SetSelection( long from, long to ) { wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") ); - if (from == -1 && to == -1) - { - from = 0; - to = GetValue().length(); - } - if ( IsMultiLine() ) { + if (from == -1 && to == -1) + { + from = 0; + to = GetValue().length(); + } + GtkTextIter fromi, toi; gtk_text_buffer_get_iter_at_offset( m_buffer, &fromi, from ); gtk_text_buffer_get_iter_at_offset( m_buffer, &toi, to ); - gtk_text_buffer_place_cursor( m_buffer, &toi ); - gtk_text_buffer_move_mark_by_name( m_buffer, "selection_bound", &fromi ); + gtk_text_buffer_select_range( m_buffer, &fromi, &toi ); } else // single line { @@ -1534,32 +1558,9 @@ void wxTextCtrl::OnChar( wxKeyEvent &key_event ) wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, m_windowId); event.SetEventObject(this); event.SetString(GetValue()); - if ( GetEventHandler()->ProcessEvent(event) ) + if ( HandleWindowEvent(event) ) return; } - - // FIXME: this is not the right place to do it, wxDialog::OnCharHook() - // probably is - if ( IsSingleLine() ) - { - // This will invoke the dialog default action, such - // as the clicking the default button. - - wxWindow *top_frame = m_parent; - while (top_frame->GetParent() && !(top_frame->IsTopLevel())) - top_frame = top_frame->GetParent(); - - if (top_frame && GTK_IS_WINDOW(top_frame->m_widget)) - { - GtkWindow *window = GTK_WINDOW(top_frame->m_widget); - - if (window->default_widget) - { - gtk_widget_activate (window->default_widget); - return; - } - } - } } key_event.Skip(); @@ -1613,7 +1614,7 @@ void wxTextCtrl::ChangeFontGlobally() // // TODO: it can be implemented much more efficiently for GTK2 wxASSERT_MSG( IsMultiLine(), - _T("shouldn't be called for single line controls") ); + wxT("shouldn't be called for single line controls") ); wxString value = GetValue(); if ( !value.empty() ) @@ -1665,7 +1666,7 @@ bool wxTextCtrl::SetStyle( long start, long end, const wxTextAttr& style ) gint l = gtk_text_buffer_get_char_count( m_buffer ); wxCHECK_MSG( start >= 0 && end <= l, false, - _T("invalid range in wxTextCtrl::SetStyle") ); + wxT("invalid range in wxTextCtrl::SetStyle") ); GtkTextIter starti, endi; gtk_text_buffer_get_iter_at_offset( m_buffer, &starti, start ); @@ -1680,6 +1681,50 @@ bool wxTextCtrl::SetStyle( long start, long end, const wxTextAttr& style ) return false; } +bool wxTextCtrl::GetStyle(long position, wxTextAttr& style) +{ + if ( !IsMultiLine() ) + { + // no styles for GtkEntry + return false; + } + + gint l = gtk_text_buffer_get_char_count( m_buffer ); + + wxCHECK_MSG( position >= 0 && position <= l, false, + wxT("invalid range in wxTextCtrl::GetStyle") ); + + GtkTextIter positioni; + gtk_text_buffer_get_iter_at_offset(m_buffer, &positioni, position); + + // Obtain a copy of the default attributes + GtkTextAttributes * const + pattr = gtk_text_view_get_default_attributes(GTK_TEXT_VIEW(m_text)); + wxON_BLOCK_EXIT1( g_free, pattr ); + + // And query GTK for the attributes at the given position using it as base + if ( !gtk_text_iter_get_attributes(&positioni, pattr) ) + { + style = m_defaultStyle; + } + else // have custom attributes + { + style.SetBackgroundColour(pattr->appearance.bg_color); + style.SetTextColour(pattr->appearance.fg_color); + + const wxGtkString + pangoFontString(pango_font_description_to_string(pattr->font)); + + wxFont font; + if ( font.SetNativeFontInfo(wxString(pangoFontString)) ) + style.SetFont(font); + + // TODO: set alignment, tabs and indents + } + + return true; +} + void wxTextCtrl::DoApplyWidgetStyle(GtkRcStyle *style) { gtk_widget_modify_style(m_text, style); @@ -1748,60 +1793,52 @@ wxSize wxTextCtrl::DoGetBestSize() const // freeze/thaw // ---------------------------------------------------------------------------- -void wxTextCtrl::Freeze() +void wxTextCtrl::DoFreeze() { wxCHECK_RET(m_text != NULL, wxT("invalid text ctrl")); + wxWindow::DoFreeze(); + if ( HasFlag(wxTE_MULTILINE) ) { - if (m_freezeCount++ == 0) - { - // freeze textview updates and remove buffer - g_signal_connect (m_text, "expose_event", - G_CALLBACK (gtk_text_exposed_callback), this); - g_signal_connect (m_widget, "expose_event", - G_CALLBACK (gtk_text_exposed_callback), this); - gtk_widget_set_sensitive(m_widget, false); - g_object_ref(m_buffer); - GtkTextBuffer* buf_new = gtk_text_buffer_new(NULL); - GtkTextMark* mark = GTK_TEXT_VIEW(m_text)->first_para_mark; - gtk_text_view_set_buffer(GTK_TEXT_VIEW(m_text), buf_new); - // gtk_text_view_set_buffer adds its own reference - g_object_unref(buf_new); - // This mark should be deleted when the buffer is changed, - // but it's not (in GTK+ up to at least 2.10.6). - // Otherwise these anonymous marks start to build up in the buffer, - // and Freeze takes longer and longer each time it is called. - if (GTK_IS_TEXT_MARK(mark) && !gtk_text_mark_get_deleted(mark)) - gtk_text_buffer_delete_mark(m_buffer, mark); - } + GTKFreezeWidget(m_text); + + // removing buffer dramatically speeds up insertion: + g_object_ref(m_buffer); + GtkTextBuffer* buf_new = gtk_text_buffer_new(NULL); + GtkTextMark* mark = GTK_TEXT_VIEW(m_text)->first_para_mark; + gtk_text_view_set_buffer(GTK_TEXT_VIEW(m_text), buf_new); + // gtk_text_view_set_buffer adds its own reference + g_object_unref(buf_new); + // This mark should be deleted when the buffer is changed, + // but it's not (in GTK+ up to at least 2.10.6). + // Otherwise these anonymous marks start to build up in the buffer, + // and Freeze takes longer and longer each time it is called. + if (GTK_IS_TEXT_MARK(mark) && !gtk_text_mark_get_deleted(mark)) + gtk_text_buffer_delete_mark(m_buffer, mark); } } -void wxTextCtrl::Thaw() +void wxTextCtrl::DoThaw() { if ( HasFlag(wxTE_MULTILINE) ) { - wxCHECK_RET(m_freezeCount != 0, _T("Thaw() without matching Freeze()")); + // reattach buffer: + gtk_text_view_set_buffer(GTK_TEXT_VIEW(m_text), m_buffer); + g_object_unref(m_buffer); - if (--m_freezeCount == 0) + if (m_showPositionOnThaw != NULL) { - // Reattach buffer and thaw textview updates - gtk_text_view_set_buffer(GTK_TEXT_VIEW(m_text), m_buffer); - g_object_unref(m_buffer); - gtk_widget_set_sensitive(m_widget, true); - g_signal_handlers_disconnect_by_func (m_widget, - (gpointer) gtk_text_exposed_callback, this); - g_signal_handlers_disconnect_by_func (m_text, - (gpointer) gtk_text_exposed_callback, this); - if (m_showPositionOnThaw != NULL) - { - gtk_text_view_scroll_mark_onscreen( - GTK_TEXT_VIEW(m_text), m_showPositionOnThaw); - m_showPositionOnThaw = NULL; - } + gtk_text_view_scroll_mark_onscreen( + GTK_TEXT_VIEW(m_text), m_showPositionOnThaw); + m_showPositionOnThaw = NULL; } + + // and thaw the window + GTKThawWidget(m_text); } + + wxWindow::DoThaw(); } // ---------------------------------------------------------------------------- @@ -1828,13 +1865,11 @@ void wxTextCtrl::OnUrlMouseEvent(wxMouseEvent& event) gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(m_text), &end, x, y); if (!gtk_text_iter_has_tag(&end, tag)) { - gdk_window_set_cursor(gtk_text_view_get_window(GTK_TEXT_VIEW(m_text), - GTK_TEXT_WINDOW_TEXT), m_gdkXTermCursor); + SetCursor(wxCursor(wxCURSOR_IBEAM)); return; } - gdk_window_set_cursor(gtk_text_view_get_window(GTK_TEXT_VIEW(m_text), - GTK_TEXT_WINDOW_TEXT), m_gdkHandCursor); + SetCursor(wxCursor(wxCURSOR_HAND)); start = end; if(!gtk_text_iter_begins_tag(&start, tag)) @@ -1853,8 +1888,8 @@ void wxTextCtrl::OnUrlMouseEvent(wxMouseEvent& event) InitCommandEvent(url_event); // Is that a good idea? Seems not (pleasure with gtk_text_view_start_selection_drag) - //event.Skip(!GetEventHandler()->ProcessEvent(url_event)); - GetEventHandler()->ProcessEvent(url_event); + //event.Skip(!HandleWindowEvent(url_event)); + HandleWindowEvent(url_event); } bool wxTextCtrl::GTKProcessEvent(wxEvent& event) const