#include "wx/math.h"
#endif
+#include "wx/scopeguard.h"
#include "wx/strconv.h"
#include "wx/fontutil.h" // for wxNativeFontInfo (GetNativeFontInfo())
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())
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())
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 =
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();
static void
au_check_word( GtkTextIter *s, GtkTextIter *e )
{
- static const char *URIPrefixes[] =
+ static const char *const URIPrefixes[] =
{
"http://",
"ftp://",
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;
}
}
}
+//-----------------------------------------------------------------------------
+// "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"
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;
{
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
}
//-----------------------------------------------------------------------------
-// "expose_event" from scrolled window and textview
+// "mark_set"
//-----------------------------------------------------------------------------
extern "C" {
-static gboolean
-gtk_text_exposed_callback( GtkWidget * WXUNUSED(widget),
- GdkEventExpose * WXUNUSED(event),
- wxTextCtrl * WXUNUSED(win) )
+static void mark_set(GtkTextBuffer*, GtkTextIter*, GtkTextMark* mark, GSList** markList)
{
- return TRUE;
+ if (gtk_text_mark_get_name(mark) == NULL)
+ *markList = g_slist_prepend(*markList, mark);
}
}
-
//-----------------------------------------------------------------------------
// wxTextCtrl
//-----------------------------------------------------------------------------
-IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl, wxTextCtrlBase)
-
BEGIN_EVENT_TABLE(wxTextCtrl, wxTextCtrlBase)
EVT_CHAR(wxTextCtrl::OnChar)
SetUpdateFont(false);
m_text = NULL;
- m_freezeCount = 0;
m_showPositionOnThaw = NULL;
- m_gdkHandCursor = NULL;
- m_gdkXTermCursor = NULL;
+ m_anonymousMarkList = NULL;
}
wxTextCtrl::~wxTextCtrl()
{
- if(m_gdkHandCursor)
- gdk_cursor_unref(m_gdkHandCursor);
- if(m_gdkXTermCursor)
- gdk_cursor_unref(m_gdkXTermCursor);
+ if (m_anonymousMarkList)
+ g_slist_free(m_anonymousMarkList);
}
wxTextCtrl::wxTextCtrl( 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;
? 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 );
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 );
- GTK_WIDGET_UNSET_FLAGS( m_widget, GTK_CAN_FOCUS );
+ gtk_widget_set_can_focus(m_widget, FALSE);
}
else
{
// 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 );
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 );
if (style & wxTE_READONLY)
GTKSetEditable();
- if (style & wxTE_PROCESS_ENTER)
- GTKSetActivatesDefault();
-
// left justification (alignment) is the default anyhow
if ( style & (wxTE_RIGHT | wxTE_CENTRE) )
GTKSetJustification();
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,
+ // 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",
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);
return GTK_EDITABLE(m_text);
}
+GtkEntry *wxTextCtrl::GetEntry() const
+{
+ return GTK_ENTRY(m_text);
+}
+
// ----------------------------------------------------------------------------
// flags handling
// ----------------------------------------------------------------------------
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;
wxLogWarning(_("Failed to set text in the text control."));
return;
}
+#endif
if ( !(flags & SetValue_SendEvent) )
{
{
EnableTextChangedEvents(true);
}
-
- // This was added after discussion on the list
- SetInsertionPoint(0);
}
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;
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?
// 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);
+ }
}
}
}
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;
}
}
+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() )
}
gtk_widget_set_sensitive( m_text, enable );
+ SetCursor(enable ? wxCursor(wxCURSOR_IBEAM) : wxCursor());
return true;
}
// disabled and enabled mode, or we end up with a different colour under the
// text.
wxColour oldColour = GetBackgroundColour();
- if (oldColour.Ok())
+ if (oldColour.IsOk())
{
// Need to set twice or it'll optimize the useful stuff out
if (oldColour == * wxWHITE)
{
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
{
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
{
wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, m_windowId);
event.SetEventObject(this);
event.SetString(GetValue());
- if ( GetEventHandler()->ProcessEvent(event) )
+ if ( HandleWindowEvent(event) )
return;
}
}
}
else
{
- return GTK_ENTRY(m_text)->text_area;
+ return gtk_entry_get_text_window(GTK_ENTRY(m_text));
}
}
//
// 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() )
if ( !wxControl::SetBackgroundColour( colour ) )
return false;
- if (!m_backgroundColour.Ok())
+ if (!m_backgroundColour.IsOk())
return false;
// change active background color too
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 );
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(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) )
+ {
+ 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);
// 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)
+ GTKFreezeWidget(m_text);
+
+ // removing buffer dramatically speeds up insertion:
+ g_object_ref(m_buffer);
+ GtkTextBuffer* buf_new = gtk_text_buffer_new(NULL);
+ 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);
+ // 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 (m_anonymousMarkList)
{
- // 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);
+ for (GSList* item = m_anonymousMarkList; item; item = item->next)
+ {
+ GtkTextMark* mark = static_cast<GtkTextMark*>(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;
}
}
}
-void wxTextCtrl::Thaw()
+void wxTextCtrl::DoThaw()
{
if ( HasFlag(wxTE_MULTILINE) )
{
- wxCHECK_RET(m_freezeCount != 0, _T("Thaw() without matching Freeze()"));
+ // 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_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();
}
// ----------------------------------------------------------------------------
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))
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);
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