home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / x / volume17 / tcl-editor / part07 / undoredo.c < prev   
Encoding:
C/C++ Source or Header  |  1992-03-18  |  14.8 KB  |  601 lines

  1. /* $Header: /nfs/unmvax/faculty/crowley/x/pt/RCS/undoredo.c,v 1.7 1992/03/04 17:07:18 crowley Exp crowley $ */
  2.  
  3. #include <string.h>
  4. #include <stdio.h>
  5. #include "pt.h"
  6.  
  7. #define CHANGES_TO_SHOW        30
  8.  
  9. static struct openFile * mostRecentChange = NULL;
  10.  
  11. void
  12. initChanges()
  13. {
  14.     extern struct changeItem scrapBuffer;
  15.     extern int addHandle;
  16.  
  17.     scrapBuffer.firstPiece = getFreePiece();
  18.     scrapBuffer.firstPiece->file = addHandle;
  19.     scrapBuffer.firstPiece->position = 0L;
  20.     scrapBuffer.firstPiece->length = 0L;
  21. }
  22.  
  23. struct changeItem *
  24. GetCurrentChange( ff )
  25.     struct openFile * ff;
  26. {
  27.     return ff->cmdHistory;
  28. }
  29.  
  30. static void
  31. SetCurrentChange( ff, new_change )
  32.     struct openFile * ff;
  33.     struct changeItem * new_change;
  34. {
  35.     ff->cmdHistory = new_change;
  36. }
  37.  
  38. /*ARGSUSED*/
  39. struct changeItem *
  40. GetNewChange( ff )
  41.     struct openFile * ff;
  42. {
  43.     struct changeItem * new_change;
  44.  
  45.     new_change = (struct changeItem *)
  46.             PtMalloc( sizeof(struct changeItem), "change item" );
  47.     return new_change;
  48. }
  49.  
  50. void
  51. RecordChange( ff, new_change )
  52.     struct openFile * ff;
  53.     struct changeItem * new_change;
  54. {
  55.     struct changeItem * last_change;
  56.     struct changeItem * next_change;
  57.     struct changeItem * change_to_free;
  58.     
  59.     /* link it into the chain */
  60.     last_change = GetCurrentChange( ff );
  61.     next_change = last_change->next;
  62.  
  63.     /* free all undone edits */
  64.     while( next_change != NULL ) {
  65.         change_to_free = next_change;
  66.         next_change = next_change->next;
  67.         PtFree( (char *)change_to_free );
  68.     }
  69.  
  70.     new_change->next = NULL;
  71.     new_change->prev = last_change;
  72.     last_change->next = new_change;
  73.     SetCurrentChange( ff, new_change );
  74.     mostRecentChange = ff;
  75. }
  76.  
  77. static char *
  78. GetUndoText( thisChange )
  79.     struct changeItem * thisChange;
  80. {
  81.     extern char textBuffer[];
  82.     extern struct openFile *files;
  83.     extern int maxFiles;
  84.  
  85.     struct openFile *ff;
  86.     int len = thisChange->length;
  87.     Offset limit, logByte;
  88.     int i, ch;
  89.  
  90.     /* cannot do it if the file is closed */
  91.     if( thisChange->flags & FILE_WAS_CLOSED ) {
  92.         return "File is closed";
  93.     }
  94.     /* simulate a file this was deleted from */
  95.     ff = &files[maxFiles];
  96.     ff->hiLogBuffer = -1;
  97.     ff->loLogBuffer = -1;
  98.     ff->origHandle = thisChange->fileId;
  99.     ff->fileSize = len;
  100.     ff->logPiece = thisChange->firstPiece;
  101.     ff->loLogPiece = 0;
  102.     ff->hiLogPiece = thisChange->firstPiece->length - 1;
  103.     i = 0;
  104.     logByte = 0;
  105.     if( (limit = 15) > len )
  106.         limit = len;
  107.     while( i < limit ) {
  108.         ch = getFileByte(maxFiles, logByte++);
  109.         /* temporay fix because of Tcl parsing of {...} */
  110.         if( ch == '{' )
  111.             ch = '[';
  112.         else if( ch == '}' )
  113.             ch = ']';
  114.         textBuffer[i++] = ch;
  115.     }
  116.     if( len > 33 ) {
  117.         textBuffer[i++] = '.';
  118.         textBuffer[i++] = '.';
  119.         textBuffer[i++] = '.';
  120.         logByte = len - 15;
  121.     }
  122.     while( 1 ) {
  123.         ch = getFileByte(maxFiles, logByte++);
  124.         if( ch == BLOCK_EOF )
  125.             break;
  126.         /* temporay fix because of Tcl parsing of {...} */
  127.         if( ch == '{' )
  128.             ch = '[';
  129.         else if( ch == '}' )
  130.             ch = ']';
  131.         textBuffer[i++] = ch;
  132.     }
  133.     textBuffer[i] = '\0';
  134.     return textBuffer;
  135. }
  136.  
  137. static char *
  138. flag_msg( flags )
  139.     int flags;
  140. {
  141.     static char buffer[10];
  142.  
  143.     if( flags & FILE_WAS_CLOSED )
  144.         return "closed ";
  145.     if( flags & CHANGE_WAS_UNDONE )
  146.         strcpy( buffer, "no     " );
  147.     else
  148.         strcpy( buffer, "yes    " );
  149.     if( flags & BLOCK_UNDO_BEGIN )
  150.         buffer[5] = '>';
  151.     else if( flags & BLOCK_UNDO_END )
  152.         buffer[5] = '<';
  153.     return buffer;
  154. }
  155.  
  156. void
  157. UpdateUndoList( ff )
  158.     struct openFile * ff;
  159. {
  160.     extern char msgBuffer[];
  161.     extern char textBuffer[];
  162.     extern struct window *selWindow;
  163.     extern Offset selBegin, selEnd;
  164.     extern struct window *windowList;
  165.     extern struct openFile *files;
  166.     extern int display_width;
  167.     extern int debug;
  168.  
  169.     int i;
  170.     char *s, *str;
  171.     struct changeItem *thisChange;
  172.     struct changeItem *prevChange;
  173.  
  174.     /* empty the current list */
  175.     (void)ExecTclCommand( "catch {.ub.list.list delete 0 end}" );
  176.  
  177.     /* fill undo box */
  178.     sprintf( msgBuffer,
  179.         "catch {.ub.list.list insert end {%-7s%-8s%-16s%5s%7s%6s %-20s}}",
  180.         "Done", "Type", "File", "Line", "Pos", "Len", "Chars" );
  181.     (void)ExecTclCommand( msgBuffer );
  182.     /* get the current change */
  183.     thisChange = GetCurrentChange( ff );
  184.     /* show up to 10 already undone changes */
  185.     for( i = 0; i < 10; ++i ) {
  186.         if( thisChange->next == NULL )
  187.             break;
  188.         thisChange = thisChange->next;
  189.     }
  190.     i = 0;
  191.     while( thisChange != NULL && i < CHANGES_TO_SHOW ) {
  192.         s = textBuffer;
  193.         prevChange = thisChange->prev;
  194.         switch( thisChange->type ) {
  195.         case CNULL:    /* skip null changes */
  196.             /* WAS: continue; */
  197.             sprintf( s, "%s", "*** End of list ***" );
  198.             break;
  199.         case CINSERT:
  200.             sprintf( s, "%7s%-8s%-16s%5d%7d%6d \"%s\"",
  201.                 flag_msg(thisChange->flags),
  202.                 "Insert",
  203.                 &((files[thisChange->fileId].origName)
  204.                     [thisChange->w->nameOffset]),
  205.                 thisChange->lineNumber, thisChange->position,
  206.                 thisChange->length,
  207.                 GetUndoText(thisChange) );
  208.             break;
  209.         case CDELETE:
  210.             if( prevChange->type == CMOVE )
  211.                 str = "MvFrom";
  212.             else
  213.                 str = "Delete";
  214.             sprintf( s, "%7s%-8s%-16s%5d%7d%6d \"%s\"",
  215.                 flag_msg(thisChange->flags),
  216.                 str,
  217.                 &((files[thisChange->fileId].origName)
  218.                     [thisChange->w->nameOffset]),
  219.                 thisChange->lineNumber, thisChange->position,
  220.                 thisChange->length,
  221.                 GetUndoText(thisChange) );
  222.             break;
  223.         case CMOVE:
  224.             /* not used -- a combination of CCOPY and CDELETE */
  225.             sprintf( s, "%7s%-8s%-16s%5d%7d%6d \"%s\"",
  226.                 flag_msg(thisChange->flags),
  227.                 "MoveTo",
  228.                 &((files[thisChange->fileId].origName)
  229.                     [thisChange->w->nameOffset]),
  230.                 thisChange->lineNumber, thisChange->position,
  231.                 thisChange->length,
  232.                 GetUndoText(thisChange) );
  233.             break;
  234.         case CCOPY:
  235.             sprintf( s, "%7s%-8s%-16s%5d%7d%6d \"%s\"",
  236.                 flag_msg(thisChange->flags),
  237.                 "Copy",
  238.                 &((files[thisChange->fileId].origName)
  239.                     [thisChange->w->nameOffset]),
  240.                 thisChange->lineNumber, thisChange->position,
  241.                 thisChange->length,
  242.                 GetUndoText(thisChange) );
  243.             break;
  244.         case CREPLACE:
  245.             /* not used -- a combination of CDELETE and CINSERT */
  246.             sprintf( s, "%7s%-8s%-16s%5d%7d%6d \"%s\"",
  247.                 flag_msg(thisChange->flags),
  248.                 "Replace",
  249.                 &((files[thisChange->fileId].origName)
  250.                     [thisChange->w->nameOffset]),
  251.                 thisChange->lineNumber, thisChange->position,
  252.                 thisChange->length,
  253.                 GetUndoText(thisChange) );
  254.             break;
  255.         case CMOTION:
  256.             sprintf( s, "%7s%-8s%-16s%5d%7d%6d",
  257.                 flag_msg(thisChange->flags),
  258.                 "Motion",
  259.                 &((files[thisChange->fileId].origName)
  260.                     [thisChange->w->nameOffset]),
  261.                 thisChange->lineNumber, thisChange->position,
  262.                 thisChange->length );
  263.             break;
  264.         }
  265.         sprintf(msgBuffer, "catch {.ub.list.list insert end {%s}}",s);
  266.         (void)ExecTclCommand( msgBuffer );
  267.         ++i;
  268.         thisChange = prevChange;
  269.     }
  270. }
  271.  
  272. void
  273. ShowUndos( ff )
  274.     struct openFile * ff;
  275. {
  276.     extern struct window *activeWindow;
  277.     extern int display_width;
  278.  
  279.     (void)ExecTclCommand( "MakeUndoBox" );
  280.  
  281.     UpdateUndoList( ff );
  282. }
  283.  
  284. void
  285. again( ff, mostRecent )
  286.     struct openFile * ff;
  287.     int mostRecent;
  288. {
  289.     extern char msgBuffer[];
  290.     extern struct window *selWindow;
  291.     extern Offset selBegin, selEnd;
  292.     extern struct openFile *files;
  293.  
  294.     struct changeItem *newChange;
  295.     struct changeItem *thisChange, *prevChange;
  296.     int type;
  297.  
  298.     /* check if this is a readOnly file */
  299.     if( files[selWindow->fileId].flags & READ_ONLY ) {
  300.         sprintf(msgBuffer, "File %s is read only",
  301.             files[selWindow->fileId].origName);
  302.         msg(msgBuffer, 1);
  303.         return;
  304.     }
  305.  
  306.     /* most recent in this file or voer all files? */
  307.     if( mostRecent )
  308.         ff = mostRecentChange;
  309.  
  310.     thisChange = GetCurrentChange( ff );
  311.     if( thisChange == NULL || thisChange->type == CNULL ) {
  312. noChanges:
  313.         msg("No previous change to repeat", 1);
  314.         return;
  315.     }
  316.  
  317.     /* find the change to repeat (not a delete ) */
  318.     while( 1 ) {
  319.         if( thisChange == NULL )
  320.             goto noChanges;
  321.         if( (thisChange->type)!=CDELETE && (thisChange->type)!=CNULL )
  322.             break;
  323.         thisChange = thisChange->prev;
  324.     }
  325.  
  326.     switch( thisChange->type ) {
  327.  
  328.     case CINSERT:
  329.         type = CINSERT;
  330.         goto doCopy;
  331.  
  332.     case CCOPY:
  333.     case CMOVE:
  334.         type = CCOPY;
  335.     doCopy:
  336.         /* see if the previous change was a delete */
  337.         prevChange = thisChange->prev;
  338.         if( thisChange->position == prevChange->position 
  339.          && prevChange->type == CDELETE )
  340.             /* the delete must go into the history first */
  341.             (void)deleteChars(selWindow->fileId, NOUPDATE, 0);
  342.         /* record the change before copyPieces changes things */
  343.         newChange = GetNewChange( ff );
  344.         newChange->type = type;
  345.         newChange->position = selBegin;
  346.         newChange->length = thisChange->length;
  347.         newChange->lineNumber = selWindow->numTopline;
  348.         newChange->w = thisChange->w;
  349.         newChange->flags = 0;
  350.         newChange->fileId = selWindow->fileId;
  351.         newChange->firstPiece = dupPieces(thisChange->firstPiece);
  352.         copyPieces( thisChange->firstPiece, selWindow, selBegin,
  353.             thisChange->length, 1, 0 );
  354.         RecordChange( ff, newChange );
  355.         break;
  356.     
  357.     case CDELETE:
  358.         (void)deleteChars(selWindow->fileId, UPDATEWINDOWS, 0);
  359.         break;
  360.     }
  361. }
  362.  
  363. void
  364. redo( ff, count )
  365.     struct openFile * ff;
  366.     int count;
  367. {
  368.     extern char msgBuffer[];
  369.     extern struct window *selWindow;
  370.     extern Offset selBegin, selEnd;
  371.     extern struct window *windowList;
  372.     extern int debug;
  373.  
  374.     struct changeItem *prevChange;
  375.     struct changeItem *thisChange, *nextChange;
  376.     struct window *w1;
  377.     int blockRedo = 0;
  378.  
  379. while( count > 0 || blockRedo ) {
  380.     prevChange = GetCurrentChange( ff );
  381.     thisChange = prevChange->next;
  382.     if( thisChange == NULL ) {
  383.         msg("No undone change to redo", 1);
  384.         return;
  385.     }
  386.  
  387.     if( (prevChange->flags & BLOCK_UNDO_BEGIN)
  388.      && (prevChange->type != CNULL) ) {
  389.         blockRedo = 1;
  390.     } else if( (prevChange->flags & BLOCK_UNDO_END) && blockRedo ) {
  391.         blockRedo = 0;
  392.         if( --count <= 0 )
  393.             break;
  394.     } else
  395.         --count;
  396.  
  397.     /* move up the pointer to the last done change */
  398.     SetCurrentChange( ff, thisChange );
  399.  
  400.     thisChange->flags &= ~CHANGE_WAS_UNDONE;
  401.     nextChange = thisChange->next;
  402.  
  403.     /* find a window displaying the file the change was made in */
  404.     if( thisChange->fileId != selWindow->fileId ) {
  405.         w1 = windowList;
  406.         while( w1 != NULL && w1->fileId != thisChange->fileId )
  407.             w1 = w1->nextWindow;
  408.         if( w1 == NULL ) {
  409.             msg("Cannot redo. No windows have that file open.", 1);
  410.             return;
  411.         } else
  412.             selWindow = w1;
  413.     }
  414.  
  415.     switch( thisChange->type ) {
  416.  
  417.     case CMOTION:
  418.         doGoto( (struct window *)(thisChange->firstPiece),
  419.                 thisChange->length, 0 );
  420.         break;
  421.  
  422.     case CCOPY:
  423.     case CMOVE:
  424.         showChange();
  425.         copyPieces( thisChange->firstPiece, selWindow,
  426.             thisChange->position, thisChange->length, 1, 0 );
  427.         /* if this is not a move, stop, we are done */
  428.         if( thisChange->type != CMOVE )
  429.             break;
  430.         /* if it is a move then the delete follows */
  431.         /* else finish redoing the move by dropping through */
  432.         /* to the CDELETE case to delete the MOVEd text */
  433.         nextChange->flags &= ~CHANGE_WAS_UNDONE;
  434.         thisChange = nextChange;
  435.         SetCurrentChange( ff, thisChange );
  436.  
  437.     case CDELETE:
  438.         selBegin = thisChange->position;
  439.         selEnd = selBegin + thisChange->length - 1;
  440.         selWindow = thisChange->w;
  441.         showChange();
  442.         (void)deleteChars( thisChange->fileId, UPDATEWINDOWS, 2 );
  443.         /* if the next change is an insert at the same position */
  444.         /* then redo it also */
  445.         if( nextChange == NULL || nextChange->type != CINSERT
  446.          || thisChange->position != nextChange->position )
  447.             break;
  448.         /* else drop through to redo the insert also */
  449.         thisChange = nextChange;
  450.         SetCurrentChange( ff, thisChange );
  451.         thisChange->flags &= ~CHANGE_WAS_UNDONE;
  452.  
  453.     case CINSERT:
  454.         copyPieces( thisChange->firstPiece, thisChange->w,
  455.                 thisChange->position, thisChange->length,
  456.                 1, 0 );
  457.         break;
  458.     }
  459. }
  460.  
  461. }
  462.  
  463. void
  464. undo( ff, count )
  465.     struct openFile * ff;
  466.     int count;
  467. {
  468.     extern char msgBuffer[];
  469.     extern struct window *selWindow;
  470.     extern Offset selBegin, selEnd;
  471.     extern struct window *windowList;
  472.     extern int debug;
  473.  
  474.     DoUpdate delAlso;
  475.     struct changeItem *thisChange, *prevChange;
  476.     struct window *w1;
  477.     int blockUndo = 0;
  478.  
  479. while( count > 0 || blockUndo ) {
  480.     thisChange = GetCurrentChange( ff );
  481.     if( thisChange == NULL || thisChange->type == CNULL ) {
  482. noChanges:
  483.         msg("No previous change to undo", 1);
  484.         return;
  485.     }
  486.  
  487.     /* find the change to undo */
  488.     while( 1 ) {
  489.         if( thisChange == NULL )
  490.             goto noChanges;
  491.         if( (thisChange->type) != CNULL
  492.          && ( (thisChange->flags)
  493.                  & (CHANGE_WAS_UNDONE|FILE_WAS_CLOSED) ) == 0 )
  494.             break;
  495.         thisChange = thisChange->prev;
  496.     }
  497.  
  498.     /* Note that begin and end seem to be reversed here because */
  499.     /* the begin comes with the first edit which is earliter than */
  500.     /* the last edit.  Since we are going backwards in time here */
  501.     /* we encounter the last edit (the BLOCK_UNDO_END) first and */
  502.     /* want to undo all the way to the first edit in the block */
  503.     /* (the BLOCK_UNDO_BEGIN) */
  504.     if( thisChange->flags & BLOCK_UNDO_END ) {
  505.         blockUndo = 1;
  506.     } else if( (thisChange->flags & BLOCK_UNDO_BEGIN) && blockUndo ) {
  507.         blockUndo = 0;
  508.         if( --count <= 0 )
  509.             break;
  510.     } else
  511.         --count;
  512.  
  513.     thisChange->flags |= CHANGE_WAS_UNDONE;
  514.     prevChange = thisChange->prev;
  515.  
  516.     /* back up the pointer to the last done change */
  517.     SetCurrentChange( ff, prevChange );
  518.  
  519.     /* find a window displaying the file the change was made in */
  520.     if( thisChange->fileId != selWindow->fileId ) {
  521.         w1 = windowList;
  522.         while( w1 != NULL && w1->fileId != thisChange->fileId )
  523.             w1 = w1->nextWindow;
  524.         if( w1 == NULL ) {
  525.             msg("Cannot undo. No windows have that file open.",1);
  526.             return;
  527.         } else
  528.             selWindow = w1;
  529.     }
  530.     
  531.     
  532.     switch( thisChange->type ) {
  533.  
  534.     case CMOTION:
  535.         doGoto( (struct window *)(thisChange->firstPiece),
  536.                 thisChange->lineNumber, 0 );
  537.         break;
  538.  
  539.     case CDELETE:
  540.         showChange();
  541.         copyPieces( thisChange->firstPiece, selWindow,
  542.             thisChange->position, thisChange->length, 1, 0 );
  543.         /* see if this is really the DELETE of a CMOVE */
  544.         if( prevChange->type != CMOVE )
  545.             break;
  546.         /* else finish undoing the move by dropping through */
  547.         /* to the CCOPY case to delete the MOVEd text */
  548.         prevChange->flags |= CHANGE_WAS_UNDONE;
  549.         thisChange = prevChange;
  550.         SetCurrentChange( ff, thisChange->prev );
  551.  
  552.     case CCOPY:
  553.         selBegin = thisChange->position;
  554.         selEnd = selBegin + thisChange->length - 1;
  555.         showChange();
  556.         (void)deleteChars( thisChange->fileId, UPDATEWINDOWS, 2 );
  557.         break;
  558.  
  559.     case CINSERT:
  560.         /* delete the characters inserted */
  561.         selBegin = thisChange->position;
  562.         selEnd = selBegin + thisChange->length - 1;
  563.         /* test this first so we can avoid updating the */
  564.         /* screen twice once for the delete and again for */
  565.         /* the copy to follow */
  566.         if( thisChange->position == prevChange->position 
  567.          && prevChange->type == CDELETE )
  568.             delAlso = NOUPDATE;
  569.         else
  570.             delAlso = UPDATEWINDOWS;
  571.         showChange();
  572.         (void)deleteChars( thisChange->fileId, delAlso, 2 );
  573.         /* see if there is a previous, related delete to undo */
  574.         if( delAlso == NOUPDATE ) {
  575.             prevChange->flags |= CHANGE_WAS_UNDONE;
  576.             SetCurrentChange( ff, prevChange->prev );
  577.             copyPieces( prevChange->firstPiece, selWindow,
  578.                 selBegin, prevChange->length, 1, 0 );
  579.         }
  580.         break;
  581.     }
  582. }
  583. }
  584.  
  585.  
  586. /* make sure the change is visible */
  587. void
  588. showChange()
  589. {
  590.     extern Offset selBegin, selEnd;
  591.     extern struct window *selWindow;
  592.     extern struct window *windowList;
  593.     
  594.     /* if the selection window is not on top */
  595.     /* or if the selection is not in the window */
  596.     /* then move the window to show the selection */
  597.     if( windowList != selWindow  || selBegin < selWindow->posTopline
  598.                     || selEnd > selWindow->posBotline )
  599.          doGoSel(selWindow);
  600. }
  601.