dthelp: Change to ANSI function definitions
[oweals/cde.git] / cde / programs / dtmail / dtmail / DtEditor.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 libraries 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: DtEditor.C /main/11 1998/02/03 10:28:15 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 #ifndef I_HAVE_NO_IDENT
44 #else
45 #endif
46
47 #ifdef DTEDITOR
48
49 #include <Xm/Xm.h>
50 #include <Xm/Form.h>
51 #include <Xm/Text.h>
52 #include <assert.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <ctype.h>
56 #include <X11/IntrinsicP.h>
57 #include <Xm/Text.h>
58 #include <Xm/CutPaste.h>
59
60 #include "Help.hh"
61 #include "RoamApp.h"
62 #include "DtEditor.hh"
63 #include "MailMsg.h"               // DT_catd defined here
64
65 CDEM_DtWidgetEditor::CDEM_DtWidgetEditor(
66     Widget parent,
67     DtMailEditor *owner_of_editor
68 )
69 {
70     my_parent   = parent;
71     my_owner    = owner_of_editor;
72     my_text     = NULL;
73     my_text_core = NULL;
74     _modified_text = NULL;
75     _modified_text_buflen = 0;
76
77     begin_ins_bracket = NULL;
78     indent_str = NULL;
79     end_ins_bracket = NULL;
80     _auto_show_cursor = FALSE;
81
82     _buffer     = NULL;
83     _buf_len    = (unsigned long) 0;
84     text_already_selected = FALSE;
85 }
86
87 CDEM_DtWidgetEditor::~CDEM_DtWidgetEditor() 
88 {
89     if (my_text) {
90         // No DtEditor API equivalent
91         // Remove the callbacks first.
92
93         XtRemoveCallback(my_text, DtNtextSelectCallback,
94             &CDEM_DtWidgetEditor::text_selected_callback, this);
95         XtRemoveCallback(my_text, DtNtextDeselectCallback,
96             &CDEM_DtWidgetEditor::text_unselected_callback, this);
97         XtRemoveCallback( my_text, XmNhelpCallback, HelpTexteditCB, this ) ;
98
99         XtDestroyWidget(my_text);
100     }
101     if (_buffer) {
102         delete _buffer;
103         _buffer = NULL;
104     }
105
106     if(_modified_text )  {
107         if(_modified_text->ptr)  {
108                 free(_modified_text->ptr);
109                 _modified_text->ptr = NULL;
110         }
111         free(_modified_text);
112         _modified_text = NULL;
113     }
114     if (NULL != indent_str)
115       free((void*) indent_str);
116 }
117
118 void
119 CDEM_DtWidgetEditor::initialize()
120 {
121     
122     int i = 0;
123
124     Arg args[10];
125     int n = 0;
126
127 #if 0
128     short rows, cols;
129     DtMailEnv  error;
130     DtMail::Session * d_session = theRoamApp.session()->session();
131     DtMail::MailRc * mailrc = d_session->mailRc(error);
132
133     const char * value = NULL;
134     mailrc->getValue(error, "popuplines", &value);
135     if (error.isSet()) {
136         value = strdup("24");
137     }
138     rows = strtol(value, NULL, 10);
139     if (NULL != value)
140       free((void*) value);
141
142     // If toolcols is set, overwrite the column width with "toolcols" value.
143     // Otherwise, default resource value will be used.
144     value = NULL;
145     mailrc->getValue(error, "toolcols", &value);
146     if (!error.isSet()){
147         cols = strtol(value, NULL, 10);      
148         XtSetArg(args[i], DtNcolumns, cols); i++;
149         if (NULL != value)
150           free((void*) value);
151     } else {
152         /*
153          * Default XmNcolumns
154          * MB_CUR_MAX == 1 : SingleByteLanguage
155          * MB_CUR_MAX >  1 : MultiByteLanguage
156          */
157         if ( MB_CUR_MAX == 1 )
158             value = "80";
159         else
160             value = "40";
161
162         cols = strtol(value, NULL, 10);      
163         XtSetArg(args[i], DtNcolumns, cols); i++;
164     }
165 #endif
166
167     XtSetArg(args[i], DtNeditable, FALSE); i++;
168     XtSetArg(args[i], DtNrows, 24); i++;
169     if ( MB_CUR_MAX == 1 ) {
170       XtSetArg(args[i], DtNcolumns, 80); i++;
171     } else {
172       XtSetArg(args[i], DtNcolumns, 40); i++;
173     }
174     XtSetArg(args[i], DtNcursorPositionVisible, FALSE); i++;
175
176     my_text = DtCreateEditor(my_parent, "Text", args, i);
177
178     update_display_from_props();
179     XtAddCallback(my_text, DtNtextSelectCallback,
180            &CDEM_DtWidgetEditor::text_selected_callback, this);
181     XtAddCallback(my_text, DtNtextDeselectCallback,
182            &CDEM_DtWidgetEditor::text_unselected_callback, this);
183     XtAddCallback( my_text, XmNhelpCallback, HelpTexteditCB, this ) ;
184
185     XtAddEventHandler(my_text, ButtonPressMask,
186                         FALSE, MenuButtonHandler, 
187                         (XtPointer) this);
188
189     XtManageChild(my_text);
190
191 }
192
193
194 char*
195 CDEM_DtWidgetEditor::get_contents()
196 {
197     
198     DtEditorErrorCode status;
199     static DtEditorContentRec content;
200
201     content.type = DtEDITOR_TEXT;
202     
203     // Get the contents with hardCarriageReturns = TRUE and
204     // markContentsAsSaved = TRUE.
205
206     /*
207      * If hardCarriageReturns = TRUE, the performace of DtEditorGetContents()
208      * suffers since according to man 3 DtEditorGetContents,
209      *
210      *    The hardCarriageReturns argument, if set to True, indicates
211      *    that the DtEditor widget should replace any soft line feeds
212      *    (word wraps) with <newline>s when saving the data.  When
213      *    hardCarriageReturns is set to False, any line wrapped
214      *    because it reaches the right edge of the window is saved as
215      *    one complete line.
216      * 
217      * And current default value of DtNwordWrap is TRUE. See dtmail/Dtmail.
218      * My temporary and non-good solution is
219      *    - Change default to False.
220      *    - If DtNwordWarp == TRUE,
221      *          call DtEditorGetContents(my_text, &content, TRUE, TRUE)
222      *      if not,
223      *          call DtEditorGetContents(my_text, &content, False, TRUE)
224      * This value can be controllable by a resource file or Format menu.
225      *
226      * Goofy ? but...................;-(
227      */
228     Arg     args[1];
229     Boolean ww;
230
231     XtSetArg( args[0], DtNwordWrap, &ww );
232     XtGetValues( my_text, args, 1 );
233     status = DtEditorGetContents(my_text, &content, ww, TRUE);
234     
235     return(content.value.string);
236 }
237
238 void
239 CDEM_DtWidgetEditor::set_contents(
240     const char *contents,
241     const unsigned long len
242 )
243 {
244     DtEditorContentRec content;
245     DtEditorErrorCode status;
246
247     this->my_owner->needBuf(&_buffer, &_buf_len, len + 1);
248     this->my_owner->stripCRLF(&_buffer, contents, len);
249
250     content.type = DtEDITOR_TEXT;
251     content.value.string = _buffer;
252     status = DtEditorSetContents(my_text, &content);
253
254 }
255
256 void
257 CDEM_DtWidgetEditor::set_contents(
258     const char *path
259 )
260 {
261    DtEditorSetContentsFromFile(my_text, (char *)path);
262 }
263
264 void    
265 CDEM_DtWidgetEditor::clear_contents()
266 {
267
268 // Doesn't work yet.  Work around with setting an empty string...
269 //    DtEditorReset(my_text);
270
271     DtEditorContentRec content;
272     DtEditorErrorCode status;
273
274     content.type = DtEDITOR_TEXT;
275     content.value.string = NULL;
276     status = DtEditorSetContents(my_text, &content);
277
278 }
279
280 void
281 CDEM_DtWidgetEditor::append_to_contents(
282     const char *contents,
283     const unsigned long len
284 )
285 {
286     DtEditorContentRec rec;
287
288     rec.type = DtEDITOR_TEXT;
289
290     if ( contents[len - 1] == 0 ) {
291         rec.value.string = (char *)contents;
292     } else {
293         this->my_owner->needBuf(&_buffer, &_buf_len, len + 1);
294         this->my_owner->stripCRLF(&_buffer, contents, len);
295         rec.value.string = _buffer;
296     }
297
298     DtEditorInsert(my_text, &rec);
299 }
300
301 void
302 CDEM_DtWidgetEditor::append_to_contents(
303     const char *path
304 )
305 {
306
307     DtEditorAppendFromFile(my_text, (char *)path);
308     
309 }
310
311 void
312 CDEM_DtWidgetEditor::append_at_cursor(
313     const char *path
314 )
315 {
316     DtEditorInsertFromFile(my_text, (char *) path);
317 }
318
319 void
320 CDEM_DtWidgetEditor::append_at_cursor(
321     const char *contents,
322     const unsigned long len
323 )
324 {
325     DtEditorContentRec rec;
326
327     rec.type = DtEDITOR_TEXT;
328
329     if ( contents[len - 1] == 0 ) {
330         rec.value.string = (char *)contents;
331     } else {
332         this->my_owner->needBuf(&_buffer, &_buf_len, len + 1);
333         this->my_owner->stripCRLF(&_buffer, contents, len);
334         rec.value.string = _buffer;
335     }
336     //DtEditorAppend(my_text, &rec);
337     // Fix for the defect 179186 05-25-95
338     // The above API will insert "contents" to the end of the buffer
339     // (appending). It should change to DtEditorInsert which insert 
340     // string to the current position (the cursor's position)
341     DtEditorInsert(my_text, &rec);
342 }
343
344 Widget
345 CDEM_DtWidgetEditor::get_text_widget()
346 {
347     // We actually need to return the text widget contained
348     // within DtEditor.  For now, just return the DtEditor.
349
350     return(my_text);
351 }
352
353 Pixel
354 CDEM_DtWidgetEditor::get_text_foreground()
355 {
356     Pixel fg;
357     
358     XtVaGetValues(my_text,
359         DtNtextForeground, &fg,
360         NULL);
361
362     return(fg);
363 }
364
365
366 // DtEditor returns the bg color of the Form widget, not the 
367 // text widget that the Form contains.
368 // This explains why the attachment pane color is that of the scroll bar...
369 // DtEditor needs to return the color of its text widget.
370 // OBTW, DtNtextBackground and DtNtextForeground don't work.  They
371 // return uninitialized values
372
373 Pixel
374 CDEM_DtWidgetEditor::get_text_background()
375 {
376     Pixel bg;
377     
378     XtVaGetValues(my_text,
379         DtNtextBackground, &bg,
380         NULL);
381     
382     return(bg);
383 }
384
385 XmFontList
386 CDEM_DtWidgetEditor::get_text_fontList()
387 {
388     XmFontList fl;
389     
390     XtVaGetValues(my_text,
391         DtNtextFontList, &fl,
392         NULL);
393
394     return(fl);
395 }
396
397 Dimension
398 CDEM_DtWidgetEditor::get_text_width()
399 {
400     Dimension wid;
401     
402     XtVaGetValues(my_text,
403         XmNwidth, &wid,
404         NULL);
405     return (wid);
406 }
407
408
409 Widget
410 CDEM_DtWidgetEditor::get_editor()
411 {
412     return(my_text);
413 }
414
415 int
416 CDEM_DtWidgetEditor::get_columns()
417 {
418   short ncolumns;
419   XtVaGetValues(my_text, DtNcolumns, &ncolumns, NULL);
420   return (int) ncolumns;
421 }
422
423
424 int
425 CDEM_DtWidgetEditor::get_rows()
426 {
427   short nrows;
428   XtVaGetValues(my_text, DtNrows, &nrows, NULL);
429   return (int) nrows;
430 }
431
432 void
433 CDEM_DtWidgetEditor::set_editable(Boolean bval)
434 {
435    XtVaSetValues(my_text,
436                 DtNeditable, bval,
437                 DtNcursorPositionVisible, bval,
438                 NULL);
439 }
440
441
442 void
443 CDEM_DtWidgetEditor::set_columns(int ncolumns)
444 {
445   XtVaSetValues(my_text, DtNcolumns, ncolumns, NULL);
446 }
447
448
449 void
450 CDEM_DtWidgetEditor::set_rows(int nrows)
451 {
452   XtVaSetValues(my_text, DtNrows, nrows, NULL);
453 }
454
455 void
456 CDEM_DtWidgetEditor::undo_edit()
457 {
458
459     DtEditorUndoEdit(my_text);
460
461 }
462
463 void
464 CDEM_DtWidgetEditor::cut_selection()
465 {
466
467     DtEditorCutToClipboard(my_text);
468
469 }
470
471 void
472 CDEM_DtWidgetEditor::copy_selection()
473 {
474
475     DtEditorCopyToClipboard(my_text);
476
477 }
478
479 void
480 CDEM_DtWidgetEditor::paste_from_clipboard()
481 {
482
483     DtEditorPasteFromClipboard(my_text);
484
485 }
486
487 void
488 CDEM_DtWidgetEditor::paste_special_from_clipboard(
489     Editor::InsertFormat format
490 )
491 {
492     int status;
493     unsigned long length, recvd;
494     char *clipboard_data;
495     Display *dpy = XtDisplayOfObject(my_text);
496     Window window = XtWindowOfObject(my_text);
497
498     do {
499         status = XmClipboardInquireLength(dpy, window, "STRING", &length);
500     } while (status == ClipboardLocked);
501
502     if (length == 0) {
503         return;
504     }
505     
506     clipboard_data = XtMalloc((unsigned)length);
507     
508     do {
509         status = XmClipboardRetrieve(
510                         dpy, window, "STRING", clipboard_data, 
511                         length,  &recvd, NULL
512                         );
513     } while (status == ClipboardLocked);
514     
515     if (status != ClipboardSuccess || recvd != length) {
516         // Couldn't get all
517         
518         XtFree(clipboard_data);
519         return;
520     }
521
522     // Now modify the data such that the necessary formatting occurs
523     // within it.  Bracketting will cause a line at the beginning and
524     // end of the data.  Indenting will prepend a ">" before each line,
525     // realigning the lines if necessary.
526     // The results are stored in _modified_text so clipboard_data can 
527     // be freed immediately after this call.
528
529     this->modifyData(clipboard_data, (unsigned) length, format);
530     XtFree(clipboard_data);
531
532     // Now copy the modified data stripped of CRLFs to a buffer.
533     // Put that buffer into the structure appropriate for DtEditor.
534
535     DtEditorContentRec rec;
536
537     rec.type = DtEDITOR_TEXT;
538     
539     // Length needs to be reset since the text now contains
540     // new characters that do the necessary formatting.
541
542     length = _modified_text->length;
543
544     if ( _modified_text->ptr[(unsigned) length - 1] == 0 ) {
545         rec.value.string = (char *)_modified_text->ptr;
546     } else {
547         this->my_owner->needBuf(
548                         &_buffer, &_buf_len, 
549                         (unsigned) length + 1
550                         );
551         this->my_owner->stripCRLF(
552                         &_buffer, _modified_text->ptr, 
553                         (unsigned) length);
554         rec.value.string = _buffer;
555     }
556     
557     DtEditorInsert(my_text, &rec);
558 }
559
560
561 void
562 CDEM_DtWidgetEditor::clear_selection()
563 {
564
565     DtEditorClearSelection(my_text);
566
567 }
568
569 void
570 CDEM_DtWidgetEditor::delete_selection()
571 {
572     DtEditorDeleteSelection(my_text);
573 }
574
575 void
576 CDEM_DtWidgetEditor::set_word_wrap(
577     Boolean bval
578 )
579 {
580    XtVaSetValues(my_text, DtNwordWrap, bval, NULL);
581 }
582
583 void
584 CDEM_DtWidgetEditor::set_to_top()
585 {
586     XtVaSetValues(my_text, 
587         DtNtopCharacter, 0, 
588         DtNcursorPosition, 0,
589         NULL);
590 }
591
592
593 void
594 CDEM_DtWidgetEditor::text_selected_callback(
595     Widget,
596     void * clientData,
597     void *
598 )
599 {
600
601     CDEM_DtWidgetEditor  *obj=(CDEM_DtWidgetEditor *) clientData;
602
603     obj->text_selected();
604
605 }
606
607 void
608 CDEM_DtWidgetEditor::text_unselected_callback(
609     Widget,
610     void * clientData,
611     void *
612 )
613 {
614
615     CDEM_DtWidgetEditor  *obj=(CDEM_DtWidgetEditor *) clientData;
616
617     obj->text_unselected();
618
619 }
620
621 void
622 CDEM_DtWidgetEditor::text_selected()
623 {
624
625     if (!text_already_selected) {
626         text_already_selected = TRUE;
627         my_owner->owner()->text_selected();
628     }
629 }
630
631 void
632 CDEM_DtWidgetEditor::text_unselected()
633 {
634
635     my_owner->owner()->text_unselected();
636     text_already_selected = FALSE;
637
638 }
639
640 void
641 CDEM_DtWidgetEditor::find_change()
642 {
643    DtEditorInvokeFindChangeDialog(my_text);
644 }
645
646 void
647 CDEM_DtWidgetEditor::spell()
648 {
649    DtEditorInvokeSpellDialog(my_text);
650 }
651
652 void
653 CDEM_DtWidgetEditor::format()
654 {
655    DtEditorInvokeFormatDialog(my_text);
656 }
657
658 void
659 CDEM_DtWidgetEditor::auto_show_cursor_off()
660 {
661 }
662
663 void
664 CDEM_DtWidgetEditor::auto_show_cursor_restore()
665 {
666 }
667
668 void
669 CDEM_DtWidgetEditor::select_all()
670 {
671    DtEditorSelectAll(my_text);
672 }
673
674 void
675 CDEM_DtWidgetEditor::set_to_bottom()
676 {
677 }
678
679 int
680 CDEM_DtWidgetEditor::no_text()
681 {
682    char *text = get_contents();   
683    int text_len = strlen(text);
684    int result = 1;
685
686    for ( int k = 0;  k < text_len;  k++ ) {
687           if ( isgraph(text[k]) ) {
688                  result = 0;
689                  break;
690           }
691    }
692
693    XtFree(text);
694    return result;
695 }
696
697 void
698 CDEM_DtWidgetEditor::disable_redisplay()
699 {
700     DtEditorDisableRedisplay(my_text);
701 }
702
703 void
704 CDEM_DtWidgetEditor::enable_redisplay()
705 {
706
707     DtEditorEnableRedisplay(my_text);
708
709 }
710
711
712
713 /*
714  * This fucntion modifies the pasted data
715  * with an indent prefix before each new line or brackets it.
716  */
717
718 void
719 CDEM_DtWidgetEditor::modifyData(
720     char *sp,  // source pointer to the insert string 
721     int length, // length does not include '\0' char
722     Editor::InsertFormat insert_format
723 )
724 {
725     if(_modified_text == NULL)
726         _modified_text = (XmTextBlockRec *)calloc(1,sizeof(XmTextBlockRec));
727
728     char *maxsp = sp  + length; // maxmimum source ptr
729
730     // Allocate memory rounded off to the nearest BUFINC
731     size_t size_req = (size_t)(((length/BUFINC)+1)*BUFINC);
732     if((_modified_text_buflen < size_req)       ||
733        ((_modified_text_buflen > CDEM_DtWidgetEditor::MAXBUFSZ) && 
734         (size_req <  CDEM_DtWidgetEditor::MAXBUFSZ))    )
735         reallocPasteBuf(size_req);
736     
737     if(_modified_text->ptr == NULL)
738             return; // No memory available
739
740     switch( insert_format) {
741       case IF_INDENTED: 
742       {
743           DtMailEnv error;
744           int ip = 0;
745           
746                 // Get the indent prefix string
747           DtMail::Session *m_session = theRoamApp.session()->session();
748           m_session->mailRc(error)->getValue(error,"indentprefix", &indent_str);
749           if (error.isSet() || NULL == indent_str) 
750             indent_str = strdup("> ");
751
752           size_t indlen = strlen(indent_str);
753
754           // Copy the src buf into dest, inserting indent before '\n'
755           while(sp < maxsp) {
756
757               // Make sure there is enough space
758               // for an indent prefix, one char and a terminating '\0'
759               if(!((ip+indlen+2) < _modified_text_buflen) ) {
760                   size_req = (size_t)((((_modified_text_buflen + 
761                       indlen+2)/BUFINC)+1)*BUFINC);
762                   reallocPasteBuf(size_req);
763                   if(_modified_text->ptr == NULL)
764                       return; // No memory available
765               }
766
767               // Copy the indent string at the beginning 
768               if(!ip) { 
769                   memcpy(_modified_text->ptr, indent_str, indlen);
770                   ip += indlen;
771               }
772
773               // Copy the next byte and check for new line
774               _modified_text->ptr[ip++] = *sp++; 
775               if(*(sp-1) == '\n') {
776                   memcpy(&_modified_text->ptr[ip], indent_str, indlen);
777                   ip += indlen;
778               }
779               
780           }
781           _modified_text->ptr[ip] = '\0'; // terminate with a null char
782           _modified_text->length = ip; // Do not include '\0' char in len
783       }
784       break;
785       case IF_BRACKETED:
786     {
787         
788         if( !begin_ins_bracket)
789             begin_ins_bracket = GETMSG(DT_catd, 1, 201,
790                 "\n------------- Begin Included Message -------------\n"); 
791         if(!end_ins_bracket) 
792             end_ins_bracket = GETMSG(DT_catd, 1, 202,
793                 "\n------------- End Included Message -------------\n"); 
794         
795         size_t begin_len = strlen(begin_ins_bracket); 
796         size_t end_len = strlen(end_ins_bracket); 
797
798         // Make sure there is enough space
799         if((size_req = length + begin_len + end_len + 1) > 
800            _modified_text_buflen) {
801             size_req = (size_t) ((((size_req)/BUFINC)+1)*BUFINC);
802             reallocPasteBuf(size_req);
803         }
804
805         if(_modified_text->ptr == NULL)
806             return;
807
808         strcpy(_modified_text->ptr, begin_ins_bracket);
809         strncat(_modified_text->ptr,sp,length);
810         strcat(_modified_text->ptr, end_ins_bracket); 
811         _modified_text->length = end_len + begin_len + length;
812     }
813       break;
814     default:
815       break;
816   }
817 }
818
819 void
820 CDEM_DtWidgetEditor::MenuButtonHandler(
821     Widget ,
822     XtPointer cd,
823     XEvent *event,
824     Boolean *)
825 {
826     CDEM_DtWidgetEditor *obj = (CDEM_DtWidgetEditor *)cd;
827
828     if(event->xany.type != ButtonPress)
829         return;
830
831     XButtonEvent *be = (XButtonEvent *)event;
832
833     if(be->button == Button3)
834         obj->my_owner->owner()->postTextPopup(event);
835 }
836
837
838 #endif