]> git.saurik.com Git - wxWidgets.git/blame - src/motif/textctrl.cpp
Attempt at fixing the broken printer dialog. *Lots* of unitialised variables!
[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
66a007fb 9// Licence: wxWindows licence
4bb6408c
JS
10/////////////////////////////////////////////////////////////////////////////
11
66a007fb
VZ
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
4bb6408c 20#ifdef __GNUG__
66a007fb 21 #pragma implementation "textctrl.h"
4bb6408c
JS
22#endif
23
24#include <sys/types.h>
25#include <sys/stat.h>
26#include <fstream.h>
66a007fb 27#include <ctype.h>
4bb6408c
JS
28
29#include "wx/textctrl.h"
30#include "wx/settings.h"
31#include "wx/filefn.h"
32#include "wx/utils.h"
33
02e8b2f9 34#include <Xm/Text.h>
02e8b2f9
JS
35
36#include "wx/motif/private.h"
37
66a007fb
VZ
38// ----------------------------------------------------------------------------
39// private functions
40// ----------------------------------------------------------------------------
41
c27eab7e
VZ
42// helper: inserts the new text in the value of the text ctrl and returns the
43// result in place
44static void MergeChangesIntoString(wxString& value,
45 XmTextVerifyCallbackStruct *textStruct);
46
66a007fb
VZ
47// callbacks
48static void wxTextWindowChangedProc(Widget w, XtPointer clientData, XtPointer ptr);
49static void wxTextWindowModifyProc(Widget w, XtPointer clientData, XmTextVerifyCallbackStruct *cbs);
50static void wxTextWindowGainFocusProc(Widget w, XtPointer clientData, XmAnyCallbackStruct *cbs);
51static void wxTextWindowLoseFocusProc(Widget w, XtPointer clientData, XmAnyCallbackStruct *cbs);
52static void wxTextWindowActivateProc(Widget w, XtPointer clientData, XmAnyCallbackStruct *ptr);
02e8b2f9 53
4bb6408c 54#if !USE_SHARED_LIBRARY
66a007fb 55 IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl, wxControl)
4bb6408c 56
66a007fb
VZ
57 BEGIN_EVENT_TABLE(wxTextCtrl, wxControl)
58 EVT_DROP_FILES(wxTextCtrl::OnDropFiles)
59 EVT_CHAR(wxTextCtrl::OnChar)
60 END_EVENT_TABLE()
4bb6408c
JS
61#endif
62
66a007fb
VZ
63// ============================================================================
64// implementation
65// ============================================================================
66
67// ----------------------------------------------------------------------------
68// wxTextCtrl
69// ----------------------------------------------------------------------------
70
4bb6408c
JS
71// Text item
72wxTextCtrl::wxTextCtrl()
73#ifndef NO_TEXT_WINDOW_STREAM
66a007fb 74 : streambuf()
4bb6408c
JS
75#endif
76{
02e8b2f9
JS
77 m_tempCallbackStruct = (void*) NULL;
78 m_modified = FALSE;
dfc54541 79 m_processedDefault = FALSE;
4bb6408c
JS
80}
81
66a007fb
VZ
82bool wxTextCtrl::Create(wxWindow *parent,
83 wxWindowID id,
2d120f83
JS
84 const wxString& value,
85 const wxPoint& pos,
66a007fb
VZ
86 const wxSize& size,
87 long style,
2d120f83
JS
88 const wxValidator& validator,
89 const wxString& name)
4bb6408c 90{
02e8b2f9
JS
91 m_tempCallbackStruct = (void*) NULL;
92 m_modified = FALSE;
dfc54541 93 m_processedDefault = FALSE;
94b49b93
JS
94 // m_backgroundColour = parent->GetBackgroundColour();
95 m_backgroundColour = * wxWHITE;
0d57be45 96 m_foregroundColour = parent->GetForegroundColour();
66a007fb 97
4bb6408c
JS
98 SetName(name);
99 SetValidator(validator);
66a007fb
VZ
100 if (parent)
101 parent->AddChild(this);
102
4bb6408c 103 m_windowStyle = style;
66a007fb 104
4bb6408c 105 if ( id == -1 )
2d120f83 106 m_windowId = (int)NewControlId();
4bb6408c 107 else
2d120f83 108 m_windowId = id;
66a007fb 109
02e8b2f9 110 Widget parentWidget = (Widget) parent->GetClientWidget();
66a007fb 111
02e8b2f9 112 bool wantHorizScrolling = ((m_windowStyle & wxHSCROLL) != 0);
66a007fb 113
02e8b2f9
JS
114 // If we don't have horizontal scrollbars, we want word wrap.
115 bool wantWordWrap = !wantHorizScrolling;
66a007fb 116
02e8b2f9
JS
117 if (m_windowStyle & wxTE_MULTILINE)
118 {
119 Arg args[2];
120 XtSetArg (args[0], XmNscrollHorizontal, wantHorizScrolling ? True : False);
121 XtSetArg (args[1], XmNwordWrap, wantWordWrap ? True : False);
66a007fb
VZ
122
123 m_mainWidget = (WXWidget) XmCreateScrolledText(parentWidget,
124 (char*)name.c_str(),
125 args, 2);
126
02e8b2f9 127 XtVaSetValues ((Widget) m_mainWidget,
66a007fb
VZ
128 XmNeditable, ((style & wxTE_READONLY) ? False : True),
129 XmNeditMode, XmMULTI_LINE_EDIT,
130 NULL);
02e8b2f9
JS
131 XtManageChild ((Widget) m_mainWidget);
132 }
133 else
134 {
66a007fb
VZ
135 m_mainWidget = (WXWidget)XtVaCreateManagedWidget
136 (
137 (char*)name.c_str(),
138 xmTextWidgetClass,
139 parentWidget,
140 NULL
141 );
142
02e8b2f9
JS
143 // TODO: Is this relevant? What does it do?
144 int noCols = 2;
145 if (!value.IsNull() && (value.Length() > (unsigned int) noCols))
146 noCols = value.Length();
66a007fb
VZ
147 XtVaSetValues((Widget) m_mainWidget,
148 XmNcolumns, noCols,
149 NULL);
02e8b2f9 150 }
66a007fb
VZ
151
152 // remove border if asked for
153 if ( style & wxNO_BORDER )
154 {
155 XtVaSetValues((Widget)m_mainWidget,
156 XmNshadowThickness, 0,
157 NULL);
158 }
159
160 if ( !!value )
161 XmTextSetString ((Widget) m_mainWidget, (char*)value.c_str());
162
163 // install callbacks
02e8b2f9 164 XtAddCallback((Widget) m_mainWidget, XmNvalueChangedCallback, (XtCallbackProc)wxTextWindowChangedProc, (XtPointer)this);
66a007fb 165
02e8b2f9 166 XtAddCallback((Widget) m_mainWidget, XmNmodifyVerifyCallback, (XtCallbackProc)wxTextWindowModifyProc, (XtPointer)this);
66a007fb 167
dfc54541 168 XtAddCallback((Widget) m_mainWidget, XmNactivateCallback, (XtCallbackProc)wxTextWindowActivateProc, (XtPointer)this);
66a007fb 169
02e8b2f9 170 XtAddCallback((Widget) m_mainWidget, XmNfocusCallback, (XtCallbackProc)wxTextWindowGainFocusProc, (XtPointer)this);
66a007fb 171
02e8b2f9 172 XtAddCallback((Widget) m_mainWidget, XmNlosingFocusCallback, (XtCallbackProc)wxTextWindowLoseFocusProc, (XtPointer)this);
66a007fb
VZ
173
174 // font
4b5f3fe6
JS
175 m_windowFont = parent->GetFont();
176 ChangeFont(FALSE);
66a007fb 177
02e8b2f9
JS
178 SetCanAddEventHandler(TRUE);
179 AttachWidget (parent, m_mainWidget, (WXWidget) NULL, pos.x, pos.y, size.x, size.y);
66a007fb 180
0d57be45 181 ChangeBackgroundColour();
66a007fb 182
4bb6408c
JS
183 return TRUE;
184}
185
02e8b2f9 186WXWidget wxTextCtrl::GetTopWidget() const
4bb6408c 187{
dfc54541 188 return ((m_windowStyle & wxTE_MULTILINE) ? (WXWidget) XtParent((Widget) m_mainWidget) : m_mainWidget);
4bb6408c
JS
189}
190
02e8b2f9 191wxString wxTextCtrl::GetValue() const
4bb6408c 192{
c27eab7e
VZ
193 wxString str; // result
194
dfc54541 195 if (m_windowStyle & wxTE_PASSWORD)
c27eab7e
VZ
196 {
197 // the value is stored always in m_value because it can't be retrieved
198 // from the text control
199 str = m_value;
200 }
dfc54541
JS
201 else
202 {
c27eab7e 203 // just get the string from Motif
dfc54541 204 char *s = XmTextGetString ((Widget) m_mainWidget);
c27eab7e 205 if ( s )
dfc54541 206 {
c27eab7e 207 str = s;
dfc54541 208 XtFree (s);
2d120f83 209 }
c27eab7e
VZ
210 //else: return empty string
211
212 if ( m_tempCallbackStruct )
dfc54541 213 {
c27eab7e
VZ
214 // the string in the control isn't yet updated, can't use it as is
215 MergeChangesIntoString(str, (XmTextVerifyCallbackStruct *)
216 m_tempCallbackStruct);
dfc54541
JS
217 }
218 }
c27eab7e
VZ
219
220 return str;
4bb6408c
JS
221}
222
02e8b2f9 223void wxTextCtrl::SetValue(const wxString& value)
4bb6408c 224{
dfc54541 225 m_inSetValue = TRUE;
66a007fb
VZ
226
227 XmTextSetString ((Widget) m_mainWidget, (char*)value.c_str());
228
dfc54541 229 m_inSetValue = FALSE;
4bb6408c
JS
230}
231
232// Clipboard operations
233void wxTextCtrl::Copy()
234{
dfc54541 235 XmTextCopy((Widget) m_mainWidget, CurrentTime);
4bb6408c
JS
236}
237
238void wxTextCtrl::Cut()
239{
dfc54541 240 XmTextCut((Widget) m_mainWidget, CurrentTime);
4bb6408c
JS
241}
242
243void wxTextCtrl::Paste()
244{
dfc54541 245 XmTextPaste((Widget) m_mainWidget);
4bb6408c
JS
246}
247
248void wxTextCtrl::SetEditable(bool editable)
249{
dfc54541 250 XmTextSetEditable((Widget) m_mainWidget, (Boolean) editable);
4bb6408c
JS
251}
252
253void wxTextCtrl::SetInsertionPoint(long pos)
254{
dfc54541 255 XmTextSetInsertionPosition ((Widget) m_mainWidget, (XmTextPosition) pos);
4bb6408c
JS
256}
257
258void wxTextCtrl::SetInsertionPointEnd()
259{
260 long pos = GetLastPosition();
261 SetInsertionPoint(pos);
262}
263
264long wxTextCtrl::GetInsertionPoint() const
265{
dfc54541 266 return (long) XmTextGetInsertionPosition ((Widget) m_mainWidget);
4bb6408c
JS
267}
268
269long wxTextCtrl::GetLastPosition() const
270{
dfc54541 271 return (long) XmTextGetLastPosition ((Widget) m_mainWidget);
4bb6408c
JS
272}
273
274void wxTextCtrl::Replace(long from, long to, const wxString& value)
275{
dfc54541 276 XmTextReplace ((Widget) m_mainWidget, (XmTextPosition) from, (XmTextPosition) to,
2d120f83 277 (char*) (const char*) value);
4bb6408c
JS
278}
279
280void wxTextCtrl::Remove(long from, long to)
281{
dfc54541 282 XmTextSetSelection ((Widget) m_mainWidget, (XmTextPosition) from, (XmTextPosition) to,
66a007fb 283 (Time) 0);
dfc54541 284 XmTextRemove ((Widget) m_mainWidget);
4bb6408c
JS
285}
286
287void wxTextCtrl::SetSelection(long from, long to)
288{
dfc54541 289 XmTextSetSelection ((Widget) m_mainWidget, (XmTextPosition) from, (XmTextPosition) to,
66a007fb 290 (Time) 0);
4bb6408c
JS
291}
292
293bool wxTextCtrl::LoadFile(const wxString& file)
294{
295 if (!wxFileExists(file))
296 return FALSE;
66a007fb 297
4bb6408c 298 m_fileName = file;
66a007fb 299
4bb6408c 300 Clear();
66a007fb 301
1a3ac83f
JS
302 Widget textWidget = (Widget) m_mainWidget;
303 FILE *fp;
66a007fb 304
1a3ac83f
JS
305 struct stat statb;
306 if ((stat ((char*) (const char*) file, &statb) == -1) || (statb.st_mode & S_IFMT) != S_IFREG ||
2d120f83 307 !(fp = fopen ((char*) (const char*) file, "r")))
4bb6408c 308 {
2d120f83 309 return FALSE;
1a3ac83f
JS
310 }
311 else
312 {
2d120f83
JS
313 long len = statb.st_size;
314 char *text;
315 if (!(text = XtMalloc ((unsigned) (len + 1))))
316 {
317 fclose (fp);
318 return FALSE;
319 }
320 if (fread (text, sizeof (char), len, fp) != (size_t) len)
321 {
322 }
323 fclose (fp);
66a007fb 324
2d120f83
JS
325 text[len] = 0;
326 XmTextSetString (textWidget, text);
327 // m_textPosition = len;
328 XtFree (text);
329 m_modified = FALSE;
330 return TRUE;
4bb6408c 331 }
4bb6408c
JS
332}
333
334// If file is null, try saved file name first
335// Returns TRUE if succeeds.
336bool wxTextCtrl::SaveFile(const wxString& file)
337{
338 wxString theFile(file);
339 if (theFile == "")
340 theFile = m_fileName;
341 if (theFile == "")
342 return FALSE;
343 m_fileName = theFile;
66a007fb 344
2d120f83
JS
345 Widget textWidget = (Widget) m_mainWidget;
346 FILE *fp;
66a007fb 347
2d120f83 348 if (!(fp = fopen ((char*) (const char*) theFile, "w")))
1a3ac83f 349 {
2d120f83 350 return FALSE;
1a3ac83f 351 }
2d120f83 352 else
1a3ac83f 353 {
2d120f83
JS
354 char *text = XmTextGetString (textWidget);
355 long len = XmTextGetLastPosition (textWidget);
66a007fb 356
2d120f83
JS
357 if (fwrite (text, sizeof (char), len, fp) != (size_t) len)
358 {
359 // Did not write whole file
360 }
361 // Make sure newline terminates the file
362 if (text[len - 1] != '\n')
363 fputc ('\n', fp);
66a007fb 364
2d120f83
JS
365 fclose (fp);
366 XtFree (text);
367 m_modified = FALSE;
368 return TRUE;
1a3ac83f 369 }
4bb6408c
JS
370}
371
372void wxTextCtrl::WriteText(const wxString& text)
373{
dfc54541
JS
374 long textPosition = GetInsertionPoint() + strlen (text);
375 XmTextInsert ((Widget) m_mainWidget, GetInsertionPoint(), (char*) (const char*) text);
376 XtVaSetValues ((Widget) m_mainWidget, XmNcursorPosition, textPosition, NULL);
377 SetInsertionPoint(textPosition);
378 XmTextShowPosition ((Widget) m_mainWidget, textPosition);
379 m_modified = TRUE;
4bb6408c
JS
380}
381
4dba84be
JS
382void wxTextCtrl::AppendText(const wxString& text)
383{
384 long textPosition = GetLastPosition() + strlen(text);
385 XmTextInsert ((Widget) m_mainWidget, GetLastPosition(), (char*) (const char*) text);
386 XtVaSetValues ((Widget) m_mainWidget, XmNcursorPosition, textPosition, NULL);
387 SetInsertionPoint(textPosition);
388 XmTextShowPosition ((Widget) m_mainWidget, textPosition);
389 m_modified = TRUE;
390}
391
4bb6408c
JS
392void wxTextCtrl::Clear()
393{
02e8b2f9 394 XmTextSetString ((Widget) m_mainWidget, "");
02e8b2f9 395 m_modified = FALSE;
4bb6408c
JS
396}
397
398bool wxTextCtrl::IsModified() const
399{
02e8b2f9 400 return m_modified;
4bb6408c
JS
401}
402
403// Makes 'unmodified'
404void wxTextCtrl::DiscardEdits()
405{
dfc54541
JS
406 XmTextSetString ((Widget) m_mainWidget, "");
407 m_modified = FALSE;
4bb6408c
JS
408}
409
410int wxTextCtrl::GetNumberOfLines() const
411{
dfc54541
JS
412 // HIDEOUSLY inefficient, but we have no choice.
413 char *s = XmTextGetString ((Widget) m_mainWidget);
414 if (s)
415 {
2d120f83
JS
416 long i = 0;
417 int currentLine = 0;
418 bool finished = FALSE;
419 while (!finished)
420 {
421 int ch = s[i];
422 if (ch == '\n')
423 {
424 currentLine++;
425 i++;
426 }
427 else if (ch == 0)
428 {
429 finished = TRUE;
430 }
431 else
432 i++;
433 }
66a007fb 434
2d120f83
JS
435 XtFree (s);
436 return currentLine;
dfc54541 437 }
4bb6408c
JS
438 return 0;
439}
440
441long wxTextCtrl::XYToPosition(long x, long y) const
442{
dfc54541 443/* It seems, that there is a bug in some versions of the Motif library,
2d120f83
JS
444 so the original wxWin-Code doesn't work. */
445 /*
446 Widget textWidget = (Widget) handle;
447 return (long) XmTextXYToPos (textWidget, (Position) x, (Position) y);
448 */
dfc54541
JS
449 /* Now a little workaround: */
450 long r=0;
451 for (int i=0; i<y; i++) r+=(GetLineLength(i)+1);
66a007fb 452 return r+x;
4bb6408c
JS
453}
454
455void wxTextCtrl::PositionToXY(long pos, long *x, long *y) const
456{
dfc54541
JS
457 Position xx, yy;
458 XmTextPosToXY((Widget) m_mainWidget, pos, &xx, &yy);
459 *x = xx; *y = yy;
4bb6408c
JS
460}
461
462void wxTextCtrl::ShowPosition(long pos)
463{
dfc54541 464 XmTextShowPosition ((Widget) m_mainWidget, (XmTextPosition) pos);
4bb6408c
JS
465}
466
467int wxTextCtrl::GetLineLength(long lineNo) const
468{
dfc54541
JS
469 wxString str = GetLineText (lineNo);
470 return (int) str.Length();
4bb6408c
JS
471}
472
473wxString wxTextCtrl::GetLineText(long lineNo) const
474{
dfc54541
JS
475 // HIDEOUSLY inefficient, but we have no choice.
476 char *s = XmTextGetString ((Widget) m_mainWidget);
66a007fb 477
dfc54541
JS
478 if (s)
479 {
480 wxString buf("");
481 long i;
482 int currentLine = 0;
483 for (i = 0; currentLine != lineNo && s[i]; i++ )
2d120f83
JS
484 if (s[i] == '\n')
485 currentLine++;
486 // Now get the text
487 int j;
488 for (j = 0; s[i] && s[i] != '\n'; i++, j++ )
489 buf += s[i];
66a007fb 490
2d120f83
JS
491 XtFree(s);
492 return buf;
493 }
494 else
495 return wxEmptyString;
4bb6408c
JS
496}
497
498/*
2d120f83
JS
499* Text item
500*/
501
4bb6408c
JS
502void wxTextCtrl::Command(wxCommandEvent & event)
503{
504 SetValue (event.GetString());
505 ProcessCommand (event);
506}
507
508void wxTextCtrl::OnDropFiles(wxDropFilesEvent& event)
509{
510 // By default, load the first file into the text window.
511 if (event.GetNumberOfFiles() > 0)
512 {
513 LoadFile(event.GetFiles()[0]);
514 }
515}
516
517// The streambuf code was partly taken from chapter 3 by Jerry Schwarz of
518// AT&T's "C++ Lanuage System Release 3.0 Library Manual" - Stein Somers
519
520//=========================================================================
66a007fb 521// Called then the buffer is full (gcc 2.6.3)
4bb6408c
JS
522// or when "endl" is output (Borland 4.5)
523//=========================================================================
524// Class declaration using multiple inheritance doesn't work properly for
525// Borland. See note in wb_text.h.
526#ifndef NO_TEXT_WINDOW_STREAM
527int wxTextCtrl::overflow(int c)
528{
2d120f83
JS
529 // Make sure there is a holding area
530 if ( allocate()==EOF )
531 {
532 wxError("Streambuf allocation failed","Internal error");
533 return EOF;
534 }
66a007fb 535
2d120f83
JS
536 // Verify that there are no characters in get area
537 if ( gptr() && gptr() < egptr() )
538 {
539 wxError("wxTextCtrl::overflow: Who's trespassing my get area?","Internal error");
540 return EOF;
541 }
66a007fb 542
2d120f83
JS
543 // Reset get area
544 setg(0,0,0);
66a007fb 545
2d120f83
JS
546 // Make sure there is a put area
547 if ( ! pptr() )
548 {
549 /* This doesn't seem to be fatal so comment out error message */
550 // wxError("Put area not opened","Internal error");
551 setp( base(), base() );
552 }
66a007fb 553
2d120f83
JS
554 // Determine how many characters have been inserted but no consumed
555 int plen = pptr() - pbase();
66a007fb 556
2d120f83
JS
557 // Now Jerry relies on the fact that the buffer is at least 2 chars
558 // long, but the holding area "may be as small as 1" ???
559 // And we need an additional \0, so let's keep this inefficient but
560 // safe copy.
66a007fb 561
2d120f83
JS
562 // If c!=EOF, it is a character that must also be comsumed
563 int xtra = c==EOF? 0 : 1;
66a007fb 564
2d120f83
JS
565 // Write temporary C-string to wxTextWindow
566 {
567 char *txt = new char[plen+xtra+1];
568 memcpy(txt, pbase(), plen);
569 txt[plen] = (char)c; // append c
570 txt[plen+xtra] = '\0'; // append '\0' or overwrite c
571 // If the put area already contained \0, output will be truncated there
572 WriteText(txt);
573 delete[] txt;
574 }
66a007fb 575
2d120f83
JS
576 // Reset put area
577 setp(pbase(), epptr());
66a007fb 578
4bb6408c 579#if defined(__WATCOMC__)
2d120f83 580 return __NOT_EOF;
4bb6408c 581#elif defined(zapeof) // HP-UX (all cfront based?)
2d120f83 582 return zapeof(c);
4bb6408c 583#else
2d120f83 584 return c!=EOF ? c : 0; // this should make everybody happy
4bb6408c
JS
585#endif
586}
587
588//=========================================================================
589// called then "endl" is output (gcc) or then explicit sync is done (Borland)
590//=========================================================================
591int wxTextCtrl::sync()
592{
2d120f83
JS
593 // Verify that there are no characters in get area
594 if ( gptr() && gptr() < egptr() )
595 {
596 wxError("Who's trespassing my get area?","Internal error");
597 return EOF;
598 }
66a007fb 599
2d120f83 600 if ( pptr() && pptr() > pbase() ) return overflow(EOF);
66a007fb 601
2d120f83
JS
602 return 0;
603 /* OLD CODE
604 int len = pptr() - pbase();
605 char *txt = new char[len+1];
606 strncpy(txt, pbase(), len);
607 txt[len] = '\0';
608 (*this) << txt;
609 setp(pbase(), epptr());
610 delete[] txt;
611 return 0;
612 */
4bb6408c
JS
613}
614
615//=========================================================================
616// Should not be called by a "ostream". Used by a "istream"
617//=========================================================================
618int wxTextCtrl::underflow()
619{
2d120f83 620 return EOF;
4bb6408c
JS
621}
622#endif
623
624wxTextCtrl& wxTextCtrl::operator<<(const wxString& s)
625{
4dba84be 626 AppendText(s);
4bb6408c
JS
627 return *this;
628}
629
630wxTextCtrl& wxTextCtrl::operator<<(float f)
631{
632 wxString str;
633 str.Printf("%.2f", f);
4dba84be 634 AppendText(str);
4bb6408c
JS
635 return *this;
636}
637
638wxTextCtrl& wxTextCtrl::operator<<(double d)
639{
640 wxString str;
641 str.Printf("%.2f", d);
4dba84be 642 AppendText(str);
4bb6408c
JS
643 return *this;
644}
645
646wxTextCtrl& wxTextCtrl::operator<<(int i)
647{
648 wxString str;
649 str.Printf("%d", i);
4dba84be 650 AppendText(str);
4bb6408c
JS
651 return *this;
652}
653
654wxTextCtrl& wxTextCtrl::operator<<(long i)
655{
656 wxString str;
657 str.Printf("%ld", i);
4dba84be 658 AppendText(str);
4bb6408c
JS
659 return *this;
660}
661
662wxTextCtrl& wxTextCtrl::operator<<(const char c)
663{
664 char buf[2];
66a007fb 665
4bb6408c
JS
666 buf[0] = c;
667 buf[1] = 0;
4dba84be 668 AppendText(buf);
4bb6408c
JS
669 return *this;
670}
671
02e8b2f9
JS
672void wxTextCtrl::OnChar(wxKeyEvent& event)
673{
2d120f83
JS
674 // Indicates that we should generate a normal command, because
675 // we're letting default behaviour happen (otherwise it's vetoed
676 // by virtue of overriding OnChar)
677 m_processedDefault = TRUE;
66a007fb 678
2d120f83 679 if (m_tempCallbackStruct)
02e8b2f9 680 {
2d120f83
JS
681 XmTextVerifyCallbackStruct *textStruct =
682 (XmTextVerifyCallbackStruct *) m_tempCallbackStruct;
683 textStruct->doit = True;
684 if (isascii(event.m_keyCode) && (textStruct->text->length == 1))
685 {
686 textStruct->text->ptr[0] = ((event.m_keyCode == WXK_RETURN) ? 10 : event.m_keyCode);
687 }
02e8b2f9 688 }
02e8b2f9
JS
689}
690
4b5f3fe6 691void wxTextCtrl::ChangeFont(bool keepOriginalSize)
0d57be45 692{
4b5f3fe6 693 wxWindow::ChangeFont(keepOriginalSize);
0d57be45
JS
694}
695
696void wxTextCtrl::ChangeBackgroundColour()
697{
321db4b6 698 wxWindow::ChangeBackgroundColour();
66a007fb 699
94b49b93 700 /* TODO: should scrollbars be affected? Should probably have separate
2d120f83
JS
701 * function to change them (by default, taken from wxSystemSettings)
702 */
94b49b93
JS
703 if (m_windowStyle & wxTE_MULTILINE)
704 {
705 Widget parent = XtParent ((Widget) m_mainWidget);
706 Widget hsb, vsb;
66a007fb 707
94b49b93 708 XtVaGetValues (parent,
2d120f83
JS
709 XmNhorizontalScrollBar, &hsb,
710 XmNverticalScrollBar, &vsb,
711 NULL);
94b49b93
JS
712 wxColour backgroundColour = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE);
713 if (hsb)
714 DoChangeBackgroundColour((WXWidget) hsb, backgroundColour, TRUE);
715 if (vsb)
716 DoChangeBackgroundColour((WXWidget) vsb, backgroundColour, TRUE);
66a007fb 717
94b49b93
JS
718 DoChangeBackgroundColour((WXWidget) parent, m_backgroundColour, TRUE);
719 }
0d57be45
JS
720}
721
722void wxTextCtrl::ChangeForegroundColour()
723{
321db4b6 724 wxWindow::ChangeForegroundColour();
66a007fb 725
94b49b93
JS
726 if (m_windowStyle & wxTE_MULTILINE)
727 {
728 Widget parent = XtParent ((Widget) m_mainWidget);
729 Widget hsb, vsb;
66a007fb 730
94b49b93 731 XtVaGetValues (parent,
2d120f83
JS
732 XmNhorizontalScrollBar, &hsb,
733 XmNverticalScrollBar, &vsb,
734 NULL);
66a007fb 735
2d120f83
JS
736 /* TODO: should scrollbars be affected? Should probably have separate
737 * function to change them (by default, taken from wxSystemSettings)
738 if (hsb)
94b49b93 739 DoChangeForegroundColour((WXWidget) hsb, m_foregroundColour);
2d120f83 740 if (vsb)
94b49b93 741 DoChangeForegroundColour((WXWidget) vsb, m_foregroundColour);
2d120f83 742 */
94b49b93
JS
743 DoChangeForegroundColour((WXWidget) parent, m_foregroundColour);
744 }
0d57be45
JS
745}
746
c27eab7e
VZ
747void wxTextCtrl::DoSendEvents(void *wxcbs, long keycode)
748{
749 // we're in process of updating the text control
750 m_tempCallbackStruct = wxcbs;
751
752 XmTextVerifyCallbackStruct *cbs = (XmTextVerifyCallbackStruct *)wxcbs;
753
754 wxKeyEvent event (wxEVT_CHAR);
755 event.SetId(GetId());
756 event.m_keyCode = keycode;
757 event.SetEventObject(this);
758
759 // Only if wxTextCtrl::OnChar is called will this be set to True (and
760 // the character passed through)
761 cbs->doit = False;
762
763 GetEventHandler()->ProcessEvent(event);
764
765 if ( !InSetValue() && m_processedDefault )
766 {
767 // Can generate a command
768 wxCommandEvent commandEvent(wxEVT_COMMAND_TEXT_UPDATED, GetId());
769 commandEvent.SetEventObject(this);
770 ProcessCommand(commandEvent);
771 }
772
773 // do it after the (user) event handlers processed the events because
774 // otherwise GetValue() would return incorrect (not yet updated value)
775 m_tempCallbackStruct = NULL;
776}
777
778// ----------------------------------------------------------------------------
779// helpers and Motif callbacks
780// ----------------------------------------------------------------------------
781
782static void MergeChangesIntoString(wxString& value,
783 XmTextVerifyCallbackStruct *cbs)
784{
785 /* _sm_
786 * At least on my system (SunOS 4.1.3 + Motif 1.2), you need to think of
787 * every event as a replace event. cbs->text->ptr gives the replacement
788 * text, cbs->startPos gives the index of the first char affected by the
789 * replace, and cbs->endPos gives the index one more than the last char
790 * affected by the replace (startPos == endPos implies an empty range).
791 * Hence, a deletion is represented by replacing all input text with a
792 * blank string ("", *not* NULL!). A simple insertion that does not
793 * overwrite any text has startPos == endPos.
794 */
795
796 if ( !value )
797 {
798 // easy case: the ol value was empty
799 value = cbs->text->ptr;
800 }
801 else
802 {
803 // merge the changes into the value
804 const char * const passwd = value;
805 int len = value.length();
806
807 len += strlen(cbs->text->ptr) + 1; // + new text (if any) + NUL
808 len -= cbs->endPos - cbs->startPos; // - text from affected region.
809
810 char * newS = new char [len];
811 char * dest = newS,
812 * insert = cbs->text->ptr;
813
814 // Copy (old) text from passwd, up to the start posn of the change.
815 int i;
816 const char * p = passwd;
817 for (i = 0; i < cbs->startPos; ++i)
818 *dest++ = *p++;
819
820 // Copy the text to be inserted).
821 while (*insert)
822 *dest++ = *insert++;
823
824 // Finally, copy into newS any remaining text from passwd[endPos] on.
825 for (p = passwd + cbs->endPos; *p; )
826 *dest++ = *p++;
827 *dest = 0;
828
829 value = newS;
830
831 delete[] newS;
832 }
833}
834
835static void
836wxTextWindowChangedProc (Widget w, XtPointer clientData, XtPointer ptr)
02e8b2f9 837{
2d120f83
JS
838 if (!wxGetWindowFromTable(w))
839 // Widget has been deleted!
840 return;
66a007fb 841
2d120f83
JS
842 wxTextCtrl *tw = (wxTextCtrl *) clientData;
843 tw->SetModified(TRUE);
02e8b2f9
JS
844}
845
66a007fb 846static void
02e8b2f9
JS
847wxTextWindowModifyProc (Widget w, XtPointer clientData, XmTextVerifyCallbackStruct *cbs)
848{
dfc54541
JS
849 wxTextCtrl *tw = (wxTextCtrl *) clientData;
850 tw->m_processedDefault = FALSE;
66a007fb 851
c27eab7e
VZ
852 // First, do some stuff if it's a password control: in this case, we need
853 // to store the string inside the class because GetValue() can't retrieve
854 // it from the text ctrl. We do *not* do it in other circumstances because
855 // it would double the amount of memory needed.
66a007fb 856
c27eab7e 857 if ( tw->GetWindowStyleFlag() & wxTE_PASSWORD )
dfc54541 858 {
c27eab7e 859 MergeChangesIntoString(tw->m_value, cbs);
66a007fb 860
c27eab7e 861 if ( cbs->text->length > 0 )
dfc54541
JS
862 {
863 int i;
864 for (i = 0; i < cbs->text->length; ++i)
865 cbs->text->ptr[i] = '*';
c27eab7e 866 cbs->text->ptr[i] = '\0';
dfc54541
JS
867 }
868 }
66a007fb 869
c27eab7e
VZ
870 // If we're already within an OnChar, return: probably a programmatic
871 // insertion.
dfc54541
JS
872 if (tw->m_tempCallbackStruct)
873 return;
66a007fb 874
dfc54541
JS
875 // Check for a backspace
876 if (cbs->startPos == (cbs->currInsert - 1))
877 {
c27eab7e 878 tw->DoSendEvents((void *)cbs, WXK_DELETE);
66a007fb 879
dfc54541
JS
880 return;
881 }
66a007fb 882
c27eab7e 883 // Pasting operation: let it through without calling OnChar
dfc54541
JS
884 if (cbs->text->length > 1)
885 return;
66a007fb 886
dfc54541
JS
887 // Something other than text
888 if (cbs->text->ptr == NULL)
889 return;
66a007fb 890
c27eab7e
VZ
891 // normal key press
892 char ch = cbs->text->ptr[0];
893 tw->DoSendEvents((void *)cbs, ch == '\n' ? '\r' : ch);
02e8b2f9
JS
894}
895
66a007fb 896static void
02e8b2f9
JS
897wxTextWindowGainFocusProc (Widget w, XtPointer clientData, XmAnyCallbackStruct *cbs)
898{
2d120f83
JS
899 if (!wxGetWindowFromTable(w))
900 return;
66a007fb 901
2d120f83
JS
902 wxTextCtrl *tw = (wxTextCtrl *) clientData;
903 wxFocusEvent event(wxEVT_SET_FOCUS, tw->GetId());
904 event.SetEventObject(tw);
905 tw->GetEventHandler()->ProcessEvent(event);
02e8b2f9
JS
906}
907
66a007fb 908static void
02e8b2f9
JS
909wxTextWindowLoseFocusProc (Widget w, XtPointer clientData, XmAnyCallbackStruct *cbs)
910{
2d120f83
JS
911 if (!wxGetWindowFromTable(w))
912 return;
66a007fb 913
2d120f83
JS
914 wxTextCtrl *tw = (wxTextCtrl *) clientData;
915 wxFocusEvent event(wxEVT_KILL_FOCUS, tw->GetId());
916 event.SetEventObject(tw);
917 tw->GetEventHandler()->ProcessEvent(event);
02e8b2f9 918}
dfc54541
JS
919
920static void wxTextWindowActivateProc(Widget w, XtPointer clientData,
2d120f83 921 XmAnyCallbackStruct *ptr)
dfc54541 922{
2d120f83
JS
923 if (!wxGetWindowFromTable(w))
924 return;
66a007fb 925
2d120f83 926 wxTextCtrl *tw = (wxTextCtrl *) clientData;
66a007fb 927
2d120f83
JS
928 if (tw->InSetValue())
929 return;
66a007fb 930
2d120f83
JS
931 wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER);
932 event.SetId(tw->GetId());
933 event.SetEventObject(tw);
934 tw->ProcessCommand(event);
dfc54541 935}
c27eab7e 936