]> git.saurik.com Git - wxWidgets.git/blame - src/gtk1/textctrl.cpp
conditional compilation for Universal Interfaces (3.4 or later)
[wxWidgets.git] / src / gtk1 / textctrl.cpp
CommitLineData
c801d85f
KB
1/////////////////////////////////////////////////////////////////////////////
2// Name: textctrl.cpp
3// Purpose:
4// Author: Robert Roebling
f96aa4d9 5// Id: $Id$
a81258be 6// Copyright: (c) 1998 Robert Roebling, Vadim Zeitlin
13289f04 7// Licence: wxWindows licence
c801d85f
KB
8/////////////////////////////////////////////////////////////////////////////
9
10#ifdef __GNUG__
11#pragma implementation "textctrl.h"
12#endif
13
14#include "wx/textctrl.h"
15#include "wx/utils.h"
ae0bdb01 16#include "wx/intl.h"
a1f79c1e 17#include "wx/log.h"
ae0bdb01 18#include "wx/settings.h"
fc71ef6e 19#include "wx/panel.h"
c801d85f 20
a81258be
RR
21#include <sys/types.h>
22#include <sys/stat.h>
23#include <ctype.h>
24
83624f79
RR
25#include "gdk/gdk.h"
26#include "gtk/gtk.h"
b292e2f5
RR
27#include "gdk/gdkkeysyms.h"
28
acfd422a
RR
29//-----------------------------------------------------------------------------
30// idle system
31//-----------------------------------------------------------------------------
32
33extern void wxapp_install_idle_handler();
34extern bool g_isIdle;
35
b292e2f5
RR
36//-----------------------------------------------------------------------------
37// data
38//-----------------------------------------------------------------------------
39
65045edd
RR
40extern bool g_blockEventsOnDrag;
41extern wxCursor g_globalCursor;
b292e2f5 42
c801d85f 43//-----------------------------------------------------------------------------
2f2aa628 44// "changed"
c801d85f
KB
45//-----------------------------------------------------------------------------
46
805dd538 47static void
2830bf19 48gtk_text_changed_callback( GtkWidget *WXUNUSED(widget), wxTextCtrl *win )
484e45bf 49{
a2053b27 50 if (!win->m_hasVMT) return;
805dd538 51
bb69661b 52 if (g_isIdle)
3c679789
RR
53 wxapp_install_idle_handler();
54
034be888 55 win->SetModified();
01041145 56 win->UpdateFontIfNeeded();
8bbe427f 57
f03fc89f 58 wxCommandEvent event( wxEVT_COMMAND_TEXT_UPDATED, win->GetId() );
8a85884a 59 event.SetString( win->GetValue() );
2830bf19
RR
60 event.SetEventObject( win );
61 win->GetEventHandler()->ProcessEvent( event );
6de97a3b 62}
112892b9 63
2830bf19 64//-----------------------------------------------------------------------------
034be888 65// "changed" from vertical scrollbar
2830bf19
RR
66//-----------------------------------------------------------------------------
67
805dd538 68static void
034be888 69gtk_scrollbar_changed_callback( GtkWidget *WXUNUSED(widget), wxTextCtrl *win )
2830bf19 70{
3c679789 71 if (!win->m_hasVMT) return;
bb69661b
VZ
72
73 if (g_isIdle)
3c679789 74 wxapp_install_idle_handler();
acfd422a 75
2830bf19
RR
76 win->CalculateScrollbar();
77}
78
fc71ef6e
JS
79//-----------------------------------------------------------------------------
80// "focus_in_event"
81//-----------------------------------------------------------------------------
82
83wxWindow *FindFocusedChild(wxWindow *win);
84extern wxWindow *g_focusWindow;
85extern bool g_blockEventsOnDrag;
86// extern bool g_isIdle;
87
88static gint gtk_text_focus_in_callback( GtkWidget *widget, GdkEvent *WXUNUSED(event), wxWindow *win )
89{
90 // Necessary?
91#if 0
92 if (g_isIdle)
93 wxapp_install_idle_handler();
94#endif
95 if (!win->m_hasVMT) return FALSE;
96 if (g_blockEventsOnDrag) return FALSE;
97
98 g_focusWindow = win;
99
100 wxPanel *panel = wxDynamicCast(win->GetParent(), wxPanel);
101 if (panel)
102 {
103 panel->SetLastFocus(win);
104 }
105
106#ifdef HAVE_XIM
107 if (win->m_ic)
108 gdk_im_begin(win->m_ic, win->m_wxwindow->window);
109#endif
110
111#if 0
112#ifdef wxUSE_CARET
113 // caret needs to be informed about focus change
114 wxCaret *caret = win->GetCaret();
115 if ( caret )
116 {
117 caret->OnSetFocus();
118 }
119#endif // wxUSE_CARET
120#endif
121
122 wxFocusEvent event( wxEVT_SET_FOCUS, win->GetId() );
123 event.SetEventObject( win );
124
125 if (win->GetEventHandler()->ProcessEvent( event ))
126 {
127 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "focus_in_event" );
128 return TRUE;
129 }
130
131 return FALSE;
132}
133
134//-----------------------------------------------------------------------------
135// "focus_out_event"
136//-----------------------------------------------------------------------------
137
138static gint gtk_text_focus_out_callback( GtkWidget *widget, GdkEvent *WXUNUSED(event), wxWindow *win )
139{
140#if 0
141 if (g_isIdle)
142 wxapp_install_idle_handler();
143#endif
144
145 if (!win->m_hasVMT) return FALSE;
146 if (g_blockEventsOnDrag) return FALSE;
147
148#if 0
149 // if the focus goes out of our app alltogether, OnIdle() will send
150 // wxActivateEvent, otherwise gtk_window_focus_in_callback() will reset
151 // g_sendActivateEvent to -1
152 g_sendActivateEvent = 0;
153#endif
154
155 wxWindow *winFocus = FindFocusedChild(win);
156 if ( winFocus )
157 win = winFocus;
158
159 g_focusWindow = (wxWindow *)NULL;
160
161#ifdef HAVE_XIM
162 if (win->m_ic)
163 gdk_im_end();
164#endif
165
166#if 0
167#ifdef wxUSE_CARET
168 // caret needs to be informed about focus change
169 wxCaret *caret = win->GetCaret();
170 if ( caret )
171 {
172 caret->OnKillFocus();
173 }
174#endif // wxUSE_CARET
175#endif
176
177 wxFocusEvent event( wxEVT_KILL_FOCUS, win->GetId() );
178 event.SetEventObject( win );
179
180 if (win->GetEventHandler()->ProcessEvent( event ))
181 {
182 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "focus_out_event" );
183 return TRUE;
184 }
185
186 return FALSE;
187}
188
2f2aa628
RR
189//-----------------------------------------------------------------------------
190// wxTextCtrl
191//-----------------------------------------------------------------------------
192
193IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl,wxControl)
194
c801d85f 195BEGIN_EVENT_TABLE(wxTextCtrl, wxControl)
2830bf19 196 EVT_CHAR(wxTextCtrl::OnChar)
e702ff0f
JS
197
198 EVT_MENU(wxID_CUT, wxTextCtrl::OnCut)
199 EVT_MENU(wxID_COPY, wxTextCtrl::OnCopy)
200 EVT_MENU(wxID_PASTE, wxTextCtrl::OnPaste)
201 EVT_MENU(wxID_UNDO, wxTextCtrl::OnUndo)
202 EVT_MENU(wxID_REDO, wxTextCtrl::OnRedo)
203
204 EVT_UPDATE_UI(wxID_CUT, wxTextCtrl::OnUpdateCut)
205 EVT_UPDATE_UI(wxID_COPY, wxTextCtrl::OnUpdateCopy)
206 EVT_UPDATE_UI(wxID_PASTE, wxTextCtrl::OnUpdatePaste)
207 EVT_UPDATE_UI(wxID_UNDO, wxTextCtrl::OnUpdateUndo)
208 EVT_UPDATE_UI(wxID_REDO, wxTextCtrl::OnUpdateRedo)
c801d85f
KB
209END_EVENT_TABLE()
210
01041145 211void wxTextCtrl::Init()
f5abe911
RR
212{
213 m_modified = FALSE;
01041145 214 m_updateFont = FALSE;
3ca6a5f0
BP
215 m_text =
216 m_vScrollbar = (GtkWidget *)NULL;
f5abe911 217}
13289f04 218
13111b2a
VZ
219wxTextCtrl::wxTextCtrl( wxWindow *parent,
220 wxWindowID id,
221 const wxString &value,
222 const wxPoint &pos,
223 const wxSize &size,
224 long style,
225 const wxValidator& validator,
226 const wxString &name )
f5abe911 227{
01041145
VZ
228 Init();
229
f5abe911
RR
230 Create( parent, id, value, pos, size, style, validator, name );
231}
c801d85f 232
13111b2a
VZ
233bool wxTextCtrl::Create( wxWindow *parent,
234 wxWindowID id,
235 const wxString &value,
236 const wxPoint &pos,
237 const wxSize &size,
238 long style,
239 const wxValidator& validator,
240 const wxString &name )
c801d85f 241{
2830bf19 242 m_needParent = TRUE;
b292e2f5 243 m_acceptsFocus = TRUE;
484e45bf 244
4dcaf11a
RR
245 if (!PreCreation( parent, pos, size ) ||
246 !CreateBase( parent, id, pos, size, style, validator, name ))
247 {
223d09f6 248 wxFAIL_MSG( wxT("wxTextCtrl creation failed") );
13111b2a 249 return FALSE;
4dcaf11a 250 }
6de97a3b 251
805dd538 252
034be888 253 m_vScrollbarVisible = FALSE;
13289f04 254
2830bf19 255 bool multi_line = (style & wxTE_MULTILINE) != 0;
ab46dc18 256 if (multi_line)
2830bf19 257 {
7d6d2cd4 258#if (GTK_MINOR_VERSION > 2)
034be888
RR
259 /* a multi-line edit control: create a vertical scrollbar by default and
260 horizontal if requested */
2830bf19 261 bool bHasHScrollbar = (style & wxHSCROLL) != 0;
7d6d2cd4
RR
262#else
263 bool bHasHScrollbar = FALSE;
264#endif
805dd538 265
034be888 266 /* create our control ... */
2830bf19
RR
267 m_text = gtk_text_new( (GtkAdjustment *) NULL, (GtkAdjustment *) NULL );
268
034be888 269 /* ... and put into the upper left hand corner of the table */
2830bf19 270 m_widget = gtk_table_new(bHasHScrollbar ? 2 : 1, 2, FALSE);
5664fc32 271 GTK_WIDGET_UNSET_FLAGS( m_widget, GTK_CAN_FOCUS );
2830bf19 272 gtk_table_attach( GTK_TABLE(m_widget), m_text, 0, 1, 0, 1,
41dee9d0 273 (GtkAttachOptions)(GTK_FILL | GTK_EXPAND | GTK_SHRINK),
f5368809
RR
274 (GtkAttachOptions)(GTK_FILL | GTK_EXPAND | GTK_SHRINK),
275 0, 0);
bb69661b 276
5c387335
RR
277 /* always wrap words */
278 gtk_text_set_word_wrap( GTK_TEXT(m_text), TRUE );
bb69661b 279
7d6d2cd4 280#if (GTK_MINOR_VERSION > 2)
034be888 281 /* put the horizontal scrollbar in the lower left hand corner */
2830bf19
RR
282 if (bHasHScrollbar)
283 {
284 GtkWidget *hscrollbar = gtk_hscrollbar_new(GTK_TEXT(m_text)->hadj);
5664fc32 285 GTK_WIDGET_UNSET_FLAGS( hscrollbar, GTK_CAN_FOCUS );
2830bf19 286 gtk_table_attach(GTK_TABLE(m_widget), hscrollbar, 0, 1, 1, 2,
8ce63e9d 287 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL | GTK_SHRINK),
13289f04
VZ
288 GTK_FILL,
289 0, 0);
2830bf19 290 gtk_widget_show(hscrollbar);
13289f04 291
3358d36e
VZ
292 /* don't wrap lines, otherwise we wouldn't need the scrollbar */
293 gtk_text_set_line_wrap( GTK_TEXT(m_text), FALSE );
5c387335 294 }
7d6d2cd4 295#endif
bb69661b 296
ab46dc18
RR
297 /* finally, put the vertical scrollbar in the upper right corner */
298 m_vScrollbar = gtk_vscrollbar_new( GTK_TEXT(m_text)->vadj );
299 GTK_WIDGET_UNSET_FLAGS( m_vScrollbar, GTK_CAN_FOCUS );
ab46dc18
RR
300 gtk_table_attach(GTK_TABLE(m_widget), m_vScrollbar, 1, 2, 0, 1,
301 GTK_FILL,
302 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL | GTK_SHRINK),
303 0, 0);
2830bf19
RR
304 }
305 else
306 {
034be888 307 /* a single-line text control: no need for scrollbars */
2830bf19
RR
308 m_widget =
309 m_text = gtk_entry_new();
310 }
484e45bf 311
db434467
RR
312 m_parent->DoAddChild( this );
313
314 PostCreation();
315
316 SetFont( parent->GetFont() );
317
318 wxSize size_best( DoGetBestSize() );
319 wxSize new_size( size );
0279e844 320 if (new_size.x == -1)
db434467 321 new_size.x = size_best.x;
0279e844 322 if (new_size.y == -1)
db434467 323 new_size.y = size_best.y;
0279e844
RR
324 if ((new_size.x != size.x) || (new_size.y != size.y))
325 SetSize( new_size.x, new_size.y );
484e45bf 326
2830bf19 327 if (multi_line)
2830bf19 328 gtk_widget_show(m_text);
13289f04 329
ab46dc18
RR
330 if (multi_line)
331 {
332 gtk_signal_connect(GTK_OBJECT(GTK_TEXT(m_text)->vadj), "changed",
333 (GtkSignalFunc) gtk_scrollbar_changed_callback, (gpointer) this );
fc71ef6e
JS
334
335 gtk_signal_connect( GTK_OBJECT(GTK_TEXT(m_text)), "focus_in_event",
336 GTK_SIGNAL_FUNC(gtk_text_focus_in_callback), (gpointer)this );
337
338 gtk_signal_connect( GTK_OBJECT(GTK_TEXT(m_text)), "focus_out_event",
339 GTK_SIGNAL_FUNC(gtk_text_focus_out_callback), (gpointer)this );
340 }
341 else
342 {
343 gtk_signal_connect( GTK_OBJECT(m_text), "focus_in_event",
344 GTK_SIGNAL_FUNC(gtk_text_focus_in_callback), (gpointer)this );
345
346 gtk_signal_connect( GTK_OBJECT(m_text), "focus_out_event",
347 GTK_SIGNAL_FUNC(gtk_text_focus_out_callback), (gpointer)this );
ab46dc18 348 }
3358d36e 349
291a8f20 350 if (!value.IsEmpty())
2830bf19
RR
351 {
352 gint tmp = 0;
3358d36e
VZ
353
354#if GTK_MINOR_VERSION == 0
355 // if we don't realize it, GTK 1.0.6 dies with a SIGSEGV in
356 // gtk_editable_insert_text()
357 gtk_widget_realize(m_text);
358#endif // GTK 1.0
359
05939a81 360#if wxUSE_UNICODE
3358d36e 361 wxWX2MBbuf val = value.mbc_str();
05939a81 362 gtk_editable_insert_text( GTK_EDITABLE(m_text), val, strlen(val), &tmp );
3358d36e 363#else // !Unicode
2830bf19 364 gtk_editable_insert_text( GTK_EDITABLE(m_text), value, value.Length(), &tmp );
3358d36e
VZ
365#endif // Unicode/!Unicode
366
291a8f20
RR
367 if (multi_line)
368 {
3358d36e
VZ
369 /* bring editable's cursor uptodate. bug in GTK. */
370
371 GTK_EDITABLE(m_text)->current_pos = gtk_text_get_point( GTK_TEXT(m_text) );
372 }
2830bf19 373 }
484e45bf 374
2830bf19
RR
375 if (style & wxTE_PASSWORD)
376 {
377 if (!multi_line)
378 gtk_entry_set_visibility( GTK_ENTRY(m_text), FALSE );
379 }
8bbe427f 380
2830bf19
RR
381 if (style & wxTE_READONLY)
382 {
383 if (!multi_line)
384 gtk_entry_set_editable( GTK_ENTRY(m_text), FALSE );
385 }
386 else
387 {
388 if (multi_line)
389 gtk_text_set_editable( GTK_TEXT(m_text), 1 );
390 }
3358d36e 391
ce16e5d7
RR
392 /* we want to be notified about text changes */
393 gtk_signal_connect( GTK_OBJECT(m_text), "changed",
394 GTK_SIGNAL_FUNC(gtk_text_changed_callback), (gpointer)this);
395
012a03e0 396 SetBackgroundColour( wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOW) );
034be888 397 SetForegroundColour( parent->GetForegroundColour() );
805dd538 398
65045edd 399 m_cursor = wxCursor( wxCURSOR_IBEAM );
13111b2a 400
2830bf19 401 Show( TRUE );
805dd538 402
2830bf19
RR
403 return TRUE;
404}
484e45bf 405
2830bf19
RR
406void wxTextCtrl::CalculateScrollbar()
407{
408 if ((m_windowStyle & wxTE_MULTILINE) == 0) return;
f96aa4d9 409
2830bf19 410 GtkAdjustment *adj = GTK_TEXT(m_text)->vadj;
805dd538 411
2830bf19
RR
412 if (adj->upper - adj->page_size < 0.8)
413 {
414 if (m_vScrollbarVisible)
415 {
034be888 416 gtk_widget_hide( m_vScrollbar );
034be888 417 m_vScrollbarVisible = FALSE;
2830bf19
RR
418 }
419 }
420 else
421 {
422 if (!m_vScrollbarVisible)
423 {
034be888 424 gtk_widget_show( m_vScrollbar );
034be888 425 m_vScrollbarVisible = TRUE;
2830bf19
RR
426 }
427 }
6de97a3b 428}
c801d85f 429
03f38c58 430wxString wxTextCtrl::GetValue() const
c801d85f 431{
223d09f6 432 wxCHECK_MSG( m_text != NULL, wxT(""), wxT("invalid text ctrl") );
8bbe427f 433
2830bf19
RR
434 wxString tmp;
435 if (m_windowStyle & wxTE_MULTILINE)
436 {
437 gint len = gtk_text_get_length( GTK_TEXT(m_text) );
438 char *text = gtk_editable_get_chars( GTK_EDITABLE(m_text), 0, len );
dcf924a3 439 tmp = wxString(text,*wxConvCurrent);
2830bf19
RR
440 g_free( text );
441 }
442 else
443 {
dcf924a3 444 tmp = wxString(gtk_entry_get_text( GTK_ENTRY(m_text) ),*wxConvCurrent);
2830bf19
RR
445 }
446 return tmp;
6de97a3b 447}
c801d85f
KB
448
449void wxTextCtrl::SetValue( const wxString &value )
450{
223d09f6 451 wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
8bbe427f 452
2830bf19
RR
453 if (m_windowStyle & wxTE_MULTILINE)
454 {
455 gint len = gtk_text_get_length( GTK_TEXT(m_text) );
456 gtk_editable_delete_text( GTK_EDITABLE(m_text), 0, len );
457 len = 0;
05939a81 458#if wxUSE_UNICODE
01041145 459 wxWX2MBbuf tmpbuf = value.mbc_str();
05939a81
OK
460 gtk_editable_insert_text( GTK_EDITABLE(m_text), tmpbuf, strlen(tmpbuf), &len );
461#else
01041145 462 gtk_editable_insert_text( GTK_EDITABLE(m_text), value.mbc_str(), value.Length(), &len );
05939a81 463#endif
2830bf19
RR
464 }
465 else
466 {
01041145 467 gtk_entry_set_text( GTK_ENTRY(m_text), value.mbc_str() );
2830bf19 468 }
f6bcfd97
BP
469
470 // GRG, Jun/2000: Changed this after a lot of discussion in
471 // the lists. wxWindows 2.2 will have a set of flags to
472 // customize this behaviour.
473 SetInsertionPoint(0);
474
475 m_modified = FALSE;
6de97a3b 476}
c801d85f
KB
477
478void wxTextCtrl::WriteText( const wxString &text )
479{
223d09f6 480 wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
8bbe427f 481
2df7be7f 482 if (text.IsEmpty()) return;
484e45bf 483
2830bf19
RR
484 if (m_windowStyle & wxTE_MULTILINE)
485 {
291a8f20 486 /* this moves the cursor pos to behind the inserted text */
3358d36e 487 gint len = GTK_EDITABLE(m_text)->current_pos;
13111b2a 488
05939a81 489#if wxUSE_UNICODE
3358d36e 490 wxWX2MBbuf buf = text.mbc_str();
05939a81
OK
491 gtk_editable_insert_text( GTK_EDITABLE(m_text), buf, strlen(buf), &len );
492#else
2830bf19 493 gtk_editable_insert_text( GTK_EDITABLE(m_text), text, text.Length(), &len );
05939a81 494#endif
3358d36e
VZ
495
496 /* bring editable's cursor uptodate. bug in GTK. */
497 GTK_EDITABLE(m_text)->current_pos = gtk_text_get_point( GTK_TEXT(m_text) );
2830bf19
RR
498 }
499 else
500 {
c46554be 501 /* this moves the cursor pos to behind the inserted text */
3358d36e 502 gint len = GTK_EDITABLE(m_text)->current_pos;
05939a81 503#if wxUSE_UNICODE
3358d36e 504 wxWX2MBbuf buf = text.mbc_str();
05939a81
OK
505 gtk_editable_insert_text( GTK_EDITABLE(m_text), buf, strlen(buf), &len );
506#else
c46554be 507 gtk_editable_insert_text( GTK_EDITABLE(m_text), text, text.Length(), &len );
05939a81 508#endif
3358d36e
VZ
509
510 /* bring editable's cursor uptodate. bug in GTK. */
511 GTK_EDITABLE(m_text)->current_pos += text.Len();
512
513 /* bring entry's cursor uptodate. bug in GTK. */
514 gtk_entry_set_position( GTK_ENTRY(m_text), GTK_EDITABLE(m_text)->current_pos );
2830bf19 515 }
6de97a3b 516}
c801d85f 517
a6e21573
HH
518void wxTextCtrl::AppendText( const wxString &text )
519{
223d09f6 520 wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
a6e21573 521
2df7be7f
RR
522 if (text.IsEmpty()) return;
523
a6e21573
HH
524 if (m_windowStyle & wxTE_MULTILINE)
525 {
bb69661b
VZ
526 bool hasSpecialAttributes = m_font.Ok() ||
527 m_foregroundColour.Ok() ||
528 m_backgroundColour.Ok();
529 if ( hasSpecialAttributes )
530 {
531 gtk_text_insert( GTK_TEXT(m_text),
532 m_font.GetInternalFont(),
533 m_foregroundColour.GetColor(),
534 m_backgroundColour.GetColor(),
c980c992 535 text.mbc_str(), text.length());
bb69661b
VZ
536
537 }
538 else
539 {
540 /* we'll insert at the last position */
541 gint len = gtk_text_get_length( GTK_TEXT(m_text) );
05939a81 542#if wxUSE_UNICODE
bb69661b
VZ
543 wxWX2MBbuf buf = text.mbc_str();
544 gtk_editable_insert_text( GTK_EDITABLE(m_text), buf, strlen(buf), &len );
05939a81 545#else
bb69661b 546 gtk_editable_insert_text( GTK_EDITABLE(m_text), text, text.Length(), &len );
05939a81 547#endif
bb69661b 548 }
3358d36e
VZ
549
550 /* bring editable's cursor uptodate. bug in GTK. */
551 GTK_EDITABLE(m_text)->current_pos = gtk_text_get_point( GTK_TEXT(m_text) );
a6e21573
HH
552 }
553 else
554 {
05939a81 555 gtk_entry_append_text( GTK_ENTRY(m_text), text.mbc_str() );
a6e21573
HH
556 }
557}
558
debe6624 559wxString wxTextCtrl::GetLineText( long lineNo ) const
c801d85f 560{
a81258be
RR
561 if (m_windowStyle & wxTE_MULTILINE)
562 {
563 gint len = gtk_text_get_length( GTK_TEXT(m_text) );
564 char *text = gtk_editable_get_chars( GTK_EDITABLE(m_text), 0, len );
565
566 if (text)
567 {
223d09f6 568 wxString buf(wxT(""));
a81258be
RR
569 long i;
570 int currentLine = 0;
571 for (i = 0; currentLine != lineNo && text[i]; i++ )
572 if (text[i] == '\n')
573 currentLine++;
574 // Now get the text
575 int j;
576 for (j = 0; text[i] && text[i] != '\n'; i++, j++ )
577 buf += text[i];
8bbe427f 578
a81258be
RR
579 g_free( text );
580 return buf;
581 }
582 else
583 return wxEmptyString;
584 }
585 else
586 {
587 if (lineNo == 0) return GetValue();
588 return wxEmptyString;
589 }
6de97a3b 590}
c801d85f 591
a81258be
RR
592void wxTextCtrl::OnDropFiles( wxDropFilesEvent &WXUNUSED(event) )
593{
ac0d36b5
HH
594 /* If you implement this, don't forget to update the documentation!
595 * (file docs/latex/wx/text.tex) */
223d09f6 596 wxFAIL_MSG( wxT("wxTextCtrl::OnDropFiles not implemented") );
a81258be 597}
112892b9 598
0efe5ba7 599bool wxTextCtrl::PositionToXY(long pos, long *x, long *y ) const
c801d85f 600{
96385642 601 if ( m_windowStyle & wxTE_MULTILINE )
805dd538 602 {
96385642
VZ
603 wxString text = GetValue();
604
6085b116 605 // cast to prevent warning. But pos really should've been unsigned.
2829d9e3 606 if( (unsigned long)pos > text.Len() )
96385642
VZ
607 return FALSE;
608
ac0d36b5
HH
609 *x=0; // First Col
610 *y=0; // First Line
2829d9e3 611
05939a81
OK
612 const wxChar* stop = text.c_str() + pos;
613 for ( const wxChar *p = text.c_str(); p < stop; p++ )
96385642 614 {
223d09f6 615 if (*p == wxT('\n'))
96385642
VZ
616 {
617 (*y)++;
ac0d36b5 618 *x=0;
96385642
VZ
619 }
620 else
621 (*x)++;
622 }
805dd538 623 }
96385642
VZ
624 else // single line control
625 {
2829d9e3 626 if ( pos <= GTK_ENTRY(m_text)->text_length )
96385642 627 {
ac0d36b5 628 *y = 0;
96385642
VZ
629 *x = pos;
630 }
631 else
632 {
633 // index out of bounds
634 return FALSE;
635 }
8bbe427f 636 }
96385642
VZ
637
638 return TRUE;
6de97a3b 639}
c801d85f 640
e3ca08dd 641long wxTextCtrl::XYToPosition(long x, long y ) const
c801d85f 642{
2830bf19 643 if (!(m_windowStyle & wxTE_MULTILINE)) return 0;
805dd538 644
2830bf19 645 long pos=0;
ac0d36b5 646 for( int i=0; i<y; i++ ) pos += GetLineLength(i) + 1; // one for '\n'
8bbe427f 647
3358d36e 648 pos += x;
2830bf19 649 return pos;
6de97a3b 650}
c801d85f 651
a81258be 652int wxTextCtrl::GetLineLength(long lineNo) const
c801d85f 653{
a81258be
RR
654 wxString str = GetLineText (lineNo);
655 return (int) str.Length();
6de97a3b 656}
c801d85f 657
a81258be 658int wxTextCtrl::GetNumberOfLines() const
c801d85f 659{
2830bf19 660 if (m_windowStyle & wxTE_MULTILINE)
a81258be 661 {
2830bf19
RR
662 gint len = gtk_text_get_length( GTK_TEXT(m_text) );
663 char *text = gtk_editable_get_chars( GTK_EDITABLE(m_text), 0, len );
664
665 if (text)
666 {
667 int currentLine = 0;
668 for (int i = 0; i < len; i++ )
96385642 669 {
2830bf19 670 if (text[i] == '\n')
96385642
VZ
671 currentLine++;
672 }
2830bf19 673 g_free( text );
2829d9e3
VZ
674
675 // currentLine is 0 based, add 1 to get number of lines
676 return currentLine + 1;
2830bf19
RR
677 }
678 else
96385642 679 {
2830bf19 680 return 0;
96385642 681 }
a81258be
RR
682 }
683 else
2830bf19 684 {
96385642 685 return 1;
2830bf19 686 }
6de97a3b 687}
c801d85f 688
debe6624 689void wxTextCtrl::SetInsertionPoint( long pos )
c801d85f 690{
223d09f6 691 wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
3358d36e
VZ
692
693 if (m_windowStyle & wxTE_MULTILINE)
291a8f20
RR
694 {
695 /* seems to be broken in GTK 1.0.X:
3358d36e
VZ
696 gtk_text_set_point( GTK_TEXT(m_text), (int)pos ); */
697
291a8f20
RR
698 gtk_signal_disconnect_by_func( GTK_OBJECT(m_text),
699 GTK_SIGNAL_FUNC(gtk_text_changed_callback), (gpointer)this);
3358d36e 700
291a8f20 701 /* we fake a set_point by inserting and deleting. as the user
3358d36e
VZ
702 isn't supposed to get to know about thos non-sense, we
703 disconnect so that no events are sent to the user program. */
704
291a8f20
RR
705 gint tmp = (gint)pos;
706 gtk_editable_insert_text( GTK_EDITABLE(m_text), " ", 1, &tmp );
3358d36e
VZ
707 gtk_editable_delete_text( GTK_EDITABLE(m_text), tmp-1, tmp );
708
291a8f20
RR
709 gtk_signal_connect( GTK_OBJECT(m_text), "changed",
710 GTK_SIGNAL_FUNC(gtk_text_changed_callback), (gpointer)this);
3358d36e
VZ
711
712 /* bring editable's cursor uptodate. another bug in GTK. */
713
714 GTK_EDITABLE(m_text)->current_pos = gtk_text_get_point( GTK_TEXT(m_text) );
ac0d36b5 715 }
2830bf19 716 else
291a8f20 717 {
d59051dd 718 gtk_entry_set_position( GTK_ENTRY(m_text), (int)pos );
3358d36e
VZ
719
720 /* bring editable's cursor uptodate. bug in GTK. */
721
b02da6b1 722 GTK_EDITABLE(m_text)->current_pos = (guint32)pos;
291a8f20 723 }
6de97a3b 724}
c801d85f 725
03f38c58 726void wxTextCtrl::SetInsertionPointEnd()
c801d85f 727{
223d09f6 728 wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
8bbe427f 729
d59051dd
VZ
730 if (m_windowStyle & wxTE_MULTILINE)
731 SetInsertionPoint(gtk_text_get_length(GTK_TEXT(m_text)));
732 else
733 gtk_entry_set_position( GTK_ENTRY(m_text), -1 );
6de97a3b 734}
c801d85f 735
debe6624 736void wxTextCtrl::SetEditable( bool editable )
c801d85f 737{
223d09f6 738 wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
8bbe427f 739
2830bf19
RR
740 if (m_windowStyle & wxTE_MULTILINE)
741 gtk_text_set_editable( GTK_TEXT(m_text), editable );
742 else
743 gtk_entry_set_editable( GTK_ENTRY(m_text), editable );
6de97a3b 744}
c801d85f 745
68df5777
RR
746bool wxTextCtrl::Enable( bool enable )
747{
748 if (!wxWindowBase::Enable(enable))
749 {
750 // nothing to do
751 return FALSE;
752 }
f6bcfd97 753
68df5777
RR
754 if (m_windowStyle & wxTE_MULTILINE)
755 {
756 gtk_text_set_editable( GTK_TEXT(m_text), enable );
757 }
758 else
759 {
760 gtk_widget_set_sensitive( m_text, enable );
761 }
762
763 return TRUE;
764}
765
0efe5ba7
VZ
766void wxTextCtrl::DiscardEdits()
767{
768 m_modified = FALSE;
769}
770
debe6624 771void wxTextCtrl::SetSelection( long from, long to )
c801d85f 772{
223d09f6 773 wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
8bbe427f 774
a1f79c1e
VZ
775 if ( (m_windowStyle & wxTE_MULTILINE) &&
776 !GTK_TEXT(m_text)->line_start_cache )
777 {
778 // tell the programmer that it didn't work
779 wxLogDebug(_T("Can't call SetSelection() before realizing the control"));
780 return;
781 }
782
2830bf19 783 gtk_editable_select_region( GTK_EDITABLE(m_text), (gint)from, (gint)to );
6de97a3b 784}
c801d85f 785
910f1f8c 786void wxTextCtrl::ShowPosition( long WXUNUSED(pos) )
c801d85f 787{
910f1f8c 788// SetInsertionPoint( pos );
6de97a3b 789}
c801d85f 790
03f38c58 791long wxTextCtrl::GetInsertionPoint() const
c801d85f 792{
223d09f6 793 wxCHECK_MSG( m_text != NULL, 0, wxT("invalid text ctrl") );
8bbe427f 794
2830bf19 795 return (long) GTK_EDITABLE(m_text)->current_pos;
6de97a3b 796}
c801d85f 797
03f38c58 798long wxTextCtrl::GetLastPosition() const
c801d85f 799{
223d09f6 800 wxCHECK_MSG( m_text != NULL, 0, wxT("invalid text ctrl") );
8bbe427f 801
2830bf19
RR
802 int pos = 0;
803 if (m_windowStyle & wxTE_MULTILINE)
804 pos = gtk_text_get_length( GTK_TEXT(m_text) );
805 else
806 pos = GTK_ENTRY(m_text)->text_length;
805dd538 807
ac0d36b5 808 return (long)pos;
6de97a3b 809}
c801d85f 810
debe6624 811void wxTextCtrl::Remove( long from, long to )
c801d85f 812{
223d09f6 813 wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
8bbe427f 814
2830bf19 815 gtk_editable_delete_text( GTK_EDITABLE(m_text), (gint)from, (gint)to );
6de97a3b 816}
c801d85f 817
debe6624 818void wxTextCtrl::Replace( long from, long to, const wxString &value )
c801d85f 819{
223d09f6 820 wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
8bbe427f 821
2830bf19 822 gtk_editable_delete_text( GTK_EDITABLE(m_text), (gint)from, (gint)to );
bb69661b 823
2df7be7f
RR
824 if (!value.IsEmpty())
825 {
826 gint pos = (gint)from;
05939a81 827#if wxUSE_UNICODE
2df7be7f
RR
828 wxWX2MBbuf buf = value.mbc_str();
829 gtk_editable_insert_text( GTK_EDITABLE(m_text), buf, strlen(buf), &pos );
05939a81 830#else
2df7be7f 831 gtk_editable_insert_text( GTK_EDITABLE(m_text), value, value.Length(), &pos );
05939a81 832#endif
2df7be7f 833 }
6de97a3b 834}
c801d85f 835
03f38c58 836void wxTextCtrl::Cut()
c801d85f 837{
223d09f6 838 wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
8bbe427f 839
d345e841 840#if (GTK_MINOR_VERSION > 0)
2830bf19 841 gtk_editable_cut_clipboard( GTK_EDITABLE(m_text) );
75ed1d15 842#else
2830bf19 843 gtk_editable_cut_clipboard( GTK_EDITABLE(m_text), 0 );
75ed1d15 844#endif
6de97a3b 845}
c801d85f 846
03f38c58 847void wxTextCtrl::Copy()
c801d85f 848{
223d09f6 849 wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
8bbe427f 850
d345e841 851#if (GTK_MINOR_VERSION > 0)
2830bf19 852 gtk_editable_copy_clipboard( GTK_EDITABLE(m_text) );
75ed1d15 853#else
2830bf19 854 gtk_editable_copy_clipboard( GTK_EDITABLE(m_text), 0 );
75ed1d15 855#endif
6de97a3b 856}
c801d85f 857
03f38c58 858void wxTextCtrl::Paste()
c801d85f 859{
223d09f6 860 wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
8bbe427f 861
d345e841 862#if (GTK_MINOR_VERSION > 0)
2830bf19 863 gtk_editable_paste_clipboard( GTK_EDITABLE(m_text) );
75ed1d15 864#else
2830bf19 865 gtk_editable_paste_clipboard( GTK_EDITABLE(m_text), 0 );
75ed1d15 866#endif
6de97a3b 867}
c801d85f 868
ca8b28f2
JS
869bool wxTextCtrl::CanCopy() const
870{
871 // Can copy if there's a selection
872 long from, to;
873 GetSelection(& from, & to);
874 return (from != to) ;
875}
876
877bool wxTextCtrl::CanCut() const
878{
879 // Can cut if there's a selection
880 long from, to;
881 GetSelection(& from, & to);
dbf28859 882 return (from != to) && (IsEditable());
ca8b28f2
JS
883}
884
885bool wxTextCtrl::CanPaste() const
886{
887 return IsEditable() ;
888}
889
890// Undo/redo
891void wxTextCtrl::Undo()
892{
893 // TODO
223d09f6 894 wxFAIL_MSG( wxT("wxTextCtrl::Undo not implemented") );
ca8b28f2
JS
895}
896
897void wxTextCtrl::Redo()
898{
899 // TODO
223d09f6 900 wxFAIL_MSG( wxT("wxTextCtrl::Redo not implemented") );
ca8b28f2
JS
901}
902
903bool wxTextCtrl::CanUndo() const
904{
905 // TODO
223d09f6 906 wxFAIL_MSG( wxT("wxTextCtrl::CanUndo not implemented") );
ca8b28f2
JS
907 return FALSE;
908}
909
910bool wxTextCtrl::CanRedo() const
911{
912 // TODO
223d09f6 913 wxFAIL_MSG( wxT("wxTextCtrl::CanRedo not implemented") );
ca8b28f2
JS
914 return FALSE;
915}
916
917// If the return values from and to are the same, there is no
918// selection.
2d4cc5b6 919void wxTextCtrl::GetSelection(long* fromOut, long* toOut) const
ca8b28f2 920{
223d09f6 921 wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
bb69661b 922
2d4cc5b6
VZ
923 long from, to;
924 if ( !(GTK_EDITABLE(m_text)->has_selection) )
05060eeb 925 {
2d4cc5b6
VZ
926 from =
927 to = GetInsertionPoint();
928 }
929 else // got selection
930 {
931 from = (long) GTK_EDITABLE(m_text)->selection_start_pos;
932 to = (long) GTK_EDITABLE(m_text)->selection_end_pos;
933
934 if ( from > to )
935 {
936 // exchange them to be compatible with wxMSW
937 long tmp = from;
938 from = to;
939 to = tmp;
940 }
05060eeb 941 }
bb69661b 942
2d4cc5b6
VZ
943 if ( fromOut )
944 *fromOut = from;
945 if ( toOut )
946 *toOut = to;
ca8b28f2
JS
947}
948
949bool wxTextCtrl::IsEditable() const
950{
223d09f6 951 wxCHECK_MSG( m_text != NULL, FALSE, wxT("invalid text ctrl") );
05060eeb
RR
952
953 return GTK_EDITABLE(m_text)->editable;
ca8b28f2
JS
954}
955
0efe5ba7
VZ
956bool wxTextCtrl::IsModified() const
957{
958 return m_modified;
959}
960
03f38c58 961void wxTextCtrl::Clear()
c801d85f 962{
223d09f6 963 SetValue( wxT("") );
6de97a3b 964}
c801d85f 965
903f689b 966void wxTextCtrl::OnChar( wxKeyEvent &key_event )
c801d85f 967{
223d09f6 968 wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
805dd538 969
2830bf19
RR
970 if ((key_event.KeyCode() == WXK_RETURN) && (m_windowStyle & wxPROCESS_ENTER))
971 {
972 wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, m_windowId);
973 event.SetEventObject(this);
f6bcfd97 974 event.SetString(GetValue());
2830bf19
RR
975 if (GetEventHandler()->ProcessEvent(event)) return;
976 }
903f689b 977
da048e3d
RR
978 if ((key_event.KeyCode() == WXK_RETURN) && !(m_windowStyle & wxTE_MULTILINE))
979 {
980 wxWindow *top_frame = m_parent;
8487f887 981 while (top_frame->GetParent() && !(top_frame->IsTopLevel()))
da048e3d 982 top_frame = top_frame->GetParent();
13111b2a
VZ
983 GtkWindow *window = GTK_WINDOW(top_frame->m_widget);
984
985 if (window->default_widget)
da048e3d
RR
986 {
987 gtk_widget_activate (window->default_widget);
13111b2a
VZ
988 return;
989 }
da048e3d
RR
990 }
991
2830bf19 992 key_event.Skip();
6de97a3b 993}
c801d85f 994
03f38c58 995GtkWidget* wxTextCtrl::GetConnectWidget()
e3e65dac 996{
ae0bdb01 997 return GTK_WIDGET(m_text);
6de97a3b 998}
e3e65dac 999
903f689b
RR
1000bool wxTextCtrl::IsOwnGtkWindow( GdkWindow *window )
1001{
ae0bdb01
RR
1002 if (m_windowStyle & wxTE_MULTILINE)
1003 return (window == GTK_TEXT(m_text)->text_area);
1004 else
1005 return (window == GTK_ENTRY(m_text)->text_area);
903f689b 1006}
e3e65dac 1007
bb69661b
VZ
1008// the font will change for subsequent text insertiongs
1009bool wxTextCtrl::SetFont( const wxFont &font )
868a2826 1010{
223d09f6 1011 wxCHECK_MSG( m_text != NULL, FALSE, wxT("invalid text ctrl") );
8bbe427f 1012
bb69661b
VZ
1013 if ( !wxWindowBase::SetFont(font) )
1014 {
1015 // font didn't change, nothing to do
1016 return FALSE;
1017 }
1018
1019 if ( m_windowStyle & wxTE_MULTILINE )
1020 {
01041145 1021 m_updateFont = TRUE;
bb69661b 1022
01041145 1023 ChangeFontGlobally();
bb69661b
VZ
1024 }
1025
1026 return TRUE;
58614078
RR
1027}
1028
01041145
VZ
1029void wxTextCtrl::ChangeFontGlobally()
1030{
1031 // this method is very inefficient and hence should be called as rarely as
1032 // possible!
1033 wxASSERT_MSG( (m_windowStyle & wxTE_MULTILINE) && m_updateFont,
1034 _T("shouldn't be called for single line controls") );
1035
1036 wxString value = GetValue();
1037 if ( !value.IsEmpty() )
1038 {
1039 Clear();
1040 AppendText(value);
1041
1042 m_updateFont = FALSE;
1043 }
1044}
1045
1046void wxTextCtrl::UpdateFontIfNeeded()
1047{
1048 if ( m_updateFont )
1049 ChangeFontGlobally();
1050}
1051
f03fc89f 1052bool wxTextCtrl::SetForegroundColour( const wxColour &WXUNUSED(colour) )
58614078 1053{
223d09f6 1054 wxCHECK_MSG( m_text != NULL, FALSE, wxT("invalid text ctrl") );
8bbe427f 1055
ae0bdb01 1056 // doesn't work
f03fc89f 1057 return FALSE;
868a2826 1058}
e3e65dac 1059
f03fc89f 1060bool wxTextCtrl::SetBackgroundColour( const wxColour &colour )
68dda785 1061{
223d09f6 1062 wxCHECK_MSG( m_text != NULL, FALSE, wxT("invalid text ctrl") );
a81258be 1063
ae0bdb01 1064 wxControl::SetBackgroundColour( colour );
3358d36e 1065
f03fc89f
VZ
1066 if (!m_widget->window)
1067 return FALSE;
8bbe427f 1068
ae0bdb01 1069 wxColour sysbg = wxSystemSettings::GetSystemColour( wxSYS_COLOUR_BTNFACE );
805dd538
VZ
1070 if (sysbg.Red() == colour.Red() &&
1071 sysbg.Green() == colour.Green() &&
ae0bdb01
RR
1072 sysbg.Blue() == colour.Blue())
1073 {
f03fc89f 1074 return FALSE; // FIXME or TRUE?
805dd538
VZ
1075 }
1076
f03fc89f
VZ
1077 if (!m_backgroundColour.Ok())
1078 return FALSE;
8bbe427f 1079
ae0bdb01
RR
1080 if (m_windowStyle & wxTE_MULTILINE)
1081 {
1082 GdkWindow *window = GTK_TEXT(m_text)->text_area;
f03fc89f
VZ
1083 if (!window)
1084 return FALSE;
ae0bdb01
RR
1085 m_backgroundColour.CalcPixel( gdk_window_get_colormap( window ) );
1086 gdk_window_set_background( window, m_backgroundColour.GetColor() );
1087 gdk_window_clear( window );
1088 }
f03fc89f
VZ
1089
1090 return TRUE;
58614078
RR
1091}
1092
1093void wxTextCtrl::ApplyWidgetStyle()
1094{
ae0bdb01
RR
1095 if (m_windowStyle & wxTE_MULTILINE)
1096 {
2830bf19 1097 // how ?
805dd538 1098 }
ae0bdb01
RR
1099 else
1100 {
1101 SetWidgetStyle();
1102 gtk_widget_set_style( m_text, m_widgetStyle );
1103 }
68dda785 1104}
f96aa4d9 1105
e702ff0f
JS
1106void wxTextCtrl::OnCut(wxCommandEvent& WXUNUSED(event))
1107{
1108 Cut();
1109}
1110
1111void wxTextCtrl::OnCopy(wxCommandEvent& WXUNUSED(event))
1112{
1113 Copy();
1114}
1115
1116void wxTextCtrl::OnPaste(wxCommandEvent& WXUNUSED(event))
1117{
1118 Paste();
1119}
1120
1121void wxTextCtrl::OnUndo(wxCommandEvent& WXUNUSED(event))
1122{
1123 Undo();
1124}
1125
1126void wxTextCtrl::OnRedo(wxCommandEvent& WXUNUSED(event))
1127{
1128 Redo();
1129}
1130
1131void wxTextCtrl::OnUpdateCut(wxUpdateUIEvent& event)
1132{
1133 event.Enable( CanCut() );
1134}
1135
1136void wxTextCtrl::OnUpdateCopy(wxUpdateUIEvent& event)
1137{
1138 event.Enable( CanCopy() );
1139}
1140
1141void wxTextCtrl::OnUpdatePaste(wxUpdateUIEvent& event)
1142{
1143 event.Enable( CanPaste() );
1144}
1145
1146void wxTextCtrl::OnUpdateUndo(wxUpdateUIEvent& event)
1147{
1148 event.Enable( CanUndo() );
1149}
1150
1151void wxTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event)
1152{
1153 event.Enable( CanRedo() );
1154}
65045edd
RR
1155
1156void wxTextCtrl::OnInternalIdle()
1157{
1158 wxCursor cursor = m_cursor;
1159 if (g_globalCursor.Ok()) cursor = g_globalCursor;
1160
307f16e8 1161 if (cursor.Ok())
65045edd 1162 {
65045edd 1163 GdkWindow *window = (GdkWindow*) NULL;
13111b2a 1164 if (HasFlag(wxTE_MULTILINE))
65045edd
RR
1165 window = GTK_TEXT(m_text)->text_area;
1166 else
1167 window = GTK_ENTRY(m_text)->text_area;
13111b2a 1168
65045edd
RR
1169 if (window)
1170 gdk_window_set_cursor( window, cursor.GetCursor() );
1171
1172 if (!g_globalCursor.Ok())
1173 cursor = *wxSTANDARD_CURSOR;
1174
1175 window = m_widget->window;
247e5b16 1176 if ((window) && !(GTK_WIDGET_NO_WINDOW(m_widget)))
65045edd
RR
1177 gdk_window_set_cursor( window, cursor.GetCursor() );
1178 }
26bf1ce0
VZ
1179
1180 UpdateWindowUI();
65045edd 1181}
f68586e5
VZ
1182
1183wxSize wxTextCtrl::DoGetBestSize() const
1184{
1185 // FIXME should be different for multi-line controls...
0279e844
RR
1186 wxSize ret( wxControl::DoGetBestSize() );
1187 return wxSize(80, ret.y);
f68586e5 1188}