appletundo.c

Go to the documentation of this file.
00001 
00002 /*  $Id: appletundo.c 355 2005-01-11 22:48:55Z andreradke $    */
00003 
00004 /* copyright 1991-96 UserLand Software, Inc. All Rights Reserved.*/
00005 
00006 
00007 #include "appletinternal.h"
00008 
00009 
00010 
00011 
00012 #define noaction 0
00013 
00014 
00015 
00016 static boolean flmultipleundo = false;
00017 
00018 hdlundostack undostack = nil;
00019 
00020 hdlundostack redostack = nil;
00021 
00022 
00023 typedef boolean (*globalscallback) (long, boolean);
00024 
00025 
00026 static boolean initstack (short basesize, short elemsize, hdlstack *hstack) {
00027 
00028     /*
00029     generic handle-based stack initialization
00030     */
00031 
00032     ptrstack ps;
00033 
00034     if (!newclearhandle ((long) basesize, (Handle *) hstack))
00035         return (false);
00036 
00037     ps = **hstack;
00038 
00039     ps->topstack = 0;
00040     
00041     ps->basesize = basesize;
00042     
00043     ps->elemsize = elemsize;
00044     
00045     return (true);
00046     } /*initstack*/
00047 
00048 
00049 static boolean pushstack (hdlstack hstack, ptrvoid pelem) {
00050 
00051     /*
00052     push a new element, pelem, onto the top of the handle-based 
00053     stack hstack
00054     */
00055 
00056     if (!enlargehandle ((Handle) hstack, (long) (**hstack).elemsize, pelem))
00057         return (false);
00058 
00059     ++(**hstack).topstack;
00060 
00061     return (true);
00062     } /*pushstack*/
00063 
00064 
00065 static boolean popstack (hdlstack hstack, ptrvoid pelem) {
00066 
00067     /*
00068     pop the top element off of the handle-based stack hstack, copying
00069     into pelem
00070     */
00071 
00072     long elemsize = (**hstack).elemsize;
00073     long newsize;
00074 
00075     if (!(**hstack).topstack) /*check for empty stack*/
00076         return (false);
00077 
00078     newsize = gethandlesize ((Handle) hstack) - (long) elemsize;
00079 
00080     moveleft (* (char **) hstack + newsize, pelem, elemsize);
00081 
00082     sethandlesize ((Handle) hstack, newsize); /*should never fail; going smaller*/
00083 
00084     --(**hstack).topstack;
00085 
00086     return (true);
00087     } /*popstack*/
00088 
00089 
00090 static void swapundostacks (void) {
00091 
00092     /*
00093     redo is often identical to undo, but sometimes operates on the 
00094     opposite stacks.  swapping stacks avoids having to duplicate a lot 
00095     of code, or having to pass the current stack as a parameter everywhere
00096     
00097     12/11/90 dmb: must change fields in windowinfo too, in case undo 
00098     routines cause globals to be pushed and popped
00099     */
00100     
00101     hdlappwindow ha = app.appwindow;
00102     hdlundostack htempstack = undostack;
00103     
00104     undostack = redostack;
00105     
00106     redostack = htempstack;
00107     
00108     /*keep true globals in sync*/
00109     
00110     (**ha).undostack = (Handle) undostack;
00111     
00112     (**ha).redostack = (Handle) redostack;
00113     } /*swapundostacks*/
00114 
00115 
00116 static boolean pushstep (undocallback pundo, Handle hundo, boolean flaction) {
00117 
00118     /*
00119     push a new step, defined by an undo routine and its data, onto 
00120     the undo stack.  any number of steps can accumulate to undo a 
00121     single action.  if no action is pending, just toss the undo by 
00122     calling the undo routine with flundo false
00123     */
00124     
00125     tyundorecord step;
00126     
00127     if (!undostack || (**undostack).ixaction == noaction) {
00128         
00129         (*pundo) (hundo, false);
00130         
00131         return (true);
00132         }
00133         
00134     step.undoroutine = pundo;
00135     
00136     step.hundodata = hundo;
00137     
00138     step.flactionstep = flaction;
00139     
00140     return (pushstack ((hdlstack) undostack, &step));
00141     } /*pushstep*/
00142 
00143 
00144 boolean pushundostep (undocallback pundo, Handle hundo) {
00145 
00146     /*
00147     push a new step, defined by an undo routine and its data, onto 
00148     the undo stack.  any number of steps can accumulate to undo a 
00149     single action.  if no action is pending, just toss the undo by 
00150     calling the undo routine with flundo false
00151     */
00152     
00153     return (pushstep (pundo, hundo, false));
00154     } /*pushundostep*/
00155 
00156 
00157 static boolean pushactionstep (short ixaction, long globaldata) {
00158     
00159     return (pushstep ((undocallback) ixaction, (Handle) globaldata, true));
00160     } /*pushactionstep*/
00161 
00162 
00163 static boolean popundostep (tyundorecord *pstep) {
00164     
00165     /*
00166     pop the next step off of the undo stack.  this is usually done only 
00167     within the undo code (in this file), but may be called from a 
00168     higher level under certain circumstances.
00169     */
00170     
00171     return (popstack ((hdlstack) undostack, pstep));
00172     } /*popundostep*/
00173 
00174 
00175 static boolean pushaction (short ixaction, long globaldata) {
00176 
00177     /*
00178     start recording undos for a new action.  ixaction is a non-zero index 
00179     into a stringlist of undoable actions, used to build the menu item.
00180     
00181     to mark the juncture between the previous action steps (if any), the 
00182     index of the previous action is pushed onto the stack as a psuedo-step.
00183     */
00184     
00185     hdlundostack hstack = undostack;
00186     short ixlastaction = (**hstack).ixaction;
00187     
00188     if (!hstack)
00189         return (false);
00190     
00191     /* if previous action exists, try pushing it */
00192     
00193     if (ixlastaction != noaction  &&  !pushactionstep (ixlastaction, (**hstack).globaldata))
00194         return (false);
00195     
00196     /* set the new action */
00197     
00198     (**hstack).globaldata = globaldata;
00199     
00200     (**hstack).ixaction = ixaction;
00201     
00202     return (true);
00203     } /*pushaction*/
00204 
00205 
00206 static void killactions (void) {
00207 
00208     /*
00209     release all pending undo steps, for all actions.
00210     
00211     7/9/91 dmb: need to set up globals, but only if an undo step is actually 
00212     tossed
00213     */
00214     
00215     hdlundostack hstack = undostack;
00216     long globaldata = (**hstack).globaldata;
00217     tyundorecord step;
00218     
00219     while (popundostep (&step)) { /*pop stack until empty; ignore action records*/
00220         
00221         if (step.flactionstep)
00222             globaldata = (long) step.hundodata;
00223         
00224         else {
00225             
00226             (*(globalscallback) app.setundoglobalscallback) (globaldata, false); /*set up environment*/
00227             
00228             (*step.undoroutine) (step.hundodata, false); /*ignore errors*/
00229             }
00230         }
00231     
00232     (**hstack).ixaction = noaction;
00233     } /*killactions*/
00234 
00235 
00236 boolean pushundoaction (short ixaction) {
00237 
00238     /*
00239     start recording undos for a new action.  ixaction is a non-zero index 
00240     into a stringlist of undoable actions, used to build the menu item.
00241     
00242     any pending redo must be killed; since we're making a new change, we're 
00243     creating a new future than cannot be comingled with the old future that 
00244     was undone
00245 
00246     for single-level undo, this routine also kills any existing undo.  
00247     for multiple-level undo, subsequent undos are added to the same undo stack.
00248     */
00249     
00250     long globaldata;
00251     
00252     if (undostack == nil)
00253         return (false);
00254     
00255     if (!flmultipleundo)
00256         killactions ();
00257     
00258     swapundostacks (); /*to redo*/
00259     
00260     killactions ();
00261     
00262     swapundostacks (); /*restore*/
00263     
00264     if ((ixaction == noaction) || !(*(boolean (*)(long *))app.getundoglobalscallback) (&globaldata))
00265         globaldata = 0L;
00266     
00267     return (pushaction (ixaction, globaldata));
00268     } /*pushundoaction*/
00269 
00270 
00271 boolean popundoaction (void) {
00272 
00273     /*
00274     remove the topmost action's steps from the undo stack, releasing the 
00275     undo by calling the undo routines with flundo false.
00276     
00277     7/9/91 dmb: need to set up globals, but only if an undo step is actually 
00278     tossed
00279     */
00280     
00281     hdlundostack hstack = undostack;
00282     tyundorecord step;
00283     short ixaction;
00284     long globaldata;
00285     
00286     ixaction = noaction;
00287     
00288     globaldata = 0L;
00289     
00290     while (popundostep (&step)) { /*pop stack until empty; ignore action records*/
00291     
00292         if (step.flactionstep) { /*new action record; finished with this undo*/
00293             
00294             ixaction = (short) step.undoroutine;
00295             
00296             globaldata = (long) step.hundodata;
00297             
00298             break;
00299             }
00300         
00301         (*(globalscallback) app.setundoglobalscallback) ((**hstack).globaldata, false); /*set up environment*/
00302         
00303         (*step.undoroutine) (step.hundodata, false); /*ignore errors*/
00304         }
00305     
00306     (**hstack).ixaction = ixaction; /*update current action*/
00307     
00308     (**hstack).globaldata = globaldata;
00309     
00310     return (true);
00311     } /*popundoaction*/
00312 
00313 
00314 boolean undolastaction (boolean flbuildredo) {
00315     
00316     hdlundostack hstack = undostack;
00317     short ixaction;
00318     long globaldata;
00319     tyundorecord step;
00320     
00321     ixaction = (**hstack).ixaction;
00322     
00323     if (ixaction == noaction)
00324         return (false); /*nothing to undo*/
00325     
00326     globaldata = (**hstack).globaldata;
00327     
00328     swapundostacks ();
00329     
00330     if (flbuildredo)
00331         pushaction (ixaction, globaldata);
00332     else
00333         pushaction (noaction, 0L);
00334     
00335     swapundostacks ();
00336     
00337     (*(globalscallback) app.setundoglobalscallback) (globaldata, true); /*set up environment, as required*/
00338     
00339     ixaction = noaction; /*default if new action isn't found*/
00340     
00341     globaldata = 0L;
00342     
00343     while (popundostep (&step)) { /*pop stack until empty or action record is found*/
00344     
00345         if (step.flactionstep) { /*new action record; finished with this undo*/
00346             
00347             ixaction = (short) step.undoroutine;
00348             
00349             globaldata = (long) step.hundodata;
00350             
00351             break;
00352             }
00353         
00354         swapundostacks ();
00355         
00356         (*step.undoroutine) (step.hundodata, true); /*xxx error checking here*/
00357         
00358         swapundostacks ();  
00359         }
00360     
00361     (**hstack).ixaction = ixaction; /*update current action*/
00362     
00363     (**hstack).globaldata = globaldata; /*update current action*/
00364     
00365     return (true); /*xxx*/
00366     } /*undolastaction*/
00367 
00368 
00369 boolean redolastaction (boolean flbuildundo) {
00370 
00371     swapundostacks ();
00372 
00373     undolastaction (flbuildundo);
00374 
00375     swapundostacks ();
00376     
00377     return (true);
00378     } /*redolastaction*/
00379 
00380 
00381 boolean getundoaction (short *ixaction) {
00382 
00383     *ixaction = (**undostack).ixaction;
00384     
00385     return (*ixaction != noaction);
00386     } /*getundoaction*/
00387 
00388 
00389 boolean getredoaction (short *ixaction) {
00390 
00391     *ixaction = (**redostack).ixaction;
00392     
00393     return (*ixaction != noaction);
00394     } /*getredoaction*/
00395 
00396 
00397 boolean getundomenuitem (bigstring bsundo, boolean *flenabled) {
00398     
00399     short ixbase, ixaction;
00400     bigstring bsaction;
00401     
00402     if (app.appwindow == nil) {
00403         
00404         getstringlist (undolistnumber, undostring, bsundo);
00405         
00406         *flenabled = true;
00407         }
00408     else { /* try to get redo first, else try to get undo */
00409         
00410         getstringlist (undolistnumber, cantundoitem, bsundo); /*default*/
00411         
00412         *flenabled = false;
00413         
00414         if (getredoaction (&ixaction))
00415             ixbase = redostring;
00416         else {
00417             if (getundoaction (&ixaction))
00418                 ixbase = undostring;
00419             }
00420         
00421         if (ixaction > 0) { /*it's an undoable operation*/
00422             
00423             getstringlist (undolistnumber, ixbase, bsundo);
00424             
00425             if (getstringlist (undolistnumber, ixaction, bsaction))
00426                 pushstring (bsaction, bsundo);
00427             
00428             *flenabled = true;
00429             }
00430         }
00431     
00432     return (stringlength (bsundo) > 0);
00433     } /*getundomenuitem*/
00434 
00435  
00436 void killundo (void) {
00437 
00438     /*
00439     release all pending undo and redo steps, for all actions.
00440     */
00441     
00442     killactions (); /*kill undo*/
00443     
00444     swapundostacks ();
00445     
00446     killactions (); /*kill redo*/
00447     
00448     swapundostacks ();
00449     } /*killundo*/
00450 
00451 
00452 boolean newundostack (hdlundostack *hstack) {
00453     
00454     if (!initstack (sizeof (tyundostack), sizeof (tyundorecord), (hdlstack *) hstack))
00455         return (false);
00456     
00457     (***hstack).ixaction = noaction;
00458     
00459     return (true);
00460     } /*newundostack*/
00461 
00462 
00463 boolean disposeundostack (hdlundostack hstack) {
00464     
00465     #ifdef notdefined   
00466         if (hstack != nil)
00467             if ((**hstack).ixaction != noaction)
00468                 alertdialog ("\pDisposing non-empty undo!");
00469     #endif
00470     
00471     disposehandle ((Handle) hstack);
00472     
00473     return (true);
00474     } /*disposeundostack*/
00475 
00476 
00477 

Generated on Wed May 31 18:19:44 2006 for frontierkernel 10.1.10a by  doxygen 1.4.6