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