dthelp: Change to ANSI function definitions
[oweals/cde.git] / cde / programs / dtmail / dtmail / DtMailEditor.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: DtMailEditor.C /main/10 1998/07/24 16:05:41 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 #include <Xm/Form.h>
48 #include <Xm/SeparatoG.h>
49 #include <Dt/Dnd.h>
50 #include "DtMailEditor.hh"
51 #include "XmTextEditor.h"
52 #include "AttachArea.h"
53 #include "Attachment.h"
54 #ifdef DTEDITOR
55 #include "DtEditor.hh"
56 #endif
57
58 #include "EUSDebug.hh"
59
60
61 extern "C" {
62 extern XtPointer _XmStringUngenerate (
63                                 XmString string,
64                                 XmStringTag tag,
65                                 XmTextType tag_type,
66                                 XmTextType output_type);
67 }
68
69 #ifndef ABS
70 #define ABS(x) (((x) > 0) ? (x) : (-(x)))
71 #endif
72
73 #ifndef DRAG_THRESHOLD
74 #define DRAG_THRESHOLD 4
75 #endif
76
77 DtMailEditor::DtMailEditor(
78     Widget parent,
79     AbstractEditorParent *owner
80 ) : UIComponent("DtMailEditor")
81 {
82     _myOwner      = owner;
83     
84     // Create a manager widget (say a form) and set it to the private
85     // variable _container. Parent private instances to this widget.
86     // Expose only _container externally (for attachment stuff...)
87
88     _w = XmCreateForm(parent, "DtMailEditor", NULL, 0);
89     installDestroyHandler();
90
91 #ifdef DTEDITOR
92     if ( use_XmTextEditor ) {
93         _myTextEditor = new XmTextEditor(_w, this);
94     } else {
95         _myTextEditor = new CDEM_DtWidgetEditor(_w, this);
96     }
97 #else
98     _myTextEditor = new XmTextEditor(_w, this);
99 #endif
100
101     _myAttachArea = new AttachArea(_w, this, "AttachPane");
102
103     _showAttachArea = TRUE;
104     _doingDrag = FALSE;
105     _separator = NULL;
106     _msgHandle = NULL;
107     _dragX = -1;
108     _dragY = -1;
109     _editable = FALSE;
110     _container = NULL;
111
112 }
113
114 DtMailEditor::~DtMailEditor()
115 {
116     unmanageAttachArea();
117     delete _myAttachArea;
118     delete _myTextEditor;
119 }
120
121 void
122 DtMailEditor::initialize()
123 {
124
125     Widget editor_widget;
126
127     _myTextEditor->initialize();
128
129     _separator = XtVaCreateManagedWidget("Sep1",
130                                          xmSeparatorGadgetClass,
131                                          _w,
132                                          XmNtopOffset, 1,
133                                          XmNbottomOffset, 1,
134                                          XmNrightAttachment, XmATTACH_FORM,
135                                          XmNleftAttachment, XmATTACH_FORM,
136                                          NULL);
137
138     // Create an *UNMANAGED* attachArea.
139     // If the message has an attachment, the DtMailEditor instance
140     // will receive a manageAttachArea where it will adjust the
141     // attachments and manage the attachArea accordingly 
142     
143     _myAttachArea->initialize();
144
145     attachDropRegister();
146     if (!_editable)
147         attachDropDisable();
148
149     editor_widget = _myTextEditor->get_editor();
150
151     XtVaSetValues(editor_widget,
152                   XmNtopAttachment,XmATTACH_FORM, 
153                   XmNbottomAttachment, XmATTACH_WIDGET,
154                   XmNbottomWidget, _separator,
155                   XmNrightAttachment,XmATTACH_FORM,
156                   XmNrightOffset, 4,
157                   XmNleftAttachment,XmATTACH_FORM, 
158                   XmNleftOffset, 3,
159                   NULL );
160
161     XtVaSetValues(_myAttachArea->baseWidget(),
162                   XmNrightAttachment, XmATTACH_FORM,
163                   XmNrightOffset, 3,
164                   XmNleftAttachment, XmATTACH_FORM,
165                   XmNleftOffset, 5,
166                   XmNbottomAttachment, XmATTACH_FORM,
167                   NULL);
168     
169     XtVaSetValues(_separator,
170                   XmNbottomAttachment, XmATTACH_WIDGET,
171                   XmNbottomWidget, _myAttachArea->baseWidget(),
172                   NULL);
173
174     // Unmanage the attachArea.  If a message has attachments,
175     // manageAttachArea() will get called by the consumer of this
176     // class.
177
178     this->unmanageAttachArea(); 
179
180     XtManageChild(_w);
181
182 }
183
184 AbstractEditorParent *
185 DtMailEditor::owner()
186 {
187     return (_myOwner);
188 }
189
190 Editor*
191 DtMailEditor::textEditor()
192 {
193     return(_myTextEditor);
194 }
195
196 AttachArea*
197 DtMailEditor::attachArea()
198 {
199     return(_myAttachArea);
200 }
201
202 Widget
203 DtMailEditor::container()
204 {
205     return(_w);
206 }
207
208 void
209 DtMailEditor::setEditable(Boolean bval)
210 {
211     textEditor()->set_editable(bval);
212     _editable = bval;
213     if (_editable)
214         attachDropEnable();
215     else
216         attachDropDisable();
217 }
218
219 void
220 DtMailEditor::manageAttachArea()
221 {
222
223     if (!_showAttachArea && (_myAttachArea->getIconCount() == 0)) {
224         return;
225     }
226
227     _myAttachArea->manage();
228     XtManageChild(_separator);
229
230     Widget editor_widget = _myTextEditor->get_editor();
231
232     XtVaSetValues(editor_widget,
233         XmNbottomAttachment, XmATTACH_WIDGET,
234         XmNbottomWidget, _separator,
235         NULL );
236
237     XtVaSetValues(_separator,
238                   XmNbottomAttachment, XmATTACH_WIDGET,
239                   XmNbottomWidget, _myAttachArea->baseWidget(),
240                   NULL);
241
242     XtVaSetValues(_myAttachArea->baseWidget(),
243                 XmNbottomAttachment, XmATTACH_FORM,
244                 NULL);
245
246 }
247
248 void
249 DtMailEditor::unmanageAttachArea()
250 {
251
252     // Already unmanaged?
253     if (!XtIsManaged(_myAttachArea->baseWidget())) return;
254
255     Widget editor_widget = _myTextEditor->get_editor();
256
257     XtVaSetValues(editor_widget,
258         XmNbottomAttachment, XmATTACH_FORM,
259         NULL );
260
261     _myAttachArea->unmanage();
262
263     XtUnmanageChild(_separator);
264
265 }
266
267 // Initialize _msgHandle
268 void
269 DtMailEditor::setMsgHnd(DtMail::Message * msgHandle)
270 {
271     _msgHandle = msgHandle;
272 }
273
274 // attachTransferCallback
275 //
276 // Handles the transfer of data that is dropped on the attachment list.
277 // The data is turned into an attachment and appended to the list.
278 //
279 void
280 DtMailEditor::attachTransferCallback(
281     Widget      /* widget */,
282     XtPointer   client_data,
283     XtPointer   call_data)
284 {
285     DtDndTransferCallbackStruct *transferInfo =
286                                 (DtDndTransferCallbackStruct *) call_data;
287     DtDndContext *dropData = transferInfo->dropData;
288     int          numItems = dropData->numItems, ii;
289     DtMailEditor *editor = (DtMailEditor *) client_data;
290     DtMailEnv    mail_error;
291     DtMailBuffer buf;
292     char         *attachname;
293
294     DebugPrintf(3, "In DtMailEditor::attachTransferCallback\n");
295
296     // Initialize mail_error.
297     mail_error.clear();
298
299     switch (transferInfo->dropData->protocol) {
300
301         case DtDND_FILENAME_TRANSFER:
302
303             // Loop through the dropped files and turn each
304             // into an attachment.
305
306             for (ii = 0; ii < numItems; ii++) {
307                 editor->owner()->add_att(dropData->data.files[ii]);
308             }
309             break;
310
311         case DtDND_BUFFER_TRANSFER:
312
313             // Loop through the dropped buffers and turn each
314             // into an attachment.
315
316             for (ii = 0; ii < numItems; ii++) {
317
318                 buf.buffer = (char *)dropData->data.buffers[ii].bp;
319                 buf.size = (unsigned long)dropData->data.buffers[ii].size;
320                 attachname = dropData->data.buffers[ii].name;
321
322                 if (!attachname)
323                     attachname = "Untitled";
324
325                 editor->owner()->add_att(attachname, buf);
326             }
327             break;
328
329         default:
330             transferInfo->status = DtDND_FAILURE;
331             return;
332     }
333 }
334
335
336 // attachDropRegister
337 //
338 // Register the attachment list to accept drops of buffer and files
339 //
340 void
341 DtMailEditor::attachDropRegister()
342 {
343     static XtCallbackRec transferCBRec[] = { 
344         {&DtMailEditor::attachTransferCallback, NULL}, {NULL, NULL} };
345
346     // Pass the DtMailEditor object (this) as clientData.
347     transferCBRec[0].closure = (XtPointer) this;
348
349     DtDndVaDropRegister(_myAttachArea->baseWidget(),
350         DtDND_FILENAME_TRANSFER | DtDND_BUFFER_TRANSFER,
351         (unsigned char)(XmDROP_COPY), transferCBRec,
352         DtNtextIsBuffer, TRUE,
353         NULL);
354 }
355
356 // attachDropEnable
357 //
358 // Enable the attachment list for drops by restoring the operation
359 //
360 void
361 DtMailEditor::attachDropEnable()
362 {
363     Arg args[1];
364
365     XtSetArg(args[0], XmNdropSiteOperations, XmDROP_MOVE | XmDROP_COPY);
366     XmDropSiteUpdate(_myAttachArea->baseWidget(), args, 1);
367 }
368
369 // attachDropDisable
370 //
371 // Disable the attachment list for drops by setting the operation to noop
372 //
373 void
374 DtMailEditor::attachDropDisable()
375 {
376     Arg args[1];
377
378     XtSetArg(args[0], XmNdropSiteOperations, XmDROP_NOOP);
379     XmDropSiteUpdate(_myAttachArea->baseWidget(), args, 1);
380 }
381
382 // attachConvertCallback
383 //
384 // Provides the selected attachments for the drag
385 //
386 void
387 DtMailEditor::attachConvertCallback(
388     Widget      /* dragContext */,
389     XtPointer   clientData,
390     XtPointer   callData)
391 {
392     DtDndConvertCallbackStruct *convertInfo =
393                                 (DtDndConvertCallbackStruct *) callData;
394     DtDndBuffer         *buffers = convertInfo->dragData->data.buffers;
395     DtMailEditor        *editor = (DtMailEditor *) clientData;
396     int                 numIcons = editor->attachArea()->getIconCount();
397     Attachment          **list = editor->attachArea()->getList();
398     int                 ii, current = 0;
399     char                *name = NULL;
400     XmString            str;
401     DtMailEnv           mail_error;
402
403
404     DebugPrintf(3, "In DtMailEditor::attachConvertCallback\n");
405
406     switch(convertInfo->reason) {
407         case DtCR_DND_CONVERT_DATA:
408             for (ii = 0; ii < numIcons; ii++) {
409                 if (!list[ii]->isDeleted() && list[ii]->isSelected()) {
410                     buffers[current].bp = list[ii]->getContents();
411                     buffers[current].size = (int)list[ii]->getContentsSize();
412                     str = list[ii]->getLabel();
413                     buffers[current].name =
414                         (char *) _XmStringUngenerate(
415                                         str, NULL,
416                                         XmMULTIBYTE_TEXT, XmMULTIBYTE_TEXT);
417                     XmStringFree(str);
418                     current++;
419                 }
420             }
421             break;
422
423         case DtCR_DND_CONVERT_DELETE:
424             editor->attachArea()->deleteSelectedAttachments(mail_error);
425             break;
426
427         default:
428             convertInfo->status = DtDND_FAILURE;
429     }
430 }
431
432 // attachDragFinishCallback
433 //
434 // Clean up from the convert callback and restore state
435 //
436 void
437 DtMailEditor::attachDragFinishCallback(
438     Widget      /* widget */,
439     XtPointer   clientData,
440     XtPointer   callData)
441 {
442     DtDndDragFinishCallbackStruct *finishInfo =
443                 (DtDndDragFinishCallbackStruct *) callData;
444     DtDndContext        *dragData = finishInfo->dragData;
445     DtMailEditor *editor = (DtMailEditor *) clientData;
446     int          ii;
447
448     DebugPrintf(3, "In DtMailEditor::attachDragFinishCallback\n");
449
450     editor->setDoingDrag(FALSE);
451     editor->setDragX(-1);
452     editor->setDragY(-1);
453
454     if (editor->editable())
455         editor->attachDropEnable();
456
457     for (ii = 0; ii < dragData->numItems; ii++) {
458         XtFree((char *)dragData->data.buffers[ii].name);
459     }
460 }
461
462 void
463 DtMailEditor::attachDragStart( Widget widget, 
464                                 XEvent *event)
465 {
466     static XtCallbackRec convertCBRec[] = {
467         {&DtMailEditor::attachConvertCallback, NULL}, {NULL, NULL} };
468     static XtCallbackRec dragFinishCBRec[] = {
469         {&DtMailEditor::attachDragFinishCallback, NULL}, {NULL, NULL} };
470     int         itemCount;
471     unsigned char operations;
472
473     convertCBRec[0].closure = (XtPointer) this;
474     dragFinishCBRec[0].closure = (XtPointer) this;
475
476     attachDropDisable();
477
478     // Count the number of items to be dragged.
479     itemCount = attachArea()->getSelectedIconCount();
480
481     setDoingDrag(TRUE);
482
483     if (editable()) {
484         operations = (unsigned char)(XmDROP_COPY | XmDROP_MOVE);
485     } else {
486         operations = (unsigned char)(XmDROP_COPY);
487     }
488
489     if (DtDndVaDragStart(widget, event, DtDND_BUFFER_TRANSFER, itemCount,
490                 operations, convertCBRec, dragFinishCBRec,
491 //              DtNsourceIcon, dragIcon,
492                 NULL)
493             == NULL) {
494             
495             DebugPrintf(3, "DragStart returned NULL.\n");
496     }
497 }
498
499 void
500 DtMailEditor::attachDragMotionHandler(
501     Widget      widget,
502     XEvent      *event)
503 {
504     int         diffX, diffY;
505
506     if (!doingDrag()) {
507         // If the drag is just starting, set initial button down coordinates.
508         if (dragX() == -1 && dragY() == -1) {
509             setDragX(event->xmotion.x);
510             setDragY(event->xmotion.y);
511         }
512
513         // Find out how far the pointer has moved since the button press.
514         diffX = dragX() - event->xmotion.x;
515         diffY = dragY() - event->xmotion.y;
516
517         if ((ABS(diffX) >= DRAG_THRESHOLD) ||
518             (ABS(diffY) >= DRAG_THRESHOLD)) {
519                 attachDragStart(widget, event);
520         }
521     }
522 }
523
524 #ifdef notdef
525 void
526 DtMailEditor::attachDragSetup()
527 {
528     Boolean     btn1_transfer;
529     Widget      widget = _myAttachArea->baseWidget();
530
531     DebugPrintf(3, "In DtMailEditor::attachDragSetup()\n");
532
533     XtAddEventHandler(widget, Button1MotionMask, FALSE,
534                 (XtEventHandler)&DtMailEditor::attachDragMotionHandler,
535                 (XtPointer)this);
536
537     XtVaGetValues(
538         (Widget)XmGetXmDisplay(XtDisplayOfObject(widget)),
539         "enableBtn1Transfer", &btn1_transfer,
540         NULL);
541
542     if (!btn1_transfer) {
543         XtAddEventHandler(widget, Button2MotionMask, FALSE,
544                 (XtEventHandler)&DtMailEditor::attachDragMotionHandler,
545                 (XtPointer)this);
546     }
547 }
548 #endif
549
550 void
551 DtMailEditor::stripCRLF(char **buffer, const char * buf, const unsigned long len)
552 {
553     char * out = *buffer;
554     int          _len;
555
556
557     for (const char * in = buf; in < (buf + len);) {
558         _len = mblen( in, MB_CUR_MAX );
559         if ( _len <= 0 )
560             break;
561         if ( ( _len == 1 ) && ( *in == '\r' ) ){
562             in += 1;
563             continue;
564         }
565         strncpy( out, in, _len );
566         out += _len, in += _len;
567     }
568     *out = 0;
569 }
570
571 void
572 DtMailEditor::needBuf(char **buffer, unsigned long *buflen, unsigned long newlen)
573 {
574     if (newlen > *buflen) {
575         // Need a bigger buffer.
576         if (*buffer) {
577             delete [] *buffer;
578         }
579
580         *buffer = new char[newlen];
581         *buflen = newlen;
582     } else {
583         // Clear buffer content -- get ready for new data
584         if (*buffer) {
585             memset(*buffer, 0, (unsigned int)*buflen);  
586         }
587         }
588 }
589
590 void
591 DtMailEditor::showAttachArea()
592 {
593     _showAttachArea = TRUE;
594     this->manageAttachArea();
595
596 }
597
598 void
599 DtMailEditor::hideAttachArea()
600 {
601     _showAttachArea = FALSE;
602     this->unmanageAttachArea();
603 }