X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/289532452089421ddadbd4726a8469511a19ab76..12bb29f5432174ecbd65549bda832d70d34a98ae:/src/gtk/textctrl.cpp diff --git a/src/gtk/textctrl.cpp b/src/gtk/textctrl.cpp index e88d1e2aed..10a90eaad7 100644 --- a/src/gtk/textctrl.cpp +++ b/src/gtk/textctrl.cpp @@ -30,7 +30,9 @@ #include #include +#include #include "wx/gtk/private.h" +#include "wx/gtk/private/gtk2-compat.h" // ---------------------------------------------------------------------------- // helpers @@ -110,6 +112,18 @@ static void wxGtkTextApplyTagsFromAttr(GtkWidget *text, NULL ); gtk_text_buffer_apply_tag (text_buffer, tag, start, end); } + if ( font.GetStrikethrough() ) + { + g_snprintf(buf, sizeof(buf), "WXFONTSTRIKETHROUGH"); + 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, + "strikethrough-set", TRUE, + "strikethrough", TRUE, + NULL ); + gtk_text_buffer_apply_tag (text_buffer, tag, start, end); + } } if (attr.HasTextColour()) @@ -155,25 +169,29 @@ static void wxGtkTextApplyTagsFromAttr(GtkWidget *text, GtkJustification align; switch (attr.GetAlignment()) { - default: - align = GTK_JUSTIFY_LEFT; - break; case wxTEXT_ALIGNMENT_RIGHT: align = GTK_JUSTIFY_RIGHT; break; case wxTEXT_ALIGNMENT_CENTER: align = GTK_JUSTIFY_CENTER; break; + case wxTEXT_ALIGNMENT_JUSTIFIED: +#ifdef __WXGTK3__ + align = GTK_JUSTIFY_FILL; + break; +#elif GTK_CHECK_VERSION(2,11,0) // gtk+ doesn't support justify before gtk+-2.11.0 with pango-1.17 being available // (but if new enough pango isn't available it's a mere gtk warning) -#if GTK_CHECK_VERSION(2,11,0) - case wxTEXT_ALIGNMENT_JUSTIFIED: if (!gtk_check_version(2,11,0)) + { align = GTK_JUSTIFY_FILL; - else - align = GTK_JUSTIFY_LEFT; - break; + break; + } + // fallthrough #endif + default: + align = GTK_JUSTIFY_LEFT; + break; } g_snprintf(buf, sizeof(buf), "WXALIGNMENT %d", align); @@ -391,7 +409,7 @@ au_check_word( GtkTextIter *s, GtkTextIter *e ) for( n = 0; n < WXSIZEOF(URIPrefixes); ++n ) { prefix_len = strlen(URIPrefixes[n]); - if((len > prefix_len) && !strncasecmp(text, URIPrefixes[n], prefix_len)) + if((len > prefix_len) && !wxStrnicmp(text, URIPrefixes[n], prefix_len)) break; } @@ -443,6 +461,25 @@ au_check_range(GtkTextIter *s, //----------------------------------------------------------------------------- extern "C" { + +// Normal version used for detecting IME input and generating appropriate +// events for it. +void +wx_insert_text_callback(GtkTextBuffer* buffer, + GtkTextIter* WXUNUSED(end), + gchar *text, + gint WXUNUSED(len), + wxTextCtrl *win) +{ + if ( win->GTKOnInsertText(text) ) + { + // If we already handled the new text insertion, don't do it again. + g_signal_stop_emission_by_name (buffer, "insert_text"); + } +} + + +// And an "after" version used for detecting URLs in the text. static void au_insert_text_callback(GtkTextBuffer * WXUNUSED(buffer), GtkTextIter *end, @@ -528,8 +565,6 @@ gtk_text_changed_callback( GtkWidget *WXUNUSED(widget), wxTextCtrl *win ) if ( win->IgnoreTextUpdate() ) return; - if (!win->m_hasVMT) return; - if ( win->MarkDirtyOnChange() ) win->MarkDirty(); @@ -538,44 +573,14 @@ gtk_text_changed_callback( GtkWidget *WXUNUSED(widget), wxTextCtrl *win ) } //----------------------------------------------------------------------------- -// clipboard events: "copy-clipboard", "cut-clipboard", "paste-clipboard" +// "mark_set" //----------------------------------------------------------------------------- -// 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->HandleWindowEvent( 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 ) +static void mark_set(GtkTextBuffer*, GtkTextIter*, GtkTextMark* mark, GSList** markList) { - handle_text_clipboard_callback( - widget, win, wxEVT_COMMAND_TEXT_PASTE, "paste-clipboard" ); + if (gtk_text_mark_get_name(mark) == NULL) + *markList = g_slist_prepend(*markList, mark); } } @@ -619,11 +624,25 @@ void wxTextCtrl::Init() SetUpdateFont(false); m_text = NULL; + m_buffer = NULL; m_showPositionOnThaw = NULL; + m_anonymousMarkList = NULL; } wxTextCtrl::~wxTextCtrl() { + if (m_text) + GTKDisconnect(m_text); + if (m_buffer) + GTKDisconnect(m_buffer); + + // this is also done by wxWindowGTK dtor, but has to be done here so our + // DoThaw() override is called + while (IsFrozen()) + Thaw(); + + if (m_anonymousMarkList) + g_slist_free(m_anonymousMarkList); } wxTextCtrl::wxTextCtrl( wxWindow *parent, @@ -660,10 +679,13 @@ bool wxTextCtrl::Create( wxWindow *parent, if (multi_line) { + m_buffer = gtk_text_buffer_new(NULL); + gulong sig_id = g_signal_connect(m_buffer, "mark_set", G_CALLBACK(mark_set), &m_anonymousMarkList); // Create view - m_text = gtk_text_view_new(); - - m_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(m_text) ); + m_text = gtk_text_view_new_with_buffer(m_buffer); + // gtk_text_view_set_buffer adds its own reference + g_object_unref(m_buffer); + g_signal_handler_disconnect(m_buffer, sig_id); // create "ShowPosition" marker GtkTextIter iter; @@ -678,7 +700,7 @@ bool wxTextCtrl::Create( wxWindow *parent, ? GTK_POLICY_NEVER : GTK_POLICY_AUTOMATIC ); // for ScrollLines/Pages - m_scrollBar[1] = (GtkRange*)((GtkScrolledWindow*)m_widget)->vscrollbar; + m_scrollBar[1] = GTK_RANGE(gtk_scrolled_window_get_vscrollbar(GTK_SCROLLED_WINDOW(m_widget))); // Insert view into scrolled window gtk_container_add( GTK_CONTAINER(m_widget), m_text ); @@ -689,7 +711,7 @@ bool wxTextCtrl::Create( wxWindow *parent, gtk_widget_add_events( GTK_WIDGET(m_text), GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK ); - GTK_WIDGET_UNSET_FLAGS( m_widget, GTK_CAN_FOCUS ); + gtk_widget_set_can_focus(m_widget, FALSE); } else { @@ -757,7 +779,7 @@ bool wxTextCtrl::Create( wxWindow *parent, GtkTextIter start, end; // We create our wxUrl tag here for slight efficiency gain - we - // don't have to check for the tag existance in callbacks, + // don't have to check for the tag existence in callbacks, // hereby it's guaranteed to exist. gtk_text_buffer_create_tag(m_buffer, "wxUrl", "foreground", "blue", @@ -784,21 +806,23 @@ bool wxTextCtrl::Create( wxWindow *parent, gtk_text_buffer_get_end_iter(m_buffer, &end); au_check_range(&start, &end); } + + // Also connect a normal (not "after") signal handler for checking for + // the IME-generated input. + g_signal_connect(m_buffer, "insert_text", + G_CALLBACK(wx_insert_text_callback), this); } else // single line { // do the right thing with Enter presses depending on whether we have // wxTE_PROCESS_ENTER or not GTKSetActivatesDefault(); + + GTKConnectInsertTextSignal(GTK_ENTRY(m_text)); } - 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); + GTKConnectClipboardSignals(m_text); m_cursor = wxCursor( wxCURSOR_IBEAM ); @@ -817,6 +841,30 @@ GtkEntry *wxTextCtrl::GetEntry() const return GTK_ENTRY(m_text); } +int wxTextCtrl::GTKIMFilterKeypress(GdkEventKey* event) const +{ +#if GTK_CHECK_VERSION(2, 22, 0) + if ( gtk_check_version(2, 12, 0) == 0 ) + { + if ( IsSingleLine() ) + { + return wxTextEntry::GTKIMFilterKeypress(event); + } + else + { + return gtk_text_view_im_context_filter_keypress( + GTK_TEXT_VIEW(m_text), + event + ); + } + } +#else // GTK+ < 2.22 + wxUnusedVar(event); +#endif // GTK+ 2.22+ + + return FALSE; +} + // ---------------------------------------------------------------------------- // flags handling // ---------------------------------------------------------------------------- @@ -1082,13 +1130,19 @@ void wxTextCtrl::WriteText( const wxString &text ) // Insert the text wxGtkTextInsert( m_text, m_buffer, m_defaultStyle, buffer ); - GtkAdjustment *adj = gtk_scrolled_window_get_vadjustment( GTK_SCROLLED_WINDOW(m_widget) ); // Scroll to cursor, but only if scrollbar thumb is at the very bottom // won't work when frozen, text view is not using m_buffer then - if (!IsFrozen() && wxIsSameDouble(adj->value, adj->upper - adj->page_size)) + if (!IsFrozen()) { - gtk_text_view_scroll_to_mark( GTK_TEXT_VIEW(m_text), - gtk_text_buffer_get_insert( m_buffer ), 0.0, FALSE, 0.0, 1.0 ); + GtkAdjustment* adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(m_widget)); + const double value = gtk_adjustment_get_value(adj); + const double upper = gtk_adjustment_get_upper(adj); + const double page_size = gtk_adjustment_get_page_size(adj); + if (wxIsSameDouble(value, upper - page_size)) + { + gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(m_text), + gtk_text_buffer_get_insert(m_buffer), 0, false, 0, 1); + } } } @@ -1141,7 +1195,7 @@ bool wxTextCtrl::PositionToXY(long pos, long *x, long *y ) const } else // single line control { - if ( pos <= GTK_ENTRY(m_text)->text_length ) + if (pos <= gtk_entry_get_text_length(GTK_ENTRY(m_text))) { if ( y ) *y = 0; @@ -1194,6 +1248,37 @@ int wxTextCtrl::GetLineLength(long lineNo) const } } +wxPoint wxTextCtrl::DoPositionToCoords(long pos) const +{ + if ( !IsMultiLine() ) + { + // Single line text entry (GtkTextEntry) doesn't have support for + // getting the coordinates for the given offset. Perhaps we could + // find them ourselves by using GetTextExtent() but for now just leave + // it unimplemented, this function is more useful for multiline + // controls anyhow. + return wxDefaultPosition; + } + + // Window coordinates for the given position is calculated by getting + // the buffer coordinates and converting them to window coordinates. + GtkTextView *textview = GTK_TEXT_VIEW(m_text); + + GtkTextIter iter; + gtk_text_buffer_get_iter_at_offset(m_buffer, &iter, pos); + + GdkRectangle bufferCoords; + gtk_text_view_get_iter_location(textview, &iter, &bufferCoords); + + gint winCoordX = 0, + winCoordY = 0; + gtk_text_view_buffer_to_window_coords(textview, GTK_TEXT_WINDOW_WIDGET, + bufferCoords.x, bufferCoords.y, + &winCoordX, &winCoordY); + + return wxPoint(winCoordX, winCoordY); +} + int wxTextCtrl::GetNumberOfLines() const { if ( IsMultiLine() ) @@ -1256,26 +1341,6 @@ bool wxTextCtrl::Enable( bool enable ) return true; } -// wxGTK-specific: called recursively by Enable, -// to give widgets an opportunity to correct their colours after they -// have been changed by Enable -void wxTextCtrl::OnEnabled(bool WXUNUSED(enable)) -{ - // If we have a custom background colour, we use this colour in both - // disabled and enabled mode, or we end up with a different colour under the - // text. - wxColour oldColour = GetBackgroundColour(); - if (oldColour.Ok()) - { - // Need to set twice or it'll optimize the useful stuff out - if (oldColour == * wxWHITE) - SetBackgroundColour(*wxBLACK); - else - SetBackgroundColour(*wxWHITE); - SetBackgroundColour(oldColour); - } -} - void wxTextCtrl::MarkDirty() { m_modified = true; @@ -1532,7 +1597,7 @@ bool wxTextCtrl::IsEditable() const if ( IsMultiLine() ) { - return gtk_text_view_get_editable(GTK_TEXT_VIEW(m_text)); + return gtk_text_view_get_editable(GTK_TEXT_VIEW(m_text)) != 0; } else { @@ -1553,7 +1618,7 @@ void wxTextCtrl::OnChar( wxKeyEvent &key_event ) { if ( HasFlag(wxTE_PROCESS_ENTER) ) { - wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, m_windowId); + wxCommandEvent event(wxEVT_TEXT_ENTER, m_windowId); event.SetEventObject(this); event.SetString(GetValue()); if ( HandleWindowEvent(event) ) @@ -1578,7 +1643,12 @@ GdkWindow *wxTextCtrl::GTKGetWindow(wxArrayGdkWindows& WXUNUSED(windows)) const } else { - return GTK_ENTRY(m_text)->text_area; +#ifdef __WXGTK3__ + // no access to internal GdkWindows + return NULL; +#else + return gtk_entry_get_text_window(GTK_ENTRY(m_text)); +#endif } } @@ -1642,7 +1712,7 @@ bool wxTextCtrl::SetBackgroundColour( const wxColour &colour ) if ( !wxControl::SetBackgroundColour( colour ) ) return false; - if (!m_backgroundColour.Ok()) + if (!m_backgroundColour.IsOk()) return false; // change active background color too @@ -1698,7 +1768,7 @@ bool wxTextCtrl::GetStyle(long position, wxTextAttr& style) // 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 ); + wxON_BLOCK_EXIT1(gtk_text_attributes_unref, pattr); // And query GTK for the attributes at the given position using it as base if ( !gtk_text_iter_get_attributes(&positioni, pattr) ) @@ -1725,7 +1795,7 @@ bool wxTextCtrl::GetStyle(long position, wxTextAttr& style) void wxTextCtrl::DoApplyWidgetStyle(GtkRcStyle *style) { - gtk_widget_modify_style(m_text, style); + GTKApplyStyle(m_text, style); } void wxTextCtrl::OnCut(wxCommandEvent& WXUNUSED(event)) @@ -1780,13 +1850,71 @@ void wxTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event) wxSize wxTextCtrl::DoGetBestSize() const { - // FIXME should be different for multi-line controls... - wxSize ret( wxControl::DoGetBestSize() ); - wxSize best(80, ret.y); - CacheBestSize(best); - return best; + return DoGetSizeFromTextSize(80); } +wxSize wxTextCtrl::DoGetSizeFromTextSize(int xlen, int ylen) const +{ + wxASSERT_MSG( m_widget, wxS("GetSizeFromTextSize called before creation") ); + + wxSize tsize(xlen, 0); + int cHeight = GetCharHeight(); + + if ( IsSingleLine() ) + { + if ( HasFlag(wxBORDER_NONE) ) + { + tsize.y = cHeight; +#ifdef __WXGTK3__ + tsize.IncBy(9, 0); +#else + tsize.IncBy(4, 0); +#endif // GTK3 + } + else + { + // default height + tsize.y = GTKGetPreferredSize(m_widget).y; + // Add the margins we have previously set, but only the horizontal border + // as vertical one has been taken account at GTKGetPreferredSize(). + // Also get other GTK+ margins. + tsize.IncBy( GTKGetEntryMargins(GetEntry()).x, 0); + } + } + + //multiline + else + { + // add space for vertical scrollbar + if ( m_scrollBar[1] && !(m_windowStyle & wxTE_NO_VSCROLL) ) + tsize.IncBy(GTKGetPreferredSize(GTK_WIDGET(m_scrollBar[1])).x + 3, 0); + + // height + tsize.y = cHeight; + if ( ylen <= 0 ) + { + tsize.y = 1 + cHeight * wxMax(wxMin(GetNumberOfLines(), 10), 2); + // add space for horizontal scrollbar + if ( m_scrollBar[0] && (m_windowStyle & wxHSCROLL) ) + tsize.IncBy(0, GTKGetPreferredSize(GTK_WIDGET(m_scrollBar[0])).y + 3); + } + + if ( !HasFlag(wxBORDER_NONE) ) + { + // hardcode borders, margins, etc + tsize.IncBy(5, 4); + } + } + + // Perhaps the user wants something different from CharHeight, or ylen + // is used as the height of a multiline text. + if ( ylen > 0 ) + tsize.IncBy(0, ylen - cHeight); + + return tsize; +} + + // ---------------------------------------------------------------------------- // freeze/thaw // ---------------------------------------------------------------------------- @@ -1795,25 +1923,31 @@ void wxTextCtrl::DoFreeze() { wxCHECK_RET(m_text != NULL, wxT("invalid text ctrl")); - wxWindow::DoFreeze(); + GTKFreezeWidget(m_text); if ( HasFlag(wxTE_MULTILINE) ) { - 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). + // These marks should be deleted when the buffer is changed, + // but they are not (in GTK+ up to at least 3.0.1). // 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); + if (m_anonymousMarkList) + { + for (GSList* item = m_anonymousMarkList; item; item = item->next) + { + GtkTextMark* mark = static_cast(item->data); + if (GTK_IS_TEXT_MARK(mark) && !gtk_text_mark_get_deleted(mark)) + gtk_text_buffer_delete_mark(m_buffer, mark); + } + g_slist_free(m_anonymousMarkList); + m_anonymousMarkList = NULL; + } } } @@ -1822,8 +1956,10 @@ void wxTextCtrl::DoThaw() if ( HasFlag(wxTE_MULTILINE) ) { // reattach buffer: + gulong sig_id = g_signal_connect(m_buffer, "mark_set", G_CALLBACK(mark_set), &m_anonymousMarkList); gtk_text_view_set_buffer(GTK_TEXT_VIEW(m_text), m_buffer); g_object_unref(m_buffer); + g_signal_handler_disconnect(m_buffer, sig_id); if (m_showPositionOnThaw != NULL) { @@ -1831,12 +1967,9 @@ void wxTextCtrl::DoThaw() GTK_TEXT_VIEW(m_text), m_showPositionOnThaw); m_showPositionOnThaw = NULL; } - - // and thaw the window - GTKThawWidget(m_text); } - wxWindow::DoThaw(); + GTKThawWidget(m_text); } // ---------------------------------------------------------------------------- @@ -1876,7 +2009,7 @@ void wxTextCtrl::OnUrlMouseEvent(wxMouseEvent& event) gtk_text_iter_forward_to_tag_toggle(&end, tag); // Native context menu is probably not desired on an URL. - // Consider making this dependant on ProcessEvent(wxTextUrlEvent) return value + // Consider making this dependent on ProcessEvent(wxTextUrlEvent) return value if(event.GetEventType() == wxEVT_RIGHT_DOWN) event.Skip(false); @@ -1906,7 +2039,7 @@ bool wxTextCtrl::GTKProcessEvent(wxEvent& event) const wxVisualAttributes wxTextCtrl::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant)) { - return GetDefaultAttributesFromGTKWidget(gtk_entry_new, true); + return GetDefaultAttributesFromGTKWidget(gtk_entry_new(), true); } #endif // wxUSE_TEXTCTRL