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