Use InheritAttributes for wxGTK widgets so they will check
[wxWidgets.git] / src / gtk1 / textctrl.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: textctrl.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Id: $Id$
6 // Copyright: (c) 1998 Robert Roebling, Vadim Zeitlin
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
11 #pragma implementation "textctrl.h"
12 #endif
13
14 // For compilers that support precompilation, includes "wx.h".
15 #include "wx/wxprec.h"
16
17 #include "wx/textctrl.h"
18 #include "wx/utils.h"
19 #include "wx/intl.h"
20 #include "wx/log.h"
21 #include "wx/settings.h"
22 #include "wx/panel.h"
23 #include "wx/strconv.h"
24 #include "wx/fontutil.h" // for wxNativeFontInfo (GetNativeFontInfo())
25
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <ctype.h>
29 #include <math.h> // for fabs
30
31 #include "wx/gtk/private.h"
32 #include <gdk/gdkkeysyms.h>
33
34 //-----------------------------------------------------------------------------
35 // idle system
36 //-----------------------------------------------------------------------------
37
38 extern void wxapp_install_idle_handler();
39 extern bool g_isIdle;
40
41 //-----------------------------------------------------------------------------
42 // data
43 //-----------------------------------------------------------------------------
44
45 extern bool g_blockEventsOnDrag;
46 extern wxCursor g_globalCursor;
47 extern wxWindowGTK *g_delayedFocus;
48
49 // ----------------------------------------------------------------------------
50 // helpers
51 // ----------------------------------------------------------------------------
52
53 #ifdef __WXGTK20__
54 static void wxGtkTextInsert(GtkWidget *text,
55 GtkTextBuffer *text_buffer,
56 const wxTextAttr& attr,
57 wxCharBuffer buffer)
58
59 {
60 PangoFontDescription *font_description = attr.HasFont()
61 ? attr.GetFont().GetNativeFontInfo()->description
62 : NULL;
63
64 GdkColor *colFg = attr.HasTextColour() ? attr.GetTextColour().GetColor()
65 : NULL;
66
67 GdkColor *colBg = attr.HasBackgroundColour()
68 ? attr.GetBackgroundColour().GetColor()
69 : NULL;
70
71 GtkTextTag *tag;
72 tag = gtk_text_buffer_create_tag( text_buffer, NULL, "font-desc", font_description,
73 "foreground-gdk", colFg,
74 "background-gdk", colBg, NULL );
75
76 GtkTextIter iter;
77 gtk_text_buffer_get_iter_at_mark( text_buffer, &iter,
78 gtk_text_buffer_get_insert (text_buffer) );
79
80 gtk_text_buffer_insert_with_tags( text_buffer, &iter, buffer, strlen(buffer), tag, NULL );
81 }
82 #else
83 static void wxGtkTextInsert(GtkWidget *text,
84 const wxTextAttr& attr,
85 const char *txt,
86 size_t len)
87 {
88 GdkFont *font = attr.HasFont() ? attr.GetFont().GetInternalFont()
89 : NULL;
90
91 GdkColor *colFg = attr.HasTextColour() ? attr.GetTextColour().GetColor()
92 : NULL;
93
94 GdkColor *colBg = attr.HasBackgroundColour()
95 ? attr.GetBackgroundColour().GetColor()
96 : NULL;
97
98 gtk_text_insert( GTK_TEXT(text), font, colFg, colBg, txt, len );
99 }
100 #endif // GTK 1.x
101
102 // ----------------------------------------------------------------------------
103 // "insert_text" for GtkEntry
104 // ----------------------------------------------------------------------------
105
106 static void
107 gtk_insert_text_callback(GtkEditable *editable,
108 const gchar *new_text,
109 gint new_text_length,
110 gint *position,
111 wxTextCtrl *win)
112 {
113 if (g_isIdle)
114 wxapp_install_idle_handler();
115
116 // we should only be called if we have a max len limit at all
117 GtkEntry *entry = GTK_ENTRY (editable);
118
119 wxCHECK_RET( entry->text_max_length, _T("shouldn't be called") );
120
121 // check that we don't overflow the max length limit
122 //
123 // FIXME: this doesn't work when we paste a string which is going to be
124 // truncated
125 if ( entry->text_length == entry->text_max_length )
126 {
127 // we don't need to run the base class version at all
128 gtk_signal_emit_stop_by_name(GTK_OBJECT(editable), "insert_text");
129
130 // remember that the next changed signal is to be ignored to avoid
131 // generating a dummy wxEVT_COMMAND_TEXT_UPDATED event
132 win->IgnoreNextTextUpdate();
133
134 // and generate the correct one ourselves
135 wxCommandEvent event(wxEVT_COMMAND_TEXT_MAXLEN, win->GetId());
136 event.SetEventObject(win);
137 event.SetString(win->GetValue());
138 win->GetEventHandler()->ProcessEvent( event );
139 }
140 }
141
142 //-----------------------------------------------------------------------------
143 // "changed"
144 //-----------------------------------------------------------------------------
145
146 static void
147 gtk_text_changed_callback( GtkWidget *widget, wxTextCtrl *win )
148 {
149 if ( win->IgnoreTextUpdate() )
150 return;
151
152 if (!win->m_hasVMT) return;
153
154 if (g_isIdle)
155 wxapp_install_idle_handler();
156
157 win->SetModified();
158 win->UpdateFontIfNeeded();
159
160 wxCommandEvent event( wxEVT_COMMAND_TEXT_UPDATED, win->GetId() );
161 event.SetEventObject( win );
162 event.SetString( win->GetValue() );
163 win->GetEventHandler()->ProcessEvent( event );
164 }
165
166 //-----------------------------------------------------------------------------
167 // "changed" from vertical scrollbar
168 //-----------------------------------------------------------------------------
169
170 #ifndef __WXGTK20__
171 static void
172 gtk_scrollbar_changed_callback( GtkWidget *WXUNUSED(widget), wxTextCtrl *win )
173 {
174 if (!win->m_hasVMT) return;
175
176 if (g_isIdle)
177 wxapp_install_idle_handler();
178
179 win->CalculateScrollbar();
180 }
181 #endif
182
183 // ----------------------------------------------------------------------------
184 // redraw callback for multiline text
185 // ----------------------------------------------------------------------------
186
187 #ifndef __WXGTK20__
188
189 // redrawing a GtkText from inside a wxYield() call results in crashes (the
190 // text sample shows it in its "Add lines" command which shows wxProgressDialog
191 // which implicitly calls wxYield()) so we override GtkText::draw() and simply
192 // don't do anything if we're inside wxYield()
193
194 extern bool wxIsInsideYield;
195
196 extern "C" {
197 typedef void (*GtkDrawCallback)(GtkWidget *widget, GdkRectangle *rect);
198 }
199
200 static GtkDrawCallback gs_gtk_text_draw = NULL;
201
202 extern "C"
203 void wxgtk_text_draw( GtkWidget *widget, GdkRectangle *rect)
204 {
205 if ( !wxIsInsideYield )
206 {
207 wxCHECK_RET( gs_gtk_text_draw != wxgtk_text_draw,
208 _T("infinite recursion in wxgtk_text_draw aborted") );
209
210 gs_gtk_text_draw(widget, rect);
211 }
212 }
213
214 #endif // __WXGTK20__
215
216 //-----------------------------------------------------------------------------
217 // wxTextCtrl
218 //-----------------------------------------------------------------------------
219
220 IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl,wxControl)
221
222 BEGIN_EVENT_TABLE(wxTextCtrl, wxControl)
223 EVT_CHAR(wxTextCtrl::OnChar)
224
225 EVT_MENU(wxID_CUT, wxTextCtrl::OnCut)
226 EVT_MENU(wxID_COPY, wxTextCtrl::OnCopy)
227 EVT_MENU(wxID_PASTE, wxTextCtrl::OnPaste)
228 EVT_MENU(wxID_UNDO, wxTextCtrl::OnUndo)
229 EVT_MENU(wxID_REDO, wxTextCtrl::OnRedo)
230
231 EVT_UPDATE_UI(wxID_CUT, wxTextCtrl::OnUpdateCut)
232 EVT_UPDATE_UI(wxID_COPY, wxTextCtrl::OnUpdateCopy)
233 EVT_UPDATE_UI(wxID_PASTE, wxTextCtrl::OnUpdatePaste)
234 EVT_UPDATE_UI(wxID_UNDO, wxTextCtrl::OnUpdateUndo)
235 EVT_UPDATE_UI(wxID_REDO, wxTextCtrl::OnUpdateRedo)
236 END_EVENT_TABLE()
237
238 void wxTextCtrl::Init()
239 {
240 m_ignoreNextUpdate =
241 m_modified = FALSE;
242 m_updateFont = FALSE;
243 m_text =
244 m_vScrollbar = (GtkWidget *)NULL;
245 }
246
247 wxTextCtrl::wxTextCtrl( wxWindow *parent,
248 wxWindowID id,
249 const wxString &value,
250 const wxPoint &pos,
251 const wxSize &size,
252 long style,
253 const wxValidator& validator,
254 const wxString &name )
255 {
256 Init();
257
258 Create( parent, id, value, pos, size, style, validator, name );
259 }
260
261 bool wxTextCtrl::Create( wxWindow *parent,
262 wxWindowID id,
263 const wxString &value,
264 const wxPoint &pos,
265 const wxSize &size,
266 long style,
267 const wxValidator& validator,
268 const wxString &name )
269 {
270 m_needParent = TRUE;
271 m_acceptsFocus = TRUE;
272
273 if (!PreCreation( parent, pos, size ) ||
274 !CreateBase( parent, id, pos, size, style, validator, name ))
275 {
276 wxFAIL_MSG( wxT("wxTextCtrl creation failed") );
277 return FALSE;
278 }
279
280
281 m_vScrollbarVisible = FALSE;
282
283 bool multi_line = (style & wxTE_MULTILINE) != 0;
284
285 #ifdef __WXGTK20__
286 GtkTextBuffer *buffer = NULL;
287 #endif
288
289 if (multi_line)
290 {
291 #ifdef __WXGTK20__
292 // Create view
293 m_text = gtk_text_view_new();
294
295 buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(m_text) );
296
297 // create scrolled window
298 m_widget = gtk_scrolled_window_new( NULL, NULL );
299 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( m_widget ),
300 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
301
302 // Insert view into scrolled window
303 gtk_container_add( GTK_CONTAINER(m_widget), m_text );
304
305 // Global settings which can be overridden by tags, I guess.
306 if (HasFlag( wxHSCROLL ))
307 gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW( m_text ), GTK_WRAP_NONE );
308 else
309 gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW( m_text ), GTK_WRAP_WORD );
310
311 if (!HasFlag(wxNO_BORDER))
312 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(m_widget), GTK_SHADOW_IN );
313 #else
314 // create our control ...
315 m_text = gtk_text_new( (GtkAdjustment *) NULL, (GtkAdjustment *) NULL );
316
317 // ... and put into the upper left hand corner of the table
318 bool bHasHScrollbar = FALSE;
319 m_widget = gtk_table_new(bHasHScrollbar ? 2 : 1, 2, FALSE);
320 GTK_WIDGET_UNSET_FLAGS( m_widget, GTK_CAN_FOCUS );
321 gtk_table_attach( GTK_TABLE(m_widget), m_text, 0, 1, 0, 1,
322 (GtkAttachOptions)(GTK_FILL | GTK_EXPAND | GTK_SHRINK),
323 (GtkAttachOptions)(GTK_FILL | GTK_EXPAND | GTK_SHRINK),
324 0, 0);
325
326 // always wrap words
327 gtk_text_set_word_wrap( GTK_TEXT(m_text), TRUE );
328
329 // finally, put the vertical scrollbar in the upper right corner
330 m_vScrollbar = gtk_vscrollbar_new( GTK_TEXT(m_text)->vadj );
331 GTK_WIDGET_UNSET_FLAGS( m_vScrollbar, GTK_CAN_FOCUS );
332 gtk_table_attach(GTK_TABLE(m_widget), m_vScrollbar, 1, 2, 0, 1,
333 GTK_FILL,
334 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL | GTK_SHRINK),
335 0, 0);
336 #endif
337 }
338 else
339 {
340 // a single-line text control: no need for scrollbars
341 m_widget =
342 m_text = gtk_entry_new();
343 }
344
345 m_parent->DoAddChild( this );
346
347 m_focusWidget = m_text;
348
349 PostCreation();
350 InheritAttributes();
351
352 wxSize size_best( DoGetBestSize() );
353 wxSize new_size( size );
354 if (new_size.x == -1)
355 new_size.x = size_best.x;
356 if (new_size.y == -1)
357 new_size.y = size_best.y;
358 if ((new_size.x != size.x) || (new_size.y != size.y))
359 SetSize( new_size.x, new_size.y );
360
361 if (multi_line)
362 gtk_widget_show(m_text);
363
364 #ifndef __WXGTK20__
365 if (multi_line)
366 {
367 gtk_signal_connect(GTK_OBJECT(GTK_TEXT(m_text)->vadj), "changed",
368 (GtkSignalFunc) gtk_scrollbar_changed_callback, (gpointer) this );
369
370 // only initialize gs_gtk_text_draw once, starting from the next the
371 // klass::draw will already be wxgtk_text_draw
372 if ( !gs_gtk_text_draw )
373 {
374 GtkDrawCallback&
375 draw = GTK_WIDGET_CLASS(GTK_OBJECT(m_text)->klass)->draw;
376
377 gs_gtk_text_draw = draw;
378
379 draw = wxgtk_text_draw;
380 }
381 }
382 #endif // GTK+ 1.x
383
384 if (!value.IsEmpty())
385 {
386 #ifdef __WXGTK20__
387 SetValue( value );
388 #else
389
390 #if !GTK_CHECK_VERSION(1, 2, 0)
391 // if we don't realize it, GTK 1.0.6 dies with a SIGSEGV in
392 // gtk_editable_insert_text()
393 gtk_widget_realize(m_text);
394 #endif // GTK 1.0
395
396 gint tmp = 0;
397 #if wxUSE_UNICODE
398 wxWX2MBbuf val = value.mbc_str();
399 gtk_editable_insert_text( GTK_EDITABLE(m_text), val, strlen(val), &tmp );
400 #else
401 gtk_editable_insert_text( GTK_EDITABLE(m_text), value, value.Length(), &tmp );
402 #endif
403
404 if (multi_line)
405 {
406 // Bring editable's cursor uptodate. Bug in GTK.
407 SET_EDITABLE_POS(m_text, gtk_text_get_point( GTK_TEXT(m_text) ));
408 }
409
410 #endif
411 }
412
413 if (style & wxTE_PASSWORD)
414 {
415 if (!multi_line)
416 gtk_entry_set_visibility( GTK_ENTRY(m_text), FALSE );
417 }
418
419 if (style & wxTE_READONLY)
420 {
421 if (!multi_line)
422 gtk_entry_set_editable( GTK_ENTRY(m_text), FALSE );
423 #ifdef __WXGTK20__
424 else
425 gtk_text_view_set_editable( GTK_TEXT_VIEW( m_text), FALSE);
426 }
427 #else
428 }
429 else
430 {
431 if (multi_line)
432 gtk_text_set_editable( GTK_TEXT(m_text), 1 );
433 }
434 #endif
435
436 // We want to be notified about text changes.
437 #ifdef __WXGTK20__
438 if (multi_line)
439 {
440 g_signal_connect( G_OBJECT(buffer), "changed",
441 GTK_SIGNAL_FUNC(gtk_text_changed_callback), (gpointer)this);
442 }
443 else
444 #endif
445 {
446 gtk_signal_connect( GTK_OBJECT(m_text), "changed",
447 GTK_SIGNAL_FUNC(gtk_text_changed_callback), (gpointer)this);
448 }
449
450 // we don't set a valid background colour, because the window
451 // manager should use a default one
452 m_backgroundColour = wxColour();
453
454 wxColour colFg = parent->GetForegroundColour();
455 SetForegroundColour( colFg );
456
457 m_cursor = wxCursor( wxCURSOR_IBEAM );
458
459 wxTextAttr attrDef( colFg, m_backgroundColour, parent->GetFont() );
460 SetDefaultStyle( attrDef );
461
462 Show( TRUE );
463
464 return TRUE;
465 }
466
467 void wxTextCtrl::CalculateScrollbar()
468 {
469 #ifndef __WXGTK20__
470 if ((m_windowStyle & wxTE_MULTILINE) == 0) return;
471
472 GtkAdjustment *adj = GTK_TEXT(m_text)->vadj;
473
474 if (adj->upper - adj->page_size < 0.8)
475 {
476 if (m_vScrollbarVisible)
477 {
478 gtk_widget_hide( m_vScrollbar );
479 m_vScrollbarVisible = FALSE;
480 }
481 }
482 else
483 {
484 if (!m_vScrollbarVisible)
485 {
486 gtk_widget_show( m_vScrollbar );
487 m_vScrollbarVisible = TRUE;
488 }
489 }
490 #endif
491 }
492
493 wxString wxTextCtrl::GetValue() const
494 {
495 wxCHECK_MSG( m_text != NULL, wxT(""), wxT("invalid text ctrl") );
496
497 wxString tmp;
498 if (m_windowStyle & wxTE_MULTILINE)
499 {
500 #ifdef __WXGTK20__
501 GtkTextBuffer *text_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(m_text) );
502
503 GtkTextIter start;
504 gtk_text_buffer_get_start_iter( text_buffer, &start );
505 GtkTextIter end;
506 gtk_text_buffer_get_end_iter( text_buffer, &end );
507 gchar *text = gtk_text_buffer_get_text( text_buffer, &start, &end, TRUE );
508
509 #if wxUSE_UNICODE
510 wxWCharBuffer buffer( wxConvUTF8.cMB2WX( text ) );
511 #else
512 wxCharBuffer buffer( wxConvLocal.cWC2WX( wxConvUTF8.cMB2WC( text ) ) );
513 #endif
514 tmp = buffer;
515
516 g_free( text );
517 #else
518 gint len = gtk_text_get_length( GTK_TEXT(m_text) );
519 char *text = gtk_editable_get_chars( GTK_EDITABLE(m_text), 0, len );
520 tmp = text;
521 g_free( text );
522 #endif
523 }
524 else
525 {
526 tmp = wxGTK_CONV_BACK( gtk_entry_get_text( GTK_ENTRY(m_text) ) );
527 }
528
529 return tmp;
530 }
531
532 void wxTextCtrl::SetValue( const wxString &value )
533 {
534 wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
535
536 if (m_windowStyle & wxTE_MULTILINE)
537 {
538 #ifdef __WXGTK20__
539
540 #if wxUSE_UNICODE
541 wxCharBuffer buffer( wxConvUTF8.cWX2MB( value) );
542 #else
543 wxCharBuffer buffer( wxConvUTF8.cWC2MB( wxConvLocal.cWX2WC( value ) ) );
544 #endif
545 GtkTextBuffer *text_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(m_text) );
546 gtk_text_buffer_set_text( text_buffer, buffer, strlen(buffer) );
547
548 #else
549 gint len = gtk_text_get_length( GTK_TEXT(m_text) );
550 gtk_editable_delete_text( GTK_EDITABLE(m_text), 0, len );
551 len = 0;
552 gtk_editable_insert_text( GTK_EDITABLE(m_text), value.mbc_str(), value.Length(), &len );
553 #endif
554 }
555 else
556 {
557 gtk_entry_set_text( GTK_ENTRY(m_text), wxGTK_CONV( value ) );
558 }
559
560 // GRG, Jun/2000: Changed this after a lot of discussion in
561 // the lists. wxWindows 2.2 will have a set of flags to
562 // customize this behaviour.
563 SetInsertionPoint(0);
564
565 m_modified = FALSE;
566 }
567
568 void wxTextCtrl::WriteText( const wxString &text )
569 {
570 wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
571
572 if ( text.empty() )
573 return;
574
575 if ( m_windowStyle & wxTE_MULTILINE )
576 {
577 #ifdef __WXGTK20__
578
579 #if wxUSE_UNICODE
580 wxCharBuffer buffer( wxConvUTF8.cWX2MB( text ) );
581 #else
582 wxCharBuffer buffer( wxConvUTF8.cWC2MB( wxConvLocal.cWX2WC( text ) ) );
583 #endif
584 GtkTextBuffer *text_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(m_text) );
585
586 // TODO: Call whatever is needed to delete the selection.
587 wxGtkTextInsert( m_text, text_buffer, m_defaultStyle, buffer );
588
589 GtkAdjustment *adj = gtk_scrolled_window_get_vadjustment( GTK_SCROLLED_WINDOW(m_widget) );
590 // Scroll to cursor, but only if scrollbar thumb is at the very bottom
591 if ( adj->value == adj->upper - adj->page_size )
592 {
593 gtk_text_view_scroll_to_mark( GTK_TEXT_VIEW(m_text),
594 gtk_text_buffer_get_insert( text_buffer ), 0.0, FALSE, 0.0, 1.0 );
595 }
596 #else // GTK 1.x
597 // After cursor movements, gtk_text_get_point() is wrong by one.
598 gtk_text_set_point( GTK_TEXT(m_text), GET_EDITABLE_POS(m_text) );
599
600 // always use m_defaultStyle, even if it is empty as otherwise
601 // resetting the style and appending some more text wouldn't work: if
602 // we don't specify the style explicitly, the old style would be used
603 gtk_editable_delete_selection( GTK_EDITABLE(m_text) );
604 wxGtkTextInsert(m_text, m_defaultStyle, text.c_str(), text.Len());
605
606 // we called wxGtkTextInsert with correct font, no need to do anything
607 // in UpdateFontIfNeeded() any longer
608 if ( !text.empty() )
609 {
610 m_updateFont = FALSE;
611 }
612
613 // Bring editable's cursor back uptodate.
614 SET_EDITABLE_POS(m_text, gtk_text_get_point( GTK_TEXT(m_text) ));
615 #endif // GTK 1.x/2.0
616 }
617 else // single line
618 {
619 // First remove the selection if there is one
620 gtk_editable_delete_selection( GTK_EDITABLE(m_text) );
621
622 // This moves the cursor pos to behind the inserted text.
623 gint len = GET_EDITABLE_POS(m_text);
624
625 #ifdef __WXGTK20__
626
627 #if wxUSE_UNICODE
628 wxCharBuffer buffer( wxConvUTF8.cWX2MB( text ) );
629 #else
630 wxCharBuffer buffer( wxConvUTF8.cWC2MB( wxConvLocal.cWX2WC( text ) ) );
631 #endif
632 gtk_editable_insert_text( GTK_EDITABLE(m_text), buffer, strlen(buffer), &len );
633
634 #else
635 gtk_editable_insert_text( GTK_EDITABLE(m_text), text.c_str(), text.Len(), &len );
636 #endif
637
638 // Bring entry's cursor uptodate.
639 gtk_entry_set_position( GTK_ENTRY(m_text), len );
640 }
641
642 m_modified = TRUE;
643 }
644
645 void wxTextCtrl::AppendText( const wxString &text )
646 {
647 SetInsertionPointEnd();
648 WriteText( text );
649 }
650
651 wxString wxTextCtrl::GetLineText( long lineNo ) const
652 {
653 if (m_windowStyle & wxTE_MULTILINE)
654 {
655 #ifndef __WXGTK20__
656 gint len = gtk_text_get_length( GTK_TEXT(m_text) );
657 char *text = gtk_editable_get_chars( GTK_EDITABLE(m_text), 0, len );
658
659 if (text)
660 {
661 wxString buf(wxT(""));
662 long i;
663 int currentLine = 0;
664 for (i = 0; currentLine != lineNo && text[i]; i++ )
665 if (text[i] == '\n')
666 currentLine++;
667 // Now get the text
668 int j;
669 for (j = 0; text[i] && text[i] != '\n'; i++, j++ )
670 buf += text[i];
671
672 g_free( text );
673 return buf;
674 }
675 else
676 #endif
677 {
678 return wxEmptyString;
679 }
680 }
681 else
682 {
683 if (lineNo == 0) return GetValue();
684 return wxEmptyString;
685 }
686 }
687
688 void wxTextCtrl::OnDropFiles( wxDropFilesEvent &WXUNUSED(event) )
689 {
690 /* If you implement this, don't forget to update the documentation!
691 * (file docs/latex/wx/text.tex) */
692 wxFAIL_MSG( wxT("wxTextCtrl::OnDropFiles not implemented") );
693 }
694
695 bool wxTextCtrl::PositionToXY(long pos, long *x, long *y ) const
696 {
697 if ( m_windowStyle & wxTE_MULTILINE )
698 {
699 wxString text = GetValue();
700
701 // cast to prevent warning. But pos really should've been unsigned.
702 if( (unsigned long)pos > text.Len() )
703 return FALSE;
704
705 *x=0; // First Col
706 *y=0; // First Line
707
708 const wxChar* stop = text.c_str() + pos;
709 for ( const wxChar *p = text.c_str(); p < stop; p++ )
710 {
711 if (*p == wxT('\n'))
712 {
713 (*y)++;
714 *x=0;
715 }
716 else
717 (*x)++;
718 }
719 }
720 else // single line control
721 {
722 if ( pos <= GTK_ENTRY(m_text)->text_length )
723 {
724 *y = 0;
725 *x = pos;
726 }
727 else
728 {
729 // index out of bounds
730 return FALSE;
731 }
732 }
733
734 return TRUE;
735 }
736
737 long wxTextCtrl::XYToPosition(long x, long y ) const
738 {
739 if (!(m_windowStyle & wxTE_MULTILINE)) return 0;
740
741 long pos=0;
742 for( int i=0; i<y; i++ ) pos += GetLineLength(i) + 1; // one for '\n'
743
744 pos += x;
745 return pos;
746 }
747
748 int wxTextCtrl::GetLineLength(long lineNo) const
749 {
750 wxString str = GetLineText (lineNo);
751 return (int) str.Length();
752 }
753
754 int wxTextCtrl::GetNumberOfLines() const
755 {
756 if (m_windowStyle & wxTE_MULTILINE)
757 {
758 #ifdef __WXGTK20__
759 GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(m_text) );
760
761 return gtk_text_buffer_get_line_count( buffer );
762 #else
763 gint len = gtk_text_get_length( GTK_TEXT(m_text) );
764 char *text = gtk_editable_get_chars( GTK_EDITABLE(m_text), 0, len );
765
766 if (text)
767 {
768 int currentLine = 0;
769 for (int i = 0; i < len; i++ )
770 {
771 if (text[i] == '\n')
772 currentLine++;
773 }
774 g_free( text );
775
776 // currentLine is 0 based, add 1 to get number of lines
777 return currentLine + 1;
778 }
779 else
780 {
781 return 0;
782 }
783 #endif
784 }
785 else
786 {
787 return 1;
788 }
789 }
790
791 void wxTextCtrl::SetInsertionPoint( long pos )
792 {
793 wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
794
795 if (m_windowStyle & wxTE_MULTILINE)
796 {
797 #ifdef __WXGTK20__
798 GtkTextBuffer *text_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(m_text) );
799 GtkTextIter iter;
800 gtk_text_buffer_get_iter_at_offset( text_buffer, &iter, pos );
801 gtk_text_buffer_place_cursor( text_buffer, &iter );
802 #else
803 gtk_signal_disconnect_by_func( GTK_OBJECT(m_text),
804 GTK_SIGNAL_FUNC(gtk_text_changed_callback), (gpointer)this);
805
806 /* we fake a set_point by inserting and deleting. as the user
807 isn't supposed to get to know about this non-sense, we
808 disconnect so that no events are sent to the user program. */
809
810 gint tmp = (gint)pos;
811 gtk_editable_insert_text( GTK_EDITABLE(m_text), " ", 1, &tmp );
812 gtk_editable_delete_text( GTK_EDITABLE(m_text), tmp-1, tmp );
813
814 gtk_signal_connect( GTK_OBJECT(m_text), "changed",
815 GTK_SIGNAL_FUNC(gtk_text_changed_callback), (gpointer)this);
816
817 // bring editable's cursor uptodate. Bug in GTK.
818 SET_EDITABLE_POS(m_text, gtk_text_get_point( GTK_TEXT(m_text) ));
819 #endif
820 }
821 else
822 {
823 gtk_entry_set_position( GTK_ENTRY(m_text), (int)pos );
824
825 // Bring editable's cursor uptodate. Bug in GTK.
826 SET_EDITABLE_POS(m_text, (guint32)pos);
827 }
828 }
829
830 void wxTextCtrl::SetInsertionPointEnd()
831 {
832 wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
833
834 if (m_windowStyle & wxTE_MULTILINE)
835 {
836 #ifdef __WXGTK20__
837 GtkTextBuffer *text_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(m_text) );
838 GtkTextIter end;
839 gtk_text_buffer_get_end_iter( text_buffer, &end );
840 gtk_text_buffer_place_cursor( text_buffer, &end );
841 #else
842 SetInsertionPoint(gtk_text_get_length(GTK_TEXT(m_text)));
843 #endif
844 }
845 else
846 {
847 gtk_entry_set_position( GTK_ENTRY(m_text), -1 );
848 }
849 }
850
851 void wxTextCtrl::SetEditable( bool editable )
852 {
853 wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
854
855 if (m_windowStyle & wxTE_MULTILINE)
856 {
857 #ifdef __WXGTK20__
858 gtk_text_view_set_editable( GTK_TEXT_VIEW(m_text), editable );
859 #else
860 gtk_text_set_editable( GTK_TEXT(m_text), editable );
861 #endif
862 }
863 else
864 {
865 gtk_entry_set_editable( GTK_ENTRY(m_text), editable );
866 }
867 }
868
869 bool wxTextCtrl::Enable( bool enable )
870 {
871 if (!wxWindowBase::Enable(enable))
872 {
873 // nothing to do
874 return FALSE;
875 }
876
877 if (m_windowStyle & wxTE_MULTILINE)
878 {
879 #ifdef __WXGTK20__
880 SetEditable( enable );
881 #else
882 gtk_text_set_editable( GTK_TEXT(m_text), enable );
883 OnParentEnable(enable);
884 #endif
885 }
886 else
887 {
888 gtk_widget_set_sensitive( m_text, enable );
889 }
890
891 return TRUE;
892 }
893
894 // wxGTK-specific: called recursively by Enable,
895 // to give widgets an oppprtunity to correct their colours after they
896 // have been changed by Enable
897 void wxTextCtrl::OnParentEnable( bool enable )
898 {
899 // If we have a custom background colour, we use this colour in both
900 // disabled and enabled mode, or we end up with a different colour under the
901 // text.
902 wxColour oldColour = GetBackgroundColour();
903 if (oldColour.Ok())
904 {
905 // Need to set twice or it'll optimize the useful stuff out
906 if (oldColour == * wxWHITE)
907 SetBackgroundColour(*wxBLACK);
908 else
909 SetBackgroundColour(*wxWHITE);
910 SetBackgroundColour(oldColour);
911 }
912 }
913
914 void wxTextCtrl::MarkDirty()
915 {
916 m_modified = TRUE;
917 }
918
919 void wxTextCtrl::DiscardEdits()
920 {
921 m_modified = FALSE;
922 }
923
924 // ----------------------------------------------------------------------------
925 // max text length support
926 // ----------------------------------------------------------------------------
927
928 void wxTextCtrl::IgnoreNextTextUpdate()
929 {
930 m_ignoreNextUpdate = TRUE;
931 }
932
933 bool wxTextCtrl::IgnoreTextUpdate()
934 {
935 if ( m_ignoreNextUpdate )
936 {
937 m_ignoreNextUpdate = FALSE;
938
939 return TRUE;
940 }
941
942 return FALSE;
943 }
944
945 void wxTextCtrl::SetMaxLength(unsigned long len)
946 {
947 if ( !HasFlag(wxTE_MULTILINE) )
948 {
949 gtk_entry_set_max_length(GTK_ENTRY(m_text), len);
950
951 // there is a bug in GTK+ 1.2.x: "changed" signal is emitted even if
952 // we had tried to enter more text than allowed by max text length and
953 // the text wasn't really changed
954 //
955 // to detect this and generate TEXT_MAXLEN event instead of
956 // TEXT_CHANGED one in this case we also catch "insert_text" signal
957 //
958 // when max len is set to 0 we disconnect our handler as it means that
959 // we shouldn't check anything any more
960 if ( len )
961 {
962 gtk_signal_connect( GTK_OBJECT(m_text),
963 "insert_text",
964 GTK_SIGNAL_FUNC(gtk_insert_text_callback),
965 (gpointer)this);
966 }
967 else // no checking
968 {
969 gtk_signal_disconnect_by_func
970 (
971 GTK_OBJECT(m_text),
972 GTK_SIGNAL_FUNC(gtk_insert_text_callback),
973 (gpointer)this
974 );
975 }
976 }
977 }
978
979 void wxTextCtrl::SetSelection( long from, long to )
980 {
981 wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
982
983 if (from == -1 && to == -1)
984 {
985 from = 0;
986 to = GetValue().Length();
987 }
988
989 #ifndef __WXGTK20__
990 if ( (m_windowStyle & wxTE_MULTILINE) &&
991 !GTK_TEXT(m_text)->line_start_cache )
992 {
993 // tell the programmer that it didn't work
994 wxLogDebug(_T("Can't call SetSelection() before realizing the control"));
995 return;
996 }
997 #endif
998
999 if (m_windowStyle & wxTE_MULTILINE)
1000 {
1001 #ifdef __WXGTK20__
1002 GtkTextBuffer *buf = gtk_text_view_get_buffer( GTK_TEXT_VIEW(m_text) );
1003
1004 GtkTextIter fromi, toi;
1005 gtk_text_buffer_get_iter_at_offset( buf, &fromi, from );
1006 gtk_text_buffer_get_iter_at_offset( buf, &toi, to );
1007
1008 gtk_text_buffer_place_cursor( buf, &toi );
1009 gtk_text_buffer_move_mark_by_name( buf, "selection_bound", &fromi );
1010 #else
1011 gtk_editable_select_region( GTK_EDITABLE(m_text), (gint)from, (gint)to );
1012 #endif
1013 }
1014 else
1015 {
1016 gtk_editable_select_region( GTK_EDITABLE(m_text), (gint)from, (gint)to );
1017 }
1018 }
1019
1020 void wxTextCtrl::ShowPosition( long pos )
1021 {
1022 if (m_windowStyle & wxTE_MULTILINE)
1023 {
1024 #ifdef __WXGTK20__
1025 GtkTextBuffer *buf = gtk_text_view_get_buffer( GTK_TEXT_VIEW(m_text) );
1026 GtkTextIter iter;
1027 gtk_text_buffer_get_start_iter( buf, &iter );
1028 gtk_text_iter_set_offset( &iter, pos );
1029 GtkTextMark *mark = gtk_text_buffer_create_mark( buf, NULL, &iter, TRUE );
1030 gtk_text_view_scroll_to_mark( GTK_TEXT_VIEW(m_text), mark, 0.0, FALSE, 0.0, 0.0 );
1031 #else // GTK 1.x
1032 GtkAdjustment *vp = GTK_TEXT(m_text)->vadj;
1033 float totalLines = (float) GetNumberOfLines();
1034 long posX;
1035 long posY;
1036 PositionToXY(pos, &posX, &posY);
1037 float posLine = (float) posY;
1038 float p = (posLine/totalLines)*(vp->upper - vp->lower) + vp->lower;
1039 gtk_adjustment_set_value(GTK_TEXT(m_text)->vadj, p);
1040 #endif // GTK 1.x/2.x
1041 }
1042 }
1043
1044 long wxTextCtrl::GetInsertionPoint() const
1045 {
1046 wxCHECK_MSG( m_text != NULL, 0, wxT("invalid text ctrl") );
1047
1048 #ifdef __WXGTK20__
1049 if (m_windowStyle & wxTE_MULTILINE)
1050 {
1051 GtkTextBuffer *text_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(m_text) );
1052
1053 // There is no direct accessor for the cursor, but
1054 // internally, the cursor is the "mark" called
1055 // "insert" in the text view's btree structure.
1056
1057 GtkTextMark *mark = gtk_text_buffer_get_insert( text_buffer );
1058 GtkTextIter cursor;
1059 gtk_text_buffer_get_iter_at_mark( text_buffer, &cursor, mark );
1060
1061 return gtk_text_iter_get_offset( &cursor );
1062 }
1063 else
1064 #endif
1065 {
1066 return (long) GET_EDITABLE_POS(m_text);
1067 }
1068 }
1069
1070 long wxTextCtrl::GetLastPosition() const
1071 {
1072 wxCHECK_MSG( m_text != NULL, 0, wxT("invalid text ctrl") );
1073
1074 int pos = 0;
1075
1076 if (m_windowStyle & wxTE_MULTILINE)
1077 {
1078 #ifdef __WXGTK20__
1079 GtkTextBuffer *text_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(m_text) );
1080 GtkTextIter end;
1081 gtk_text_buffer_get_end_iter( text_buffer, &end );
1082
1083 pos = gtk_text_iter_get_offset( &end );
1084 #else
1085 pos = gtk_text_get_length( GTK_TEXT(m_text) );
1086 #endif
1087 }
1088 else
1089 {
1090 pos = GTK_ENTRY(m_text)->text_length;
1091 }
1092
1093 return (long)pos;
1094 }
1095
1096 void wxTextCtrl::Remove( long from, long to )
1097 {
1098 wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
1099
1100 #ifdef __WXGTK20__
1101 if (m_windowStyle & wxTE_MULTILINE)
1102 {
1103 GtkTextBuffer *
1104 text_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(m_text) );
1105
1106 GtkTextIter fromi, toi;
1107 gtk_text_buffer_get_iter_at_offset( text_buffer, &fromi, from );
1108 gtk_text_buffer_get_iter_at_offset( text_buffer, &toi, to );
1109
1110 gtk_text_buffer_delete( text_buffer, &fromi, &toi );
1111 }
1112 else // single line
1113 #endif
1114 gtk_editable_delete_text( GTK_EDITABLE(m_text), (gint)from, (gint)to );
1115 }
1116
1117 void wxTextCtrl::Replace( long from, long to, const wxString &value )
1118 {
1119 wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
1120
1121 Remove( from, to );
1122
1123 if (!value.IsEmpty())
1124 {
1125 #ifdef __WXGTK20__
1126 SetInsertionPoint( from );
1127 WriteText( value );
1128 #else // GTK 1.x
1129 gint pos = (gint)from;
1130 #if wxUSE_UNICODE
1131 wxWX2MBbuf buf = value.mbc_str();
1132 gtk_editable_insert_text( GTK_EDITABLE(m_text), buf, strlen(buf), &pos );
1133 #else
1134 gtk_editable_insert_text( GTK_EDITABLE(m_text), value, value.Length(), &pos );
1135 #endif // wxUSE_UNICODE
1136 #endif // GTK 1.x/2.x
1137 }
1138 }
1139
1140 void wxTextCtrl::Cut()
1141 {
1142 wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
1143
1144 #ifndef __WXGTK20__
1145 gtk_editable_cut_clipboard( GTK_EDITABLE(m_text) DUMMY_CLIPBOARD_ARG );
1146 #endif
1147 }
1148
1149 void wxTextCtrl::Copy()
1150 {
1151 wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
1152
1153 #ifndef __WXGTK20__
1154 gtk_editable_copy_clipboard( GTK_EDITABLE(m_text) DUMMY_CLIPBOARD_ARG );
1155 #endif
1156 }
1157
1158 void wxTextCtrl::Paste()
1159 {
1160 wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
1161
1162 #ifndef __WXGTK20__
1163 gtk_editable_paste_clipboard( GTK_EDITABLE(m_text) DUMMY_CLIPBOARD_ARG );
1164 #endif
1165 }
1166
1167 // Undo/redo
1168 void wxTextCtrl::Undo()
1169 {
1170 // TODO
1171 wxFAIL_MSG( wxT("wxTextCtrl::Undo not implemented") );
1172 }
1173
1174 void wxTextCtrl::Redo()
1175 {
1176 // TODO
1177 wxFAIL_MSG( wxT("wxTextCtrl::Redo not implemented") );
1178 }
1179
1180 bool wxTextCtrl::CanUndo() const
1181 {
1182 // TODO
1183 //wxFAIL_MSG( wxT("wxTextCtrl::CanUndo not implemented") );
1184 return FALSE;
1185 }
1186
1187 bool wxTextCtrl::CanRedo() const
1188 {
1189 // TODO
1190 //wxFAIL_MSG( wxT("wxTextCtrl::CanRedo not implemented") );
1191 return FALSE;
1192 }
1193
1194 // If the return values from and to are the same, there is no
1195 // selection.
1196 void wxTextCtrl::GetSelection(long* fromOut, long* toOut) const
1197 {
1198 wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
1199
1200 gint from = -1;
1201 gint to = -1;
1202 bool haveSelection = FALSE;
1203
1204 #ifdef __WXGTK20__
1205 if (m_windowStyle & wxTE_MULTILINE)
1206 {
1207 GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (m_text));
1208 GtkTextIter ifrom, ito;
1209 if ( gtk_text_buffer_get_selection_bounds(buffer, &ifrom, &ito) )
1210 {
1211 haveSelection = TRUE;
1212 from = gtk_text_iter_get_offset(&ifrom);
1213 to = gtk_text_iter_get_offset(&ito);
1214 }
1215 }
1216 else // not multi-line
1217 {
1218 if ( gtk_editable_get_selection_bounds( GTK_EDITABLE(m_text),
1219 &from, &to) )
1220 {
1221 haveSelection = TRUE;
1222 }
1223 }
1224 #else // not GTK2
1225 if ( (GTK_EDITABLE(m_text)->has_selection) )
1226 {
1227 haveSelection = TRUE;
1228 from = (long) GTK_EDITABLE(m_text)->selection_start_pos;
1229 to = (long) GTK_EDITABLE(m_text)->selection_end_pos;
1230 }
1231 #endif
1232
1233 if (! haveSelection )
1234 from = to = GetInsertionPoint();
1235
1236 if ( from > to )
1237 {
1238 // exchange them to be compatible with wxMSW
1239 gint tmp = from;
1240 from = to;
1241 to = tmp;
1242 }
1243
1244 if ( fromOut )
1245 *fromOut = from;
1246 if ( toOut )
1247 *toOut = to;
1248 }
1249
1250
1251 bool wxTextCtrl::IsEditable() const
1252 {
1253 wxCHECK_MSG( m_text != NULL, FALSE, wxT("invalid text ctrl") );
1254
1255 #ifdef __WXGTK20__
1256 if (m_windowStyle & wxTE_MULTILINE)
1257 {
1258 return gtk_text_view_get_editable(GTK_TEXT_VIEW(m_text));
1259 }
1260 else
1261 {
1262 return gtk_editable_get_editable(GTK_EDITABLE(m_text));
1263 }
1264 #else
1265 return GTK_EDITABLE(m_text)->editable;
1266 #endif
1267 }
1268
1269 bool wxTextCtrl::IsModified() const
1270 {
1271 return m_modified;
1272 }
1273
1274 void wxTextCtrl::Clear()
1275 {
1276 SetValue( wxT("") );
1277 }
1278
1279 void wxTextCtrl::OnChar( wxKeyEvent &key_event )
1280 {
1281 wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
1282
1283 if ((key_event.GetKeyCode() == WXK_RETURN) && (m_windowStyle & wxPROCESS_ENTER))
1284 {
1285 wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, m_windowId);
1286 event.SetEventObject(this);
1287 event.SetString(GetValue());
1288 if (GetEventHandler()->ProcessEvent(event)) return;
1289 }
1290
1291 if ((key_event.GetKeyCode() == WXK_RETURN) && !(m_windowStyle & wxTE_MULTILINE))
1292 {
1293 // This will invoke the dialog default action, such
1294 // as the clicking the default button.
1295
1296 wxWindow *top_frame = m_parent;
1297 while (top_frame->GetParent() && !(top_frame->IsTopLevel()))
1298 top_frame = top_frame->GetParent();
1299
1300 if (top_frame && GTK_IS_WINDOW(top_frame->m_widget))
1301 {
1302 GtkWindow *window = GTK_WINDOW(top_frame->m_widget);
1303
1304 if (window->default_widget)
1305 {
1306 gtk_widget_activate (window->default_widget);
1307 return;
1308 }
1309 }
1310 }
1311
1312 key_event.Skip();
1313 }
1314
1315 GtkWidget* wxTextCtrl::GetConnectWidget()
1316 {
1317 return GTK_WIDGET(m_text);
1318 }
1319
1320 bool wxTextCtrl::IsOwnGtkWindow( GdkWindow *window )
1321 {
1322 if (m_windowStyle & wxTE_MULTILINE)
1323 {
1324 #ifdef __WXGTK20__
1325 return window == gtk_text_view_get_window( GTK_TEXT_VIEW( m_text ), GTK_TEXT_WINDOW_TEXT ); // pure guesswork
1326 #else
1327 return (window == GTK_TEXT(m_text)->text_area);
1328 #endif
1329 }
1330 else
1331 {
1332 return (window == GTK_ENTRY(m_text)->text_area);
1333 }
1334 }
1335
1336 // the font will change for subsequent text insertiongs
1337 bool wxTextCtrl::SetFont( const wxFont &font )
1338 {
1339 wxCHECK_MSG( m_text != NULL, FALSE, wxT("invalid text ctrl") );
1340
1341 if ( !wxTextCtrlBase::SetFont(font) )
1342 {
1343 // font didn't change, nothing to do
1344 return FALSE;
1345 }
1346
1347 if ( m_windowStyle & wxTE_MULTILINE )
1348 {
1349 m_updateFont = TRUE;
1350
1351 m_defaultStyle.SetFont(font);
1352
1353 ChangeFontGlobally();
1354 }
1355
1356 return TRUE;
1357 }
1358
1359 void wxTextCtrl::ChangeFontGlobally()
1360 {
1361 // this method is very inefficient and hence should be called as rarely as
1362 // possible!
1363 wxASSERT_MSG( (m_windowStyle & wxTE_MULTILINE) && m_updateFont,
1364 _T("shouldn't be called for single line controls") );
1365
1366 wxString value = GetValue();
1367 if ( !value.IsEmpty() )
1368 {
1369 m_updateFont = FALSE;
1370
1371 Clear();
1372 AppendText(value);
1373 }
1374 }
1375
1376 void wxTextCtrl::UpdateFontIfNeeded()
1377 {
1378 if ( m_updateFont )
1379 ChangeFontGlobally();
1380 }
1381
1382 bool wxTextCtrl::SetForegroundColour(const wxColour& colour)
1383 {
1384 if ( !wxControl::SetForegroundColour(colour) )
1385 return FALSE;
1386
1387 // update default fg colour too
1388 m_defaultStyle.SetTextColour(colour);
1389
1390 return TRUE;
1391 }
1392
1393 bool wxTextCtrl::SetBackgroundColour( const wxColour &colour )
1394 {
1395 wxCHECK_MSG( m_text != NULL, FALSE, wxT("invalid text ctrl") );
1396
1397 if ( !wxControl::SetBackgroundColour( colour ) )
1398 return FALSE;
1399
1400 if (!m_widget->window)
1401 return FALSE;
1402
1403 wxColour sysbg = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE );
1404 if (sysbg.Red() == colour.Red() &&
1405 sysbg.Green() == colour.Green() &&
1406 sysbg.Blue() == colour.Blue())
1407 {
1408 return FALSE; // FIXME or TRUE?
1409 }
1410
1411 if (!m_backgroundColour.Ok())
1412 return FALSE;
1413
1414 if (m_windowStyle & wxTE_MULTILINE)
1415 {
1416 #ifndef __WXGTK20__
1417 GdkWindow *window = GTK_TEXT(m_text)->text_area;
1418 if (!window)
1419 return FALSE;
1420 m_backgroundColour.CalcPixel( gdk_window_get_colormap( window ) );
1421 gdk_window_set_background( window, m_backgroundColour.GetColor() );
1422 gdk_window_clear( window );
1423 #endif
1424 }
1425
1426 // change active background color too
1427 m_defaultStyle.SetBackgroundColour( colour );
1428
1429 return TRUE;
1430 }
1431
1432 bool wxTextCtrl::SetStyle( long start, long end, const wxTextAttr& style )
1433 {
1434 if ( m_windowStyle & wxTE_MULTILINE )
1435 {
1436 if ( style.IsDefault() )
1437 {
1438 // nothing to do
1439 return TRUE;
1440 }
1441 #ifdef __WXGTK20__
1442 GtkTextBuffer *text_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(m_text) );
1443 gint l = gtk_text_buffer_get_char_count( text_buffer );
1444
1445 wxCHECK_MSG( start >= 0 && end <= l, FALSE,
1446 _T("invalid range in wxTextCtrl::SetStyle") );
1447
1448 GtkTextIter starti, endi;
1449 gtk_text_buffer_get_iter_at_offset( text_buffer, &starti, start );
1450 gtk_text_buffer_get_iter_at_offset( text_buffer, &endi, end );
1451
1452 // use the attributes from style which are set in it and fall back
1453 // first to the default style and then to the text control default
1454 // colours for the others
1455 wxTextAttr attr = wxTextAttr::Combine(style, m_defaultStyle, this);
1456
1457 PangoFontDescription *font_description = attr.HasFont()
1458 ? attr.GetFont().GetNativeFontInfo()->description
1459 : NULL;
1460
1461 GdkColor *colFg = attr.HasTextColour() ? attr.GetTextColour().GetColor()
1462 : NULL;
1463
1464 GdkColor *colBg = attr.HasBackgroundColour()
1465 ? attr.GetBackgroundColour().GetColor()
1466 : NULL;
1467
1468 GtkTextTag *tag;
1469 tag = gtk_text_buffer_create_tag( text_buffer, NULL, "font-desc", font_description,
1470 "foreground-gdk", colFg,
1471 "background-gdk", colBg, NULL );
1472 gtk_text_buffer_apply_tag( text_buffer, tag, &starti, &endi );
1473
1474 return TRUE;
1475 #else
1476 // VERY dirty way to do that - removes the required text and re-adds it
1477 // with styling (FIXME)
1478
1479 gint l = gtk_text_get_length( GTK_TEXT(m_text) );
1480
1481 wxCHECK_MSG( start >= 0 && end <= l, FALSE,
1482 _T("invalid range in wxTextCtrl::SetStyle") );
1483
1484 gint old_pos = gtk_editable_get_position( GTK_EDITABLE(m_text) );
1485 char *text = gtk_editable_get_chars( GTK_EDITABLE(m_text), start, end );
1486 wxString tmp(text,*wxConvCurrent);
1487 g_free( text );
1488
1489 gtk_editable_delete_text( GTK_EDITABLE(m_text), start, end );
1490 gtk_editable_set_position( GTK_EDITABLE(m_text), start );
1491
1492 #if wxUSE_UNICODE
1493 wxWX2MBbuf buf = tmp.mbc_str();
1494 const char *txt = buf;
1495 size_t txtlen = strlen(buf);
1496 #else
1497 const char *txt = tmp;
1498 size_t txtlen = tmp.length();
1499 #endif
1500
1501 // use the attributes from style which are set in it and fall back
1502 // first to the default style and then to the text control default
1503 // colours for the others
1504 wxGtkTextInsert(m_text,
1505 wxTextAttr::Combine(style, m_defaultStyle, this),
1506 txt,
1507 txtlen);
1508
1509 /* does not seem to help under GTK+ 1.2 !!!
1510 gtk_editable_set_position( GTK_EDITABLE(m_text), old_pos ); */
1511 SetInsertionPoint( old_pos );
1512 #endif
1513 return TRUE;
1514 }
1515 else // singe line
1516 {
1517 // cannot do this for GTK+'s Entry widget
1518 return FALSE;
1519 }
1520 }
1521
1522 void wxTextCtrl::ApplyWidgetStyle()
1523 {
1524 if (m_windowStyle & wxTE_MULTILINE)
1525 {
1526 // how ?
1527 }
1528 else
1529 {
1530 SetWidgetStyle();
1531 gtk_widget_set_style( m_text, m_widgetStyle );
1532 }
1533 }
1534
1535 void wxTextCtrl::OnCut(wxCommandEvent& WXUNUSED(event))
1536 {
1537 Cut();
1538 }
1539
1540 void wxTextCtrl::OnCopy(wxCommandEvent& WXUNUSED(event))
1541 {
1542 Copy();
1543 }
1544
1545 void wxTextCtrl::OnPaste(wxCommandEvent& WXUNUSED(event))
1546 {
1547 Paste();
1548 }
1549
1550 void wxTextCtrl::OnUndo(wxCommandEvent& WXUNUSED(event))
1551 {
1552 Undo();
1553 }
1554
1555 void wxTextCtrl::OnRedo(wxCommandEvent& WXUNUSED(event))
1556 {
1557 Redo();
1558 }
1559
1560 void wxTextCtrl::OnUpdateCut(wxUpdateUIEvent& event)
1561 {
1562 event.Enable( CanCut() );
1563 }
1564
1565 void wxTextCtrl::OnUpdateCopy(wxUpdateUIEvent& event)
1566 {
1567 event.Enable( CanCopy() );
1568 }
1569
1570 void wxTextCtrl::OnUpdatePaste(wxUpdateUIEvent& event)
1571 {
1572 event.Enable( CanPaste() );
1573 }
1574
1575 void wxTextCtrl::OnUpdateUndo(wxUpdateUIEvent& event)
1576 {
1577 event.Enable( CanUndo() );
1578 }
1579
1580 void wxTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event)
1581 {
1582 event.Enable( CanRedo() );
1583 }
1584
1585 void wxTextCtrl::OnInternalIdle()
1586 {
1587 wxCursor cursor = m_cursor;
1588 if (g_globalCursor.Ok()) cursor = g_globalCursor;
1589
1590 if (cursor.Ok())
1591 {
1592 #ifndef __WXGTK20__
1593 GdkWindow *window = (GdkWindow*) NULL;
1594 if (HasFlag(wxTE_MULTILINE))
1595 window = GTK_TEXT(m_text)->text_area;
1596 else
1597 window = GTK_ENTRY(m_text)->text_area;
1598
1599 if (window)
1600 gdk_window_set_cursor( window, cursor.GetCursor() );
1601
1602 if (!g_globalCursor.Ok())
1603 cursor = *wxSTANDARD_CURSOR;
1604
1605 window = m_widget->window;
1606 if ((window) && !(GTK_WIDGET_NO_WINDOW(m_widget)))
1607 gdk_window_set_cursor( window, cursor.GetCursor() );
1608 #endif
1609 }
1610
1611 if (g_delayedFocus == this)
1612 {
1613 if (GTK_WIDGET_REALIZED(m_widget))
1614 {
1615 gtk_widget_grab_focus( m_widget );
1616 g_delayedFocus = NULL;
1617 }
1618 }
1619
1620 if (wxUpdateUIEvent::CanUpdate(this))
1621 UpdateWindowUI(wxUPDATE_UI_FROMIDLE);
1622 }
1623
1624 wxSize wxTextCtrl::DoGetBestSize() const
1625 {
1626 // FIXME should be different for multi-line controls...
1627 wxSize ret( wxControl::DoGetBestSize() );
1628 return wxSize(80, ret.y);
1629 }
1630
1631 // ----------------------------------------------------------------------------
1632 // freeze/thaw
1633 // ----------------------------------------------------------------------------
1634
1635 void wxTextCtrl::Freeze()
1636 {
1637 #ifndef __WXGTK20__
1638 if ( HasFlag(wxTE_MULTILINE) )
1639 {
1640 gtk_text_freeze(GTK_TEXT(m_text));
1641 }
1642 #endif
1643 }
1644
1645 void wxTextCtrl::Thaw()
1646 {
1647 #ifndef __WXGTK20__
1648 if ( HasFlag(wxTE_MULTILINE) )
1649 {
1650 GTK_TEXT(m_text)->vadj->value = 0.0;
1651
1652 gtk_text_thaw(GTK_TEXT(m_text));
1653 }
1654 #endif
1655 }
1656
1657 // ----------------------------------------------------------------------------
1658 // scrolling
1659 // ----------------------------------------------------------------------------
1660
1661 GtkAdjustment *wxTextCtrl::GetVAdj() const
1662 {
1663 #ifdef __WXGTK20__
1664 return NULL;
1665 #else
1666 return HasFlag(wxTE_MULTILINE) ? GTK_TEXT(m_text)->vadj : NULL;
1667 #endif
1668 }
1669
1670 bool wxTextCtrl::DoScroll(GtkAdjustment *adj, int diff)
1671 {
1672 #ifndef __WXGTK20__
1673 float value = adj->value + diff;
1674
1675 if ( value < 0 )
1676 value = 0;
1677
1678 float upper = adj->upper - adj->page_size;
1679 if ( value > upper )
1680 value = upper;
1681
1682 // did we noticeably change the scroll position?
1683 if ( fabs(adj->value - value) < 0.2 )
1684 {
1685 // well, this is what Robert does in wxScrollBar, so it must be good...
1686 return FALSE;
1687 }
1688
1689 adj->value = value;
1690
1691 gtk_signal_emit_by_name(GTK_OBJECT(adj), "value_changed");
1692
1693 #endif
1694 return TRUE;
1695 }
1696
1697 bool wxTextCtrl::ScrollLines(int lines)
1698 {
1699 #ifdef __WXGTK20__
1700 return FALSE;
1701 #else
1702 GtkAdjustment *adj = GetVAdj();
1703 if ( !adj )
1704 return FALSE;
1705
1706 // this is hardcoded to 10 in GTK+ 1.2 (great idea)
1707 static const int KEY_SCROLL_PIXELS = 10;
1708
1709 return DoScroll(adj, lines*KEY_SCROLL_PIXELS);
1710 #endif
1711 }
1712
1713 bool wxTextCtrl::ScrollPages(int pages)
1714 {
1715 #ifdef __WXGTK20__
1716 return FALSE;
1717 #else
1718 GtkAdjustment *adj = GetVAdj();
1719 if ( !adj )
1720 return FALSE;
1721
1722 return DoScroll(adj, (int)ceil(pages*adj->page_increment));
1723 #endif
1724 }
1725