+ m_cursor = wxCursor( wxCURSOR_IBEAM );
+
+ wxTextAttr attrDef(GetForegroundColour(), GetBackgroundColour(), GetFont());
+ SetDefaultStyle( attrDef );
+
+ return true;
+}
+
+
+void wxTextCtrl::CalculateScrollbar()
+{
+#ifndef __WXGTK20__
+ if ((m_windowStyle & wxTE_MULTILINE) == 0) return;
+
+ GtkAdjustment *adj = GTK_TEXT(m_text)->vadj;
+
+ if (adj->upper - adj->page_size < 0.8)
+ {
+ if (m_vScrollbarVisible)
+ {
+ gtk_widget_hide( m_vScrollbar );
+ m_vScrollbarVisible = false;
+ }
+ }
+ else
+ {
+ if (!m_vScrollbarVisible)
+ {
+ gtk_widget_show( m_vScrollbar );
+ m_vScrollbarVisible = true;
+ }
+ }
+#endif
+}
+
+wxString wxTextCtrl::GetValue() const
+{
+ wxCHECK_MSG( m_text != NULL, wxEmptyString, wxT("invalid text ctrl") );
+
+ wxString tmp;
+ if (m_windowStyle & wxTE_MULTILINE)
+ {
+#ifdef __WXGTK20__
+ GtkTextIter start;
+ 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 );
+
+#if wxUSE_UNICODE
+ wxWCharBuffer buffer( wxConvUTF8.cMB2WX( text ) );
+#else
+ wxCharBuffer buffer( wxConvLocal.cWC2WX( wxConvUTF8.cMB2WC( text ) ) );
+#endif
+ if ( buffer )
+ tmp = buffer;
+
+ g_free( text );
+#else
+ gint len = gtk_text_get_length( GTK_TEXT(m_text) );
+ char *text = gtk_editable_get_chars( GTK_EDITABLE(m_text), 0, len );
+ tmp = text;
+ g_free( text );
+#endif
+ }
+ else
+ {
+ tmp = wxGTK_CONV_BACK( gtk_entry_get_text( GTK_ENTRY(m_text) ) );
+ }
+
+ return tmp;
+}
+
+void wxTextCtrl::SetValue( const wxString &value )
+{
+ wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
+
+ if (m_windowStyle & wxTE_MULTILINE)
+ {
+#ifdef __WXGTK20__
+
+#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();
+
+ if ( !buffer )
+ {
+ // what else can we do? at least don't crash...
+ return;
+ }
+
+ gtk_text_buffer_set_text( m_buffer, buffer, strlen(buffer) );
+
+#else
+ gint len = gtk_text_get_length( GTK_TEXT(m_text) );
+ gtk_editable_delete_text( GTK_EDITABLE(m_text), 0, len );
+ len = 0;
+ gtk_editable_insert_text( GTK_EDITABLE(m_text), value.mbc_str(), value.Length(), &len );
+#endif
+ }
+ else
+ {
+ 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 )
+{
+ wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
+
+ 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;
+
+ if ( m_windowStyle & wxTE_MULTILINE )
+ {
+#ifdef __WXGTK20__
+
+#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;
+ }
+
+ // TODO: Call whatever is needed to delete the selection.
+ 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
+ if ( wxIsSameDouble(adj->value, adj->upper - adj->page_size) )
+ {
+ 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 );
+ }
+#else // GTK 1.x
+ // After cursor movements, gtk_text_get_point() is wrong by one.
+ gtk_text_set_point( GTK_TEXT(m_text), GET_EDITABLE_POS(m_text) );
+
+ // always use m_defaultStyle, even if it is empty as otherwise
+ // resetting the style and appending some more text wouldn't work: if
+ // we don't specify the style explicitly, the old style would be used
+ gtk_editable_delete_selection( GTK_EDITABLE(m_text) );
+ wxGtkTextInsert(m_text, m_defaultStyle, text.c_str(), text.Len());
+
+ // we called wxGtkTextInsert with correct font, no need to do anything
+ // in UpdateFontIfNeeded() any longer
+ if ( !text.empty() )
+ {
+ SetUpdateFont(false);
+ }
+
+ // Bring editable's cursor back uptodate.
+ SET_EDITABLE_POS(m_text, gtk_text_get_point( GTK_TEXT(m_text) ));
+#endif // GTK 1.x/2.0
+ }
+ else // single line
+ {
+ // First remove the selection if there is one
+ gtk_editable_delete_selection( GTK_EDITABLE(m_text) );
+
+ // This moves the cursor pos to behind the inserted text.
+ gint len = GET_EDITABLE_POS(m_text);
+
+#ifdef __WXGTK20__
+
+#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 );
+
+#else
+ gtk_editable_insert_text( GTK_EDITABLE(m_text), text.c_str(), text.Len(), &len );
+#endif
+
+ // Bring entry's cursor uptodate.
+ gtk_entry_set_position( GTK_ENTRY(m_text), len );
+ }
+
+ m_modified = oldModified;
+}
+
+void wxTextCtrl::AppendText( const wxString &text )
+{
+ SetInsertionPointEnd();
+ WriteText( text );
+}
+
+wxString wxTextCtrl::GetLineText( long lineNo ) const
+{
+ if (m_windowStyle & wxTE_MULTILINE)
+ {
+#ifndef __WXGTK20__
+ gint len = gtk_text_get_length( GTK_TEXT(m_text) );
+ char *text = gtk_editable_get_chars( GTK_EDITABLE(m_text), 0, len );
+
+ if (text)
+ {
+ wxString buf;
+ long i;
+ int currentLine = 0;
+ for (i = 0; currentLine != lineNo && text[i]; i++ )
+ if (text[i] == '\n')
+ currentLine++;
+ // Now get the text
+ int j;
+ for (j = 0; text[i] && text[i] != '\n'; i++, j++ )
+ buf += text[i];
+
+ g_free( text );
+ return buf;
+ }
+ else
+ {
+ return wxEmptyString;
+ }
+#else
+ 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;
+#endif
+ }
+ else
+ {
+ if (lineNo == 0) return GetValue();
+ return wxEmptyString;
+ }
+}
+
+void wxTextCtrl::OnDropFiles( wxDropFilesEvent &WXUNUSED(event) )
+{
+ /* If you implement this, don't forget to update the documentation!
+ * (file docs/latex/wx/text.tex) */
+ wxFAIL_MSG( wxT("wxTextCtrl::OnDropFiles not implemented") );
+}
+
+bool wxTextCtrl::PositionToXY(long pos, long *x, long *y ) const
+{
+ if ( m_windowStyle & wxTE_MULTILINE )
+ {
+#ifdef __WXGTK20__
+ GtkTextIter iter;
+ gtk_text_buffer_get_iter_at_offset(m_buffer, &iter, pos);
+ if (gtk_text_iter_is_end(&iter))
+ return false;
+
+ *y = gtk_text_iter_get_line(&iter);
+ *x = gtk_text_iter_get_line_offset(&iter);
+#else
+ wxString text = GetValue();
+
+ // cast to prevent warning. But pos really should've been unsigned.
+ if( (unsigned long)pos > text.Len() )
+ return false;
+
+ *x=0; // First Col
+ *y=0; // First Line
+
+ const wxChar* stop = text.c_str() + pos;
+ for ( const wxChar *p = text.c_str(); p < stop; p++ )
+ {
+ if (*p == wxT('\n'))
+ {
+ (*y)++;
+ *x=0;
+ }
+ else
+ (*x)++;
+ }
+#endif
+ }
+ else // single line control
+ {
+ if ( pos <= GTK_ENTRY(m_text)->text_length )
+ {
+ *y = 0;
+ *x = pos;
+ }
+ else
+ {
+ // index out of bounds
+ return false;
+ }
+ }
+
+ return true;
+}
+
+long wxTextCtrl::XYToPosition(long x, long y ) const
+{
+ if (!(m_windowStyle & wxTE_MULTILINE)) return 0;
+
+#ifdef __WXGTK20__
+ GtkTextIter iter;
+ if (y >= gtk_text_buffer_get_line_count (m_buffer))
+ return -1;
+
+ gtk_text_buffer_get_iter_at_line(m_buffer, &iter, y);
+ if (x >= gtk_text_iter_get_chars_in_line (&iter))
+ return -1;
+
+ return gtk_text_iter_get_offset(&iter) + x;
+#else
+ long pos=0;
+ for( int i=0; i<y; i++ ) pos += GetLineLength(i) + 1; // one for '\n'
+
+ pos += x;
+ return pos;
+#endif
+}
+
+int wxTextCtrl::GetLineLength(long lineNo) const
+{
+#ifdef __WXGTK20__
+ if (m_windowStyle & wxTE_MULTILINE)
+ {
+ int last_line = gtk_text_buffer_get_line_count( m_buffer ) - 1;
+ if (lineNo > last_line)
+ return -1;
+
+ GtkTextIter iter;
+ gtk_text_buffer_get_iter_at_line(m_buffer, &iter, lineNo);
+ // get_chars_in_line return includes paragraph delimiters, so need to subtract 1 IF it is not the last line
+ return gtk_text_iter_get_chars_in_line(&iter) - ((lineNo == last_line) ? 0 : 1);
+ }
+ else
+#endif
+ {
+ wxString str = GetLineText (lineNo);
+ return (int) str.Length();
+ }
+}
+
+int wxTextCtrl::GetNumberOfLines() const
+{
+ if (m_windowStyle & wxTE_MULTILINE)
+ {
+#ifdef __WXGTK20__
+ return gtk_text_buffer_get_line_count( m_buffer );
+#else
+ gint len = gtk_text_get_length( GTK_TEXT(m_text) );
+ char *text = gtk_editable_get_chars( GTK_EDITABLE(m_text), 0, len );
+
+ if (text)
+ {
+ int currentLine = 0;
+ for (int i = 0; i < len; i++ )
+ {
+ if (text[i] == '\n')
+ currentLine++;
+ }
+ g_free( text );
+
+ // currentLine is 0 based, add 1 to get number of lines
+ return currentLine + 1;
+ }
+ else
+ {
+ return 0;
+ }
+#endif
+ }
+ else
+ {
+ return 1;
+ }
+}
+
+void wxTextCtrl::SetInsertionPoint( long pos )
+{
+ wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
+
+ if ( IsMultiLine() )
+ {
+#ifdef __WXGTK20__
+ GtkTextIter iter;
+ gtk_text_buffer_get_iter_at_offset( m_buffer, &iter, pos );
+ gtk_text_buffer_place_cursor( m_buffer, &iter );
+ gtk_text_view_scroll_mark_onscreen
+ (
+ GTK_TEXT_VIEW(m_text),
+ gtk_text_buffer_get_insert( m_buffer )
+ );
+#else // GTK+ 1.x
+ gtk_signal_disconnect_by_func( GTK_OBJECT(m_text),
+ GTK_SIGNAL_FUNC(gtk_text_changed_callback), (gpointer)this);
+
+ /* we fake a set_point by inserting and deleting. as the user
+ isn't supposed to get to know about this non-sense, we
+ disconnect so that no events are sent to the user program. */
+
+ gint tmp = (gint)pos;
+ gtk_editable_insert_text( GTK_EDITABLE(m_text), " ", 1, &tmp );
+ gtk_editable_delete_text( GTK_EDITABLE(m_text), tmp-1, tmp );
+
+ gtk_signal_connect( GTK_OBJECT(m_text), "changed",
+ GTK_SIGNAL_FUNC(gtk_text_changed_callback), (gpointer)this);
+
+ // bring editable's cursor uptodate. Bug in GTK.
+ SET_EDITABLE_POS(m_text, gtk_text_get_point( GTK_TEXT(m_text) ));
+#endif // GTK+ 2/1
+ }
+ else
+ {
+ gtk_entry_set_position( GTK_ENTRY(m_text), (int)pos );
+
+ // Bring editable's cursor uptodate. Bug in GTK.
+ SET_EDITABLE_POS(m_text, (guint32)pos);
+ }
+}
+
+void wxTextCtrl::SetInsertionPointEnd()
+{
+ wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
+
+ if (m_windowStyle & wxTE_MULTILINE)
+ {
+#ifdef __WXGTK20__
+ GtkTextIter end;
+ gtk_text_buffer_get_end_iter( m_buffer, &end );
+ gtk_text_buffer_place_cursor( m_buffer, &end );
+#else
+ SetInsertionPoint(gtk_text_get_length(GTK_TEXT(m_text)));
+#endif
+ }
+ else
+ {
+ gtk_entry_set_position( GTK_ENTRY(m_text), -1 );
+ }
+}
+
+void wxTextCtrl::SetEditable( bool editable )
+{
+ wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
+
+ if (m_windowStyle & wxTE_MULTILINE)
+ {
+#ifdef __WXGTK20__
+ gtk_text_view_set_editable( GTK_TEXT_VIEW(m_text), editable );
+#else
+ gtk_text_set_editable( GTK_TEXT(m_text), editable );
+#endif
+ }
+ else
+ {
+ gtk_entry_set_editable( GTK_ENTRY(m_text), editable );
+ }
+}
+
+bool wxTextCtrl::Enable( bool enable )
+{
+ if (!wxWindowBase::Enable(enable))
+ {
+ // nothing to do
+ return false;
+ }
+
+ if (m_windowStyle & wxTE_MULTILINE)
+ {
+#ifdef __WXGTK20__
+ SetEditable( enable );
+#else
+ gtk_text_set_editable( GTK_TEXT(m_text), enable );
+ OnParentEnable(enable);
+#endif
+ }
+ else
+ {
+ gtk_widget_set_sensitive( m_text, enable );
+ }
+
+ return true;
+}
+
+// wxGTK-specific: called recursively by Enable,
+// to give widgets an oppprtunity to correct their colours after they
+// have been changed by Enable
+void wxTextCtrl::OnParentEnable( bool 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;
+}
+
+void wxTextCtrl::DiscardEdits()
+{
+ m_modified = false;
+}
+
+// ----------------------------------------------------------------------------
+// max text length support
+// ----------------------------------------------------------------------------
+
+void wxTextCtrl::IgnoreNextTextUpdate()
+{
+ m_ignoreNextUpdate = true;
+}
+
+bool wxTextCtrl::IgnoreTextUpdate()
+{
+ if ( m_ignoreNextUpdate )
+ {
+ m_ignoreNextUpdate = false;
+
+ return true;
+ }
+
+ return false;
+}
+
+void wxTextCtrl::SetMaxLength(unsigned long len)
+{
+ if ( !HasFlag(wxTE_MULTILINE) )
+ {
+ gtk_entry_set_max_length(GTK_ENTRY(m_text), len);
+
+ // there is a bug in GTK+ 1.2.x: "changed" signal is emitted even if
+ // we had tried to enter more text than allowed by max text length and
+ // the text wasn't really changed
+ //
+ // to detect this and generate TEXT_MAXLEN event instead of
+ // TEXT_CHANGED one in this case we also catch "insert_text" signal
+ //
+ // when max len is set to 0 we disconnect our handler as it means that
+ // we shouldn't check anything any more
+ if ( len )
+ {
+ gtk_signal_connect( GTK_OBJECT(m_text),
+ "insert_text",
+ GTK_SIGNAL_FUNC(gtk_insert_text_callback),
+ (gpointer)this);
+ }
+ else // no checking
+ {
+ gtk_signal_disconnect_by_func
+ (
+ GTK_OBJECT(m_text),
+ GTK_SIGNAL_FUNC(gtk_insert_text_callback),
+ (gpointer)this
+ );
+ }
+ }
+}
+
+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();
+ }
+
+#ifndef __WXGTK20__
+ if ( (m_windowStyle & wxTE_MULTILINE) &&
+ !GTK_TEXT(m_text)->line_start_cache )
+ {
+ // tell the programmer that it didn't work
+ wxLogDebug(_T("Can't call SetSelection() before realizing the control"));
+ return;
+ }
+#endif
+
+ if (m_windowStyle & wxTE_MULTILINE)
+ {
+#ifdef __WXGTK20__
+ 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 );
+#else
+ gtk_editable_select_region( GTK_EDITABLE(m_text), (gint)from, (gint)to );
+#endif
+ }
+ else
+ {
+ gtk_editable_select_region( GTK_EDITABLE(m_text), (gint)from, (gint)to );
+ }
+}
+
+void wxTextCtrl::ShowPosition( long pos )
+{
+ if (m_windowStyle & wxTE_MULTILINE)
+ {
+#ifdef __WXGTK20__
+ GtkTextIter iter;
+ gtk_text_buffer_get_start_iter( m_buffer, &iter );
+ gtk_text_iter_set_offset( &iter, pos );
+ GtkTextMark *mark = gtk_text_buffer_create_mark( m_buffer, NULL, &iter, TRUE );
+ gtk_text_view_scroll_to_mark( GTK_TEXT_VIEW(m_text), mark, 0.0, FALSE, 0.0, 0.0 );
+#else // GTK 1.x
+ GtkAdjustment *vp = GTK_TEXT(m_text)->vadj;
+ float totalLines = (float) GetNumberOfLines();
+ long posX;
+ long posY;
+ PositionToXY(pos, &posX, &posY);
+ float posLine = (float) posY;
+ float p = (posLine/totalLines)*(vp->upper - vp->lower) + vp->lower;
+ gtk_adjustment_set_value(GTK_TEXT(m_text)->vadj, p);
+#endif // GTK 1.x/2.x
+ }
+}
+
+#ifdef __WXGTK20__
+
+wxTextCtrlHitTestResult
+wxTextCtrl::HitTest(const wxPoint& pt, long *pos) const
+{
+ if ( !IsMultiLine() )
+ {
+ // not supported
+ return wxTE_HT_UNKNOWN;
+ }
+
+ int x, y;
+ gtk_text_view_window_to_buffer_coords
+ (
+ GTK_TEXT_VIEW(m_text),
+ GTK_TEXT_WINDOW_TEXT,
+ pt.x, pt.y,
+ &x, &y
+ );
+
+ GtkTextIter iter;
+ gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(m_text), &iter, x, y);
+ if ( pos )
+ *pos = gtk_text_iter_get_offset(&iter);
+
+ return wxTE_HT_ON_TEXT;
+}
+
+#endif // __WXGTK20__
+
+long wxTextCtrl::GetInsertionPoint() const
+{
+ wxCHECK_MSG( m_text != NULL, 0, wxT("invalid text ctrl") );
+
+#ifdef __WXGTK20__
+ if (m_windowStyle & wxTE_MULTILINE)
+ {
+ // There is no direct accessor for the cursor, but
+ // internally, the cursor is the "mark" called
+ // "insert" in the text view's btree structure.
+
+ GtkTextMark *mark = gtk_text_buffer_get_insert( m_buffer );
+ GtkTextIter cursor;
+ gtk_text_buffer_get_iter_at_mark( m_buffer, &cursor, mark );
+
+ return gtk_text_iter_get_offset( &cursor );
+ }
+ else
+#endif
+ {
+ return (long) GET_EDITABLE_POS(m_text);
+ }
+}
+
+wxTextPos wxTextCtrl::GetLastPosition() const
+{
+ wxCHECK_MSG( m_text != NULL, 0, wxT("invalid text ctrl") );
+
+ int pos = 0;
+
+ if (m_windowStyle & wxTE_MULTILINE)
+ {
+#ifdef __WXGTK20__
+ GtkTextIter end;
+ gtk_text_buffer_get_end_iter( m_buffer, &end );
+
+ pos = gtk_text_iter_get_offset( &end );
+#else
+ pos = gtk_text_get_length( GTK_TEXT(m_text) );
+#endif
+ }
+ else
+ {
+ pos = GTK_ENTRY(m_text)->text_length;
+ }
+
+ return (long)pos;
+}
+
+void wxTextCtrl::Remove( long from, long to )
+{
+ wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
+
+#ifdef __WXGTK20__
+ if (m_windowStyle & wxTE_MULTILINE)
+ {
+ 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_delete( m_buffer, &fromi, &toi );
+ }
+ else // single line
+#endif
+ gtk_editable_delete_text( GTK_EDITABLE(m_text), (gint)from, (gint)to );
+}
+
+void wxTextCtrl::Replace( long from, long to, const wxString &value )
+{
+ wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
+
+ Remove( from, to );
+
+ if (!value.empty())
+ {
+#ifdef __WXGTK20__
+ SetInsertionPoint( from );
+ WriteText( value );
+#else // GTK 1.x
+ gint pos = (gint)from;
+#if wxUSE_UNICODE
+ wxWX2MBbuf buf = value.mbc_str();
+ gtk_editable_insert_text( GTK_EDITABLE(m_text), buf, strlen(buf), &pos );
+#else
+ gtk_editable_insert_text( GTK_EDITABLE(m_text), value, value.Length(), &pos );
+#endif // wxUSE_UNICODE
+#endif // GTK 1.x/2.x
+ }
+}
+
+void wxTextCtrl::Cut()
+{
+ wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
+
+#ifdef __WXGTK20__
+ if (m_windowStyle & wxTE_MULTILINE)
+ g_signal_emit_by_name(m_text, "cut-clipboard");
+ else
+#endif
+ gtk_editable_cut_clipboard(GTK_EDITABLE(m_text) DUMMY_CLIPBOARD_ARG);
+}
+
+void wxTextCtrl::Copy()
+{
+ wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
+
+#ifdef __WXGTK20__
+ if (m_windowStyle & wxTE_MULTILINE)
+ g_signal_emit_by_name(m_text, "copy-clipboard");
+ else
+#endif
+ gtk_editable_copy_clipboard(GTK_EDITABLE(m_text) DUMMY_CLIPBOARD_ARG);
+}
+
+void wxTextCtrl::Paste()
+{
+ wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
+
+#ifdef __WXGTK20__
+ if (m_windowStyle & wxTE_MULTILINE)
+ g_signal_emit_by_name(m_text, "paste-clipboard");
+ else
+#endif
+ gtk_editable_paste_clipboard(GTK_EDITABLE(m_text) DUMMY_CLIPBOARD_ARG);
+}
+
+// Undo/redo
+void wxTextCtrl::Undo()
+{
+ // TODO
+ wxFAIL_MSG( wxT("wxTextCtrl::Undo not implemented") );
+}
+
+void wxTextCtrl::Redo()
+{
+ // TODO
+ wxFAIL_MSG( wxT("wxTextCtrl::Redo not implemented") );
+}
+
+bool wxTextCtrl::CanUndo() const
+{
+ // TODO
+ //wxFAIL_MSG( wxT("wxTextCtrl::CanUndo not implemented") );
+ return false;
+}
+
+bool wxTextCtrl::CanRedo() const
+{
+ // TODO
+ //wxFAIL_MSG( wxT("wxTextCtrl::CanRedo not implemented") );
+ return false;
+}
+
+// If the return values from and to are the same, there is no
+// selection.
+void wxTextCtrl::GetSelection(long* fromOut, long* toOut) const
+{
+ wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
+
+ gint from = -1;
+ gint to = -1;
+ bool haveSelection = false;
+
+#ifdef __WXGTK20__
+ if (m_windowStyle & wxTE_MULTILINE)
+ {
+ GtkTextIter ifrom, ito;
+ if ( gtk_text_buffer_get_selection_bounds(m_buffer, &ifrom, &ito) )
+ {
+ haveSelection = true;
+ from = gtk_text_iter_get_offset(&ifrom);
+ to = gtk_text_iter_get_offset(&ito);
+ }
+ }
+ else // not multi-line
+ {
+ if ( gtk_editable_get_selection_bounds( GTK_EDITABLE(m_text),
+ &from, &to) )
+ {
+ haveSelection = true;
+ }
+ }
+#else // not GTK2
+ if ( (GTK_EDITABLE(m_text)->has_selection) )
+ {
+ haveSelection = true;
+ from = (long) GTK_EDITABLE(m_text)->selection_start_pos;
+ to = (long) GTK_EDITABLE(m_text)->selection_end_pos;
+ }
+#endif
+
+ if (! haveSelection )
+ from = to = GetInsertionPoint();
+
+ if ( from > to )
+ {
+ // exchange them to be compatible with wxMSW
+ gint tmp = from;
+ from = to;
+ to = tmp;
+ }
+
+ if ( fromOut )
+ *fromOut = from;
+ if ( toOut )
+ *toOut = to;
+}
+
+
+bool wxTextCtrl::IsEditable() const
+{
+ wxCHECK_MSG( m_text != NULL, false, wxT("invalid text ctrl") );
+
+#ifdef __WXGTK20__
+ if (m_windowStyle & wxTE_MULTILINE)
+ {
+ return gtk_text_view_get_editable(GTK_TEXT_VIEW(m_text));
+ }
+ else
+ {
+ return gtk_editable_get_editable(GTK_EDITABLE(m_text));
+ }
+#else
+ return GTK_EDITABLE(m_text)->editable;
+#endif
+}
+
+bool wxTextCtrl::IsModified() const
+{
+ return m_modified;