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