X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/6f85e712009f9b66c200302e75ae9dfbf5cc9c8d..7325e21c4fda06b0f4a2ea5fd72169bae06a0d19:/src/gtk/textctrl.cpp diff --git a/src/gtk/textctrl.cpp b/src/gtk/textctrl.cpp index 74425264af..4417e2c99c 100644 --- a/src/gtk/textctrl.cpp +++ b/src/gtk/textctrl.cpp @@ -1,5 +1,5 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: textctrl.cpp +// Name: src/gtk/textctrl.cpp // Purpose: // Author: Robert Roebling // Id: $Id$ @@ -11,12 +11,16 @@ #include "wx/wxprec.h" #include "wx/textctrl.h" -#include "wx/utils.h" -#include "wx/intl.h" -#include "wx/log.h" + +#ifndef WX_PRECOMP + #include "wx/intl.h" + #include "wx/log.h" + #include "wx/utils.h" + #include "wx/panel.h" + #include "wx/settings.h" +#endif + #include "wx/math.h" -#include "wx/settings.h" -#include "wx/panel.h" #include "wx/strconv.h" #include "wx/fontutil.h" // for wxNativeFontInfo (GetNativeFontInfo()) @@ -28,18 +32,10 @@ #include "wx/gtk/private.h" #include -//----------------------------------------------------------------------------- -// idle system -//----------------------------------------------------------------------------- - -extern void wxapp_install_idle_handler(); -extern bool g_isIdle; - //----------------------------------------------------------------------------- // data //----------------------------------------------------------------------------- -extern wxCursor g_globalCursor; extern wxWindowGTK *g_delayedFocus; // ---------------------------------------------------------------------------- @@ -92,6 +88,19 @@ static void wxGtkTextApplyTagsFromAttr(GtkTextBuffer *text_buffer, NULL ); gtk_text_buffer_apply_tag (text_buffer, tag, start, end); g_free (font_string); + + if (attr.GetFont().GetUnderlined()) + { + g_snprintf(buf, sizeof(buf), "WXFONTUNDERLINE"); + 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, + "underline-set", TRUE, + "underline", PANGO_UNDERLINE_SINGLE, + NULL ); + gtk_text_buffer_apply_tag (text_buffer, tag, start, end); + } } if (attr.HasTextColour()) @@ -445,7 +454,8 @@ gtk_text_changed_callback( GtkWidget *widget, wxTextCtrl *win ) if (g_isIdle) wxapp_install_idle_handler(); - win->SetModified(); + if ( win->MarkDirtyOnChange() ) + win->MarkDirty(); wxCommandEvent event( wxEVT_COMMAND_TEXT_UPDATED, win->GetId() ); event.SetEventObject( win ); @@ -453,6 +463,48 @@ gtk_text_changed_callback( GtkWidget *widget, wxTextCtrl *win ) } } +//----------------------------------------------------------------------------- +// clipboard events: "copy-clipboard", "cut-clipboard", "paste-clipboard" +//----------------------------------------------------------------------------- + +// common part of the event handlers below +static void +handle_text_clipboard_callback( GtkWidget *widget, wxTextCtrl *win, + wxEventType eventType, const gchar * signal_name) +{ + wxClipboardTextEvent event( eventType, win->GetId() ); + event.SetEventObject( win ); + if ( win->GetEventHandler()->ProcessEvent( event ) ) + { + // don't let the default processing to take place if we did something + // ourselves in the event handler + g_signal_stop_emission_by_name (widget, signal_name); + } +} + +extern "C" { +static void +gtk_copy_clipboard_callback( GtkWidget *widget, wxTextCtrl *win ) +{ + handle_text_clipboard_callback( + widget, win, wxEVT_COMMAND_TEXT_COPY, "copy-clipboard" ); +} + +static void +gtk_cut_clipboard_callback( GtkWidget *widget, wxTextCtrl *win ) +{ + handle_text_clipboard_callback( + widget, win, wxEVT_COMMAND_TEXT_CUT, "cut-clipboard" ); +} + +static void +gtk_paste_clipboard_callback( GtkWidget *widget, wxTextCtrl *win ) +{ + handle_text_clipboard_callback( + widget, win, wxEVT_COMMAND_TEXT_PASTE, "paste-clipboard" ); +} +} + //----------------------------------------------------------------------------- // "expose_event" from scrolled window and textview //----------------------------------------------------------------------------- @@ -500,11 +552,13 @@ END_EVENT_TABLE() void wxTextCtrl::Init() { + m_dontMarkDirty = m_ignoreNextUpdate = m_modified = false; + SetUpdateFont(false); - m_text = - m_vScrollbar = (GtkWidget *)NULL; + + m_text = NULL; m_frozenness = 0; m_gdkHandCursor = NULL; m_gdkXTermCursor = NULL; @@ -551,9 +605,6 @@ bool wxTextCtrl::Create( wxWindow *parent, return false; } - - m_vScrollbarVisible = false; - bool multi_line = (style & wxTE_MULTILINE) != 0; if (multi_line) @@ -567,6 +618,8 @@ bool wxTextCtrl::Create( wxWindow *parent, m_widget = gtk_scrolled_window_new( NULL, NULL ); gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( m_widget ), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC ); + // for ScrollLines/Pages + m_scrollBar[1] = (GtkRange*)((GtkScrolledWindow*)m_widget)->vscrollbar; // Insert view into scrolled window gtk_container_add( GTK_CONTAINER(m_widget), m_text ); @@ -594,8 +647,7 @@ bool wxTextCtrl::Create( wxWindow *parent, gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW( m_text ), wrap ); - if (!HasFlag(wxNO_BORDER)) - gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(m_widget), GTK_SHADOW_IN ); + GtkScrolledWindowSetBorder(m_widget, style); gtk_widget_add_events( GTK_WIDGET(m_text), GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK ); @@ -608,7 +660,7 @@ bool wxTextCtrl::Create( wxWindow *parent, m_text = gtk_entry_new(); if (style & wxNO_BORDER) - g_object_set( GTK_ENTRY(m_text), "has-frame", FALSE, NULL ); + g_object_set (m_text, "has-frame", FALSE, NULL); } m_parent->DoAddChild( this ); @@ -618,7 +670,9 @@ bool wxTextCtrl::Create( wxWindow *parent, PostCreation(size); if (multi_line) + { gtk_widget_show(m_text); + } if (!value.empty()) { @@ -709,6 +763,13 @@ bool wxTextCtrl::Create( wxWindow *parent, G_CALLBACK (gtk_text_changed_callback), this); } + g_signal_connect (m_text, "copy-clipboard", + G_CALLBACK (gtk_copy_clipboard_callback), this); + g_signal_connect (m_text, "cut-clipboard", + G_CALLBACK (gtk_cut_clipboard_callback), this); + g_signal_connect (m_text, "paste-clipboard", + G_CALLBACK (gtk_paste_clipboard_callback), this); + m_cursor = wxCursor( wxCURSOR_IBEAM ); wxTextAttr attrDef(GetForegroundColour(), GetBackgroundColour(), GetFont()); @@ -735,19 +796,18 @@ wxString wxTextCtrl::GetValue() const gtk_text_buffer_get_end_iter( m_buffer, &end ); gchar *text = gtk_text_buffer_get_text( m_buffer, &start, &end, TRUE ); -#if wxUSE_UNICODE - wxWCharBuffer buffer( wxConvUTF8.cMB2WX( text ) ); -#else - wxCharBuffer buffer( wxConvLocal.cWC2WX( wxConvUTF8.cMB2WC( text ) ) ); -#endif - if ( buffer ) - tmp = buffer; + const wxWxCharBuffer buf = wxGTK_CONV_BACK(text); + if ( buf ) + tmp = buf; g_free( text ); } else { - tmp = wxGTK_CONV_BACK( gtk_entry_get_text( GTK_ENTRY(m_text) ) ); + const gchar *text = gtk_entry_get_text( GTK_ENTRY(m_text) ); + const wxWxCharBuffer buf = wxGTK_CONV_BACK( text ); + if ( buf ) + tmp = buf; } return tmp; @@ -757,35 +817,41 @@ void wxTextCtrl::SetValue( const wxString &value ) { wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") ); + // the control won't be modified any more as we programmatically replace + // all the existing text, so reset the flag and don't set it again (and do + // it now, before the text event handler is ran so that IsModified() called + // from there returns the expected value) + m_modified = false; + DontMarkDirtyOnNextChange(); + if (m_windowStyle & wxTE_MULTILINE) { -#if wxUSE_UNICODE - wxCharBuffer buffer( wxConvUTF8.cWX2MB( value) ); -#else - wxCharBuffer buffer( wxConvUTF8.cWC2MB( wxConvLocal.cWX2WC( value ) ) ); -#endif - if (gtk_text_buffer_get_char_count(m_buffer) != 0) - IgnoreNextTextUpdate(); - + const wxCharBuffer buffer(wxGTK_CONV(value)); if ( !buffer ) { // what else can we do? at least don't crash... return; } + if (gtk_text_buffer_get_char_count(m_buffer) != 0) + IgnoreNextTextUpdate(); + gtk_text_buffer_set_text( m_buffer, buffer, strlen(buffer) ); } - else + else // single line { - gtk_entry_set_text( GTK_ENTRY(m_text), wxGTK_CONV( value ) ); + // 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) ); } // 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. SetInsertionPoint(0); - - m_modified = false; } void wxTextCtrl::WriteText( const wxString &text ) @@ -795,25 +861,26 @@ void wxTextCtrl::WriteText( const wxString &text ) if ( text.empty() ) return; - // gtk_text_changed_callback() will set m_modified to true but m_modified - // shouldn't be changed by the program writing to the text control itself, - // so save the old value and restore when we're done - bool oldModified = m_modified; + const wxCharBuffer buffer(wxGTK_CONV(text)); + if ( !buffer ) + { + // what else can we do? at least don't crash... + return; + } + + // we're changing the text programmatically + DontMarkDirtyOnNextChange(); if ( m_windowStyle & wxTE_MULTILINE ) { -#if wxUSE_UNICODE - wxCharBuffer buffer( wxConvUTF8.cWX2MB( text ) ); -#else - wxCharBuffer buffer( wxConvUTF8.cWC2MB( wxConvLocal.cWX2WC( text ) ) ); -#endif - if ( !buffer ) - { - // what else can we do? at least don't crash... - return; - } + // First remove the selection if there is one + // TODO: Is there an easier GTK specific way to do this? + long from, to; + GetSelection(&from, &to); + if (from != to) + Remove(from, to); - // TODO: Call whatever is needed to delete the selection. + // Insert the text wxGtkTextInsert( m_text, m_buffer, m_defaultStyle, buffer ); GtkAdjustment *adj = gtk_scrolled_window_get_vadjustment( GTK_SCROLLED_WINDOW(m_widget) ); @@ -832,24 +899,11 @@ void wxTextCtrl::WriteText( const wxString &text ) // This moves the cursor pos to behind the inserted text. gint len = gtk_editable_get_position(GTK_EDITABLE(m_text)); -#if wxUSE_UNICODE - wxCharBuffer buffer( wxConvUTF8.cWX2MB( text ) ); -#else - wxCharBuffer buffer( wxConvUTF8.cWC2MB( wxConvLocal.cWX2WC( text ) ) ); -#endif - if ( !buffer ) - { - // what else can we do? at least don't crash... - return; - } - gtk_editable_insert_text( GTK_EDITABLE(m_text), buffer, strlen(buffer), &len ); // Bring entry's cursor uptodate. gtk_editable_set_position( GTK_EDITABLE(m_text), len ); } - - m_modified = oldModified; } void wxTextCtrl::AppendText( const wxString &text ) @@ -945,16 +999,44 @@ int wxTextCtrl::GetLineLength(long lineNo) const else { wxString str = GetLineText (lineNo); - return (int) str.Length(); + return (int) str.length(); } } int wxTextCtrl::GetNumberOfLines() const { - if (m_windowStyle & wxTE_MULTILINE) - return gtk_text_buffer_get_line_count( m_buffer ); - else + if ( m_windowStyle & wxTE_MULTILINE ) + { + 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; + } + else // single line + { return 1; + } } void wxTextCtrl::SetInsertionPoint( long pos ) @@ -1063,11 +1145,6 @@ void wxTextCtrl::DiscardEdits() // max text length support // ---------------------------------------------------------------------------- -void wxTextCtrl::IgnoreNextTextUpdate() -{ - m_ignoreNextUpdate = true; -} - bool wxTextCtrl::IgnoreTextUpdate() { if ( m_ignoreNextUpdate ) @@ -1080,6 +1157,18 @@ bool wxTextCtrl::IgnoreTextUpdate() return false; } +bool wxTextCtrl::MarkDirtyOnChange() +{ + if ( m_dontMarkDirty ) + { + m_dontMarkDirty = false; + + return false; + } + + return true; +} + void wxTextCtrl::SetMaxLength(unsigned long len) { if ( !HasFlag(wxTE_MULTILINE) ) @@ -1115,7 +1204,7 @@ void wxTextCtrl::SetSelection( long from, long to ) if (from == -1 && to == -1) { from = 0; - to = GetValue().Length(); + to = GetValue().length(); } if (m_windowStyle & wxTE_MULTILINE) @@ -1375,7 +1464,7 @@ void wxTextCtrl::OnChar( wxKeyEvent &key_event ) { wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") ); - if ((key_event.GetKeyCode() == WXK_RETURN) && (m_windowStyle & wxPROCESS_ENTER)) + if ((key_event.GetKeyCode() == WXK_RETURN) && (m_windowStyle & wxTE_PROCESS_ENTER)) { wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, m_windowId); event.SetEventObject(this); @@ -1584,9 +1673,6 @@ void wxTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event) void wxTextCtrl::OnInternalIdle() { - wxCursor cursor = m_cursor; - if (g_globalCursor.Ok()) cursor = g_globalCursor; - if (g_delayedFocus == this) { if (GTK_WIDGET_REALIZED(m_widget)) @@ -1704,64 +1790,6 @@ void wxTextCtrl::OnUrlMouseEvent(wxMouseEvent& event) GetEventHandler()->ProcessEvent(url_event); } -// ---------------------------------------------------------------------------- -// scrolling -// ---------------------------------------------------------------------------- - -GtkAdjustment *wxTextCtrl::GetVAdj() const -{ - if ( !IsMultiLine() ) - return NULL; - - return gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(m_widget)); -} - -bool wxTextCtrl::DoScroll(GtkAdjustment *adj, int diff) -{ - float value = adj->value + diff; - - if ( value < 0 ) - value = 0; - - float upper = adj->upper - adj->page_size; - if ( value > upper ) - value = upper; - - // did we noticeably change the scroll position? - if ( fabs(adj->value - value) < 0.2 ) - { - // well, this is what Robert does in wxScrollBar, so it must be good... - return false; - } - - adj->value = value; - - gtk_adjustment_value_changed(GTK_ADJUSTMENT(adj)); - - return true; -} - -bool wxTextCtrl::ScrollLines(int lines) -{ - GtkAdjustment *adj = GetVAdj(); - if ( !adj ) - return false; - - int diff = (int)ceil(lines*adj->step_increment); - - return DoScroll(adj, diff); -} - -bool wxTextCtrl::ScrollPages(int pages) -{ - GtkAdjustment *adj = GetVAdj(); - if ( !adj ) - return false; - - return DoScroll(adj, (int)ceil(pages*adj->page_increment)); -} - - // static wxVisualAttributes wxTextCtrl::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))