-fpermissive to allow GCC to compile old C++
[oweals/cde.git] / cde / programs / dtmail / dtmail / XmTextEditor.C
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /*
24  *+SNOTICE
25  *
26  *      $TOG: XmTextEditor.C /main/13 1998/02/03 10:29:51 mgreess $
27  *
28  *      RESTRICTED CONFIDENTIAL INFORMATION:
29  *      
30  *      The information in this document is subject to special
31  *      restrictions in a confidential disclosure agreement between
32  *      HP, IBM, Sun, USL, SCO and Univel.  Do not distribute this
33  *      document outside HP, IBM, Sun, USL, SCO, or Univel without
34  *      Sun's specific written approval.  This document and all copies
35  *      and derivative works thereof must be returned or destroyed at
36  *      Sun's request.
37  *
38  *      Copyright 1993 Sun Microsystems, Inc.  All rights reserved.
39  *
40  *+ENOTICE
41  */
42
43 #include <EUSCompat.h>
44 #include <unistd.h>
45 #include <fcntl.h>
46 #include <sys/stat.h>
47
48 #if defined(NEED_MMAP_WRAPPER)
49 extern "C" {
50 #endif
51
52 #include <sys/types.h>
53 #include <sys/mman.h>
54
55 #if defined(NEED_MMAP_WRAPPER)
56 }
57 #endif
58
59 #include <Xm/Form.h>
60 #include <Xm/Text.h>
61 #include <assert.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <ctype.h>
65
66 #include "XmTextEditor.h"
67 #include <RoamApp.h>
68 #include <MailMsg.h>
69
70 XmTextEditor::XmTextEditor(
71     Widget parent,
72     DtMailEditor *owner_of_editor
73 )
74 {
75     my_parent   = parent;
76     my_owner    = owner_of_editor;
77     _w    = NULL;
78         my_text = NULL;
79
80     _buffer = NULL;
81     _buf_len = 0;
82     _modified_text = NULL;
83     _modified_text_buflen = 0;
84     begin_ins_bracket = NULL;
85     indent_str = NULL;
86     end_ins_bracket = NULL;
87     _auto_show_cursor = FALSE;
88     text_already_selected = FALSE;
89 }
90
91 XmTextEditor::~XmTextEditor() 
92 {
93     if (_buffer) {
94         delete _buffer;
95         _buffer = NULL;
96     }
97
98     if(_modified_text )  {
99         if(_modified_text->ptr)  {
100                 free(_modified_text->ptr);
101                 _modified_text->ptr = NULL;
102         }
103         free(_modified_text);
104         _modified_text = NULL;
105     }
106     if (NULL != indent_str)
107       free((void*) indent_str);
108 }
109
110 void
111 XmTextEditor::initialize()
112 {
113     Arg args[10];
114     int n = 0;
115
116 #if 0
117     short rows, cols;
118     DtMailEnv  error;
119     DtMail::Session * d_session = theRoamApp.session()->session();
120     DtMail::MailRc * mailrc = d_session->mailRc(error);
121
122     const char * value;
123     mailrc->getValue(error, "popuplines", &value);
124     if (error.isSet()) {
125         value = strdup("24");
126     }
127     rows = (short) strtol(value, NULL, 10);
128     if (NULL != value)
129       free((void*) value);
130
131     // If toolcols is set, overwrite the column width with "toolcols" value.
132     // Otherwise, default resource value will be used.
133     value = NULL;
134     mailrc->getValue(error, "toolcols", &value);
135     if (!error.isSet()){
136         cols = (short) strtol(value, NULL, 10);
137         XtSetArg(args[n], XmNcolumns, cols); n++;
138         if (NULL != value)
139           free((void*) value);
140     } else {
141         /*
142          * Default XmNcolumns
143          * MB_CUR_MAX == 1 : SingleByteLanguage
144          * MB_CUR_MAX >  1 : MultiByteLanguage
145          */
146         if ( MB_CUR_MAX == 1 )
147             value = "80";
148         else
149             value = "40";
150     }
151 #endif
152
153     XtSetArg(args[n], XmNeditable, FALSE); n++;
154     XtSetArg(args[n], XmNrows, 24); n++;
155     if ( MB_CUR_MAX == 1 ) {
156       XtSetArg(args[n], XmNcolumns, 80); n++;
157     } else {
158       XtSetArg(args[n], XmNcolumns, 40); n++;
159     }
160
161     _w = XmCreateScrolledText(my_parent, "Text", args, n );
162
163     update_display_from_props();
164     XtManageChild(_w);
165     XtAddEventHandler(XtParent(_w), ButtonPressMask,
166                         FALSE, MenuButtonHandler, 
167                         (XtPointer) this);
168 }
169
170 void
171 XmTextEditor::set_contents(
172                            const char *contents,
173                            const unsigned long len
174                            )
175 {
176     if (contents[len - 1] == 0) {
177         XmTextSetString(_w, (char *)contents);
178     }
179     else {
180         this->my_owner->needBuf(&_buffer, &_buf_len, len + 1);
181         this->my_owner->stripCRLF(&_buffer, contents, len);
182         XmTextSetString(_w, _buffer);
183     }
184 }
185
186 void
187 XmTextEditor::set_contents(const char * path)
188 {
189     loadFile(path, 0);
190 }
191
192 char*
193 XmTextEditor::get_contents()
194 {
195     return(XmTextGetString(_w));
196 }
197
198 void
199 XmTextEditor::append_to_contents(
200     const char *contents,
201     const unsigned long len
202 )
203 {
204
205     if (contents[len - 1] == 0) {
206         XmTextInsert( _w, XmTextGetLastPosition( _w ), 
207                       (char *)contents);
208     }
209     else {
210         // Not null terminated, and most likely has crlf instead of lf.
211         //
212         this->my_owner->needBuf(&_buffer, &_buf_len, len + 1);
213         this->my_owner->stripCRLF(&_buffer, contents, len);
214         XmTextInsert(_w, XmTextGetLastPosition(_w), _buffer);
215     }
216 }
217
218 void
219 XmTextEditor::append_to_contents(const char * path)
220 {
221     loadFile(path, (const int) XmTextGetLastPosition(_w));
222 }
223
224 void
225 XmTextEditor::append_at_cursor(
226                                  const char *contents,
227                                  const unsigned long len
228                                  )
229 {
230     if (contents[len - 1] == 0) {
231         XmTextInsert(
232                 _w, 
233                 XmTextGetInsertionPosition(_w), 
234                 (char *)contents
235                 );
236     }
237     else {
238         // Not null terminated, and most likely has crlf instead of lf.
239         //
240         this->my_owner->needBuf(&_buffer, &_buf_len, len + 1);
241         this->my_owner->stripCRLF(&_buffer, contents, len);
242         XmTextInsert(
243                 _w, 
244                 XmTextGetInsertionPosition(_w), 
245                 _buffer);
246     }
247 }
248
249 void
250 XmTextEditor::append_at_cursor(const char * path)
251 {
252     loadFile(path, (const int)XmTextGetInsertionPosition(_w));
253 }
254
255 void    
256 XmTextEditor::clear_contents()
257 {
258
259     XmTextSetString(_w, "");
260
261 }
262
263 Widget
264 XmTextEditor::get_text_widget()
265 {
266     return _w;
267 }
268
269 Pixel
270 XmTextEditor::get_text_foreground()
271 {
272     Pixel fg;
273     
274     XtVaGetValues(_w,
275         XmNforeground, &fg,
276         NULL);
277     return(fg);
278 }
279
280
281 Pixel
282 XmTextEditor::get_text_background()
283 {
284     Pixel bg;
285     
286     XtVaGetValues(_w,
287         XmNbackground, &bg,
288         NULL);
289     return(bg);
290 }
291
292 XmFontList
293 XmTextEditor::get_text_fontList()
294 {
295     XmFontList fl;
296     
297     XtVaGetValues(_w,
298         XmNfontList, &fl,
299         NULL);
300     return(fl);
301 }
302
303 Dimension
304 XmTextEditor::get_text_width()
305 {
306     Dimension wid;
307     
308     XtVaGetValues(_w,
309         XmNwidth, &wid,
310         NULL);
311     return (wid);
312 }
313
314 Widget
315 XmTextEditor::get_editor()
316 {
317     return XtParent(_w);
318 }
319
320 void
321 XmTextEditor::set_editable(
322         Boolean bval
323 )
324 {
325   XmTextSetEditable( _w, bval);
326
327   // If not editable, turn off the cursor?
328   XtVaSetValues(_w, XmNcursorPositionVisible, bval, NULL);
329 }
330
331
332 int
333 XmTextEditor::get_columns()
334 {
335   short ncolumns = 0;;
336   XtVaGetValues(_w, XmNcolumns, &ncolumns, NULL);
337   return ncolumns;
338 }
339
340
341 int
342 XmTextEditor::get_rows()
343 {
344   short nrows = 0;
345   XtVaGetValues(_w, XmNrows, &nrows, NULL);
346   return nrows;
347 }
348
349 void
350 XmTextEditor::set_columns(int ncolumns)
351 {
352   XtVaSetValues(_w, XmNcolumns, ncolumns, NULL);
353 }
354
355
356 void
357 XmTextEditor::set_rows(int nrows)
358 {
359   XtVaSetValues(_w, XmNrows, nrows, NULL);
360 }
361
362
363 // TOGO void
364 // TOGO XmTextEditor::set_container(
365 // TOGO    CMContainer
366 // TOGO )
367 // TOGO {
368 // TOGO    // Extract text and display?
369 // TOGO }
370
371 // TOGO CMContainer
372 // TOGO XmTextEditor::get_container()
373 // TOGO {
374 // TOGO    return(NULL);    
375 // TOGO }
376
377 void
378 XmTextEditor::auto_show_cursor_off()
379 {
380     // Get the original value of XmNautoShowCursorPosition
381     XtVaGetValues(_w,
382                   XmNautoShowCursorPosition, &_auto_show_cursor,
383                   NULL);
384
385     // Set it to false so we don't scroll with the cursor.
386     XtVaSetValues(_w,
387                   XmNautoShowCursorPosition, FALSE,
388                   NULL);
389 }
390
391 void
392 XmTextEditor::auto_show_cursor_restore()
393 {
394     // Restore the original value of XmNautoShowCursorPosition.
395     XtVaSetValues(_w,
396                   XmNautoShowCursorPosition, _auto_show_cursor,
397                   NULL);
398 }
399
400 void
401 XmTextEditor::set_to_top()
402 {
403
404     XmTextShowPosition(_w, 0);
405     XmTextSetInsertionPosition(_w, 0);
406
407 }
408
409 void
410 XmTextEditor::set_to_bottom()
411 {
412         XmTextSetInsertionPosition( _w, XmTextGetLastPosition(_w) );
413 }
414
415 #ifdef DEAD_WOOD
416 void
417 XmTextEditor::focus_callback(
418     Widget,
419     void *clientData,
420     void *
421 )
422 {
423  
424     XmTextEditor  *obj=(XmTextEditor *) clientData;
425
426     obj->obtained_focus();
427
428 }
429 #endif /* DEAD_WOOD */
430
431 void
432 XmTextEditor::obtained_focus()
433 {
434     // If there is text already selected, then the highlighted
435     // text is unhighlighted when the widget gets the focus
436     // next.  Need to disable the parent's menu items now.
437
438     if (XmTextGetSelection(_w) != NULL) {
439         text_already_selected = TRUE;
440     }
441
442     if (text_already_selected)
443         this->text_unselected();
444 }
445
446 void
447 XmTextEditor::text_selected_callback(
448     Widget,
449     void *clientData,
450     void *
451 )
452 {
453
454     XmTextEditor  *obj=(XmTextEditor *) clientData;
455
456     obj->text_selected();
457
458 }
459
460 void
461 XmTextEditor::text_unselected_callback(
462     Widget,
463     void *,
464     void *
465 )
466 {
467
468 //    XmTextEditor  *obj=(XmTextEditor *) clientData;
469
470 //    obj->text_unselected();
471
472 }
473
474 void
475 XmTextEditor::text_selected()
476 {
477     if (!text_already_selected) {
478         text_already_selected = TRUE;
479         my_owner->owner()->text_selected();
480     }
481 }
482
483 void
484 XmTextEditor::text_unselected()
485 {
486     text_already_selected = FALSE;
487     my_owner->owner()->text_unselected();
488 }
489
490 int
491 XmTextEditor::no_text()
492 {
493         char *text = get_contents();;
494         int text_len = strlen(text);
495         int result = 1;
496
497         for ( int k = 0;  k < text_len;  k++ ) {
498            if ( isgraph(text[k]) ) {
499                   result = 0;
500                   break;
501            }
502         }
503         XtFree(text);
504         return result;
505 }
506
507 void
508 XmTextEditor::undo_edit()
509 {
510         // This is to be consistent with DtEditor.
511     // Do nothing since Motif XmText does not support this.
512 }
513
514 void
515 XmTextEditor::set_word_wrap(Boolean)
516 {
517         // This is to be consistent with DtEditor.
518     // Do nothing since Motif XmText does not support this.
519 }
520
521 void
522 XmTextEditor::find_change()
523 {
524         // This is to be consistent with DtEditor.
525     // Do nothing since Motif XmText does not support this.
526 }
527
528 void
529 XmTextEditor::spell()
530 {
531         // This is to be consistent with DtEditor.
532     // Do nothing since Motif XmText does not support this.
533 }
534
535 void
536 XmTextEditor::format()
537 {
538         // This is to be consistent with DtEditor.
539     // Do nothing since Motif XmText does not support this.
540 }
541
542 void
543 XmTextEditor::cut_selection()
544 {
545         // Shouldn't really use CurrentTime
546         XmTextCut( _w, CurrentTime );
547 }
548
549 void
550 XmTextEditor::copy_selection()
551 {
552         // Shouldn't really use CurrentTime
553         XmTextCopy( _w, CurrentTime );
554 }
555
556 void
557 XmTextEditor::paste_from_clipboard()
558 {
559         XmTextPaste( _w );
560 }
561
562 void
563 XmTextEditor::paste_special_from_clipboard(Editor::InsertFormat format)
564 {
565         PSClientData cd;
566
567         cd.obj = this;
568         cd.insert_format = format;
569
570         XtAddCallback(_w, XmNmodifyVerifyCallback, 
571                         modify_verify_callback, (XtPointer)&cd);
572
573         XmTextPaste( _w );
574
575         XtRemoveCallback(_w, XmNmodifyVerifyCallback, 
576                         modify_verify_callback, (XtPointer)&cd);
577 }
578
579
580 // Removes the selection and leaves the resulting white space.
581 void
582 XmTextEditor::clear_selection()
583 {
584         // Shouldn't really use CurrentTime
585         // XmTextClearSelection( _w, CurrentTime );
586
587         // BUG in XmTextClearSelection.  Selection is not cleared.  Only the cursor
588         // is advanced to the last selected position.  Therefore need the following
589         // workaround.
590
591         XmTextPosition left, right;
592
593         if ( XmTextGetSelectionPosition( _w, &left, &right ) ) {
594            char *sel = XmTextGetSelection( _w );
595            // NOTE:  Modifying buffer returned by XmTextGetSelection.
596            // Future Motif implementation might return read only buffer.
597            if (sel != NULL) {
598                 memset( sel, ' ', strlen(sel) );
599                 // XmTextInsert( _w, left, sel );
600                 XmTextReplace( _w, left, right, sel );
601                 XtFree(sel);
602            }
603         }
604 }
605
606 // Removes the selection and compresses the resulting white space.
607 void
608 XmTextEditor::delete_selection()
609 {
610         XmTextRemove( _w );
611 }
612
613 void
614 XmTextEditor::select_all()
615 {
616         XmTextSetSelection( _w, 0, XmTextGetLastPosition(_w), CurrentTime );
617 }
618
619 void
620 XmTextEditor::disable_redisplay(void)
621 {
622     XmTextDisableRedisplay(_w);
623 }
624
625 void
626 XmTextEditor::enable_redisplay(void)
627 {
628     XmTextEnableRedisplay(_w);
629 }
630
631 void
632 XmTextEditor::loadFile(const char * path, const int pos)
633 {
634     int fd = open(path, O_RDONLY);
635     if (fd < 0) {
636         return;
637     }
638
639     struct stat info;
640     if (fstat(fd, &info) < 0) {
641         close(fd);
642         return;
643     }
644
645     int page_size = (int)sysconf(_SC_PAGESIZE);
646     size_t map_size = (size_t)(info.st_size + 
647                                 (page_size - (info.st_size % page_size)));
648     char * map;
649
650 #if defined(__osf__)
651     // Need the (char *) for compile to work in ALL cases ... for the
652     // POSIX "mmap" is (void *).  Also, this version of mmap does NOT
653     // allow requested length to be greater than the file size ...
654     // in contradiction to the documentation (don't round up).
655     map = (char *) mmap(0, info.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
656 #else
657     map = (char *) mmap(0, map_size, PROT_READ, MAP_PRIVATE, fd, 0);
658 #endif
659
660     if (map == (char *)-1) {
661         // We could not map it for some reason. Let's just read it into
662         // _buffer and pass it to XmText.
663         //
664         this->my_owner->needBuf(&_buffer, &_buf_len, info.st_size + 1);
665         if (read(fd, _buffer, (unsigned int) info.st_size) < 0) {
666             close(fd);
667             return;
668         }
669         _buffer[info.st_size] = 0;
670         XmTextInsert(_w, pos, _buffer);
671     }
672     else {
673         // We now have a mapped file. XmText wants a zero terminated
674         // buffer. We get luck with mmap because unless the file is
675         // an even page size, we will have some zero fill bytes that
676         // are legal to access.
677         //
678         // Of course in the case of an even page size file we must
679         // copy the buffer, terminate it and then give it to XmText.
680         //
681         if (info.st_size < map_size) {
682             XmTextInsert(_w, pos, map);
683         }
684         else {
685             this->my_owner->needBuf(&_buffer, &_buf_len, info.st_size + 1);
686             this->my_owner->stripCRLF(&_buffer, map, info.st_size);
687             XmTextInsert(_w, pos, _buffer);
688         }
689         munmap(map, map_size);
690     }
691
692     close(fd);
693 }
694
695 void 
696 XmTextEditor::modify_verify_callback(
697         Widget , XtPointer client, XtPointer call_data)
698 {
699         PSClientData *cd = (PSClientData *)client;
700         XmTextVerifyCallbackStruct *modify_info = 
701                         (XmTextVerifyCallbackStruct *)call_data;
702
703         // Make sure we are being called programmatically
704         if(modify_info->event != (XEvent *)NULL)
705                 return;
706
707         cd->obj->modifyPasteData(modify_info, cd->insert_format);
708 }
709
710 /*
711  * This fucntion modifies the pasted data
712  * with an indent prefix before each new line or brackets it.
713  */
714
715 void
716 XmTextEditor::modifyPasteData(
717         XmTextVerifyCallbackStruct *modify_info,
718         Editor::InsertFormat insert_format)
719 {
720         // The toolkit does not use this any more, this must be weird stuff.
721         if(modify_info->text->format == XmFMT_16_BIT)
722                 return;
723
724         if(_modified_text == NULL)
725                 _modified_text = (XmTextBlockRec *)calloc(1,sizeof(XmTextBlockRec));
726
727         char *sp = modify_info->text->ptr; // source pointer to the insert string 
728         int length = modify_info->text->length; // length does not include '\0' char
729         char *maxsp = sp  + length; // maxmimum source ptr
730
731         // Allocate memory rounded off to the nearest BUFINC
732         size_t size_req = (size_t)(((length/BUFINC)+1)*BUFINC);
733         if((_modified_text_buflen < size_req)   ||
734                   ((_modified_text_buflen > XmTextEditor::MAXBUFSZ) && 
735                         (size_req <  XmTextEditor::MAXBUFSZ))   )
736                 reallocPasteBuf(size_req);
737
738         if(_modified_text->ptr == NULL)
739                 return; // No memory available
740
741         switch( insert_format) {
742         case IF_INDENTED:       
743                 {
744                 DtMailEnv error;
745                 int ip = 0;
746
747                 // Get the indent prefix string
748                 DtMail::Session *m_session = 
749                                 theRoamApp.session()->session();
750                 m_session->mailRc(error)->getValue(error, 
751                                 "indentprefix", &indent_str);
752                 if ( error.isSet() || !indent_str) 
753                         indent_str = strdup("> ");
754
755                 size_t indlen = strlen(indent_str);
756
757                 // Copy the src buf into dest, inserting indent before '\n'
758                 while(sp < maxsp) {
759
760                         // Make sure there is enough space
761                         // for an indent prefix, one char and a terminating '\0'
762                         if(!((ip+indlen+2) < _modified_text_buflen) ) {
763                                 size_req = (size_t)((((_modified_text_buflen + 
764                                                 indlen+2)/BUFINC)+1)*BUFINC);
765                                 reallocPasteBuf(size_req);
766                                 if(_modified_text->ptr == NULL)
767                                         return; // No memory available
768                         }
769
770                         // Copy the indent string at the beginning 
771                         if(!ip) { 
772                                 memcpy(_modified_text->ptr, indent_str, indlen);
773                                 ip += indlen;
774                         }
775
776                         // Copy the next byte and check for new line
777                         _modified_text->ptr[ip++] = *sp++; 
778                         if(*(sp-1) == '\n') {
779                                 memcpy(&_modified_text->ptr[ip], indent_str, indlen);
780                                 ip += indlen;
781                         }
782
783                 }
784                 _modified_text->ptr[ip] = '\0'; // terminate with a null char
785                 _modified_text->length = ip; // Do not include '\0' char in len
786                 }
787                 break;
788         case IF_BRACKETED:
789                 {
790
791                 if( !begin_ins_bracket)
792                         begin_ins_bracket = GETMSG(DT_catd, 1, 199,
793                                 "\n------------- Begin Included Message -------------\n"); 
794                 if(!end_ins_bracket) 
795                         end_ins_bracket = GETMSG(DT_catd, 1, 200,
796                                 "\n------------- End Included Message -------------\n"); 
797                 
798                 size_t begin_len = strlen(begin_ins_bracket); 
799                 size_t end_len = strlen(end_ins_bracket); 
800
801                 // Make sure there is enough space
802                 if((size_req = length + begin_len + end_len + 1) > 
803                                         _modified_text_buflen) {
804                         size_req = (size_t) ((((size_req)/BUFINC)+1)*BUFINC);
805                         reallocPasteBuf(size_req);
806                 }
807
808                 if(_modified_text->ptr == NULL)
809                         return;
810
811                 strcpy(_modified_text->ptr, begin_ins_bracket);
812                 strncat(_modified_text->ptr,sp,length);
813                 strcat(_modified_text->ptr, end_ins_bracket); 
814                 _modified_text->length = end_len + begin_len + length;
815                 }
816                 break;
817         default:
818                 break;
819         }
820                         
821         _modified_text->format = modify_info->text->format;
822
823         // Stuff the modified text block ptr in the return call data
824         modify_info->text = _modified_text;
825 }
826
827 void
828 XmTextEditor::MenuButtonHandler(
829     Widget ,
830     XtPointer cd,
831     XEvent *event,
832     Boolean *)
833 {
834         XmTextEditor *obj = (XmTextEditor *)cd;
835
836         if(event->xany.type != ButtonPress)
837                 return;
838
839         XButtonEvent *be = (XButtonEvent *)event;
840
841         if(be->button == Button3)
842                 obj->my_owner->owner()->postTextPopup(event);
843 }
844