More Motif stuff, minor stubs correction
[wxWidgets.git] / src / motif / textctrl.cpp
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
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 #pragma implementation "textctrl.h"
14 #endif
15
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <fstream.h>
19
20 #include "wx/textctrl.h"
21 #include "wx/settings.h"
22 #include "wx/filefn.h"
23 #include "wx/utils.h"
24
25 #if defined(__BORLANDC__) && !defined(__WIN32__)
26 #include <alloc.h>
27 #else
28 #ifndef __GNUWIN32__
29 #include <malloc.h>
30 #endif
31 #endif
32
33 #include <Xm/Text.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <ctype.h>
37
38 #include "wx/motif/private.h"
39
40 static void
41 wxTextWindowChangedProc (Widget w, XtPointer clientData, XtPointer ptr);
42 static void
43 wxTextWindowModifyProc (Widget w, XtPointer clientData, XmTextVerifyCallbackStruct *cbs);
44 static void
45 wxTextWindowGainFocusProc (Widget w, XtPointer clientData, XmAnyCallbackStruct *cbs);
46 static void
47 wxTextWindowLoseFocusProc (Widget w, XtPointer clientData, XmAnyCallbackStruct *cbs);
48
49 #if !USE_SHARED_LIBRARY
50 IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl, wxControl)
51
52 BEGIN_EVENT_TABLE(wxTextCtrl, wxControl)
53 EVT_DROP_FILES(wxTextCtrl::OnDropFiles)
54 EVT_CHAR(wxTextCtrl::OnChar)
55 END_EVENT_TABLE()
56 #endif
57
58 // Text item
59 wxTextCtrl::wxTextCtrl()
60 #ifndef NO_TEXT_WINDOW_STREAM
61 :streambuf()
62 #endif
63 {
64 m_fileName = "";
65 m_tempCallbackStruct = (void*) NULL;
66 m_modified = FALSE;
67 }
68
69 bool wxTextCtrl::Create(wxWindow *parent, wxWindowID id,
70 const wxString& value,
71 const wxPoint& pos,
72 const wxSize& size, long style,
73 const wxValidator& validator,
74 const wxString& name)
75 {
76 m_tempCallbackStruct = (void*) NULL;
77 m_modified = FALSE;
78 m_fileName = "";
79
80 SetName(name);
81 SetValidator(validator);
82 if (parent) parent->AddChild(this);
83
84 m_windowStyle = style;
85
86 if ( id == -1 )
87 m_windowId = (int)NewControlId();
88 else
89 m_windowId = id;
90
91 Widget parentWidget = (Widget) parent->GetClientWidget();
92
93 bool wantHorizScrolling = ((m_windowStyle & wxHSCROLL) != 0);
94
95 // If we don't have horizontal scrollbars, we want word wrap.
96 bool wantWordWrap = !wantHorizScrolling;
97
98 if (m_windowStyle & wxTE_MULTILINE)
99 {
100 Arg args[2];
101 XtSetArg (args[0], XmNscrollHorizontal, wantHorizScrolling ? True : False);
102 XtSetArg (args[1], XmNwordWrap, wantWordWrap ? True : False);
103
104 m_mainWidget = (WXWidget) XmCreateScrolledText (parentWidget, (char*) (const char*) name, args, 2);
105
106 XtVaSetValues ((Widget) m_mainWidget,
107 XmNeditable, ((style & wxTE_READONLY) ? False : True),
108 XmNeditMode, XmMULTI_LINE_EDIT,
109 NULL);
110 XtManageChild ((Widget) m_mainWidget);
111 }
112 else
113 {
114 m_mainWidget = (WXWidget) XtVaCreateManagedWidget ((char*) (const char*) name,
115 xmTextWidgetClass, parentWidget,
116 NULL);
117
118 // TODO: Is this relevant? What does it do?
119 int noCols = 2;
120 if (!value.IsNull() && (value.Length() > (unsigned int) noCols))
121 noCols = value.Length();
122 XtVaSetValues ((Widget) m_mainWidget,
123 XmNcolumns, noCols,
124 NULL);
125 }
126
127 if (!value.IsNull())
128 XmTextSetString ((Widget) m_mainWidget, (char*) (const char*) value);
129
130 XtAddCallback((Widget) m_mainWidget, XmNvalueChangedCallback, (XtCallbackProc)wxTextWindowChangedProc, (XtPointer)this);
131
132 XtAddCallback((Widget) m_mainWidget, XmNmodifyVerifyCallback, (XtCallbackProc)wxTextWindowModifyProc, (XtPointer)this);
133
134 // XtAddCallback((Widget) m_mainWidget, XmNactivateCallback, (XtCallbackProc)wxTextWindowModifyProc, (XtPointer)this);
135
136 XtAddCallback((Widget) m_mainWidget, XmNfocusCallback, (XtCallbackProc)wxTextWindowGainFocusProc, (XtPointer)this);
137
138 XtAddCallback((Widget) m_mainWidget, XmNlosingFocusCallback, (XtCallbackProc)wxTextWindowLoseFocusProc, (XtPointer)this);
139
140 SetCanAddEventHandler(TRUE);
141 AttachWidget (parent, m_mainWidget, (WXWidget) NULL, pos.x, pos.y, size.x, size.y);
142
143 SetFont(* parent->GetFont());
144 ChangeColour(m_mainWidget);
145
146 return TRUE;
147 }
148
149 WXWidget wxTextCtrl::GetTopWidget() const
150 {
151 return ((m_windowStyle & wxTE_MULTILINE) ? (WXWidget) XtParent((Widget) m_mainWidget) : m_mainWidget);
152 }
153
154 wxString wxTextCtrl::GetValue() const
155 {
156 // TODO
157 return wxString("");
158 }
159
160 void wxTextCtrl::SetValue(const wxString& value)
161 {
162 // TODO
163 }
164
165 // Clipboard operations
166 void wxTextCtrl::Copy()
167 {
168 // TODO
169 }
170
171 void wxTextCtrl::Cut()
172 {
173 // TODO
174 }
175
176 void wxTextCtrl::Paste()
177 {
178 // TODO
179 }
180
181 void wxTextCtrl::SetEditable(bool editable)
182 {
183 // TODO
184 }
185
186 void wxTextCtrl::SetInsertionPoint(long pos)
187 {
188 // TODO
189 }
190
191 void wxTextCtrl::SetInsertionPointEnd()
192 {
193 long pos = GetLastPosition();
194 SetInsertionPoint(pos);
195 }
196
197 long wxTextCtrl::GetInsertionPoint() const
198 {
199 // TODO
200 return 0;
201 }
202
203 long wxTextCtrl::GetLastPosition() const
204 {
205 // TODO
206 return 0;
207 }
208
209 void wxTextCtrl::Replace(long from, long to, const wxString& value)
210 {
211 // TODO
212 }
213
214 void wxTextCtrl::Remove(long from, long to)
215 {
216 // TODO
217 }
218
219 void wxTextCtrl::SetSelection(long from, long to)
220 {
221 // TODO
222 }
223
224 bool wxTextCtrl::LoadFile(const wxString& file)
225 {
226 if (!wxFileExists(file))
227 return FALSE;
228
229 m_fileName = file;
230
231 Clear();
232
233 ifstream input((char*) (const char*) file, ios::nocreate | ios::in);
234
235 if (!input.bad())
236 {
237 struct stat stat_buf;
238 if (stat(file, &stat_buf) < 0)
239 return FALSE;
240 // This may need to be a bigger buffer than the file size suggests,
241 // if it's a UNIX file. Give it an extra 1000 just in case.
242 char *tmp_buffer = (char*)malloc((size_t)(stat_buf.st_size+1+1000));
243 long no_lines = 0;
244 long pos = 0;
245 while (!input.eof() && input.peek() != EOF)
246 {
247 input.getline(wxBuffer, 500);
248 int len = strlen(wxBuffer);
249 wxBuffer[len] = 13;
250 wxBuffer[len+1] = 10;
251 wxBuffer[len+2] = 0;
252 strcpy(tmp_buffer+pos, wxBuffer);
253 pos += strlen(wxBuffer);
254 no_lines++;
255 }
256
257 // TODO add line
258
259 free(tmp_buffer);
260
261 return TRUE;
262 }
263 return FALSE;
264 }
265
266 // If file is null, try saved file name first
267 // Returns TRUE if succeeds.
268 bool wxTextCtrl::SaveFile(const wxString& file)
269 {
270 wxString theFile(file);
271 if (theFile == "")
272 theFile = m_fileName;
273 if (theFile == "")
274 return FALSE;
275 m_fileName = theFile;
276
277 ofstream output((char*) (const char*) theFile);
278 if (output.bad())
279 return FALSE;
280
281 // TODO get and save text
282
283 return FALSE;
284 }
285
286 void wxTextCtrl::WriteText(const wxString& text)
287 {
288 // TODO write text to control
289 }
290
291 void wxTextCtrl::Clear()
292 {
293 XmTextSetString ((Widget) m_mainWidget, "");
294 // TODO: do we need position flag?
295 // m_textPosition = 0;
296 m_modified = FALSE;
297 }
298
299 bool wxTextCtrl::IsModified() const
300 {
301 return m_modified;
302 }
303
304 // Makes 'unmodified'
305 void wxTextCtrl::DiscardEdits()
306 {
307 // TODO
308 }
309
310 int wxTextCtrl::GetNumberOfLines() const
311 {
312 // TODO
313 return 0;
314 }
315
316 long wxTextCtrl::XYToPosition(long x, long y) const
317 {
318 // TODO
319 return 0;
320 }
321
322 void wxTextCtrl::PositionToXY(long pos, long *x, long *y) const
323 {
324 // TODO
325 }
326
327 void wxTextCtrl::ShowPosition(long pos)
328 {
329 // TODO
330 }
331
332 int wxTextCtrl::GetLineLength(long lineNo) const
333 {
334 // TODO
335 return 0;
336 }
337
338 wxString wxTextCtrl::GetLineText(long lineNo) const
339 {
340 // TODO
341 return wxString("");
342 }
343
344 /*
345 * Text item
346 */
347
348 void wxTextCtrl::Command(wxCommandEvent & event)
349 {
350 SetValue (event.GetString());
351 ProcessCommand (event);
352 }
353
354 void wxTextCtrl::OnDropFiles(wxDropFilesEvent& event)
355 {
356 // By default, load the first file into the text window.
357 if (event.GetNumberOfFiles() > 0)
358 {
359 LoadFile(event.GetFiles()[0]);
360 }
361 }
362
363 // The streambuf code was partly taken from chapter 3 by Jerry Schwarz of
364 // AT&T's "C++ Lanuage System Release 3.0 Library Manual" - Stein Somers
365
366 //=========================================================================
367 // Called then the buffer is full (gcc 2.6.3)
368 // or when "endl" is output (Borland 4.5)
369 //=========================================================================
370 // Class declaration using multiple inheritance doesn't work properly for
371 // Borland. See note in wb_text.h.
372 #ifndef NO_TEXT_WINDOW_STREAM
373 int wxTextCtrl::overflow(int c)
374 {
375 // Make sure there is a holding area
376 if ( allocate()==EOF )
377 {
378 wxError("Streambuf allocation failed","Internal error");
379 return EOF;
380 }
381
382 // Verify that there are no characters in get area
383 if ( gptr() && gptr() < egptr() )
384 {
385 wxError("Who's trespassing my get area?","Internal error");
386 return EOF;
387 }
388
389 // Reset get area
390 setg(0,0,0);
391
392 // Make sure there is a put area
393 if ( ! pptr() )
394 {
395 /* This doesn't seem to be fatal so comment out error message */
396 // wxError("Put area not opened","Internal error");
397 setp( base(), base() );
398 }
399
400 // Determine how many characters have been inserted but no consumed
401 int plen = pptr() - pbase();
402
403 // Now Jerry relies on the fact that the buffer is at least 2 chars
404 // long, but the holding area "may be as small as 1" ???
405 // And we need an additional \0, so let's keep this inefficient but
406 // safe copy.
407
408 // If c!=EOF, it is a character that must also be comsumed
409 int xtra = c==EOF? 0 : 1;
410
411 // Write temporary C-string to wxTextWindow
412 {
413 char *txt = new char[plen+xtra+1];
414 memcpy(txt, pbase(), plen);
415 txt[plen] = (char)c; // append c
416 txt[plen+xtra] = '\0'; // append '\0' or overwrite c
417 // If the put area already contained \0, output will be truncated there
418 WriteText(txt);
419 delete[] txt;
420 }
421
422 // Reset put area
423 setp(pbase(), epptr());
424
425 #if defined(__WATCOMC__)
426 return __NOT_EOF;
427 #elif defined(zapeof) // HP-UX (all cfront based?)
428 return zapeof(c);
429 #else
430 return c!=EOF ? c : 0; // this should make everybody happy
431 #endif
432 }
433
434 //=========================================================================
435 // called then "endl" is output (gcc) or then explicit sync is done (Borland)
436 //=========================================================================
437 int wxTextCtrl::sync()
438 {
439 // Verify that there are no characters in get area
440 if ( gptr() && gptr() < egptr() )
441 {
442 wxError("Who's trespassing my get area?","Internal error");
443 return EOF;
444 }
445
446 if ( pptr() && pptr() > pbase() ) return overflow(EOF);
447
448 return 0;
449 /* OLD CODE
450 int len = pptr() - pbase();
451 char *txt = new char[len+1];
452 strncpy(txt, pbase(), len);
453 txt[len] = '\0';
454 (*this) << txt;
455 setp(pbase(), epptr());
456 delete[] txt;
457 return 0;
458 */
459 }
460
461 //=========================================================================
462 // Should not be called by a "ostream". Used by a "istream"
463 //=========================================================================
464 int wxTextCtrl::underflow()
465 {
466 return EOF;
467 }
468 #endif
469
470 wxTextCtrl& wxTextCtrl::operator<<(const wxString& s)
471 {
472 WriteText(s);
473 return *this;
474 }
475
476 wxTextCtrl& wxTextCtrl::operator<<(float f)
477 {
478 wxString str;
479 str.Printf("%.2f", f);
480 WriteText(str);
481 return *this;
482 }
483
484 wxTextCtrl& wxTextCtrl::operator<<(double d)
485 {
486 wxString str;
487 str.Printf("%.2f", d);
488 WriteText(str);
489 return *this;
490 }
491
492 wxTextCtrl& wxTextCtrl::operator<<(int i)
493 {
494 wxString str;
495 str.Printf("%d", i);
496 WriteText(str);
497 return *this;
498 }
499
500 wxTextCtrl& wxTextCtrl::operator<<(long i)
501 {
502 wxString str;
503 str.Printf("%ld", i);
504 WriteText(str);
505 return *this;
506 }
507
508 wxTextCtrl& wxTextCtrl::operator<<(const char c)
509 {
510 char buf[2];
511
512 buf[0] = c;
513 buf[1] = 0;
514 WriteText(buf);
515 return *this;
516 }
517
518 void wxTextCtrl::OnChar(wxKeyEvent& event)
519 {
520 if (m_tempCallbackStruct)
521 {
522 XmTextVerifyCallbackStruct *textStruct =
523 (XmTextVerifyCallbackStruct *) m_tempCallbackStruct;
524 textStruct->doit = True;
525 if (isascii(event.m_keyCode) && (textStruct->text->length == 1))
526 {
527 textStruct->text->ptr[0] = ((event.m_keyCode == WXK_RETURN) ? 10 : event.m_keyCode);
528 }
529 }
530 }
531
532 static void
533 wxTextWindowChangedProc (Widget w, XtPointer clientData, XtPointer ptr)
534 {
535 if (!wxGetWindowFromTable(w))
536 // Widget has been deleted!
537 return;
538
539 wxTextCtrl *tw = (wxTextCtrl *) clientData;
540 tw->SetModified(TRUE);
541 }
542
543 static void
544 wxTextWindowModifyProc (Widget w, XtPointer clientData, XmTextVerifyCallbackStruct *cbs)
545 {
546 wxTextCtrl *tw = (wxTextCtrl *) clientData;
547
548 // If we're already within an OnChar, return: probably
549 // a programmatic insertion.
550 if (tw->m_tempCallbackStruct)
551 return;
552
553 // Check for a backspace
554 if (cbs->startPos == (cbs->currInsert - 1))
555 {
556 tw->m_tempCallbackStruct = (void*) cbs;
557
558 wxKeyEvent event (wxEVT_CHAR);
559 event.SetId(tw->GetId());
560 event.m_keyCode = WXK_DELETE;
561 event.SetEventObject(tw);
562
563 // Only if wxTextCtrl::OnChar is called
564 // will this be set to True (and the character
565 // passed through)
566 cbs->doit = False;
567
568 tw->GetEventHandler()->ProcessEvent(event);
569
570 tw->m_tempCallbackStruct = NULL;
571
572 return;
573 }
574
575 // Pasting operation: let it through without
576 // calling OnChar
577 if (cbs->text->length > 1)
578 return;
579
580 // Something other than text
581 if (cbs->text->ptr == NULL)
582 return;
583
584 tw->m_tempCallbackStruct = (void*) cbs;
585
586 wxKeyEvent event (wxEVT_CHAR);
587 event.SetId(tw->GetId());
588 event.SetEventObject(tw);
589 event.m_keyCode = (cbs->text->ptr[0] == 10 ? 13 : cbs->text->ptr[0]);
590
591 // Only if wxTextCtrl::OnChar is called
592 // will this be set to True (and the character
593 // passed through)
594 cbs->doit = False;
595
596 tw->GetEventHandler()->ProcessEvent(event);
597
598 tw->m_tempCallbackStruct = NULL;
599 }
600
601 static void
602 wxTextWindowGainFocusProc (Widget w, XtPointer clientData, XmAnyCallbackStruct *cbs)
603 {
604 if (!wxGetWindowFromTable(w))
605 return;
606
607 wxTextCtrl *tw = (wxTextCtrl *) clientData;
608 wxFocusEvent event(wxEVT_SET_FOCUS, tw->GetId());
609 event.SetEventObject(tw);
610 tw->GetEventHandler()->ProcessEvent(event);
611 }
612
613 static void
614 wxTextWindowLoseFocusProc (Widget w, XtPointer clientData, XmAnyCallbackStruct *cbs)
615 {
616 if (!wxGetWindowFromTable(w))
617 return;
618
619 wxTextCtrl *tw = (wxTextCtrl *) clientData;
620 wxFocusEvent event(wxEVT_KILL_FOCUS, tw->GetId());
621 event.SetEventObject(tw);
622 tw->GetEventHandler()->ProcessEvent(event);
623 }