]> git.saurik.com Git - wxWidgets.git/blame - src/motif/textctrl.cpp
added wxWS_EX_VALIDATE_RECURSIVELY
[wxWidgets.git] / src / motif / textctrl.cpp
CommitLineData
4bb6408c
JS
1/////////////////////////////////////////////////////////////////////////////
2// Name: textctrl.cpp
3// Purpose: wxTextCtrl
4// Author: Julian Smart
5// Modified by:
6// Created: 17/09/98
7// RCS-ID: $Id$
8// Copyright: (c) Julian Smart
65571936 9// Licence: wxWindows licence
4bb6408c
JS
10/////////////////////////////////////////////////////////////////////////////
11
66a007fb
VZ
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
14f355c2 20#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
66a007fb 21 #pragma implementation "textctrl.h"
4bb6408c
JS
22#endif
23
1248b41f
MB
24// For compilers that support precompilation, includes "wx.h".
25#include "wx/wxprec.h"
26
bcd055ae
JJ
27#ifdef __VMS
28#define XtParent XTPARENT
29#endif
30
d2c7015b
VZ
31#include "wx/defs.h"
32
4bb6408c
JS
33#include <sys/types.h>
34#include <sys/stat.h>
66a007fb 35#include <ctype.h>
4bb6408c
JS
36
37#include "wx/textctrl.h"
38#include "wx/settings.h"
39#include "wx/filefn.h"
40#include "wx/utils.h"
41
338dd992
JJ
42#ifdef __VMS__
43#pragma message disable nosimpint
44#endif
02e8b2f9 45#include <Xm/Text.h>
338dd992
JJ
46#ifdef __VMS__
47#pragma message enable nosimpint
48#endif
02e8b2f9
JS
49
50#include "wx/motif/private.h"
51
66a007fb
VZ
52// ----------------------------------------------------------------------------
53// private functions
54// ----------------------------------------------------------------------------
55
c27eab7e
VZ
56// helper: inserts the new text in the value of the text ctrl and returns the
57// result in place
58static void MergeChangesIntoString(wxString& value,
59 XmTextVerifyCallbackStruct *textStruct);
60
66a007fb
VZ
61// callbacks
62static void wxTextWindowChangedProc(Widget w, XtPointer clientData, XtPointer ptr);
63static void wxTextWindowModifyProc(Widget w, XtPointer clientData, XmTextVerifyCallbackStruct *cbs);
64static void wxTextWindowGainFocusProc(Widget w, XtPointer clientData, XmAnyCallbackStruct *cbs);
65static void wxTextWindowLoseFocusProc(Widget w, XtPointer clientData, XmAnyCallbackStruct *cbs);
66static void wxTextWindowActivateProc(Widget w, XtPointer clientData, XmAnyCallbackStruct *ptr);
02e8b2f9 67
66a007fb 68 IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl, wxControl)
4bb6408c 69
66a007fb
VZ
70 BEGIN_EVENT_TABLE(wxTextCtrl, wxControl)
71 EVT_DROP_FILES(wxTextCtrl::OnDropFiles)
72 EVT_CHAR(wxTextCtrl::OnChar)
e702ff0f
JS
73
74 EVT_MENU(wxID_CUT, wxTextCtrl::OnCut)
75 EVT_MENU(wxID_COPY, wxTextCtrl::OnCopy)
76 EVT_MENU(wxID_PASTE, wxTextCtrl::OnPaste)
77 EVT_MENU(wxID_UNDO, wxTextCtrl::OnUndo)
78 EVT_MENU(wxID_REDO, wxTextCtrl::OnRedo)
79
80 EVT_UPDATE_UI(wxID_CUT, wxTextCtrl::OnUpdateCut)
81 EVT_UPDATE_UI(wxID_COPY, wxTextCtrl::OnUpdateCopy)
82 EVT_UPDATE_UI(wxID_PASTE, wxTextCtrl::OnUpdatePaste)
83 EVT_UPDATE_UI(wxID_UNDO, wxTextCtrl::OnUpdateUndo)
84 EVT_UPDATE_UI(wxID_REDO, wxTextCtrl::OnUpdateRedo)
85
66a007fb 86 END_EVENT_TABLE()
4bb6408c 87
66a007fb
VZ
88// ============================================================================
89// implementation
90// ============================================================================
91
92// ----------------------------------------------------------------------------
93// wxTextCtrl
94// ----------------------------------------------------------------------------
95
4bb6408c
JS
96// Text item
97wxTextCtrl::wxTextCtrl()
4bb6408c 98{
02e8b2f9 99 m_tempCallbackStruct = (void*) NULL;
7d8268a1
WS
100 m_modified = false;
101 m_processedDefault = false;
4bb6408c
JS
102}
103
66a007fb
VZ
104bool wxTextCtrl::Create(wxWindow *parent,
105 wxWindowID id,
2d120f83
JS
106 const wxString& value,
107 const wxPoint& pos,
66a007fb
VZ
108 const wxSize& size,
109 long style,
2d120f83
JS
110 const wxValidator& validator,
111 const wxString& name)
4bb6408c 112{
e1aae528
MB
113 if( !CreateControl( parent, id, pos, size, style, validator, name ) )
114 return false;
115
02e8b2f9 116 m_tempCallbackStruct = (void*) NULL;
7d8268a1
WS
117 m_modified = false;
118 m_processedDefault = false;
66a007fb 119
e1aae528 120 m_backgroundColour = *wxWHITE;
66a007fb 121
02e8b2f9 122 Widget parentWidget = (Widget) parent->GetClientWidget();
66a007fb 123
02e8b2f9 124 bool wantHorizScrolling = ((m_windowStyle & wxHSCROLL) != 0);
66a007fb 125
02e8b2f9
JS
126 // If we don't have horizontal scrollbars, we want word wrap.
127 bool wantWordWrap = !wantHorizScrolling;
66a007fb 128
02e8b2f9
JS
129 if (m_windowStyle & wxTE_MULTILINE)
130 {
131 Arg args[2];
132 XtSetArg (args[0], XmNscrollHorizontal, wantHorizScrolling ? True : False);
133 XtSetArg (args[1], XmNwordWrap, wantWordWrap ? True : False);
66a007fb
VZ
134
135 m_mainWidget = (WXWidget) XmCreateScrolledText(parentWidget,
d3a80c92 136 wxConstCast(name.c_str(), char),
66a007fb
VZ
137 args, 2);
138
02e8b2f9 139 XtVaSetValues ((Widget) m_mainWidget,
66a007fb
VZ
140 XmNeditable, ((style & wxTE_READONLY) ? False : True),
141 XmNeditMode, XmMULTI_LINE_EDIT,
142 NULL);
02e8b2f9
JS
143 XtManageChild ((Widget) m_mainWidget);
144 }
145 else
146 {
66a007fb
VZ
147 m_mainWidget = (WXWidget)XtVaCreateManagedWidget
148 (
d3a80c92 149 wxConstCast(name.c_str(), char),
66a007fb
VZ
150 xmTextWidgetClass,
151 parentWidget,
152 NULL
153 );
154
17023320
MB
155 XtVaSetValues ((Widget) m_mainWidget,
156 XmNeditable, ((style & wxTE_READONLY) ? False : True),
157 NULL);
158
02e8b2f9
JS
159 // TODO: Is this relevant? What does it do?
160 int noCols = 2;
161 if (!value.IsNull() && (value.Length() > (unsigned int) noCols))
162 noCols = value.Length();
66a007fb
VZ
163 XtVaSetValues((Widget) m_mainWidget,
164 XmNcolumns, noCols,
165 NULL);
02e8b2f9 166 }
66a007fb
VZ
167
168 // remove border if asked for
169 if ( style & wxNO_BORDER )
170 {
171 XtVaSetValues((Widget)m_mainWidget,
172 XmNshadowThickness, 0,
173 NULL);
174 }
175
e1aae528 176 if ( !value.empty() )
b86de524 177 {
b86de524
MB
178 // do this instead... MB
179 //
180 XtVaSetValues( (Widget) m_mainWidget,
d3a80c92 181 XmNvalue, wxConstCast(value.c_str(), char),
b86de524 182 NULL);
b86de524 183 }
7d8268a1 184
66a007fb 185 // install callbacks
02e8b2f9 186 XtAddCallback((Widget) m_mainWidget, XmNvalueChangedCallback, (XtCallbackProc)wxTextWindowChangedProc, (XtPointer)this);
66a007fb 187
02e8b2f9 188 XtAddCallback((Widget) m_mainWidget, XmNmodifyVerifyCallback, (XtCallbackProc)wxTextWindowModifyProc, (XtPointer)this);
66a007fb 189
dfc54541 190 XtAddCallback((Widget) m_mainWidget, XmNactivateCallback, (XtCallbackProc)wxTextWindowActivateProc, (XtPointer)this);
66a007fb 191
02e8b2f9 192 XtAddCallback((Widget) m_mainWidget, XmNfocusCallback, (XtCallbackProc)wxTextWindowGainFocusProc, (XtPointer)this);
66a007fb 193
02e8b2f9 194 XtAddCallback((Widget) m_mainWidget, XmNlosingFocusCallback, (XtCallbackProc)wxTextWindowLoseFocusProc, (XtPointer)this);
66a007fb
VZ
195
196 // font
7d8268a1 197 ChangeFont(false);
66a007fb 198
e1aae528
MB
199 wxSize best = GetBestSize();
200 if( size.x != -1 ) best.x = size.x;
201 if( size.y != -1 ) best.y = size.y;
202
e1aae528
MB
203 AttachWidget (parent, m_mainWidget, (WXWidget) NULL,
204 pos.x, pos.y, best.x, best.y);
66a007fb 205
0d57be45 206 ChangeBackgroundColour();
66a007fb 207
7d8268a1 208 return true;
4bb6408c
JS
209}
210
02e8b2f9 211WXWidget wxTextCtrl::GetTopWidget() const
4bb6408c 212{
ccb234b4
MB
213 return IsMultiLine() ? (WXWidget)XtParent((Widget)m_mainWidget)
214 : m_mainWidget;
4bb6408c
JS
215}
216
02e8b2f9 217wxString wxTextCtrl::GetValue() const
4bb6408c 218{
c27eab7e
VZ
219 wxString str; // result
220
dfc54541 221 if (m_windowStyle & wxTE_PASSWORD)
c27eab7e
VZ
222 {
223 // the value is stored always in m_value because it can't be retrieved
224 // from the text control
225 str = m_value;
226 }
dfc54541
JS
227 else
228 {
c27eab7e 229 // just get the string from Motif
dfc54541 230 char *s = XmTextGetString ((Widget) m_mainWidget);
c27eab7e 231 if ( s )
dfc54541 232 {
c27eab7e 233 str = s;
dfc54541 234 XtFree (s);
2d120f83 235 }
c27eab7e
VZ
236 //else: return empty string
237
238 if ( m_tempCallbackStruct )
dfc54541 239 {
c27eab7e
VZ
240 // the string in the control isn't yet updated, can't use it as is
241 MergeChangesIntoString(str, (XmTextVerifyCallbackStruct *)
242 m_tempCallbackStruct);
dfc54541
JS
243 }
244 }
c27eab7e
VZ
245
246 return str;
4bb6408c
JS
247}
248
02e8b2f9 249void wxTextCtrl::SetValue(const wxString& value)
4bb6408c 250{
7d8268a1 251 m_inSetValue = true;
66a007fb 252
b86de524
MB
253 // do this instead... MB
254 //
ccb234b4
MB
255 // with (at least) OpenMotif 2.1 this causes a lot of flicker
256#if 0
b86de524 257 XtVaSetValues( (Widget) m_mainWidget,
d3a80c92 258 XmNvalue, wxConstCast(value.c_str(), char),
b86de524 259 NULL);
ccb234b4
MB
260#endif
261
262 Clear();
263 AppendText( value );
66a007fb 264
7d8268a1 265 m_inSetValue = false;
4bb6408c
JS
266}
267
268// Clipboard operations
269void wxTextCtrl::Copy()
270{
dfc54541 271 XmTextCopy((Widget) m_mainWidget, CurrentTime);
4bb6408c
JS
272}
273
274void wxTextCtrl::Cut()
275{
dfc54541 276 XmTextCut((Widget) m_mainWidget, CurrentTime);
4bb6408c
JS
277}
278
279void wxTextCtrl::Paste()
280{
dfc54541 281 XmTextPaste((Widget) m_mainWidget);
4bb6408c
JS
282}
283
ca8b28f2
JS
284bool wxTextCtrl::CanCopy() const
285{
286 // Can copy if there's a selection
287 long from, to;
288 GetSelection(& from, & to);
289 return (from != to) ;
290}
291
292bool wxTextCtrl::CanCut() const
293{
294 // Can cut if there's a selection
295 long from, to;
296 GetSelection(& from, & to);
dbf28859 297 return (from != to) && (IsEditable());
ca8b28f2
JS
298}
299
300bool wxTextCtrl::CanPaste() const
301{
302 return IsEditable() ;
303}
304
305// Undo/redo
306void wxTextCtrl::Undo()
307{
308 // Not possible in Motif
309}
310
311void wxTextCtrl::Redo()
312{
313 // Not possible in Motif
314}
315
316bool wxTextCtrl::CanUndo() const
317{
318 // No Undo in Motif
7d8268a1 319 return false;
ca8b28f2
JS
320}
321
322bool wxTextCtrl::CanRedo() const
323{
324 // No Redo in Motif
7d8268a1 325 return false;
ca8b28f2
JS
326}
327
328// If the return values from and to are the same, there is no
329// selection.
330void wxTextCtrl::GetSelection(long* from, long* to) const
331{
332 XmTextPosition left, right;
333
334 XmTextGetSelectionPosition((Widget) m_mainWidget, & left, & right);
335
336 *from = (long) left;
337 *to = (long) right;
338}
339
340bool wxTextCtrl::IsEditable() const
341{
342 return (XmTextGetEditable((Widget) m_mainWidget) != 0);
343}
344
4bb6408c
JS
345void wxTextCtrl::SetEditable(bool editable)
346{
dfc54541 347 XmTextSetEditable((Widget) m_mainWidget, (Boolean) editable);
4bb6408c
JS
348}
349
350void wxTextCtrl::SetInsertionPoint(long pos)
351{
dfc54541 352 XmTextSetInsertionPosition ((Widget) m_mainWidget, (XmTextPosition) pos);
4bb6408c
JS
353}
354
355void wxTextCtrl::SetInsertionPointEnd()
356{
7d8268a1 357 wxTextPos pos = GetLastPosition();
4bb6408c
JS
358 SetInsertionPoint(pos);
359}
360
361long wxTextCtrl::GetInsertionPoint() const
362{
dfc54541 363 return (long) XmTextGetInsertionPosition ((Widget) m_mainWidget);
4bb6408c
JS
364}
365
7d8268a1 366wxTextPos wxTextCtrl::GetLastPosition() const
4bb6408c 367{
dfc54541 368 return (long) XmTextGetLastPosition ((Widget) m_mainWidget);
4bb6408c
JS
369}
370
371void wxTextCtrl::Replace(long from, long to, const wxString& value)
372{
dfc54541 373 XmTextReplace ((Widget) m_mainWidget, (XmTextPosition) from, (XmTextPosition) to,
d3a80c92 374 wxConstCast(value.c_str(), char));
4bb6408c
JS
375}
376
377void wxTextCtrl::Remove(long from, long to)
378{
dfc54541 379 XmTextSetSelection ((Widget) m_mainWidget, (XmTextPosition) from, (XmTextPosition) to,
66a007fb 380 (Time) 0);
dfc54541 381 XmTextRemove ((Widget) m_mainWidget);
4bb6408c
JS
382}
383
384void wxTextCtrl::SetSelection(long from, long to)
385{
44d130a3
MB
386 if( to == -1 )
387 to = GetLastPosition();
388
dfc54541 389 XmTextSetSelection ((Widget) m_mainWidget, (XmTextPosition) from, (XmTextPosition) to,
66a007fb 390 (Time) 0);
4bb6408c
JS
391}
392
4bb6408c
JS
393void wxTextCtrl::WriteText(const wxString& text)
394{
96be256b 395 long textPosition = GetInsertionPoint() + text.length();
d3a80c92
MB
396 XmTextInsert ((Widget) m_mainWidget, GetInsertionPoint(),
397 wxConstCast(text.c_str(), char));
dfc54541
JS
398 XtVaSetValues ((Widget) m_mainWidget, XmNcursorPosition, textPosition, NULL);
399 SetInsertionPoint(textPosition);
400 XmTextShowPosition ((Widget) m_mainWidget, textPosition);
7d8268a1 401 m_modified = true;
4bb6408c
JS
402}
403
4dba84be
JS
404void wxTextCtrl::AppendText(const wxString& text)
405{
7d8268a1 406 wxTextPos textPosition = GetLastPosition() + text.length();
d3a80c92
MB
407 XmTextInsert ((Widget) m_mainWidget, GetLastPosition(),
408 wxConstCast(text.c_str(), char));
4dba84be
JS
409 XtVaSetValues ((Widget) m_mainWidget, XmNcursorPosition, textPosition, NULL);
410 SetInsertionPoint(textPosition);
411 XmTextShowPosition ((Widget) m_mainWidget, textPosition);
7d8268a1 412 m_modified = true;
4dba84be
JS
413}
414
4bb6408c
JS
415void wxTextCtrl::Clear()
416{
02e8b2f9 417 XmTextSetString ((Widget) m_mainWidget, "");
7d8268a1 418 m_modified = false;
4bb6408c
JS
419}
420
421bool wxTextCtrl::IsModified() const
422{
02e8b2f9 423 return m_modified;
4bb6408c
JS
424}
425
3a9fa0d6
VZ
426// Makes modified or unmodified
427void wxTextCtrl::MarkDirty()
428{
7d8268a1 429 m_modified = true;
3a9fa0d6
VZ
430}
431
4bb6408c
JS
432void wxTextCtrl::DiscardEdits()
433{
7d8268a1 434 m_modified = false;
4bb6408c
JS
435}
436
437int wxTextCtrl::GetNumberOfLines() const
438{
dfc54541
JS
439 // HIDEOUSLY inefficient, but we have no choice.
440 char *s = XmTextGetString ((Widget) m_mainWidget);
441 if (s)
442 {
2d120f83
JS
443 long i = 0;
444 int currentLine = 0;
7d8268a1 445 bool finished = false;
2d120f83
JS
446 while (!finished)
447 {
448 int ch = s[i];
449 if (ch == '\n')
450 {
451 currentLine++;
452 i++;
453 }
454 else if (ch == 0)
455 {
7d8268a1 456 finished = true;
2d120f83
JS
457 }
458 else
459 i++;
460 }
66a007fb 461
2d120f83
JS
462 XtFree (s);
463 return currentLine;
dfc54541 464 }
4bb6408c
JS
465 return 0;
466}
467
468long wxTextCtrl::XYToPosition(long x, long y) const
469{
dfc54541 470/* It seems, that there is a bug in some versions of the Motif library,
2d120f83
JS
471 so the original wxWin-Code doesn't work. */
472 /*
473 Widget textWidget = (Widget) handle;
474 return (long) XmTextXYToPos (textWidget, (Position) x, (Position) y);
475 */
dfc54541
JS
476 /* Now a little workaround: */
477 long r=0;
478 for (int i=0; i<y; i++) r+=(GetLineLength(i)+1);
66a007fb 479 return r+x;
4bb6408c
JS
480}
481
813c20a6 482bool wxTextCtrl::PositionToXY(long pos, long *x, long *y) const
4bb6408c 483{
dfc54541
JS
484 Position xx, yy;
485 XmTextPosToXY((Widget) m_mainWidget, pos, &xx, &yy);
813c20a6
VZ
486 if ( x )
487 *x = xx;
488 if ( y )
489 *y = yy;
490
7d8268a1 491 return true;
4bb6408c
JS
492}
493
494void wxTextCtrl::ShowPosition(long pos)
495{
dfc54541 496 XmTextShowPosition ((Widget) m_mainWidget, (XmTextPosition) pos);
4bb6408c
JS
497}
498
499int wxTextCtrl::GetLineLength(long lineNo) const
500{
dfc54541
JS
501 wxString str = GetLineText (lineNo);
502 return (int) str.Length();
4bb6408c
JS
503}
504
505wxString wxTextCtrl::GetLineText(long lineNo) const
506{
dfc54541
JS
507 // HIDEOUSLY inefficient, but we have no choice.
508 char *s = XmTextGetString ((Widget) m_mainWidget);
66a007fb 509
dfc54541
JS
510 if (s)
511 {
512 wxString buf("");
513 long i;
514 int currentLine = 0;
515 for (i = 0; currentLine != lineNo && s[i]; i++ )
2d120f83
JS
516 if (s[i] == '\n')
517 currentLine++;
518 // Now get the text
519 int j;
520 for (j = 0; s[i] && s[i] != '\n'; i++, j++ )
521 buf += s[i];
66a007fb 522
2d120f83
JS
523 XtFree(s);
524 return buf;
525 }
526 else
527 return wxEmptyString;
4bb6408c
JS
528}
529
530/*
2d120f83
JS
531* Text item
532*/
533
4bb6408c
JS
534void wxTextCtrl::Command(wxCommandEvent & event)
535{
536 SetValue (event.GetString());
537 ProcessCommand (event);
538}
539
540void wxTextCtrl::OnDropFiles(wxDropFilesEvent& event)
541{
542 // By default, load the first file into the text window.
543 if (event.GetNumberOfFiles() > 0)
544 {
545 LoadFile(event.GetFiles()[0]);
546 }
547}
548
02e8b2f9
JS
549void wxTextCtrl::OnChar(wxKeyEvent& event)
550{
2d120f83
JS
551 // Indicates that we should generate a normal command, because
552 // we're letting default behaviour happen (otherwise it's vetoed
553 // by virtue of overriding OnChar)
7d8268a1 554 m_processedDefault = true;
66a007fb 555
2d120f83 556 if (m_tempCallbackStruct)
02e8b2f9 557 {
2d120f83
JS
558 XmTextVerifyCallbackStruct *textStruct =
559 (XmTextVerifyCallbackStruct *) m_tempCallbackStruct;
560 textStruct->doit = True;
561 if (isascii(event.m_keyCode) && (textStruct->text->length == 1))
562 {
563 textStruct->text->ptr[0] = ((event.m_keyCode == WXK_RETURN) ? 10 : event.m_keyCode);
564 }
02e8b2f9 565 }
02e8b2f9
JS
566}
567
4b5f3fe6 568void wxTextCtrl::ChangeFont(bool keepOriginalSize)
0d57be45 569{
4b5f3fe6 570 wxWindow::ChangeFont(keepOriginalSize);
0d57be45
JS
571}
572
573void wxTextCtrl::ChangeBackgroundColour()
574{
321db4b6 575 wxWindow::ChangeBackgroundColour();
66a007fb 576
94b49b93 577 /* TODO: should scrollbars be affected? Should probably have separate
2d120f83
JS
578 * function to change them (by default, taken from wxSystemSettings)
579 */
94b49b93
JS
580 if (m_windowStyle & wxTE_MULTILINE)
581 {
582 Widget parent = XtParent ((Widget) m_mainWidget);
583 Widget hsb, vsb;
66a007fb 584
94b49b93 585 XtVaGetValues (parent,
2d120f83
JS
586 XmNhorizontalScrollBar, &hsb,
587 XmNverticalScrollBar, &vsb,
588 NULL);
a756f210 589 wxColour backgroundColour = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
94b49b93 590 if (hsb)
7d8268a1 591 wxDoChangeBackgroundColour((WXWidget) hsb, backgroundColour, true);
94b49b93 592 if (vsb)
7d8268a1 593 wxDoChangeBackgroundColour((WXWidget) vsb, backgroundColour, true);
66a007fb 594
1119b899 595 // MBN: why change parent background?
7d8268a1 596 // DoChangeBackgroundColour((WXWidget) parent, m_backgroundColour, true);
94b49b93 597 }
0d57be45
JS
598}
599
600void wxTextCtrl::ChangeForegroundColour()
601{
321db4b6 602 wxWindow::ChangeForegroundColour();
66a007fb 603
94b49b93
JS
604 if (m_windowStyle & wxTE_MULTILINE)
605 {
606 Widget parent = XtParent ((Widget) m_mainWidget);
607 Widget hsb, vsb;
66a007fb 608
94b49b93 609 XtVaGetValues (parent,
2d120f83
JS
610 XmNhorizontalScrollBar, &hsb,
611 XmNverticalScrollBar, &vsb,
612 NULL);
66a007fb 613
2d120f83
JS
614 /* TODO: should scrollbars be affected? Should probably have separate
615 * function to change them (by default, taken from wxSystemSettings)
616 if (hsb)
94b49b93 617 DoChangeForegroundColour((WXWidget) hsb, m_foregroundColour);
2d120f83 618 if (vsb)
94b49b93 619 DoChangeForegroundColour((WXWidget) vsb, m_foregroundColour);
2d120f83 620 */
a8680e3e 621 wxDoChangeForegroundColour((WXWidget) parent, m_foregroundColour);
94b49b93 622 }
0d57be45
JS
623}
624
c27eab7e
VZ
625void wxTextCtrl::DoSendEvents(void *wxcbs, long keycode)
626{
627 // we're in process of updating the text control
628 m_tempCallbackStruct = wxcbs;
629
630 XmTextVerifyCallbackStruct *cbs = (XmTextVerifyCallbackStruct *)wxcbs;
631
632 wxKeyEvent event (wxEVT_CHAR);
633 event.SetId(GetId());
634 event.m_keyCode = keycode;
635 event.SetEventObject(this);
636
637 // Only if wxTextCtrl::OnChar is called will this be set to True (and
638 // the character passed through)
639 cbs->doit = False;
640
641 GetEventHandler()->ProcessEvent(event);
642
643 if ( !InSetValue() && m_processedDefault )
644 {
645 // Can generate a command
646 wxCommandEvent commandEvent(wxEVT_COMMAND_TEXT_UPDATED, GetId());
647 commandEvent.SetEventObject(this);
648 ProcessCommand(commandEvent);
649 }
650
651 // do it after the (user) event handlers processed the events because
652 // otherwise GetValue() would return incorrect (not yet updated value)
653 m_tempCallbackStruct = NULL;
654}
655
e1aae528
MB
656wxSize wxDoGetSingleTextCtrlBestSize( Widget textWidget,
657 const wxWindow* window )
658{
659 Dimension xmargin, ymargin, highlight, shadow;
660 char* value;
661
662 XtVaGetValues( textWidget,
663 XmNmarginWidth, &xmargin,
664 XmNmarginHeight, &ymargin,
665 XmNvalue, &value,
666 XmNhighlightThickness, &highlight,
667 XmNshadowThickness, &shadow,
668 NULL );
1119b899 669
e1aae528
MB
670 if( !value )
671 value = "|";
672
673 int x, y;
674 window->GetTextExtent( value, &x, &y );
675
1119b899
MB
676 if( x < 100 ) x = 100;
677
e1aae528
MB
678 return wxSize( x + 2 * xmargin + 2 * highlight + 2 * shadow,
679 // MBN: +2 necessary: Lesstif bug or mine?
7d8268a1 680 y + 2 * ymargin + 2 * highlight + 2 * shadow + 2 );
e1aae528
MB
681}
682
683wxSize wxTextCtrl::DoGetBestSize() const
684{
685 if( IsSingleLine() )
686 return wxDoGetSingleTextCtrlBestSize( (Widget)m_mainWidget, this );
687 else
688 return wxWindow::DoGetBestSize();
689}
690
c27eab7e
VZ
691// ----------------------------------------------------------------------------
692// helpers and Motif callbacks
693// ----------------------------------------------------------------------------
694
695static void MergeChangesIntoString(wxString& value,
696 XmTextVerifyCallbackStruct *cbs)
697{
698 /* _sm_
699 * At least on my system (SunOS 4.1.3 + Motif 1.2), you need to think of
700 * every event as a replace event. cbs->text->ptr gives the replacement
701 * text, cbs->startPos gives the index of the first char affected by the
702 * replace, and cbs->endPos gives the index one more than the last char
703 * affected by the replace (startPos == endPos implies an empty range).
704 * Hence, a deletion is represented by replacing all input text with a
705 * blank string ("", *not* NULL!). A simple insertion that does not
706 * overwrite any text has startPos == endPos.
707 */
708
709 if ( !value )
710 {
711 // easy case: the ol value was empty
712 value = cbs->text->ptr;
713 }
714 else
715 {
716 // merge the changes into the value
717 const char * const passwd = value;
718 int len = value.length();
719
44d130a3
MB
720 len += ( cbs->text->ptr ?
721 strlen(cbs->text->ptr) :
722 0 ) + 1; // + new text (if any) + NUL
c27eab7e
VZ
723 len -= cbs->endPos - cbs->startPos; // - text from affected region.
724
725 char * newS = new char [len];
726 char * dest = newS,
727 * insert = cbs->text->ptr;
728
729 // Copy (old) text from passwd, up to the start posn of the change.
730 int i;
731 const char * p = passwd;
732 for (i = 0; i < cbs->startPos; ++i)
733 *dest++ = *p++;
734
735 // Copy the text to be inserted).
44d130a3
MB
736 if (insert)
737 while (*insert)
738 *dest++ = *insert++;
c27eab7e
VZ
739
740 // Finally, copy into newS any remaining text from passwd[endPos] on.
741 for (p = passwd + cbs->endPos; *p; )
742 *dest++ = *p++;
743 *dest = 0;
744
745 value = newS;
746
747 delete[] newS;
748 }
749}
750
751static void
af111fc3 752wxTextWindowChangedProc (Widget w, XtPointer clientData, XtPointer WXUNUSED(ptr))
02e8b2f9 753{
2d120f83
JS
754 if (!wxGetWindowFromTable(w))
755 // Widget has been deleted!
756 return;
66a007fb 757
2d120f83 758 wxTextCtrl *tw = (wxTextCtrl *) clientData;
7d8268a1 759 tw->SetModified(true);
02e8b2f9
JS
760}
761
66a007fb 762static void
af111fc3 763wxTextWindowModifyProc (Widget WXUNUSED(w), XtPointer clientData, XmTextVerifyCallbackStruct *cbs)
02e8b2f9 764{
dfc54541 765 wxTextCtrl *tw = (wxTextCtrl *) clientData;
7d8268a1 766 tw->m_processedDefault = false;
66a007fb 767
c27eab7e
VZ
768 // First, do some stuff if it's a password control: in this case, we need
769 // to store the string inside the class because GetValue() can't retrieve
770 // it from the text ctrl. We do *not* do it in other circumstances because
771 // it would double the amount of memory needed.
66a007fb 772
c27eab7e 773 if ( tw->GetWindowStyleFlag() & wxTE_PASSWORD )
dfc54541 774 {
c27eab7e 775 MergeChangesIntoString(tw->m_value, cbs);
66a007fb 776
c27eab7e 777 if ( cbs->text->length > 0 )
dfc54541
JS
778 {
779 int i;
780 for (i = 0; i < cbs->text->length; ++i)
781 cbs->text->ptr[i] = '*';
c27eab7e 782 cbs->text->ptr[i] = '\0';
dfc54541
JS
783 }
784 }
66a007fb 785
ccb234b4
MB
786 if(tw->InSetValue())
787 return;
788
c27eab7e
VZ
789 // If we're already within an OnChar, return: probably a programmatic
790 // insertion.
dfc54541
JS
791 if (tw->m_tempCallbackStruct)
792 return;
66a007fb 793
dfc54541
JS
794 // Check for a backspace
795 if (cbs->startPos == (cbs->currInsert - 1))
796 {
c27eab7e 797 tw->DoSendEvents((void *)cbs, WXK_DELETE);
66a007fb 798
dfc54541
JS
799 return;
800 }
66a007fb 801
c27eab7e 802 // Pasting operation: let it through without calling OnChar
dfc54541
JS
803 if (cbs->text->length > 1)
804 return;
66a007fb 805
dfc54541
JS
806 // Something other than text
807 if (cbs->text->ptr == NULL)
808 return;
66a007fb 809
c27eab7e
VZ
810 // normal key press
811 char ch = cbs->text->ptr[0];
812 tw->DoSendEvents((void *)cbs, ch == '\n' ? '\r' : ch);
02e8b2f9
JS
813}
814
66a007fb 815static void
af111fc3 816wxTextWindowGainFocusProc (Widget w, XtPointer clientData, XmAnyCallbackStruct *WXUNUSED(cbs))
02e8b2f9 817{
2d120f83
JS
818 if (!wxGetWindowFromTable(w))
819 return;
66a007fb 820
2d120f83
JS
821 wxTextCtrl *tw = (wxTextCtrl *) clientData;
822 wxFocusEvent event(wxEVT_SET_FOCUS, tw->GetId());
823 event.SetEventObject(tw);
824 tw->GetEventHandler()->ProcessEvent(event);
02e8b2f9
JS
825}
826
66a007fb 827static void
af111fc3 828wxTextWindowLoseFocusProc (Widget w, XtPointer clientData, XmAnyCallbackStruct *WXUNUSED(cbs))
02e8b2f9 829{
2d120f83
JS
830 if (!wxGetWindowFromTable(w))
831 return;
66a007fb 832
2d120f83
JS
833 wxTextCtrl *tw = (wxTextCtrl *) clientData;
834 wxFocusEvent event(wxEVT_KILL_FOCUS, tw->GetId());
835 event.SetEventObject(tw);
836 tw->GetEventHandler()->ProcessEvent(event);
02e8b2f9 837}
dfc54541
JS
838
839static void wxTextWindowActivateProc(Widget w, XtPointer clientData,
af111fc3 840 XmAnyCallbackStruct *WXUNUSED(ptr))
dfc54541 841{
2d120f83
JS
842 if (!wxGetWindowFromTable(w))
843 return;
66a007fb 844
2d120f83 845 wxTextCtrl *tw = (wxTextCtrl *) clientData;
66a007fb 846
2d120f83
JS
847 if (tw->InSetValue())
848 return;
66a007fb 849
2d120f83
JS
850 wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER);
851 event.SetId(tw->GetId());
852 event.SetEventObject(tw);
853 tw->ProcessCommand(event);
dfc54541 854}
c27eab7e 855
e702ff0f
JS
856void wxTextCtrl::OnCut(wxCommandEvent& WXUNUSED(event))
857{
858 Cut();
859}
860
861void wxTextCtrl::OnCopy(wxCommandEvent& WXUNUSED(event))
862{
863 Copy();
864}
865
866void wxTextCtrl::OnPaste(wxCommandEvent& WXUNUSED(event))
867{
868 Paste();
869}
870
871void wxTextCtrl::OnUndo(wxCommandEvent& WXUNUSED(event))
872{
873 Undo();
874}
875
876void wxTextCtrl::OnRedo(wxCommandEvent& WXUNUSED(event))
877{
878 Redo();
879}
880
881void wxTextCtrl::OnUpdateCut(wxUpdateUIEvent& event)
882{
883 event.Enable( CanCut() );
884}
885
886void wxTextCtrl::OnUpdateCopy(wxUpdateUIEvent& event)
887{
888 event.Enable( CanCopy() );
889}
890
891void wxTextCtrl::OnUpdatePaste(wxUpdateUIEvent& event)
892{
893 event.Enable( CanPaste() );
894}
895
896void wxTextCtrl::OnUpdateUndo(wxUpdateUIEvent& event)
897{
898 event.Enable( CanUndo() );
899}
900
901void wxTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event)
902{
903 event.Enable( CanRedo() );
904}