1 /*
2  * Author:      William Chia-Wei Cheng (bill.cheng@acm.org)
3  *
4  * Copyright (C) 2001-2009, William Chia-Wei Cheng.
5  *
6  * This file may be distributed under the terms of the Q Public License
7  * as defined by Trolltech AS of Norway and appearing in the file
8  * LICENSE.QPL included in the packaging of this file.
9  *
10  * THIS FILE IS PROVIDED AS IS WITH NO WARRANTY OF ANY KIND, INCLUDING
11  * THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
12  * PURPOSE.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,
13  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
14  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
15  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
16  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  *
18  * @(#)$Header: /mm2/home/cvs/bc-src/tgif/cmd.c,v 1.15 2011/05/16 16:21:56 william Exp $
19  */
20 
21 #define _INCLUDE_FROM_CMD_C_
22 
23 #include "tgifdefs.h"
24 
25 #include "attr.e"
26 #include "choice.e"
27 #include "cmd.e"
28 #include "color.e"
29 #include "dialog.e"
30 #include "drawing.e"
31 #include "dup.e"
32 #include "file.e"
33 #include "grid.e"
34 #include "imgproc.e"
35 #include "mainmenu.e"
36 #include "mark.e"
37 #include "miniline.e"
38 #include "move.e"
39 #include "msg.e"
40 #include "navigate.e"
41 #include "obj.e"
42 #include "page.e"
43 #include "remote.e"
44 #include "select.e"
45 #include "setup.e"
46 #include "stk.e"
47 #include "strtbl.e"
48 #include "text.e"
49 #include "util.e"
50 #include "wb.e"
51 #include "xpixmap.e"
52 
53 int		recordCmdIncludeTgifObj=FALSE;
54 int		recordCmdUsesNewColormap=FALSE;
55 int		undoingOrRedoing=FALSE;
56 int		historyDepth=(-1), historyCount=0, defaultHistoryDepth=(-1);
57 struct CmdRec	*firstCmd=NULL, *lastCmd=NULL, *curCmd=NULL;
58 
59 struct SelRec	*topSelBeforeInCmd=NULL, *botSelBeforeInCmd=NULL;
60 int		*stackingPosition=NULL;
61 int		stackingCount=0;
62 int		stackingPositionHasIds=FALSE;
63 
64 int		composingCommand=FALSE;
65 int		undoRedoRestoreDrawingMode=FALSE;
66 Colormap	preparedColormap=None;
67 
InsertCmd(PrevCmd,NextCmd,CmdPtr,ppFirstCmd,ppLastCmd)68 void InsertCmd(PrevCmd, NextCmd, CmdPtr, ppFirstCmd, ppLastCmd)
69    struct CmdRec *PrevCmd, *NextCmd, *CmdPtr;
70    struct CmdRec **ppFirstCmd, **ppLastCmd;
71    /* add CmdPtr between PrevCmd and NextCmd */
72    /* update *ppFirstCmd and *ppLastCmd */
73 {
74    CmdPtr->prev = PrevCmd;
75    CmdPtr->next = NextCmd;
76 
77    if (PrevCmd == NULL) {
78       (*ppFirstCmd) = CmdPtr;
79    } else {
80       PrevCmd->next = CmdPtr;
81    }
82    if (NextCmd == NULL) {
83       (*ppLastCmd) = CmdPtr;
84    } else {
85       NextCmd->prev = CmdPtr;
86    }
87 }
88 
89 static
GetCmdType(CmdType)90 char *GetCmdType(CmdType)
91    int CmdType;
92 {
93    switch (CmdType) {
94    case CMD_COMPOSITE: return "CMD_COMPOSITE";
95    case CMD_NEW: return "CMD_NEW";
96    case CMD_DELETE: return "CMD_DELETE";
97    case CMD_MOVE: return "CMD_MOVE";
98    case CMD_STRETCH: return "CMD_STRETCH";
99    case CMD_ONE_TO_MANY: return "CMD_ONE_TO_MANY";
100    case CMD_MANY_TO_ONE: return "CMD_MANY_TO_ONE";
101    case CMD_REPLACE: return "CMD_REPLACE";
102    case CMD_GOTO_PAGE: return "CMD_GOTO_PAGE";
103    case CMD_WB_CLEARALL: return "CMD_WB_CLEARALL";
104    case CMD_CHAT_A_LINE: return "CMD_CHAT_A_LINE";
105    case CMD_WB_SLIDESHOW: return "CMD_WB_SLIDESHOW";
106    default: return "(unknown)";
107    }
108 }
109 
110 static
CreateCmdFromSerializationBuf(cmd_type,buf)111 struct CmdRec *CreateCmdFromSerializationBuf(cmd_type, buf)
112    int cmd_type;
113    char *buf;
114 {
115    struct CmdRec *cmd_ptr=NULL;
116 
117    cmd_ptr = (struct CmdRec *)malloc(sizeof(struct CmdRec));
118    if (cmd_ptr == NULL) FailAllocMessage();
119    memset(cmd_ptr, 0, sizeof(struct CmdRec));
120 
121    cmd_ptr->type = cmd_type;
122    cmd_ptr->serialization_buf = UtilStrDup(buf);
123    if (cmd_ptr->serialization_buf == NULL) FailAllocMessage();
124 
125    return cmd_ptr;
126 }
127 
CopyAndInsertCmd(from_remote,serialization_buf,PrevCmd,NextCmd,CmdPtr,ppFirstCmd,ppLastCmd)128 void CopyAndInsertCmd(from_remote, serialization_buf, PrevCmd, NextCmd, CmdPtr,
129       ppFirstCmd, ppLastCmd)
130    int from_remote;
131    char *serialization_buf;
132    struct CmdRec *PrevCmd, *NextCmd, *CmdPtr;
133    struct CmdRec **ppFirstCmd, **ppLastCmd;
134    /* add CmdPtr between PrevCmd and NextCmd */
135    /* update *ppFirstCmd and *ppLastCmd */
136 {
137    if ((!from_remote && CmdPtr->undone) || (from_remote && !CmdPtr->undone)) {
138       sprintf(gszMsgBox, "(%s) Found an %s command, type = %s.",
139             (from_remote ? "from_remote" : "local"),
140             (CmdPtr->undone ? "undone" : "not undone"),
141             GetCmdType(CmdPtr->type));
142       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
143    } else {
144       struct CmdRec *cmd_ptr=NULL;
145 
146 #ifdef _TGIF_DBG_WB2
147       fprintf(stdout,
148             "<*><*><*>(%s) CopyAndInsertCmd() a command of type: %s.\n",
149             (from_remote ? "from_remote" : "local"),
150             GetCmdType(CmdPtr->type));
151       fflush(stdout);
152 #endif /* _TGIF_DBG_WB2 */
153 
154       cmd_ptr = CreateCmdFromSerializationBuf(CmdPtr->type, serialization_buf);
155 
156       cmd_ptr->prev = PrevCmd;
157       cmd_ptr->next = NextCmd;
158 
159       if (PrevCmd == NULL) {
160          (*ppFirstCmd) = cmd_ptr;
161       } else {
162          PrevCmd->next = cmd_ptr;
163       }
164       if (NextCmd == NULL) {
165          (*ppLastCmd) = cmd_ptr;
166       } else {
167          NextCmd->prev = cmd_ptr;
168       }
169    }
170 }
171 
172 static
UnlinkCmd(CmdPtr,ppFirstCmd,ppLastCmd)173 void UnlinkCmd(CmdPtr, ppFirstCmd, ppLastCmd)
174    struct CmdRec *CmdPtr;
175    struct CmdRec **ppFirstCmd, **ppLastCmd;
176 {
177    if ((*ppFirstCmd) == CmdPtr) {
178       (*ppFirstCmd) = CmdPtr->next;
179    } else {
180       CmdPtr->prev->next = CmdPtr->next;
181    }
182    if ((*ppLastCmd) == CmdPtr) {
183       (*ppLastCmd) = CmdPtr->prev;
184    } else {
185       CmdPtr->next->prev = CmdPtr->prev;
186    }
187 }
188 
DeleteARedoRecord(CmdPtr,percent_start,percent_end)189 void DeleteARedoRecord(CmdPtr, percent_start, percent_end)
190    struct CmdRec *CmdPtr;
191    double percent_start, percent_end;
192 {
193    struct SelRec *sel_ptr=NULL, *next_sel=NULL;
194    struct CmdRec *cmd_ptr=NULL, *prev_cmd=NULL;
195    int num_records=0;
196    double inc=0;
197 
198    if (CmdPtr->pos_before_has_ids) {
199       if (CmdPtr->pos_before != NULL) {
200          int i=0;
201          char **s_ptr=(char**)(CmdPtr->pos_before);
202 
203          for (i=0; i < CmdPtr->count_before; i++) {
204             if (s_ptr[i] != NULL) {
205                UtilFree(s_ptr[i]);
206             } else {
207                break;
208             }
209          }
210          free(CmdPtr->pos_before);
211       }
212    } else {
213       if (CmdPtr->pos_before != NULL) free(CmdPtr->pos_before);
214    }
215    if (CmdPtr->pos_after != NULL) free(CmdPtr->pos_after);
216 
217    if (percent_start >= 0.0) {
218       sprintf(gszMsgBox, TgLoadCachedString(CSTID_FLUSH_UNDO_PERCENT),
219             round(percent_start));
220       SetStringStatus(gszMsgBox);
221       CheckInterrupt(TRUE);
222    }
223    switch (CmdPtr->type) {
224    case CMD_NEW:
225       for (sel_ptr=CmdPtr->top_after; sel_ptr!=NULL; sel_ptr=next_sel) {
226          next_sel = sel_ptr->next;
227          if (CmdPtr->undone) FreeObj(sel_ptr->obj);
228          free(sel_ptr);
229       }
230       break;
231    case CMD_DELETE:
232       for (sel_ptr=CmdPtr->top_before; sel_ptr!=NULL; sel_ptr=next_sel) {
233          next_sel = sel_ptr->next;
234          if (!CmdPtr->undone) FreeObj(sel_ptr->obj);
235          free(sel_ptr);
236       }
237       break;
238    case CMD_MOVE:
239       for (sel_ptr=CmdPtr->top_before; sel_ptr!=NULL; sel_ptr=next_sel) {
240          next_sel = sel_ptr->next;
241          free(sel_ptr);
242       }
243       if (CmdPtr->subcmd != NULL) {
244          free(CmdPtr->subcmd);
245       }
246       break;
247    case CMD_STRETCH:
248       for (sel_ptr=CmdPtr->top_before; sel_ptr!=NULL; sel_ptr=next_sel) {
249          next_sel = sel_ptr->next;
250          FreeObj(sel_ptr->obj);
251          free(sel_ptr);
252       }
253       if (CmdPtr->serialized) {
254          for (sel_ptr=CmdPtr->top_after; sel_ptr!=NULL; sel_ptr=next_sel) {
255             next_sel = sel_ptr->next;
256             free(sel_ptr);
257          }
258       }
259       break;
260    case CMD_REPLACE:
261       for (sel_ptr=CmdPtr->top_before; sel_ptr!=NULL; sel_ptr=next_sel) {
262          next_sel = sel_ptr->next;
263          FreeObj(sel_ptr->obj);
264          free(sel_ptr);
265       }
266       if (CmdPtr->serialized) {
267          for (sel_ptr=CmdPtr->top_after; sel_ptr!=NULL; sel_ptr=next_sel) {
268             next_sel = sel_ptr->next;
269             free(sel_ptr);
270          }
271       }
272       break;
273    case CMD_ONE_TO_MANY:
274       for (sel_ptr=CmdPtr->top_before; sel_ptr!=NULL; sel_ptr=next_sel) {
275          next_sel = sel_ptr->next;
276          if (!CmdPtr->undone) FreeObj(sel_ptr->obj);
277          free(sel_ptr);
278       }
279       for (sel_ptr=CmdPtr->top_after; sel_ptr!=NULL; sel_ptr=next_sel) {
280          next_sel = sel_ptr->next;
281          if (CmdPtr->undone) FreeObj(sel_ptr->obj);
282          free(sel_ptr);
283       }
284       break;
285    case CMD_MANY_TO_ONE:
286       for (sel_ptr=CmdPtr->top_before; sel_ptr!=NULL; sel_ptr=next_sel) {
287          next_sel = sel_ptr->next;
288          if (!CmdPtr->undone) FreeObj(sel_ptr->obj);
289          free(sel_ptr);
290       }
291       for (sel_ptr=CmdPtr->top_after; sel_ptr!=NULL; sel_ptr=next_sel) {
292          next_sel = sel_ptr->next;
293          if (CmdPtr->undone) FreeObj(sel_ptr->obj);
294          free(sel_ptr);
295       }
296       break;
297    case CMD_COMPOSITE:
298       if (percent_start >= 0) {
299          for (cmd_ptr=CmdPtr->first; cmd_ptr!=NULL; cmd_ptr=cmd_ptr->next) {
300             num_records++;
301          }
302          if (num_records > 0) {
303             inc = (percent_end-percent_start)/((double)num_records);
304          }
305       }
306       for (cmd_ptr=CmdPtr->last; cmd_ptr!=NULL; cmd_ptr=prev_cmd) {
307          prev_cmd = cmd_ptr->prev;
308          if (percent_start >= 0) {
309             DeleteARedoRecord(cmd_ptr, percent_start,
310                   min(((double)percent_start+inc),((double)100.0)));
311             percent_start += inc;
312          } else {
313             DeleteARedoRecord(cmd_ptr, percent_start, percent_end);
314          }
315       }
316       break;
317    case CMD_CHAT_A_LINE:
318       if (CmdPtr->subcmd != NULL) {
319          UtilFree(CmdPtr->subcmd->detail.chat.buf);
320          free(CmdPtr->subcmd);
321       }
322       break;
323    case CMD_WB_SLIDESHOW:
324       if (CmdPtr->subcmd != NULL) free(CmdPtr->subcmd);
325       break;
326    case CMD_WB_CLEARALL:
327       if (CmdPtr->subcmd != NULL) free(CmdPtr->subcmd);
328       break;
329    }
330    if (gstWBInfo.do_whiteboard) {
331       if (CmdPtr->sender_process_id != NULL) {
332          UtilFree(CmdPtr->sender_process_id);
333       }
334       if (CmdPtr->serialization_buf != NULL) {
335          UtilFree(CmdPtr->serialization_buf);
336       }
337    }
338    free(CmdPtr);
339 }
340 
ClearRedoRecords(CmdPtr)341 void ClearRedoRecords(CmdPtr)
342    struct CmdRec *CmdPtr;
343 {
344    struct CmdRec *cmd_ptr=NULL, *prev_cmd=NULL;
345 
346    for (cmd_ptr=lastCmd; cmd_ptr != curCmd; cmd_ptr=prev_cmd) {
347       prev_cmd = cmd_ptr->prev;
348       DeleteARedoRecord(cmd_ptr, (-1.0), (-1.0));
349       historyCount--;
350    }
351    if ((lastCmd=curCmd) == NULL) firstCmd = NULL;
352 }
353 
CleanUpCmds()354 void CleanUpCmds()
355 {
356    struct CmdRec *cmd_ptr=NULL, *prev_cmd=NULL;
357    int num_records=0;
358 
359    for (cmd_ptr=lastCmd; cmd_ptr != NULL; cmd_ptr=cmd_ptr->prev) {
360       num_records++;
361    }
362    if (num_records > 0) {
363       double inc=(100.0/((double)num_records)), percent_start=0.0;
364 
365       ShowInterrupt(1);
366       SaveStatusStrings();
367       for (cmd_ptr=lastCmd; cmd_ptr != NULL; cmd_ptr=prev_cmd,
368             percent_start += inc) {
369          prev_cmd = cmd_ptr->prev;
370          DeleteARedoRecord(cmd_ptr, percent_start,
371                min(((double)percent_start+inc),((double)100.0)));
372       }
373       RestoreStatusStrings();
374       HideInterrupt();
375    }
376    firstCmd = lastCmd = curCmd = NULL;
377    historyCount = 0;
378 }
379 
CopySel(from_top_sel,count,to_top_sel,to_bot_sel)380 void CopySel(from_top_sel, count, to_top_sel, to_bot_sel)
381    struct SelRec *from_top_sel;
382    int count;
383    struct SelRec **to_top_sel, **to_bot_sel;
384 {
385    struct SelRec *sel_ptr=NULL;
386 
387    *to_top_sel = *to_bot_sel = NULL;
388 
389    for (sel_ptr=from_top_sel; sel_ptr!=NULL; sel_ptr=sel_ptr->next, count--) {
390       AddObjIntoSel(sel_ptr->obj, *to_bot_sel, NULL, to_top_sel, to_bot_sel);
391    }
392    if (count != 0) {
393       FatalUnexpectedError(
394             "Count != 0 in CopySel().", /* debug, do not translate */
395             TgLoadString(STID_UNDO_REDO_MAY_CAUSE_CRASH));
396    }
397 }
398 
399 static
LinkJustTheObjects(TopSel,BotSel)400 void LinkJustTheObjects(TopSel, BotSel)
401    struct SelRec *TopSel, *BotSel;
402 {
403    struct SelRec *sel_ptr=NULL, *sel_ptr_next=NULL;
404 
405    sel_ptr = TopSel;
406    sel_ptr_next = TopSel->next;
407    sel_ptr->obj->prev = NULL;
408    for ( ; sel_ptr_next!=NULL; sel_ptr=sel_ptr_next,
409          sel_ptr_next=sel_ptr->next) {
410       sel_ptr->obj->next = sel_ptr_next->obj;
411       sel_ptr_next->obj->prev = sel_ptr->obj;
412    }
413    sel_ptr->obj->next = NULL;
414 }
415 
416 static
UndoNewCmd(CmdPtr)417 void UndoNewCmd(CmdPtr)
418    struct CmdRec *CmdPtr;
419 {
420    struct SelRec *sel_ptr=NULL;
421    struct ObjRec *obj_ptr=NULL, *next_obj=NULL;
422    int pos=0, count=0;
423 
424    topSel = CmdPtr->top_after;
425    botSel = CmdPtr->bot_after;
426 
427    sel_ptr = topSel;
428    pos = count = 0;
429    for (obj_ptr=topObj; obj_ptr != NULL; obj_ptr=next_obj, pos++) {
430       next_obj = obj_ptr->next;
431       if (pos == CmdPtr->pos_after[count]) {
432          count++;
433          sel_ptr->obj = obj_ptr;
434          UnlinkObj(obj_ptr);
435          sel_ptr = sel_ptr->next;
436          if (count == CmdPtr->count_after) break;
437       }
438    }
439    LinkJustTheObjects(CmdPtr->top_after, CmdPtr->bot_after);
440 
441    UpdSelBBox();
442    topSel = botSel = NULL;
443    RedrawAnArea(botObj, selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
444          selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
445    SetFileModified(TRUE);
446    justDupped = FALSE;
447 }
448 
449 static
RedoNewCmd(CmdPtr)450 void RedoNewCmd(CmdPtr)
451    struct CmdRec *CmdPtr;
452 {
453    struct ObjRec *saved_top_obj, *saved_bot_obj;
454 
455    CopySel(CmdPtr->top_after, CmdPtr->count_after, &topSel, &botSel);
456 
457    saved_top_obj = topObj;
458    saved_bot_obj = botObj;
459 
460    curPage->top = topObj = CmdPtr->top_after->obj;
461    curPage->bot = botObj = CmdPtr->bot_after->obj;
462 
463    AdjSplineVs();
464    AdjCaches();
465 
466    curPage->top = topObj = saved_top_obj;
467    curPage->bot = botObj = saved_bot_obj;
468 
469    if (topObj == NULL) {
470       curPage->bot = botObj = botSel->obj;
471    } else {
472       topObj->prev = botSel->obj;
473    }
474    botSel->obj->next = topObj;
475    curPage->top = topObj = topSel->obj;
476 
477    UpdSelBBox();
478    RedrawAnArea(botObj, selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
479          selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
480    if (!deserializingFile) HighLightForward();
481    SetFileModified(TRUE);
482    justDupped = FALSE;
483 }
484 
485 static
ObjHasFullID(obj_ptr,psz_full_id)486 int ObjHasFullID(obj_ptr, psz_full_id)
487    struct ObjRec *obj_ptr;
488    char *psz_full_id;
489 {
490    if (*psz_full_id != '#') {
491       if (gstWBInfo.do_whiteboard) { /* debug, do not translate */
492          sprintf(gszMsgBox, "%s, psz_full_id = %s, pid = %ld.",
493                "Unexpected *psz_full_id != '#' in ObjHasFullID()",
494                psz_full_id, gstWBInfo.pid);
495          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
496       }
497    }
498    psz_full_id = strchr(psz_full_id, ':');
499    psz_full_id++;
500    if (obj_ptr->creator_full_id == NULL) {
501       char buf[MAXSTRING];
502 
503       sprintf(buf, "%1d/%s", obj_ptr->id, gszLocalPID);
504       return (strcmp(buf, psz_full_id) == 0);
505    }
506    return (strcmp(obj_ptr->creator_full_id, psz_full_id) == 0);
507 }
508 
509 static
GetPositionFromFullID(psz_full_id)510 int GetPositionFromFullID(psz_full_id)
511    char *psz_full_id;
512 {
513    int pos_to_match=INVALID;
514    char *psz=NULL;
515 
516    if (*psz_full_id != '#') {
517       if (gstWBInfo.do_whiteboard) { /* debug, do not translate */
518          sprintf(gszMsgBox, "%s, psz_full_id = %s, pid = %ld.",
519                "Unexpected *psz_full_id != '#' in GetPositionFromFullID()",
520                psz_full_id, gstWBInfo.pid);
521          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
522       }
523    }
524    psz = strchr(++psz_full_id, ':');
525    *psz = '\0';
526    if (sscanf(psz_full_id, "%d", &pos_to_match) != 1 ||
527          pos_to_match == INVALID) {
528       if (gstWBInfo.do_whiteboard) { /* debug, do not translate */
529          sprintf(gszMsgBox, "%s %s (%s), psz_full_id = %s, pid = %ld.",
530                "Format error in parsing pos_to_match in",
531                "GetPositionFromFullID()", "cannot get pos_to_match",
532                psz_full_id, gstWBInfo.pid);
533          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
534       }
535    }
536    *psz = ':';
537 
538    return pos_to_match;
539 }
540 
541 static
UndoDeleteCmd(CmdPtr)542 void UndoDeleteCmd(CmdPtr)
543    struct CmdRec *CmdPtr;
544 {
545    struct SelRec *sel_ptr=NULL;
546    struct ObjRec *obj_ptr=NULL, *next_obj=NULL;
547    struct ObjRec *saved_top_obj=NULL, *saved_bot_obj=NULL;
548    int count=0, pos=0, pos_to_match=INVALID;
549 
550    CopySel(CmdPtr->top_before, CmdPtr->count_before, &topSel, &botSel);
551 
552    saved_top_obj = topObj;
553    saved_bot_obj = botObj;
554 
555    LinkJustTheObjects(CmdPtr->top_before, CmdPtr->bot_before);
556 
557    curPage->top = topObj = CmdPtr->top_before->obj;
558    curPage->bot = botObj = CmdPtr->bot_before->obj;
559 
560    AdjSplineVs();
561    AdjCaches();
562 
563    curPage->top = topObj = saved_top_obj;
564    curPage->bot = botObj = saved_bot_obj;
565 
566    count = 0;
567    sel_ptr = topSel;
568 
569    if (CmdPtr->pos_before_has_ids) {
570       pos_to_match = GetPositionFromFullID(
571             (char*)(long)(CmdPtr->pos_before[count]));
572    } else {
573       pos_to_match = CmdPtr->pos_before[count];
574    }
575    for (obj_ptr=topObj; obj_ptr != NULL; obj_ptr=next_obj, pos++) {
576       if (pos == pos_to_match) {
577          AddObj(obj_ptr->prev, obj_ptr, sel_ptr->obj);
578          next_obj = obj_ptr;
579          count++;
580          sel_ptr = sel_ptr->next;
581          if (count == CmdPtr->count_before) break;
582 
583          if (CmdPtr->pos_before_has_ids) {
584             pos_to_match = GetPositionFromFullID(
585                   (char*)(long)(CmdPtr->pos_before[count]));
586          } else {
587             pos_to_match = CmdPtr->pos_before[count];
588          }
589       } else {
590          next_obj = obj_ptr->next;
591       }
592    }
593    for ( ; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
594       AddObj(botObj, NULL, sel_ptr->obj);
595    }
596    UpdSelBBox();
597    RedrawAnArea(botObj, selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
598          selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
599    if (!deserializingFile) HighLightForward();
600    SetFileModified(TRUE);
601    justDupped = FALSE;
602 }
603 
604 static
SetupBeforePointers(CmdPtr,modify_before_pointers,pp_actual_pos_before)605 int SetupBeforePointers(CmdPtr, modify_before_pointers, pp_actual_pos_before)
606    struct CmdRec *CmdPtr;
607    int modify_before_pointers, **pp_actual_pos_before;
608    /*
609     * CmdPtr->pos_before is an array of strings containing
610     *    "#<pos>:<oid>/<pid>:<hostname>".
611     * CmdPtr->count_before is assumed to be the number of elements in
612     *    CmdPtr->pos_before.
613     *
614     * if (modify_before_pointers) {
615     *    Setup CmdPtr->top_before and CmdPtr->bot_before.
616     *    For these cases, <oid>/<pid>:<hostname> is used to locate the
617     *          object positions to build the actual before pointers.
618     * } else {
619     *    This can only happen if (CmdPtr->serialized &&
620     *          CmdPtr->type == CMD_REPLACE && !CmdPtr->undone)
621     *    In this case, the #<pos> should be used to setup up the actual
622     *          before pointers.
623     * }
624     * if (pp_actual_pos_before != NULL) {
625     *    Create an array of integers containing the positions of the
626     *    objects referenced in CmdPtr->pos_before and return this array.
627     * }
628     */
629 {
630    int index, ok=TRUE, cur_pos=0;
631    struct ObjRec *obj_ptr=topObj;
632    struct SelRec *sel_ptr=NULL, *next_sel=NULL, *top_sel=NULL, *bot_sel=NULL;
633 
634    if (modify_before_pointers) {
635       for (sel_ptr=CmdPtr->top_before; sel_ptr != NULL; sel_ptr=next_sel) {
636          next_sel = sel_ptr->next;
637          free(sel_ptr);
638       }
639       CmdPtr->top_before = CmdPtr->bot_before = NULL;
640    }
641    if (pp_actual_pos_before != NULL) {
642       *pp_actual_pos_before = (int*)malloc(CmdPtr->count_before*sizeof(int));
643       if (*pp_actual_pos_before == NULL) FailAllocMessage();
644       memset(*pp_actual_pos_before, 0, CmdPtr->count_before*sizeof(int));
645    }
646    for (index=0; index < CmdPtr->count_before; index++) {
647       char *psz_full_id=(((char**)CmdPtr->pos_before)[index]), *psz=NULL;
648       char pid_and_host[MAXSTRING];
649       int id=(-1), found=FALSE, pos_to_match=INVALID, obj_is_tgifobj=FALSE;
650 
651       if (*psz_full_id == '#') {
652          psz = strchr(++psz_full_id, ':');
653          *psz = '\0';
654          if (!modify_before_pointers) {
655             if (sscanf(psz_full_id, "%d", &pos_to_match) != 1 ||
656                   pos_to_match == INVALID) {
657                if (gstWBInfo.do_whiteboard) { /* debug, do not translate */
658                   sprintf(gszMsgBox, "%s %s (%s), psz_full_id = %s, pid = %ld.",
659                         "Format error in parsing pos_to_match in",
660                         "SetupBeforePointers()", "cannot get pos_to_match",
661                         psz_full_id, gstWBInfo.pid);
662                   MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
663                }
664             }
665          }
666          *psz++ = ':';
667          psz_full_id = psz;
668       }
669       psz = strchr(psz_full_id, '/');
670       *pid_and_host = '\0';
671       if (psz == NULL) {
672          if (gstWBInfo.do_whiteboard) { /* debug, do not translate */
673             sprintf(gszMsgBox, "%s (%s), psz_full_id = %s, pid = %ld.",
674                   "Format error in parsing id in SetupBeforePointers()",
675                   "cannot find ':'", psz_full_id, gstWBInfo.pid);
676             MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
677          }
678          ok = FALSE;
679          break;
680       } else {
681          *psz = '\0';
682          if (sscanf(psz_full_id, "%d", &id) == 1) {
683             strcpy(pid_and_host, &psz[1]);
684             if (id == INVALID) {
685                obj_is_tgifobj = TRUE;
686             }
687          } else {
688             if (gstWBInfo.do_whiteboard) { /* debug, do not translate */
689                sprintf(gszMsgBox, "%s (%s), psz_full_id = %s, pid = %ld.",
690                      "Format error in parsing id in SetupBeforePointers()",
691                      "cannot find id", psz_full_id, gstWBInfo.pid);
692                MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
693             }
694             ok = FALSE;
695             break;
696          }
697          *psz = '/';
698       }
699       if (ok) {
700          for (obj_ptr=topObj, cur_pos=0 ; ok && obj_ptr != NULL;
701                obj_ptr=obj_ptr->next, cur_pos++) {
702             if (pos_to_match == INVALID) {
703                if (obj_ptr->creator_full_id == NULL) {
704                   if (obj_ptr->id == id &&
705                         strcmp(pid_and_host, gszLocalPID) == 0) {
706                      found = TRUE;
707                   } else if (obj_ptr->id == INVALID &&
708                         CmdPtr->include_tgif_obj && obj_is_tgifobj) {
709                      found = TRUE;
710                   }
711                } else {
712                   if (strcmp(obj_ptr->creator_full_id, psz_full_id) == 0) {
713                      found = TRUE;
714                   }
715                }
716             } else if (pos_to_match == cur_pos) {
717                found = TRUE;
718             }
719             if (found) {
720                if (modify_before_pointers) {
721                   AddObjIntoSel(obj_ptr, bot_sel, NULL, &top_sel, &bot_sel);
722                }
723                if (pp_actual_pos_before != NULL) {
724                   (*pp_actual_pos_before)[index] = cur_pos;
725                }
726                obj_ptr = obj_ptr->next;
727                cur_pos++;
728                break;
729             }
730          }
731       }
732       if (ok && !found) {
733          if (gstWBInfo.do_whiteboard) { /* debug, do not translate */
734 #ifdef _TGIF_DBG_WB2 /* debug, do not translate */
735             fprintf(stderr, "%s: '%s' %s, pid = %ld.\n\t(%s=%1d, %s='%s')\n",
736                   "@*@ Cannot find object id", psz_full_id,
737                   "in SetupBeforePointers()", gstWBInfo.pid, "id", id,
738                   "pid_and_host", pid_and_host);
739 #endif /* _TGIF_DBG_WB2 */
740          }
741          ok = FALSE;
742          break;
743       }
744    }
745    if (ok && index != CmdPtr->count_before) {
746       if (gstWBInfo.do_whiteboard) { /* debug, do not translate */
747          sprintf(gszMsgBox, "%s (%1d) %s (%1d) %s, pid = %ld.",
748                "Index", index, "!= CmdPtr->count_before", CmdPtr->count_before,
749                "in SetupBeforePointers()", gstWBInfo.pid);
750          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
751       }
752       ok = FALSE;
753    }
754    if (!ok) {
755       for (sel_ptr=top_sel; sel_ptr != NULL; sel_ptr=next_sel) {
756          next_sel = sel_ptr->next;
757          free(sel_ptr);
758       }
759       if (pp_actual_pos_before != NULL) {
760          free(*pp_actual_pos_before);
761          *pp_actual_pos_before = NULL;
762       }
763    } else {
764       if (modify_before_pointers) {
765          CmdPtr->top_before = top_sel;
766          CmdPtr->bot_before = bot_sel;
767       }
768    }
769    return ok;
770 }
771 
772 static
RedoDeleteCmd(CmdPtr)773 int RedoDeleteCmd(CmdPtr)
774    struct CmdRec *CmdPtr;
775 {
776    struct SelRec *sel_ptr;
777 
778    if (CmdPtr->serialized) {
779       if (CmdPtr->include_tgif_obj) {
780          AddObj(NULL, topObj, tgifObj);
781       }
782       if (!SetupBeforePointers(CmdPtr, TRUE, NULL)) {
783          if (CmdPtr->include_tgif_obj) {
784             UnlinkObj(topObj);
785          }
786          return FALSE;
787       }
788       if (CmdPtr->include_tgif_obj) {
789          UnlinkObj(topObj);
790       }
791    }
792    topSel = CmdPtr->top_before;
793    botSel = CmdPtr->bot_before;
794 
795    sel_ptr = topSel;
796    if (CmdPtr->serialized) {
797       for ( ; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
798          UnlinkObj(sel_ptr->obj);
799       }
800    } else {
801       struct ObjRec *obj_ptr=NULL, *next_obj=NULL;
802       int count=0;
803 
804       if (CmdPtr->pos_before_has_ids) {
805          for (obj_ptr=topObj; obj_ptr != NULL; obj_ptr=next_obj) {
806             next_obj = obj_ptr->next;
807             if (ObjHasFullID(obj_ptr,
808                   (char*)(long)(CmdPtr->pos_before[count]))) {
809                count++;
810                sel_ptr->obj = obj_ptr;
811                UnlinkObj(obj_ptr);
812                sel_ptr = sel_ptr->next;
813                if (count == CmdPtr->count_before) break;
814             }
815          }
816       } else {
817          int pos=0;
818 
819          for (obj_ptr=topObj; obj_ptr != NULL; obj_ptr=next_obj, pos++) {
820             next_obj = obj_ptr->next;
821             if (pos == CmdPtr->pos_before[count]) {
822                count++;
823                sel_ptr->obj = obj_ptr;
824                UnlinkObj(obj_ptr);
825                sel_ptr = sel_ptr->next;
826                if (count == CmdPtr->count_before) break;
827             }
828          }
829       }
830    }
831    LinkJustTheObjects(CmdPtr->top_before, CmdPtr->bot_before);
832 
833    UpdSelBBox();
834    topSel = botSel = NULL;
835    RedrawAnArea(botObj, selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
836          selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
837    SetFileModified(TRUE);
838    justDupped = FALSE;
839    return TRUE;
840 }
841 
842 static
UndoOrRedoMoveCmd(CmdPtr)843 int UndoOrRedoMoveCmd(CmdPtr)
844    struct CmdRec *CmdPtr;
845 {
846    struct SelRec *sel_ptr;
847    struct ObjRec *obj_ptr;
848    int dx, dy;
849 
850    if (CmdPtr->serialized) {
851       if (CmdPtr->include_tgif_obj) {
852          AddObj(NULL, topObj, tgifObj);
853       }
854       if (!SetupBeforePointers(CmdPtr, TRUE, NULL)) {
855          if (CmdPtr->include_tgif_obj) {
856             UnlinkObj(topObj);
857          }
858          return FALSE;
859       }
860       if (CmdPtr->include_tgif_obj) {
861          UnlinkObj(topObj);
862       }
863    }
864    dx = (CmdPtr->undone) ? CmdPtr->subcmd->detail.move.dx :
865            -(CmdPtr->subcmd->detail.move.dx);
866    dy = (CmdPtr->undone) ? CmdPtr->subcmd->detail.move.dy :
867            -(CmdPtr->subcmd->detail.move.dy);
868 
869    CopySel(CmdPtr->top_before, CmdPtr->count_before, &topSel, &botSel);
870 
871    sel_ptr = topSel;
872    if (CmdPtr->serialized) {
873       for ( ; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
874          obj_ptr = sel_ptr->obj;
875          if (!obj_ptr->locked) MoveObj(obj_ptr, dx, dy);
876       }
877    } else {
878       struct ObjRec *next_obj;
879       int count=0;
880 
881       if (CmdPtr->pos_before_has_ids) {
882          for (obj_ptr=topObj; obj_ptr != NULL; obj_ptr=next_obj) {
883             next_obj = obj_ptr->next;
884             if (ObjHasFullID(obj_ptr,
885                   (char*)(long)(CmdPtr->pos_before[count]))) {
886                count++;
887                sel_ptr->obj = obj_ptr;
888                if (!obj_ptr->locked) MoveObj(obj_ptr, dx, dy);
889                sel_ptr = sel_ptr->next;
890                if (count == CmdPtr->count_before) break;
891             }
892          }
893       } else {
894          int pos=0;
895 
896          for (obj_ptr=topObj; obj_ptr != NULL; obj_ptr=next_obj, pos++) {
897             next_obj = obj_ptr->next;
898             if (pos == CmdPtr->pos_before[count]) {
899                count++;
900                sel_ptr->obj = obj_ptr;
901                if (!obj_ptr->locked) MoveObj(obj_ptr, dx, dy);
902                sel_ptr = sel_ptr->next;
903                if (count == CmdPtr->count_before) break;
904             }
905          }
906       }
907    }
908    UpdSelBBox();
909    RedrawAreas(botObj, selLtX-GRID_ABS_SIZE(1)-dx, selLtY-GRID_ABS_SIZE(1)-dy,
910          selRbX+GRID_ABS_SIZE(1)-dx, selRbY+GRID_ABS_SIZE(1)-dy,
911          selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
912          selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
913    if (!deserializingFile) HighLightForward();
914    SetFileModified(TRUE);
915    justDupped = FALSE;
916    return TRUE;
917 }
918 
919 static
UpdateXPmObjectsInACmd(CmdPtr)920 void UpdateXPmObjectsInACmd(CmdPtr)
921    struct CmdRec *CmdPtr;
922 {
923    struct CmdRec *cmd_ptr;
924    struct SelRec *sel_ptr;
925 
926    switch (CmdPtr->type) {
927    case CMD_COMPOSITE:
928       for (cmd_ptr=CmdPtr->first; cmd_ptr!=NULL; cmd_ptr=cmd_ptr->next) {
929          UpdateXPmObjectsInACmd(cmd_ptr);
930       }
931       break;
932    case CMD_NEW:
933       for (sel_ptr=CmdPtr->top_after; sel_ptr!=NULL; sel_ptr=sel_ptr->next) {
934          UpdateXPmObjects(sel_ptr->obj);
935       }
936       break;
937    case CMD_DELETE: break;
938    case CMD_MOVE: break;
939    case CMD_STRETCH: break;
940    case CMD_REPLACE:
941       for (sel_ptr=CmdPtr->top_after; sel_ptr!=NULL; sel_ptr=sel_ptr->next) {
942          UpdateXPmObjects(sel_ptr->obj);
943       }
944       break;
945    case CMD_ONE_TO_MANY: break;
946    case CMD_MANY_TO_ONE:
947       for (sel_ptr=CmdPtr->top_before; sel_ptr!=NULL; sel_ptr=sel_ptr->next) {
948          UpdateXPmObjects(sel_ptr->obj);
949       }
950       for (sel_ptr=CmdPtr->top_after; sel_ptr!=NULL; sel_ptr=sel_ptr->next) {
951          UpdateXPmObjects(sel_ptr->obj);
952       }
953       break;
954    case CMD_GOTO_PAGE: break;
955    }
956 }
957 
958 static
UpdatePixelInACmd(CmdPtr)959 int UpdatePixelInACmd(CmdPtr)
960    struct CmdRec *CmdPtr;
961 {
962    struct CmdRec *cmd_ptr;
963    struct SelRec *sel_ptr;
964    int changed=FALSE;
965 
966    switch (CmdPtr->type) {
967    case CMD_COMPOSITE:
968       for (cmd_ptr=CmdPtr->first; cmd_ptr!=NULL; cmd_ptr=cmd_ptr->next) {
969          if (UpdatePixelInACmd(cmd_ptr)) {
970             changed = TRUE;
971          }
972       }
973       break;
974    case CMD_NEW:
975       for (sel_ptr=CmdPtr->top_after; sel_ptr!=NULL; sel_ptr=sel_ptr->next) {
976          if (UpdatePixel(sel_ptr->obj)) {
977             changed = TRUE;
978          }
979       }
980       break;
981    case CMD_DELETE: break;
982    case CMD_MOVE: break;
983    case CMD_STRETCH: break;
984    case CMD_REPLACE:
985       for (sel_ptr=CmdPtr->top_after; sel_ptr!=NULL; sel_ptr=sel_ptr->next) {
986          if (UpdatePixel(sel_ptr->obj)) {
987             changed = TRUE;
988          }
989       }
990       break;
991    case CMD_ONE_TO_MANY: break;
992    case CMD_MANY_TO_ONE:
993       for (sel_ptr=CmdPtr->top_before; sel_ptr!=NULL; sel_ptr=sel_ptr->next) {
994          if (UpdatePixel(sel_ptr->obj)) {
995             changed = TRUE;
996          }
997       }
998       for (sel_ptr=CmdPtr->top_after; sel_ptr!=NULL; sel_ptr=sel_ptr->next) {
999          if (UpdatePixel(sel_ptr->obj)) {
1000             changed = TRUE;
1001          }
1002       }
1003       break;
1004    case CMD_GOTO_PAGE: break;
1005    }
1006    return changed;
1007 }
1008 
1009 static
RefreshColormap(nRedoing,CmdPtr)1010 int RefreshColormap(nRedoing, CmdPtr)
1011    int nRedoing;
1012    struct CmdRec *CmdPtr;
1013    /*
1014     * This function mimics FlushColormap() in color.c.
1015     */
1016 {
1017    int changed=FALSE, saved_color_layers;
1018    struct ObjRec *obj_ptr=NULL;
1019    struct PageRec *page_ptr=NULL;
1020    Colormap colormap=XCopyColormapAndFree(mainDisplay, mainColormap);
1021 
1022    mainColormap = colormap;
1023    newColormapUsed = TRUE;
1024    XSetWindowColormap(mainDisplay, mainWindow, mainColormap);
1025 
1026    if (nRedoing) {
1027       /* need to scan forward to pick up objects */
1028       struct CmdRec *cmd_ptr;
1029 
1030       for (page_ptr=firstPage; page_ptr != NULL; page_ptr=page_ptr->next) {
1031          for (obj_ptr=page_ptr->bot; obj_ptr!=NULL; obj_ptr=obj_ptr->prev) {
1032             UpdateXPmObjects(obj_ptr);
1033          }
1034       }
1035       for (cmd_ptr=CmdPtr->next; cmd_ptr != NULL; cmd_ptr=cmd_ptr->next) {
1036          UpdateXPmObjectsInACmd(cmd_ptr);
1037       }
1038    } else {
1039       for (page_ptr=firstPage; page_ptr != NULL; page_ptr=page_ptr->next) {
1040          for (obj_ptr=page_ptr->bot; obj_ptr!=NULL; obj_ptr=obj_ptr->prev) {
1041             UpdateXPmObjects(obj_ptr);
1042          }
1043       }
1044    }
1045    initColorDontReload = TRUE;
1046    CleanUpColors();
1047    XFreeColormap(mainDisplay, mainColormap);
1048    mainColormap = DefaultColormap(mainDisplay, mainScreen);
1049    XSetWindowColormap(mainDisplay, mainWindow, mainColormap);
1050    newColormapUsed = FALSE;
1051    saved_color_layers = colorLayers;
1052    InitColor();
1053    initColorDontReload = FALSE;
1054    colorLayers = saved_color_layers;
1055 
1056    ShowColor(TRUE);
1057 
1058    SaveStatusStrings();
1059    gnUpdatePixelObjCount = 0;
1060    if (nRedoing) {
1061       /* need to scan forward to pick up objects */
1062       struct CmdRec *cmd_ptr;
1063 
1064       for (page_ptr=firstPage; page_ptr != NULL; page_ptr=page_ptr->next) {
1065          for (obj_ptr=page_ptr->bot; obj_ptr!=NULL; obj_ptr=obj_ptr->prev) {
1066             if (UpdatePixel(obj_ptr)) {
1067                changed = TRUE;
1068             }
1069          }
1070       }
1071       for (cmd_ptr=CmdPtr->next; cmd_ptr != NULL; cmd_ptr=cmd_ptr->next) {
1072          if (UpdatePixelInACmd(cmd_ptr)) {
1073             changed = TRUE;
1074          }
1075       }
1076    } else {
1077       for (page_ptr=firstPage; page_ptr != NULL; page_ptr=page_ptr->next) {
1078          for (obj_ptr=page_ptr->bot; obj_ptr!=NULL; obj_ptr=obj_ptr->prev) {
1079             if (UpdatePixel(obj_ptr)) {
1080                changed = TRUE;
1081             }
1082          }
1083       }
1084    }
1085    RestoreStatusStrings();
1086 
1087    DestroyPinnedMenu(MENU_COLOR);
1088    if (colorLayers) {
1089       RedrawColorWindow();
1090    }
1091    return changed;
1092 }
1093 
1094 static
UndoOrRedoReplaceCmd(CmdPtr,HighLightSingleObj)1095 int UndoOrRedoReplaceCmd(CmdPtr, HighLightSingleObj)
1096    struct CmdRec *CmdPtr;
1097    int HighLightSingleObj;
1098 {
1099    struct ObjRec *obj_ptr=NULL, *next_obj=NULL;
1100    struct SelRec *sel_ptr=NULL;
1101    struct SelRec *saved_top_sel=NULL, *saved_bot_sel=NULL;
1102    struct ObjRec *saved_top_obj=NULL, *saved_bot_obj=NULL;
1103    int ltx, lty, rbx, rby, pos, count, *pos_table, max_count;
1104    int need_clear_and_redraw=FALSE;
1105    int *actual_pos_before=NULL;
1106 
1107 #ifdef _TGIF_DBG_WB2 /* debug, do not translate */
1108    if (gstWBInfo.do_whiteboard) {
1109       fprintf(stderr, "@@@ Beginning of UndoOrRedoReplaceCmd()...\n");
1110       fprintf(stderr, "\tCmdPtr->serialized=%1d...\n", CmdPtr->serialized);
1111    }
1112 #endif /* _TGIF_DBG_WB2 */
1113    if (CmdPtr->serialized) {
1114       int shuffle=FALSE;
1115 
1116       if (CmdPtr->first_redo_after_deserialize && CmdPtr->undone) {
1117          CmdPtr->first_redo_after_deserialize = FALSE;
1118          shuffle = TRUE;
1119       }
1120 #ifdef _TGIF_DBG_WB2 /* debug, do not translate */
1121       if (gstWBInfo.do_whiteboard) {
1122          fprintf(stderr, "In UndoOrRedoReplaceCmd(), shuffle=%1d.\n", shuffle);
1123       }
1124 #endif /* _TGIF_DBG_WB2 */
1125       if (CmdPtr->include_tgif_obj) {
1126          AddObj(NULL, topObj, tgifObj);
1127       }
1128       if (shuffle) {
1129          if (!SetupBeforePointers(CmdPtr, TRUE, NULL)) {
1130             if (CmdPtr->include_tgif_obj) {
1131                UnlinkObj(topObj);
1132             }
1133             return FALSE;
1134          }
1135          topSel = CmdPtr->top_before;
1136          botSel = CmdPtr->bot_before;
1137          CmdPtr->top_before = CmdPtr->top_after;
1138          CmdPtr->bot_before = CmdPtr->bot_after;
1139          CmdPtr->top_after = topSel;
1140          CmdPtr->bot_after = botSel;
1141          topSel = botSel = NULL;
1142       } else {
1143          if (CmdPtr->undone) {
1144             struct SelRec *next_sel=NULL;
1145 
1146             for (sel_ptr=CmdPtr->top_after; sel_ptr != NULL;
1147                   sel_ptr=next_sel) {
1148                next_sel = sel_ptr->next;
1149                free(sel_ptr);
1150             }
1151             CmdPtr->top_after = CmdPtr->bot_after = NULL;
1152 
1153             saved_top_sel = CmdPtr->top_before;
1154             saved_bot_sel = CmdPtr->bot_before;
1155             CmdPtr->top_before = CmdPtr->bot_before = NULL;
1156             if (!SetupBeforePointers(CmdPtr, TRUE, NULL)) {
1157                if (CmdPtr->include_tgif_obj) {
1158                   UnlinkObj(topObj);
1159                }
1160                return FALSE;
1161             }
1162             CmdPtr->top_after = CmdPtr->top_before;
1163             CmdPtr->bot_after = CmdPtr->bot_before;
1164             CmdPtr->top_before = saved_top_sel;
1165             CmdPtr->bot_before = saved_bot_sel;
1166          } else {
1167             if (!SetupBeforePointers(CmdPtr, FALSE, &actual_pos_before)) {
1168                if (CmdPtr->include_tgif_obj) {
1169                   UnlinkObj(topObj);
1170                }
1171                return FALSE;
1172             }
1173          }
1174       }
1175       if (CmdPtr->include_tgif_obj) {
1176          UnlinkObj(topObj);
1177       }
1178    }
1179    LinkJustTheObjects(CmdPtr->top_before, CmdPtr->bot_before);
1180 
1181    CopySel(CmdPtr->top_before, CmdPtr->count_before, &topSel, &botSel);
1182 
1183    if (CmdPtr->include_tgif_obj) {
1184       AddObj(NULL, topObj, tgifObj);
1185    }
1186    pos_table = (CmdPtr->undone) ? CmdPtr->pos_before : CmdPtr->pos_after;
1187    max_count = (CmdPtr->undone) ? CmdPtr->count_before : CmdPtr->count_after;
1188 
1189    sel_ptr = topSel;
1190    if (CmdPtr->serialized && CmdPtr->undone) {
1191       struct SelRec *sel_ptr1;
1192 
1193       for (sel_ptr1=CmdPtr->top_after; sel_ptr1 != NULL;
1194             sel_ptr1=sel_ptr1->next) {
1195          sel_ptr->obj = sel_ptr1->obj;
1196          UnlinkObj(sel_ptr1->obj);
1197          sel_ptr = sel_ptr->next;
1198       }
1199    } else {
1200       if (CmdPtr->serialized && !CmdPtr->undone) {
1201          pos_table = actual_pos_before;
1202       }
1203       pos = count = 0;
1204       for (obj_ptr=topObj; obj_ptr!=NULL; obj_ptr=next_obj, pos++) {
1205          next_obj = obj_ptr->next;
1206          if (pos == pos_table[count]) {
1207             count++;
1208             sel_ptr->obj = obj_ptr;
1209             UnlinkObj(obj_ptr);
1210             sel_ptr = sel_ptr->next;
1211             if (count == max_count) break;
1212          }
1213       }
1214    }
1215    UpdSelBBox();
1216    ltx = selLtX; lty = selLtY; rbx = selRbX; rby = selRbY;
1217 
1218    saved_top_sel = topSel;
1219    saved_bot_sel = botSel;
1220    topSel = CmdPtr->top_before;
1221    botSel = CmdPtr->bot_before;
1222    CmdPtr->top_before = saved_top_sel;
1223    CmdPtr->bot_before = saved_bot_sel;
1224 
1225    saved_top_obj = topObj;
1226    saved_bot_obj = botObj;
1227 
1228    curPage->top = topObj = topSel->obj;
1229    curPage->bot = botObj = botSel->obj;
1230 
1231    AdjSplineVs();
1232    AdjCaches();
1233 
1234    curPage->top = topObj = saved_top_obj;
1235    curPage->bot = botObj = saved_bot_obj;
1236 
1237    pos_table = (CmdPtr->undone) ? CmdPtr->pos_after : CmdPtr->pos_before;
1238    max_count = (CmdPtr->undone) ? CmdPtr->count_after : CmdPtr->count_before;
1239 
1240    pos = count = 0;
1241    sel_ptr = topSel;
1242    if (CmdPtr->serialized && !CmdPtr->undone) {
1243       pos_table = actual_pos_before;
1244    }
1245    for (obj_ptr=topObj; obj_ptr!=NULL; obj_ptr=next_obj, pos++) {
1246       if (pos == pos_table[count]) {
1247          AddObj(obj_ptr->prev, obj_ptr, sel_ptr->obj);
1248          next_obj = obj_ptr;
1249          count++;
1250          sel_ptr = sel_ptr->next;
1251          if (count == max_count) break;
1252       } else {
1253          next_obj = obj_ptr->next;
1254       }
1255    }
1256    for ( ; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
1257       AddObj(botObj, NULL, sel_ptr->obj);
1258    }
1259    if (CmdPtr->include_tgif_obj) {
1260       tgifObj = topObj;
1261       UnlinkObj(topObj);
1262 
1263       sel_ptr = topSel;
1264       topSel = topSel->next;
1265       if (topSel == NULL) {
1266          botSel = NULL;
1267       } else {
1268          topSel->prev = NULL;
1269       }
1270       free(sel_ptr);
1271    }
1272    if (CmdPtr->new_colormap) {
1273       if (RefreshColormap(CmdPtr->undone, CmdPtr)) {
1274          need_clear_and_redraw = TRUE;
1275       }
1276    }
1277    UpdSelBBox();
1278    if (need_clear_and_redraw) {
1279       ClearAndRedrawDrawWindow();
1280    } else {
1281       RedrawAreas(botObj, ltx-GRID_ABS_SIZE(1), lty-GRID_ABS_SIZE(1),
1282             rbx+GRID_ABS_SIZE(1), rby+GRID_ABS_SIZE(1),
1283             selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
1284             selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
1285    }
1286    if (HighLightSingleObj && topSel != NULL) {
1287       if (!need_clear_and_redraw) {
1288          if (!deserializingFile) HighLightForward();
1289       }
1290    } else {
1291       RemoveAllSel();
1292    }
1293    SetFileModified(TRUE);
1294    justDupped = FALSE;
1295 
1296    if (actual_pos_before != NULL) free(actual_pos_before);
1297 
1298    return TRUE;
1299 }
1300 
1301 static
UndoOrRedoOneToManyCmd(CmdPtr)1302 int UndoOrRedoOneToManyCmd(CmdPtr)
1303    struct CmdRec *CmdPtr;
1304 {
1305    struct ObjRec *obj_ptr, *next_obj;
1306    struct SelRec *sel_ptr;
1307    struct ObjRec *saved_top_obj, *saved_bot_obj;
1308    int ltx, lty, rbx, rby, pos, count, *pos_table, max_count;
1309    int need_clear_and_redraw=FALSE;
1310    int *actual_pos_before=NULL;
1311 
1312    if (CmdPtr->serialized) {
1313       if (CmdPtr->include_tgif_obj) {
1314          AddObj(NULL, topObj, tgifObj);
1315       }
1316       if (CmdPtr->undone) {
1317          if (!SetupBeforePointers(CmdPtr, TRUE, &actual_pos_before)) {
1318             if (CmdPtr->include_tgif_obj) {
1319                UnlinkObj(topObj);
1320             }
1321             return FALSE;
1322          }
1323       }
1324       if (CmdPtr->include_tgif_obj) {
1325          UnlinkObj(topObj);
1326       }
1327    }
1328    if (CmdPtr->include_tgif_obj) {
1329       AddObj(NULL, topObj, tgifObj);
1330       AddNewSelObj(topObj);
1331    }
1332 
1333    sel_ptr = (CmdPtr->undone) ? CmdPtr->top_before : CmdPtr->top_after;
1334    if (CmdPtr->serialized && CmdPtr->undone) {
1335       for ( ; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
1336          UnlinkObj(sel_ptr->obj);
1337       }
1338    } else {
1339       pos_table = (CmdPtr->undone) ? CmdPtr->pos_before : CmdPtr->pos_after;
1340       max_count = (CmdPtr->undone) ? CmdPtr->count_before : CmdPtr->count_after;
1341 
1342       if (CmdPtr->serialized && CmdPtr->undone) {
1343          pos_table = actual_pos_before;
1344       }
1345       pos = count = 0;
1346       for (obj_ptr=topObj; obj_ptr!=NULL; obj_ptr=next_obj, pos++) {
1347          next_obj = obj_ptr->next;
1348          if (pos == pos_table[count]) {
1349             count++;
1350             sel_ptr->obj = obj_ptr;
1351             UnlinkObj(obj_ptr);
1352             sel_ptr = sel_ptr->next;
1353             if (count == max_count) break;
1354          }
1355       }
1356    }
1357    topSel = (CmdPtr->undone) ? CmdPtr->top_before : CmdPtr->top_after;
1358    botSel = (CmdPtr->undone) ? CmdPtr->bot_before : CmdPtr->bot_after;
1359    UpdSelBBox();
1360    ltx = selLtX; lty = selLtY; rbx = selRbX; rby = selRbY;
1361 
1362    if (CmdPtr->undone) {
1363       CopySel(CmdPtr->top_after, CmdPtr->count_after, &topSel, &botSel);
1364    } else {
1365       CopySel(CmdPtr->top_before, CmdPtr->count_before, &topSel, &botSel);
1366    }
1367    LinkJustTheObjects(topSel, botSel);
1368 
1369    saved_top_obj = topObj;
1370    saved_bot_obj = botObj;
1371 
1372    curPage->top = topObj = topSel->obj;
1373    curPage->bot = botObj = botSel->obj;
1374 
1375    AdjSplineVs();
1376    AdjCaches();
1377 
1378    curPage->top = topObj = saved_top_obj;
1379    curPage->bot = botObj = saved_bot_obj;
1380 
1381    pos_table = (CmdPtr->undone) ? CmdPtr->pos_after : CmdPtr->pos_before;
1382    max_count = (CmdPtr->undone) ? CmdPtr->count_after : CmdPtr->count_before;
1383 
1384    pos = count = 0;
1385    sel_ptr = topSel;
1386    if (CmdPtr->serialized && CmdPtr->undone) {
1387       pos_table = actual_pos_before;
1388    }
1389    for (obj_ptr=topObj; obj_ptr!=NULL; obj_ptr=next_obj, pos++) {
1390       if (pos == pos_table[count]) {
1391          AddObj(obj_ptr->prev, obj_ptr, sel_ptr->obj);
1392          next_obj = obj_ptr;
1393          count++;
1394          sel_ptr = sel_ptr->next;
1395          if (count == max_count) break;
1396       } else {
1397          next_obj = obj_ptr->next;
1398       }
1399    }
1400    for ( ; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
1401       AddObj(botObj, NULL, sel_ptr->obj);
1402    }
1403    if (CmdPtr->include_tgif_obj) {
1404       tgifObj = topObj;
1405       UnlinkObj(topObj);
1406 
1407       sel_ptr = topSel;
1408       topSel = topSel->next;
1409       if (topSel == NULL) {
1410          botSel = NULL;
1411       } else {
1412          topSel->prev = NULL;
1413       }
1414       free(sel_ptr);
1415    }
1416    if (CmdPtr->new_colormap) {
1417       if (RefreshColormap(CmdPtr->undone, CmdPtr)) {
1418          need_clear_and_redraw = TRUE;
1419       }
1420    }
1421    UpdSelBBox();
1422    if (need_clear_and_redraw) {
1423       ClearAndRedrawDrawWindow();
1424    } else {
1425       RedrawAreas(botObj, selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
1426             selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1),
1427             ltx-GRID_ABS_SIZE(1), lty-GRID_ABS_SIZE(1),
1428             rbx+GRID_ABS_SIZE(1), rby+GRID_ABS_SIZE(1));
1429    }
1430    if (!need_clear_and_redraw) {
1431       if (!deserializingFile) HighLightForward();
1432    }
1433    SetFileModified(TRUE);
1434    justDupped = FALSE;
1435 
1436    if (actual_pos_before != NULL) free(actual_pos_before);
1437 
1438    return TRUE;
1439 }
1440 
1441 static
UndoOrRedoGotoPageCmd(CmdPtr)1442 void UndoOrRedoGotoPageCmd(CmdPtr)
1443    struct CmdRec *CmdPtr;
1444 {
1445    int new_page_num;
1446 
1447    new_page_num = (CmdPtr->undone) ? CmdPtr->count_after : CmdPtr->count_before;
1448 
1449    GotoPageNum(new_page_num);
1450    ClearAndRedrawDrawWindow();
1451    ShowPage();
1452 }
1453 
1454 static
UndoOrRedoWBSlideShow(CmdPtr)1455 void UndoOrRedoWBSlideShow(CmdPtr)
1456    struct CmdRec *CmdPtr;
1457 {
1458    int into_slideshow=CmdPtr->subcmd->detail.slideshow.into_slideshow;
1459 
1460    if (CmdPtr->undone) {
1461       /* executing the command */
1462       if (!inSlideShow && into_slideshow) {
1463          EnterSlideShow();
1464       } else if (inSlideShow && !into_slideshow) {
1465          LeaveSlideShow();
1466       }
1467    }
1468 }
1469 
1470 typedef struct tagUndoRedoRestoreDrawingModeRec {
1471    int cur_choice;
1472    int text_cursor_shown;
1473    int editing_text;
1474    int cur_text_modified;
1475    int text_orig_x;
1476    int text_orig_baseline_y;
1477 } UndoRedoRestoreDrawingModeInfo;
1478 
1479 static
SetUndoRedoRestoreDrawingModeInfo(p_urrdmi,cur_choice,text_cursor_shown,editing_text,cur_text_modified,text_orig_x,text_orig_baseline_y)1480 void SetUndoRedoRestoreDrawingModeInfo(p_urrdmi, cur_choice, text_cursor_shown,
1481       editing_text, cur_text_modified, text_orig_x, text_orig_baseline_y)
1482    UndoRedoRestoreDrawingModeInfo *p_urrdmi;
1483    int cur_choice, text_cursor_shown, editing_text, cur_text_modified;
1484    int text_orig_x, text_orig_baseline_y;
1485 {
1486    p_urrdmi->cur_choice = cur_choice;
1487    p_urrdmi->text_cursor_shown = text_cursor_shown;
1488    p_urrdmi->editing_text = editing_text;
1489    p_urrdmi->cur_text_modified = cur_text_modified;
1490    p_urrdmi->text_orig_x = text_orig_x;
1491    p_urrdmi->text_orig_baseline_y = text_orig_baseline_y;
1492 }
1493 
1494 static
UndoRedoRestoreDrawingMode(p_urrdmi)1495 void UndoRedoRestoreDrawingMode(p_urrdmi)
1496    UndoRedoRestoreDrawingModeInfo *p_urrdmi;
1497 {
1498    if (inHyperSpace || !undoRedoRestoreDrawingMode) return;
1499 
1500    switch (p_urrdmi->cur_choice) {
1501    case NOTHING: break;
1502    case DRAWTEXT:
1503       if (p_urrdmi->text_cursor_shown) {
1504          if (!p_urrdmi->cur_text_modified) {
1505             /* just leave things in text mode */
1506             SetCurChoice(p_urrdmi->cur_choice);
1507          } else if (p_urrdmi->editing_text) {
1508             /*
1509              * editing text should be undone, but the cursor should be
1510              * placed back to where it starts (in p_urrdmi->cur_text_obj)
1511              */
1512             SetCurChoice(p_urrdmi->cur_choice);
1513          } else {
1514             /*
1515              * creating a new text object
1516              * should just put the cursor where it starts
1517              * (but this may be difficult)
1518              */
1519             XEvent ev;
1520 
1521             memset(&ev, 0, sizeof(XEvent));
1522             ev.type = ButtonPress;
1523             ev.xbutton.button = Button1;
1524             ev.xbutton.state = 0;
1525             ev.xbutton.x = p_urrdmi->text_orig_x;
1526             ev.xbutton.y = p_urrdmi->text_orig_baseline_y;
1527 
1528             SetCurChoice(p_urrdmi->cur_choice);
1529             DrawText(&ev);
1530          }
1531       } else {
1532          SetCurChoice(p_urrdmi->cur_choice);
1533       }
1534       break;
1535    default: SetCurChoice(p_urrdmi->cur_choice); break;
1536    }
1537 }
1538 
UndoACmd(CmdPtr,HighLight,nPerformAction)1539 void UndoACmd(CmdPtr, HighLight, nPerformAction)
1540    struct CmdRec *CmdPtr;
1541    int HighLight, nPerformAction;
1542 {
1543    struct CmdRec *cmd_ptr;
1544 
1545    if (topSel != NULL) { HighLightReverse(); RemoveAllSel(); }
1546 
1547    switch (CmdPtr->type) {
1548    case CMD_COMPOSITE:
1549       if (CmdPtr->first->type==CMD_MOVE || CmdPtr->first->type==CMD_STRETCH) {
1550          for (cmd_ptr=CmdPtr->last; cmd_ptr!=NULL; cmd_ptr=cmd_ptr->prev) {
1551             UndoACmd(cmd_ptr, FALSE, nPerformAction);
1552          }
1553       } else {
1554          for (cmd_ptr=CmdPtr->last; cmd_ptr!=NULL; cmd_ptr=cmd_ptr->prev) {
1555             UndoACmd(cmd_ptr, TRUE, nPerformAction);
1556          }
1557       }
1558       break;
1559    case CMD_NEW: UndoNewCmd(CmdPtr); break;
1560    case CMD_DELETE: UndoDeleteCmd(CmdPtr); break;
1561    case CMD_MOVE: UndoOrRedoMoveCmd(CmdPtr); break;
1562    case CMD_STRETCH: UndoOrRedoReplaceCmd(CmdPtr, TRUE); break;
1563    case CMD_REPLACE: UndoOrRedoReplaceCmd(CmdPtr, HighLight); break;
1564    case CMD_ONE_TO_MANY: UndoOrRedoOneToManyCmd(CmdPtr); break;
1565    case CMD_MANY_TO_ONE: UndoOrRedoOneToManyCmd(CmdPtr); break;
1566    case CMD_GOTO_PAGE: UndoOrRedoGotoPageCmd(CmdPtr); break;
1567    }
1568    CmdPtr->undone = TRUE;
1569 }
1570 
UndoCmd()1571 void UndoCmd()
1572 {
1573    UndoRedoRestoreDrawingModeInfo urrdmi;
1574 
1575    if (gstWBInfo.do_whiteboard) {
1576       MsgBox(TgLoadString(STID_UNDO_IN_WB), TOOL_NAME, INFO_MB);
1577       return;
1578    }
1579    SetUndoRedoRestoreDrawingModeInfo(&urrdmi, curChoice, textCursorShown,
1580          editingText, curTextModified, textOrigX, textOrigBaselineY);
1581 
1582    TieLooseEnds();
1583    SetCurChoice(NOTHING);
1584    if (curCmd == NULL) {
1585       if (!inHyperSpace && undoRedoRestoreDrawingMode) {
1586          SetCurChoice(urrdmi.cur_choice);
1587       }
1588       MsgBox(TgLoadString(STID_NO_COMMANDS_TO_UNDO), TOOL_NAME, INFO_MB);
1589       return;
1590    }
1591    undoingOrRedoing = TRUE;
1592    UndoACmd(curCmd, TRUE, TRUE);
1593    curCmd = curCmd->prev;
1594    undoingOrRedoing = FALSE;
1595 
1596    UndoRedoRestoreDrawingMode(&urrdmi);
1597 }
1598 
RedoACmd(CmdPtr,HighLight,nPerformAction)1599 int RedoACmd(CmdPtr, HighLight, nPerformAction)
1600    struct CmdRec *CmdPtr;
1601    int HighLight, nPerformAction;
1602 {
1603    int ok=TRUE;
1604    struct CmdRec *cmd_ptr;
1605 
1606    if (topSel != NULL) { HighLightReverse(); RemoveAllSel(); }
1607 
1608    switch (CmdPtr->type) {
1609    case CMD_COMPOSITE:
1610       if (CmdPtr->first->type==CMD_MOVE || CmdPtr->first->type==CMD_STRETCH) {
1611          for (cmd_ptr=CmdPtr->first->next; cmd_ptr!=NULL;
1612                cmd_ptr=cmd_ptr->next) {
1613             if (!RedoACmd(cmd_ptr, FALSE, nPerformAction)) {
1614                ok = FALSE;
1615                break;
1616             }
1617          }
1618          if (ok) {
1619             if (!RedoACmd(CmdPtr->first, TRUE, nPerformAction)) {
1620                ok = FALSE;
1621             }
1622          }
1623       } else {
1624          for (cmd_ptr=CmdPtr->first; cmd_ptr!=NULL; cmd_ptr=cmd_ptr->next) {
1625             if (!RedoACmd(cmd_ptr, TRUE, nPerformAction)) {
1626                ok = FALSE;
1627                break;
1628             }
1629          }
1630       }
1631       break;
1632    case CMD_NEW: RedoNewCmd(CmdPtr); break;
1633    case CMD_DELETE: ok = RedoDeleteCmd(CmdPtr); break;
1634    case CMD_MOVE: ok = UndoOrRedoMoveCmd(CmdPtr); break;
1635    case CMD_STRETCH: ok = UndoOrRedoReplaceCmd(CmdPtr, TRUE); break;
1636    case CMD_REPLACE: ok = UndoOrRedoReplaceCmd(CmdPtr, HighLight); break;
1637    case CMD_ONE_TO_MANY: ok = UndoOrRedoOneToManyCmd(CmdPtr); break;
1638    case CMD_MANY_TO_ONE: ok = UndoOrRedoOneToManyCmd(CmdPtr); break;
1639    case CMD_GOTO_PAGE: UndoOrRedoGotoPageCmd(CmdPtr); break;
1640    case CMD_WB_SLIDESHOW: UndoOrRedoWBSlideShow(CmdPtr); break;
1641    }
1642    CmdPtr->undone = FALSE;
1643    return ok;
1644 }
1645 
RedoCmd()1646 void RedoCmd()
1647 {
1648    UndoRedoRestoreDrawingModeInfo urrdmi;
1649 
1650    if (gstWBInfo.do_whiteboard) {
1651       MsgBox(TgLoadString(STID_REDO_IN_WB), TOOL_NAME, INFO_MB);
1652       return;
1653    }
1654    SetUndoRedoRestoreDrawingModeInfo(&urrdmi, curChoice, textCursorShown,
1655          editingText, curTextModified, textOrigX, textOrigBaselineY);
1656 
1657    TieLooseEnds();
1658    SetCurChoice(NOTHING);
1659    if (firstCmd==NULL || (curCmd!=NULL && curCmd->next==NULL)) {
1660       if (!inHyperSpace && undoRedoRestoreDrawingMode) {
1661          SetCurChoice(urrdmi.cur_choice);
1662       }
1663       MsgBox(TgLoadString(STID_NO_COMMANDS_TO_REDO), TOOL_NAME, INFO_MB);
1664       return;
1665    }
1666    if (curCmd == NULL) {
1667       curCmd = firstCmd;
1668    } else {
1669       curCmd = curCmd->next;
1670    }
1671    undoingOrRedoing = TRUE;
1672    RedoACmd(curCmd, TRUE, TRUE);
1673    undoingOrRedoing = FALSE;
1674 
1675    UndoRedoRestoreDrawingMode(&urrdmi);
1676 }
1677 
1678 struct CmdStkRec {
1679    struct CmdRec *first, *last, *cur;
1680    int history_count;
1681    struct CmdStkRec *next;
1682 };
1683 
1684 static struct CmdStkRec *topCompositeCmdStk=NULL;
1685 
StartCompositeCmd()1686 void StartCompositeCmd()
1687 {
1688    struct CmdStkRec *cmd_stk_ptr;
1689 
1690    if (historyDepth == 0) return;
1691 
1692    cmd_stk_ptr = (struct CmdStkRec *)malloc(sizeof(struct CmdStkRec));
1693    if (cmd_stk_ptr == NULL) FailAllocMessage();
1694    memset(cmd_stk_ptr, 0, sizeof(struct CmdStkRec));
1695 
1696    cmd_stk_ptr->next = topCompositeCmdStk;
1697    cmd_stk_ptr->first = firstCmd;
1698    cmd_stk_ptr->last = lastCmd;
1699    cmd_stk_ptr->cur = curCmd;
1700    cmd_stk_ptr->history_count = historyCount;
1701    topCompositeCmdStk = cmd_stk_ptr;
1702 
1703    firstCmd = lastCmd = curCmd = NULL;
1704    historyCount = 0;
1705 
1706    composingCommand = TRUE;
1707 }
1708 
EndCompositeCmd()1709 void EndCompositeCmd()
1710 {
1711    struct CmdRec *composite_cmd=NULL;
1712    int empty=FALSE;
1713 
1714    if (historyDepth == 0) return;
1715 
1716    if (firstCmd == NULL) {
1717       empty = TRUE;
1718    } else {
1719       composite_cmd = (struct CmdRec *)malloc(sizeof(struct CmdRec));
1720       if (composite_cmd == NULL) FailAllocMessage();
1721       memset(composite_cmd, 0, sizeof(struct CmdRec));
1722       composite_cmd->type = CMD_COMPOSITE;
1723       composite_cmd->include_tgif_obj = FALSE;
1724       composite_cmd->first = firstCmd;
1725       composite_cmd->last = lastCmd;
1726       composite_cmd->top_before = composite_cmd->bot_before = NULL;
1727       composite_cmd->top_after = composite_cmd->bot_after = NULL;
1728    }
1729    if (topCompositeCmdStk != NULL) {
1730       struct CmdStkRec *cmd_stk_ptr;
1731 
1732       firstCmd = topCompositeCmdStk->first;
1733       lastCmd = topCompositeCmdStk->last;
1734       curCmd = topCompositeCmdStk->cur;
1735       historyCount = topCompositeCmdStk->history_count;
1736 
1737       cmd_stk_ptr = topCompositeCmdStk;
1738       topCompositeCmdStk = topCompositeCmdStk->next;
1739       free(cmd_stk_ptr);
1740    } else {
1741       firstCmd = lastCmd = curCmd = NULL;
1742       historyCount = 0;
1743    }
1744 
1745    if (!empty) {
1746       if (curCmd == NULL) {
1747          ClearRedoRecords(firstCmd);
1748       } else if (curCmd != lastCmd) {
1749          ClearRedoRecords(curCmd);
1750       }
1751       if (++historyCount == historyDepth) {
1752          struct CmdRec *new_first_cmd=firstCmd->next;
1753 
1754          new_first_cmd->prev = NULL;
1755          firstCmd->next = NULL;
1756          DeleteARedoRecord(firstCmd, (-1.0), (-1.0));
1757          historyCount--;
1758          firstCmd = new_first_cmd;
1759       }
1760       curCmd = composite_cmd;
1761       InsertCmd(lastCmd, NULL, curCmd, &firstCmd, &lastCmd);
1762    }
1763    composingCommand = (topCompositeCmdStk != NULL);
1764 }
1765 
RestoreDefaultHistoryDepth()1766 void RestoreDefaultHistoryDepth()
1767 {
1768    CleanUpCmds();
1769    historyDepth = defaultHistoryDepth;
1770    historyCount = 0;
1771    firstCmd = lastCmd = curCmd = NULL;
1772 }
1773 
DisableUndo()1774 void DisableUndo()
1775 {
1776    if (gstWBInfo.do_whiteboard) {
1777       /* ``Undo'' is already disabled in whiteboard mode */
1778       return;
1779    }
1780    CleanUpCmds();
1781    while (composingCommand) {
1782       EndCompositeCmd();
1783       CleanUpCmds();
1784    }
1785    historyDepth = 0;
1786 }
1787 
EnableUndo()1788 void EnableUndo()
1789 {
1790    if (gstWBInfo.do_whiteboard) {
1791       /* ``Redo'' is already disabled in whiteboard mode */
1792       return;
1793    }
1794    RestoreDefaultHistoryDepth();
1795 }
1796 
PrepareStackingInfo(TopSel,BotSel,NumObjs,PreparePhase,ppnStackingPosition,pnStackingCount,pnStackingPositionHasIds)1797 struct SelRec *PrepareStackingInfo(TopSel, BotSel, NumObjs, PreparePhase,
1798       ppnStackingPosition, pnStackingCount, pnStackingPositionHasIds)
1799    struct SelRec *TopSel, *BotSel;
1800    int NumObjs, PreparePhase;
1801    int **ppnStackingPosition, *pnStackingCount, *pnStackingPositionHasIds;
1802    /*
1803     * In whiteboard mode and PreparePhase, *ppnStackingPosition will
1804     *       be a list of full_ids.  Otherwise, it will just be a
1805     *       list of indices.
1806     */
1807 {
1808    int pos=0;
1809    struct SelRec *sel_ptr=NULL;
1810    struct ObjRec *obj_ptr=NULL;
1811 
1812    if (gstWBInfo.do_whiteboard && PreparePhase) {
1813       char **ppsz_full_ids=(char**)malloc(NumObjs*sizeof(char*));
1814 
1815       if (ppsz_full_ids == NULL) FailAllocMessage();
1816       memset(ppsz_full_ids, 0, NumObjs*sizeof(char*));
1817 
1818       *ppnStackingPosition = NULL;
1819       *pnStackingCount = 0;
1820       pos = 0;
1821       sel_ptr = TopSel;
1822       for (obj_ptr=topObj; obj_ptr!=NULL; obj_ptr=obj_ptr->next, pos++) {
1823          if (obj_ptr == sel_ptr->obj) {
1824             char buf[MAXSTRING];
1825 
1826             if (obj_ptr->creator_full_id == NULL) {
1827                sprintf(buf, "#%1d:%1d/%s", pos, obj_ptr->id, gszLocalPID);
1828             } else {
1829                sprintf(buf, "#%1d:%s", pos, obj_ptr->creator_full_id);
1830             }
1831             ppsz_full_ids[(*pnStackingCount)++] = UtilStrDup(buf);
1832             if (ppsz_full_ids[(*pnStackingCount)-1] == NULL) FailAllocMessage();
1833 
1834             sel_ptr = sel_ptr->next;
1835             if (*pnStackingCount == NumObjs) break;
1836          }
1837       }
1838       *ppnStackingPosition = (int*)ppsz_full_ids;
1839       if (pnStackingPositionHasIds != NULL) *pnStackingPositionHasIds = TRUE;
1840    } else {
1841       *ppnStackingPosition = (int*)malloc(NumObjs*sizeof(int));
1842       if (*ppnStackingPosition == NULL) FailAllocMessage();
1843       memset(*ppnStackingPosition, 0, NumObjs*sizeof(int));
1844       *pnStackingCount = 0;
1845       pos = 0;
1846       sel_ptr = TopSel;
1847       for (obj_ptr=topObj; obj_ptr!=NULL; obj_ptr=obj_ptr->next, pos++) {
1848          if (obj_ptr == sel_ptr->obj) {
1849             (*ppnStackingPosition)[(*pnStackingCount)++] = pos;
1850             sel_ptr = sel_ptr->next;
1851             if (*pnStackingCount == NumObjs) break;
1852          }
1853       }
1854       if (pnStackingPositionHasIds != NULL) *pnStackingPositionHasIds = FALSE;
1855    }
1856    return sel_ptr;
1857 }
1858 
1859 static
PrepareStacking(TopSel,BotSel,NumObjs,PreparePhase)1860 void PrepareStacking(TopSel, BotSel, NumObjs, PreparePhase)
1861    struct SelRec *TopSel, *BotSel;
1862    int NumObjs, PreparePhase;
1863 {
1864    struct SelRec *sel_ptr;
1865 
1866    stackingPosition = NULL;
1867    stackingCount = 0;
1868    sel_ptr = PrepareStackingInfo(TopSel, BotSel, NumObjs, PreparePhase,
1869          &stackingPosition, &stackingCount, &stackingPositionHasIds);
1870    if (sel_ptr != NULL || stackingCount != NumObjs) {
1871       sprintf(gszMsgBox, TgLoadString(STID_SELECT_LIST_NOT_SORTED_IN),
1872             "PrepareStacking()");
1873       FatalUnexpectedError(gszMsgBox,
1874             TgLoadString(STID_UNDO_REDO_MAY_CAUSE_CRASH));
1875    }
1876 }
1877 
PrepareToRecord(CmdType,TopSel,BotSel,NumObjs)1878 void PrepareToRecord(CmdType, TopSel, BotSel, NumObjs)
1879    int CmdType, NumObjs;
1880    struct SelRec *TopSel, *BotSel;
1881 {
1882    struct SelRec *sel_ptr=NULL, *to_sel_ptr=NULL;
1883 
1884    if (historyDepth == 0) return;
1885 
1886    preparedColormap = (gnInImageProc ? mainColormap : None);
1887 
1888    topSelBeforeInCmd = botSelBeforeInCmd = NULL;
1889    stackingPosition = NULL;
1890    stackingCount = 0;
1891    stackingPositionHasIds = FALSE;
1892 
1893    switch (CmdType) {
1894    case CMD_NEW: break;
1895 
1896    case CMD_DELETE:
1897    case CMD_MOVE:
1898    case CMD_STRETCH:
1899    case CMD_REPLACE:
1900       PrepareStacking(TopSel, BotSel, NumObjs, TRUE);
1901       if (CmdType == CMD_MOVE) {
1902          CopySel(TopSel, NumObjs, &topSelBeforeInCmd, &botSelBeforeInCmd);
1903       } else {
1904          /* stackingPositionHasIds is TRUE if we are doing whiteboard */
1905          DupTheseObjects(TopSel, BotSel, &topSelBeforeInCmd,
1906                &botSelBeforeInCmd);
1907          for (sel_ptr=TopSel, to_sel_ptr=topSelBeforeInCmd; to_sel_ptr!=NULL;
1908                sel_ptr=sel_ptr->next, to_sel_ptr=to_sel_ptr->next) {
1909             CopyObjId(sel_ptr->obj, to_sel_ptr->obj);
1910             CopyObjLocks(sel_ptr->obj, to_sel_ptr->obj);
1911          }
1912       }
1913       break;
1914 
1915    case CMD_GOTO_PAGE: stackingCount = NumObjs; break;
1916    case CMD_WB_CLEARALL: break;
1917    }
1918    return;
1919 }
1920 
FreeAfterSel(CmdPtr)1921 void FreeAfterSel(CmdPtr)
1922    struct CmdRec *CmdPtr;
1923 {
1924    struct SelRec *sel_ptr=NULL, *next_sel=NULL;
1925 
1926    for (sel_ptr=CmdPtr->top_after; sel_ptr!=NULL; sel_ptr=next_sel) {
1927       next_sel = sel_ptr->next;
1928       free(sel_ptr);
1929    }
1930    CmdPtr->top_after = CmdPtr->bot_after = NULL;
1931 }
1932 
1933 static
PrepareExtendedSerializationInfoForRecordCmd(SubCmdPtr,TopSel,BotSel,NumObjs,psi)1934 void PrepareExtendedSerializationInfoForRecordCmd(SubCmdPtr, TopSel, BotSel,
1935       NumObjs, psi)
1936    struct SubCmdRec *SubCmdPtr;
1937    struct SelRec *TopSel, *BotSel;
1938    int NumObjs;
1939    SerializationInfo *psi;
1940 {
1941    psi->subcmd = SubCmdPtr;
1942    psi->top_sel = TopSel;
1943    psi->bot_sel = BotSel;
1944    psi->num_objs = NumObjs;
1945 
1946    psi->include_tgif_obj = recordCmdIncludeTgifObj;
1947    psi->new_colormap = recordCmdUsesNewColormap;
1948    psi->logical_clock = gstWBInfo.logical_clock;
1949    psi->sender_process_id = gszLocalPID;
1950 
1951    psi->top_before = topSelBeforeInCmd;
1952    psi->bot_before = botSelBeforeInCmd;
1953    psi->pos_before = stackingPosition;
1954    psi->count_before = stackingCount;
1955    psi->pos_before_has_ids = stackingPositionHasIds;
1956 }
1957 
RecordCmd(CmdType,SubCmdPtr,TopSel,BotSel,NumObjs)1958 void RecordCmd(CmdType, SubCmdPtr, TopSel, BotSel, NumObjs)
1959    int CmdType, NumObjs;
1960    struct SubCmdRec *SubCmdPtr;
1961    struct SelRec *TopSel, *BotSel;
1962 {
1963    int logical_clock=0;
1964    int wb_send_data_failed=FALSE, plain_wb_data_sz=0;
1965    char *psz_wb_data=NULL, *psz_plain_wb_data=NULL;
1966 
1967    if (gstWBInfo.do_whiteboard) {
1968       if (gstWBInfo.dont_serialize || CmdType == CMD_GOTO_PAGE) {
1969          /*
1970           * One of the important thing that happens in SerializeCmd()
1971           *         is that the local logical clock got incremented.
1972           *         If we don't increment the logical clock, we may
1973           *         create a new command that have the same logical clock
1974           *         as the one created last time we got here and that's
1975           *         illegal. Therefore, we must increment the logical clock.
1976           */
1977          logical_clock = gstWBInfo.logical_clock++;
1978       } else {
1979          SerializationInfo si;
1980 
1981          memset(&si, 0, sizeof(SerializationInfo));
1982          PrepareExtendedSerializationInfoForRecordCmd(SubCmdPtr, TopSel, BotSel,
1983                NumObjs, &si);
1984 
1985          serializingFile = TRUE;
1986 
1987          psz_plain_wb_data = NULL;
1988          plain_wb_data_sz = 0;
1989          if ((psz_wb_data=SerializeCmd(CmdType, &si, &logical_clock,
1990                &psz_plain_wb_data, &plain_wb_data_sz)) != NULL) {
1991             if (!SendWBData(psz_wb_data,logical_clock)) {
1992                wb_send_data_failed = TRUE;
1993             }
1994          }
1995          serializingFile = FALSE;
1996       }
1997    } else {
1998       if (historyDepth == 0) return;
1999 
2000       if (curCmd == NULL) {
2001          ClearRedoRecords(firstCmd);
2002       } else if (curCmd != lastCmd) {
2003          ClearRedoRecords(curCmd);
2004       }
2005       if (++historyCount == historyDepth && !composingCommand) {
2006          struct CmdRec *new_first_cmd=firstCmd->next;
2007 
2008          new_first_cmd->prev = NULL;
2009          firstCmd->next = NULL;
2010          DeleteARedoRecord(firstCmd, (-1.0), (-1.0));
2011          historyCount--;
2012          firstCmd = new_first_cmd;
2013       }
2014    }
2015    curCmd = (struct CmdRec *)malloc(sizeof(struct CmdRec));
2016    if (curCmd == NULL) FailAllocMessage();
2017    memset(curCmd, 0, sizeof(struct CmdRec));
2018    curCmd->top_before = topSelBeforeInCmd;
2019    curCmd->bot_before = botSelBeforeInCmd;
2020    if (gstWBInfo.do_whiteboard) {
2021       curCmd->serialized = TRUE;
2022    }
2023    curCmd->pos_before = stackingPosition;
2024    curCmd->count_before = stackingCount;
2025    curCmd->pos_before_has_ids = stackingPositionHasIds;
2026    curCmd->type = CmdType;
2027    curCmd->undone = FALSE;
2028    curCmd->include_tgif_obj = recordCmdIncludeTgifObj;
2029    curCmd->new_colormap = recordCmdUsesNewColormap;
2030 
2031    if (TopSel != NULL) {
2032       CopySel(TopSel, NumObjs, &(curCmd->top_after), &(curCmd->bot_after));
2033       PrepareStacking(TopSel, BotSel, NumObjs, FALSE);
2034       curCmd->pos_after = stackingPosition;
2035       curCmd->count_after = stackingCount;
2036    } else {
2037       curCmd->top_after = curCmd->bot_after = NULL;
2038       curCmd->pos_after = NULL;
2039       curCmd->count_after = 0;
2040    }
2041    if (gstWBInfo.do_whiteboard) {
2042       struct CmdRec *immed_right_cmd=NULL;
2043 
2044       curCmd->logical_clock = logical_clock;
2045       curCmd->sender_process_id = UtilStrDup(gszLocalPID);
2046       if (curCmd->sender_process_id == NULL) FailAllocMessage();
2047 
2048       if (!wb_send_data_failed) {
2049          /*
2050           * FindShadowCmdInsertionPoint() examine the logical clock of the new
2051           *       command and decides where to insert it in the shadow cmd list.
2052           */
2053          FindCmdInsertionPoint(curCmd, &immed_right_cmd);
2054 
2055          if (immed_right_cmd == NULL) {
2056             /* append */
2057             CopyAndInsertCmd(FALSE,
2058                   (psz_plain_wb_data==NULL ? psz_wb_data : psz_plain_wb_data),
2059                   gstWBInfo.last_shadow_cmd, NULL, curCmd,
2060                   &gstWBInfo.first_shadow_cmd, &gstWBInfo.last_shadow_cmd);
2061          } else {
2062             /* insert */
2063             CopyAndInsertCmd(FALSE,
2064                   (psz_plain_wb_data==NULL ? psz_wb_data : psz_plain_wb_data),
2065                   immed_right_cmd->prev, immed_right_cmd, curCmd,
2066                   &gstWBInfo.first_shadow_cmd, &gstWBInfo.last_shadow_cmd);
2067          }
2068       }
2069       immed_right_cmd = NULL;
2070       /*
2071        * FindCmdInsertionPoint() examine the logical clock of the new command
2072        *       and decides where to insert it.
2073        */
2074       FindCmdInsertionPoint(curCmd, &immed_right_cmd);
2075 
2076       if (immed_right_cmd == NULL) {
2077          /* append */
2078          InsertCmd(gstWBInfo.last_cmd, NULL, curCmd, &gstWBInfo.first_cmd,
2079                &gstWBInfo.last_cmd);
2080       } else {
2081          /* insert */
2082          InsertCmd(immed_right_cmd->prev, immed_right_cmd, curCmd,
2083                &gstWBInfo.first_cmd, &gstWBInfo.last_cmd);
2084       }
2085       if (CmdType == CMD_WB_CLEARALL) {
2086          CleanUpObsoletedWBCmds(curCmd);
2087       }
2088    } else {
2089       InsertCmd(lastCmd, NULL, curCmd, &firstCmd, &lastCmd);
2090    }
2091    switch (CmdType) {
2092    case CMD_NEW: break;
2093    case CMD_DELETE: break;
2094    case CMD_MOVE:
2095       curCmd->subcmd = (struct SubCmdRec *)malloc(sizeof(struct SubCmdRec));
2096       if (curCmd->subcmd == NULL) FailAllocMessage();
2097       memset(curCmd->subcmd, 0, sizeof(struct SubCmdRec));
2098       curCmd->subcmd->detail.move.dx = SubCmdPtr->detail.move.dx;
2099       curCmd->subcmd->detail.move.dy = SubCmdPtr->detail.move.dy;
2100       break;
2101    case CMD_STRETCH: FreeAfterSel(curCmd); break;
2102    case CMD_REPLACE: FreeAfterSel(curCmd); break;
2103    case CMD_ONE_TO_MANY: break;
2104    case CMD_MANY_TO_ONE: break;
2105    case CMD_GOTO_PAGE: curCmd->count_after = NumObjs; break;
2106    case CMD_WB_CLEARALL:
2107       curCmd->subcmd = (struct SubCmdRec *)malloc(sizeof(struct SubCmdRec));
2108       if (curCmd->subcmd == NULL) FailAllocMessage();
2109       memset(curCmd->subcmd, 0, sizeof(struct SubCmdRec));
2110       curCmd->subcmd->detail.clearall.page_style =
2111             SubCmdPtr->detail.clearall.page_style;
2112       curCmd->subcmd->detail.clearall.print_mag =
2113             SubCmdPtr->detail.clearall.print_mag;
2114       break;
2115    case CMD_CHAT_A_LINE:
2116       /* intentionally forget what has been chatted */
2117       break;
2118    case CMD_WB_SLIDESHOW:
2119       curCmd->subcmd = (struct SubCmdRec *)malloc(sizeof(struct SubCmdRec));
2120       if (curCmd->subcmd == NULL) FailAllocMessage();
2121       memset(curCmd->subcmd, 0, sizeof(struct SubCmdRec));
2122       curCmd->subcmd->detail.slideshow.into_slideshow =
2123             SubCmdPtr->detail.slideshow.into_slideshow;
2124       break;
2125    }
2126    if (gstWBInfo.do_whiteboard) {
2127       if (wb_send_data_failed) {
2128          UndoACmd(curCmd, FALSE, TRUE);
2129          UnlinkCmd(curCmd, &gstWBInfo.first_cmd, &gstWBInfo.last_cmd);
2130          DeleteARedoRecord(curCmd, (-1.0), (-1.0));
2131       }
2132       if (psz_wb_data != NULL) {
2133          SerializeFreeBuf(psz_wb_data);
2134       }
2135       if (psz_plain_wb_data != NULL) {
2136          UtilFree(psz_plain_wb_data);
2137       }
2138    }
2139    curCmd = lastCmd;
2140 }
2141 
AbortPrepareCmd(CmdType)2142 void AbortPrepareCmd(CmdType)
2143    int CmdType;
2144 {
2145    struct SelRec *sel_ptr=NULL, *next_sel=NULL;
2146 
2147    if (historyDepth == 0) return;
2148 
2149    for (sel_ptr=topSelBeforeInCmd; sel_ptr!=NULL; sel_ptr=next_sel) {
2150       next_sel = sel_ptr->next;
2151       switch (CmdType) {
2152       case CMD_REPLACE: FreeObj(sel_ptr->obj); break;
2153       }
2154       free(sel_ptr);
2155    }
2156    if (stackingPosition != NULL) {
2157       if (stackingPositionHasIds) {
2158          int i=0;
2159 
2160          for (i=0; i < stackingCount; i++) {
2161             UtilFree((char*)(long)(stackingPosition[i]));
2162          }
2163       }
2164       free(stackingPosition);
2165    }
2166    stackingPosition = NULL;
2167    stackingCount = 0;
2168    stackingPositionHasIds = FALSE;
2169    topSelBeforeInCmd = botSelBeforeInCmd = NULL;
2170 }
2171 
RecordNewObjCmd()2172 void RecordNewObjCmd()
2173 {
2174    if (historyDepth == 0) return;
2175 
2176    if (topSel==NULL) {
2177       struct SelRec *sel_ptr;
2178 
2179       sel_ptr = (struct SelRec *)malloc(sizeof(struct SelRec));
2180       if (sel_ptr == NULL) FailAllocMessage();
2181       memset(sel_ptr, 0, sizeof(struct SelRec));
2182       sel_ptr->next = sel_ptr->prev = NULL;
2183       sel_ptr->obj = topObj;
2184       PrepareToRecord(CMD_NEW, NULL, NULL, 0);
2185       RecordCmd(CMD_NEW, NULL, sel_ptr, sel_ptr, 1);
2186       free(sel_ptr);
2187    } else {
2188       PrepareToRecord(CMD_NEW, NULL, NULL, 0);
2189       RecordCmd(CMD_NEW, NULL, topSel, botSel, 1);
2190    }
2191 }
2192 
PrepareToReplaceAnObj(BeforeObjPtr)2193 void PrepareToReplaceAnObj(BeforeObjPtr)
2194    struct ObjRec *BeforeObjPtr;
2195 {
2196    struct SelRec *sel_ptr;
2197 
2198    if (historyDepth == 0) return;
2199 
2200    sel_ptr = (struct SelRec *)malloc(sizeof(struct SelRec));
2201    if (sel_ptr == NULL) FailAllocMessage();
2202    memset(sel_ptr, 0, sizeof(struct SelRec));
2203    sel_ptr->next = sel_ptr->prev = NULL;
2204    sel_ptr->obj = BeforeObjPtr;
2205    PrepareToRecord(CMD_REPLACE, sel_ptr, sel_ptr, 1);
2206    free(sel_ptr);
2207 }
2208 
RecordReplaceAnObj(AfterObjPtr)2209 void RecordReplaceAnObj(AfterObjPtr)
2210    struct ObjRec *AfterObjPtr;
2211 {
2212    struct SelRec *sel_ptr;
2213 
2214    if (historyDepth == 0) return;
2215 
2216    sel_ptr = (struct SelRec *)malloc(sizeof(struct SelRec));
2217    if (sel_ptr == NULL) FailAllocMessage();
2218    memset(sel_ptr, 0, sizeof(struct SelRec));
2219    sel_ptr->next = sel_ptr->prev = NULL;
2220    sel_ptr->obj = AfterObjPtr;
2221    RecordCmd(CMD_REPLACE, NULL, sel_ptr, sel_ptr, 1);
2222    free(sel_ptr);
2223 }
2224 
ChangeReplaceOneCmdToDeleteCmd()2225 void ChangeReplaceOneCmdToDeleteCmd()
2226 {
2227    RecordCmd(CMD_DELETE, NULL, NULL, NULL, 0);
2228 }
2229 
RecordWBClearAll()2230 void RecordWBClearAll()
2231 {
2232    if (gstWBInfo.do_whiteboard) {
2233       struct SubCmdRec subcmd;
2234 
2235       memset(&subcmd, 0, sizeof(struct SubCmdRec));
2236       subcmd.detail.clearall.page_style = pageStyle;
2237       subcmd.detail.clearall.print_mag = printMag;
2238 
2239       PrepareToRecord(CMD_WB_CLEARALL, NULL, NULL, 0);
2240       RecordCmd(CMD_WB_CLEARALL, &subcmd, NULL, NULL, 0);
2241    }
2242 }
2243 
RecordWBSlideShow(into_slideshow)2244 void RecordWBSlideShow(into_slideshow)
2245    int into_slideshow;
2246 {
2247    if (gstWBInfo.do_whiteboard) {
2248       if (pageLayoutMode == PAGE_STACK && firstPage != lastPage) {
2249          struct SubCmdRec subcmd;
2250 
2251          memset(&subcmd, 0, sizeof(struct SubCmdRec));
2252          subcmd.detail.slideshow.into_slideshow = into_slideshow;
2253 
2254          PrepareToRecord(CMD_WB_SLIDESHOW, NULL, NULL, 0);
2255          RecordCmd(CMD_WB_SLIDESHOW, &subcmd, NULL, NULL, 0);
2256       }
2257    }
2258 }
2259 
RecordWBChatALine(pSubCmdPtr)2260 void RecordWBChatALine(pSubCmdPtr)
2261    struct SubCmdRec *pSubCmdPtr;
2262 {
2263    if (gstWBInfo.do_whiteboard) {
2264       PrepareToRecord(CMD_CHAT_A_LINE, NULL, NULL, 0);
2265       RecordCmd(CMD_CHAT_A_LINE, pSubCmdPtr, NULL, NULL, 0);
2266    }
2267 }
2268 
2269