libDtSvc: Change to ANSI function definitions
[oweals/cde.git] / cde / lib / DtWidget / EditCalls.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 /* $XConsortium: EditCalls.c /main/4 1996/03/26 19:53:27 drk $
24  **********************************<+>*************************************
25  ***************************************************************************
26  **
27  **  File:        EditCalls.c
28  **
29  **  Project:     DtEditor widget interface for text edit services.
30  **
31  **  Description: Contains the public functions related to undo,
32  **               cut, copy, paste, and the internal Modify/Verify callback.
33  **  -----------
34  **
35  *******************************************************************
36  * (c) Copyright 1996 Digital Equipment Corporation.
37  * (c) Copyright 1993, 1994, 1996 Hewlett-Packard Company.
38  * (c) Copyright 1993, 1994, 1996 International Business Machines Corp.
39  * (c) Copyright 1993, 1994, 1996 Sun Microsystems, Inc.
40  * (c) Copyright 1996 Novell, Inc. 
41  * (c) Copyright 1996 FUJITSU LIMITED.
42  * (c) Copyright 1996 Hitachi.
43  * (c) Copyright 1993, 1994 Unix System Labs, Inc., a subsidiary of Novell, Inc.
44  ********************************************************************
45  **
46  **
47  **************************************************************************
48  **********************************<+>*************************************/
49
50 #include "EditorP.h"
51 #include "DtWidgetI.h"
52 #include <Xm/XmPrivate.h>  /* _XmStringSourceGetString */
53
54 void
55 _DtEditorResetUndo(
56         DtEditorWidget editor)
57 {
58
59   /*
60    * Reset deletion & insertion contexts.
61    */
62
63   if( (M_deletedText(editor) != (char *)NULL) &&
64       (strlen(M_deletedText(editor)) != 0) )
65   {
66     XtFree(M_deletedText(editor));
67     M_deletedText(editor) = (char *) NULL;
68   } 
69     
70   M_deletionStart(editor) = NO_DELETION_IN_PROGRESS;
71   M_insertStart(editor) = 0;
72   M_insertionLength(editor) = 0;
73
74 } /* end _DtEditorResetUndo */
75
76
77 Boolean
78 DtEditorUndoEdit(
79         Widget widget)
80 {
81     DtEditorWidget pPriv = (DtEditorWidget) widget;
82     _DtWidgetToAppContext(widget);
83     _DtAppLock(app);
84
85     /*
86      * Remove any insertion, and then put back any previous deletion.  
87      * The tricky part is that removing the insertion looks like a deletion 
88      * and the modifyVerify callback will save in the undo buffer, wiping
89      * out any previous deletion (before we can reinsert it).  So we have to
90      * save the previous deletion before undoing the insertion so we can
91      * reinsert it.  Make sense?
92      */
93     if(M_insertionLength(pPriv) != 0)
94     {
95         /*
96          * There is an insertion so remove everything we just inserted
97          */
98         if( (M_deletedText(pPriv) != (char *)NULL) && 
99             (strlen(M_deletedText(pPriv)) != 0) )
100         {
101             /*
102              * If there is a current deletion, save it before removing the
103              * insertion so we can put it back (i.e. undo the deletion).
104              */
105             int oldDeleteStart = M_deletionStart(pPriv);
106             char *oldDeletion = M_deletedText(pPriv);
107             M_deletedText(pPriv) = (char *)NULL;
108
109             /*
110              * 1. Delete the last insertion.
111              */
112             XmTextSetSelection(M_text(pPriv), M_insertStart(pPriv), 
113                                M_insertStart(pPriv) + M_insertionLength(pPriv),
114                                CurrentTime);
115
116             M_insertionLength(pPriv)=0;
117             XmTextRemove(M_text(pPriv));
118
119             /*
120              * 2. Put back the previous deletion.
121              */
122             XmTextInsert(M_text(pPriv), oldDeleteStart, oldDeletion);
123
124             XtFree(oldDeletion);
125         }
126         else
127         {
128             /* 
129              * There is no deletion, so we just have to remove the 
130              * insertion.
131              */
132             XmTextSetSelection(M_text(pPriv), M_insertStart(pPriv), 
133                                M_insertStart(pPriv) + M_insertionLength(pPriv),
134                                CurrentTime);
135             M_insertionLength(pPriv) = 0;
136             XmTextRemove(M_text(pPriv));
137         }
138     }
139     else if(M_deletedText(pPriv) != (char *)NULL)
140     {
141         /*
142          * Nothing has been inserted so just undo the previous deletion.
143          */
144
145         char *oldDeletion = M_deletedText(pPriv);
146         M_deletedText(pPriv) = (char *)NULL;
147
148         XmTextInsert(M_text(pPriv), M_deletionStart(pPriv), oldDeletion);
149
150         XtFree(oldDeletion);
151     }
152     else
153     {
154       /* 
155        * There is no insertion to remove or deletion to put back in
156        * (i.e. nothing to undo) so return False.
157        */
158       _DtAppUnlock(app);
159       return(False);
160     }
161
162   _DtAppUnlock(app);
163   return(True);
164
165 } /* end DtEditorUndoEdit */
166
167 Boolean
168 DtEditorCutToClipboard(
169         Widget widget)
170 {
171
172     DtEditorWidget editor = (DtEditorWidget) widget;
173     XEvent *event;
174     _DtWidgetToAppContext(widget);
175     _DtAppLock(app);
176
177     /*
178      * Create an event with a correct timestamp
179      */
180     event = (XEvent *) XtMalloc( sizeof(XEvent) );
181     event->xkey.time = XtLastTimestampProcessed( M_display(editor) );
182
183     /*
184      * Call routine to cut selection to clipboard
185      */
186     XtCallActionProc(M_text(editor), "cut-clipboard", event, NULL, 0);
187
188     XtFree( (char *) event );
189
190     _DtAppUnlock(app);
191     return(True);
192
193 }
194
195 Boolean
196 DtEditorCopyToClipboard(
197         Widget widget)
198 {
199     DtEditorWidget editor = (DtEditorWidget) widget;
200     XEvent *event;
201     _DtWidgetToAppContext(widget);
202     _DtAppLock(app);
203
204     /*
205      * Create an event with a correct timestamp
206      */
207     event = (XEvent *) XtMalloc( sizeof(XEvent) );
208     event->xkey.time = XtLastTimestampProcessed( M_display(editor) );
209
210     /*
211      * Call routine to copy selection to clipboard
212      */
213     XtCallActionProc(M_text(editor), "copy-clipboard", event, NULL, 0);
214
215     XtFree( (char *) event );
216
217     _DtAppUnlock(app);
218     return(True);
219 }
220
221 Boolean
222 DtEditorPasteFromClipboard(
223         Widget widget)
224 {
225     DtEditorWidget editor = (DtEditorWidget) widget;
226     XEvent *event;
227     _DtWidgetToAppContext(widget);
228     _DtAppLock(app);
229
230     /*
231      * Create an event with a correct timestamp
232      */
233     event = (XEvent *) XtMalloc( sizeof(XEvent) );
234     event->xkey.time = XtLastTimestampProcessed( M_display(editor) );
235
236     /*
237      * Call routine to paste contents of clipboard at insertion cursor
238      */
239     XtCallActionProc(M_text(editor), "paste-clipboard", event, NULL, 0);
240
241     XtFree( (char *) event );
242
243     _DtAppUnlock(app);
244     return(True);
245
246 }
247
248
249 /*
250  * SetUndoDeletionState maintains the contents of editStuff.undo related
251  * to deletion (the deleted text & its original starting position).  
252  *
253  * SetUndoDeletionState can also reset/invalidate the undo contents related to
254  * insertion if it detects a "new" deletion.  The idea is to treat consecutive
255  * deletions as atomic from the viewpoint of undo.  A delete is 
256  * non-consecutive if it's start or end position is not coincidental with
257  * the last deletion.  One set of consecutive insertions is allowed following
258  * deletions, and will be undone by the DtEditorUndo().  Non-consecutive
259  * insertions will reset/invalidate the deletion undo buffer.
260  */
261 static void 
262 SetUndoDeletionState(
263         DtEditorWidget pPriv,
264         XmTextVerifyCallbackStruct *cb)
265 {
266     char *pDeletedText;
267
268     /* 
269      * Get the text which will be deleted from the text widget.
270      */
271     pDeletedText = (char *)_XmStringSourceGetString( 
272                                                 (XmTextWidget) M_text(pPriv), 
273                                                 cb->startPos, 
274                                                 cb->endPos, 
275                                                 False);
276
277     if( M_deletedText(pPriv) != (char *)NULL    && 
278         M_insertionLength(pPriv) == 0           &&
279         (cb->startPos == M_deletionStart(pPriv) || 
280          cb->endPos == M_deletionStart(pPriv))      
281       )
282     {
283         /*
284          * Continuation of the current deletion.  For a continuation, there
285          * must have been no intervening insertions, and we must be deleting
286          * from the same point, either forward or backward.
287          */
288         char *oldUndo = M_deletedText(pPriv);
289         M_deletedText(pPriv) = XtMalloc( strlen(M_deletedText(pPriv)) +
290                                          strlen(pDeletedText) + 1 );
291     
292         if(cb->startPos == M_deletionStart(pPriv)) {
293             /*
294              * deleting forward - deletionStart remains the same.
295              */
296             strcpy(M_deletedText(pPriv), oldUndo);
297             strcat(M_deletedText(pPriv), pDeletedText);
298         }
299         else {
300             /*
301              * deleting backward (e.g. Backspace)
302              */
303             strcpy(M_deletedText(pPriv), pDeletedText);
304             strcat(M_deletedText(pPriv), oldUndo);
305             M_deletionStart(pPriv) = cb->startPos;
306         }
307         XtFree(oldUndo);
308     }
309     else 
310     {
311         /*
312          * Starting a new deletion context.  Replace the old deletion 
313          * context, and remove the insertion context.
314          */
315         _DtEditorResetUndo( pPriv );
316
317         M_deletedText(pPriv) = XtMalloc(strlen(pDeletedText) + 1);
318     
319         strcpy(M_deletedText(pPriv), pDeletedText);
320         M_deletionStart(pPriv) = cb->startPos;
321         M_insertStart(pPriv) = cb->startPos;
322         M_insertionLength(pPriv) = 0;
323     }
324
325     XtFree( pDeletedText );
326 } /* SetUndoDeletionState */
327
328 /*
329  * SetUndoInsertionState maintains the contents of editStuff.undo related
330  * to insertions (the number of characters inserted & the position of the
331  * first one).
332  */
333 static void 
334 SetUndoInsertionState(
335         DtEditorWidget pPriv,
336         XmTextVerifyCallbackStruct *cb)
337 {
338     if(M_insertionLength(pPriv) == 0) {
339         /*
340          * We've started a new deletion context, so reset the insertion
341          * context.
342          */
343         M_insertStart(pPriv) = cb->startPos;
344         M_insertionLength(pPriv) = _DtEditor_CountCharacters(cb->text->ptr,
345                                                     cb->text->length);
346     }
347     else
348     {
349         /*
350          * Determine if we're continuing the current insertion context
351          * or beginning a new one.
352          */
353         if(cb->startPos == (M_insertStart(pPriv) + M_insertionLength(pPriv)))
354             M_insertionLength(pPriv) += _DtEditor_CountCharacters(
355                                             cb->text->ptr, cb->text->length);
356         else
357         {
358             /*
359              * We're starting a new insertion context, so invalidate any
360              * existing deletion context, and reset the insertion context.
361              */
362             _DtEditorResetUndo( pPriv );
363             M_insertStart(pPriv) = cb->startPos;
364             M_insertionLength(pPriv) = _DtEditor_CountCharacters(
365                                         cb->text->ptr, cb->text->length);
366         }
367     }
368 } /* SetUndoInsertionState */
369
370
371 /************************************************************************
372  *
373  *  _DtEditorModifyVerifyCB - The modify/verify callback
374  *
375  *      The modify verify callback handles incoming data and the data which
376  *      will be replaced.  The replaced data is saved (for later undos).
377  *
378  *  Parameters: 
379  *      widget      - the text widget
380  *      client_data - the edit area widget
381  *      call_data   - callback structure
382  *
383  ************************************************************************/
384
385 /* ARGSUSED */
386 void 
387 _DtEditorModifyVerifyCB(
388         Widget w,
389         caddr_t client_data,
390         caddr_t call_data )
391 {
392   register XmTextVerifyCallbackStruct * cb = 
393                                 (XmTextVerifyCallbackStruct *) call_data;
394   DtEditorWidget editor = (DtEditorWidget) client_data;
395
396   /*
397    * Loading all new data so no need to set up the data for later undos
398    */
399   if (M_loadingAllNewData(editor) == True)
400   {
401     _DtEditorResetUndo( editor );
402     M_unreadChanges(editor) = False;
403     M_loadingAllNewData(editor) = False;
404   }
405   else
406   { 
407
408     /* 
409      * Adding additional data, rather than replacing all of the contents.
410      *
411      * Mark that the contents have been modified since the last time the
412      * application requested a copy.
413      */
414     M_unreadChanges(editor) = True;
415
416     /*
417      * First, account for any data which will be removed by the new data.
418      * If text is being deleted, then grab a copy for later undo's.
419      */
420     if(cb->endPos > cb->startPos)
421       SetUndoDeletionState(editor, cb);
422
423     /*
424      * If text is being inserted, then change the undo insertion state.
425      */
426     if(cb->text->length > 0)
427         SetUndoInsertionState(editor, cb);
428
429   } 
430
431 } /* end ModifyVerifyCB */
432