home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / fed0217s.zip / source / buffer.cpp < prev    next >
C/C++ Source or Header  |  2001-08-08  |  18KB  |  843 lines

  1. /*
  2. ** Module   :BUFFER.CPP
  3. ** Abstract :Class buffer methods.
  4. **
  5. ** Copyright (C) Sergey I. Yevtushenko
  6. **
  7. ** Log: Wed  05/03/1997     Updated to V0.5
  8. **      Sat  12/04/1997       Updated to V0.8
  9. **      Fri  07/11/1997       Updated to V0.9, UNDO implemented
  10. **      Sun  09/11/1997       Updated to V0.91, fixed memory leaks
  11. */
  12.  
  13. #include <string.h>
  14.  
  15. #include <buffer.h>
  16. #include <version.h>
  17.  
  18. #define UNDO    1
  19.  
  20. #ifndef max
  21. #define max(a,b) (((a) > (b)) ? (a) : (b))
  22. #define min(a,b) (((a) < (b)) ? (a) : (b))
  23. #endif
  24.  
  25.  
  26. //----------------------------------------------------------------------
  27. //
  28. // Class BUFFER
  29. //
  30. //----------------------------------------------------------------------
  31.  
  32. void Buffer::init_buffer()
  33. {
  34.     changed     =
  35.     col_block   =
  36.     cur_col     =
  37.     cur_row     =
  38.     mark_state  =
  39.     num_cols    =
  40.     old_abs_col =
  41.     old_abs_row =
  42.     start_col   =
  43.     start_row   =
  44.     tracking    =
  45.     undo_count  =
  46.     found_show  =
  47.     found_row   =
  48.     found_len   =
  49.     found_col   =
  50.     is_unix     = 0;
  51.  
  52.     word_wrap   = iWWDef;
  53.     ww_width    = iDefWidth;
  54.  
  55.     draw_save.start_row = 0;
  56.     draw_save.start_col = 0;
  57.  
  58.     hiliting    = HI_CPP;
  59.     ins_mode    = 1;
  60.     auto_indent = 1;
  61.     pal_start   = CL_EDITBOX_START;
  62.     track_head  = undobuff = 0;
  63.  
  64.     custom_syntax = 0;
  65.  
  66.     set_xlate(0);
  67. }
  68.  
  69. Buffer::Buffer(int sz):Collection(sz, 1024)
  70. {
  71.     init_buffer();
  72. }
  73.  
  74. Buffer::Buffer():Collection(1024, 1024)
  75. {
  76.     init_buffer();
  77. }
  78.  
  79. Buffer::~Buffer()
  80. {
  81.     clear_undobuff();
  82.     RemoveAll();
  83. }
  84.  
  85. void Buffer::Free(Ptr p)
  86. {
  87.     delete PLine(p);
  88. }
  89.  
  90. //----------------------------------------------------------------------
  91. // Editing routines
  92. //
  93. //----------------------------------------------------------------------
  94.  
  95. void Buffer::ins_char(Rect& rect, int chr)
  96. {
  97.     unmark();
  98.     changed = 1;
  99.  
  100.     if(chr == '\n' || chr == '\r')
  101.     {
  102.         split_line(rect);
  103.         return;
  104.     }
  105. //-----------------------------------
  106. // C/C++ Smart indent (Part 1)
  107. //-----------------------------------
  108.     if(chr == '}' && do_smart())
  109.     {
  110.         //Here trapped pressing of closing curly brace in new line
  111.         // Step 1: check line for spaces
  112.         int i;
  113.         for(i = 0; i < abs_col(); i++)
  114.         {
  115.             if(chr_out(abs_line()->char_at(i)) != ' ')
  116.                 break;
  117.         }
  118.         if(i == abs_col()) //If not, process key in usual way
  119.         {
  120.             //find prev tabstop
  121.             int pos = abs_col();
  122.  
  123.             while((--pos) % TAB_WIDTH)
  124.             {
  125.                 //Nothing
  126.             }
  127.             goto_col(rect, pos);
  128.         }
  129.     }
  130.  
  131. //    end
  132.  
  133.     int _cur_col   = cur_col;
  134.     int _start_col = start_col;
  135.  
  136.     track(opDelChar);
  137.  
  138.     cur_col += abs_line()->ins_char(chr_in(chr), abs_col());
  139.  
  140.     for(int k = 1; k < (cur_col - _cur_col); k++)
  141.         track(opDelChar);
  142.  
  143.     if(cur_col >= rect.cols)
  144.     {
  145.         start_col += cur_col - rect.cols + 1;
  146.         cur_col    = rect.cols - 1;
  147.     }
  148.  
  149.     if(_cur_col != cur_col)
  150.         track(opCurCol,(void *)_cur_col);
  151.     if(_start_col != start_col)
  152.         track(opStartCol,(void *)_start_col);
  153.  
  154.     if((word_wrap & WW_STATE) && ww_need(abs_row()))
  155.     {
  156.         int wrap_pos = 0;
  157.  
  158.         wrap_pos = ww_perform(rect, abs_row());
  159.  
  160.         if(wrap_pos)
  161.         {
  162.             if(word_wrap & WW_LONG)
  163.             {
  164.                 int i;
  165.  
  166.                 for(i = abs_row() + 1; ww_need(i) && (i < Count()); i++)
  167.                     ww_perform(rect, i);
  168.             }
  169.  
  170.             if(abs_col() >= wrap_pos)
  171.             {
  172.                 int offset = 0;
  173.  
  174.                 goto_line(rect, abs_row() + 1);
  175.  
  176.                 if(auto_indent)
  177.                 {
  178.                     for(chr = abs_line()->char_at(offset);
  179.                         chr && __issp(chr_out(chr));
  180.                         chr = abs_line()->char_at(offset))
  181.                     {
  182.                         offset++;
  183.                     }
  184.                 }
  185.  
  186.                 goto_col(rect, abs_col() - wrap_pos + offset);
  187.             }
  188.         }
  189.     }
  190.  
  191.     check_hiliting();
  192. }
  193.  
  194. int Buffer::del_char(Rect&)
  195. {
  196.     unmark();
  197.     changed = 1;
  198.     int chr = 0;
  199.  
  200.     if(chr_out(abs_line()->char_at(abs_col())) == ' ')
  201.     {
  202.         int not_eof = 0;
  203.         //Check if all chars up to end of line are spaces
  204.         for(int i = abs_col() + 1; i < abs_line()->len(); i++)
  205.         {
  206.             if(chr_out(abs_line()->char_at(i)) != ' ')
  207.             {
  208.                 not_eof = 1;
  209.                 break;
  210.             }
  211.         }
  212.  
  213.         if(!not_eof)
  214.         {
  215.             //Delete to the end of line
  216.             track(opRestoreLine,(void *)abs_line(),(void *)abs_row());
  217.             abs_line()->del_char(abs_col(), abs_line()->len() - abs_col());
  218.         }
  219.     }
  220.  
  221.     if(abs_col() >= abs_line()->len()) //Special case, merge lines
  222.     {
  223.         track(opRestoreLine,(void *)abs_line(),(void *)abs_row());
  224.  
  225.         PLine ln = PLine(Remove(abs_row() + 1));
  226.         if(ln)
  227.         {
  228.             track(opInsLine,(void *)ln,(void *)(abs_row()+1));
  229.             /*
  230.             int ins_pos = abs_col();
  231.  
  232.             while(ln->len())
  233.             {
  234.                 int chr = ln->del_char(0);
  235.                 abs_line()->ins_char(chr, ins_pos++);
  236.             }
  237.             */
  238.             abs_line()->ins_char(abs_col(), ln);
  239.         }
  240.     }
  241.     else
  242.     {
  243.         chr = chr_out(abs_line()->del_char(abs_col()));
  244.         track(opInsChar, (void *)chr);
  245.     }
  246.  
  247.     check_hiliting();
  248.  
  249.     return chr;
  250. }
  251.  
  252. void Buffer::split_line(Rect& rect)
  253. {
  254.     unmark();
  255.     PLine ln = new Line;
  256.     int chr;
  257.     int offset = 0;
  258.     changed = 1;
  259.     int smart_indent = 0;
  260.  
  261. //-----------------------------------
  262. // C/C++ Smart indent (Part 2)
  263. //-----------------------------------
  264.     if(abs_line()->char_at(abs_col() - 1) == '{' && do_smart())
  265.         smart_indent = 1;
  266. //    end
  267.  
  268.     int pos_state = abs_line()->state();
  269.  
  270.     track(opRestoreLine,(void *)abs_line(),(void *)abs_row());
  271.  
  272.     do
  273.     {
  274.         chr = abs_line()->del_char(abs_col());
  275.         ln->ins_char(chr,ln->len());
  276.     }
  277.     while(chr);
  278.  
  279.     if(auto_indent)
  280.     {
  281.         for(chr = abs_line()->char_at(offset);
  282.             chr && __issp(chr_out(chr));
  283.             chr = abs_line()->char_at(offset))
  284.         {
  285.             offset++;
  286.         }
  287.     }
  288.  
  289.     track(opDelLine,(void *)(abs_row()+1));
  290.  
  291.     ins_line(ln, abs_row()+1);
  292.  
  293.     if(hiliting)
  294.         fill_hiliting(abs_row(), pos_state);
  295.  
  296.     cursor_down(rect);
  297.     line_begin(rect);
  298.  
  299.     if(auto_indent)
  300.     {
  301.         while(offset--)
  302.             ins_char(rect, ' ');
  303.  
  304.         if(smart_indent)
  305.         {
  306.             int pos = (abs_col() / TAB_WIDTH + 1) * TAB_WIDTH;
  307.             while(abs_col() < pos)
  308.                 ins_char(rect, ' ');
  309.         }
  310.     }
  311. }
  312.  
  313. int Buffer::replace_char(Rect& rect, int chr)
  314. {
  315.     unmark();
  316.     changed = 1;
  317.  
  318.     if(chr == '\n' || chr == '\r')
  319.     {
  320.         //split_line(rect);
  321.  
  322.         cursor_down(rect);
  323.         line_begin(rect);
  324.         return 0;
  325.     }
  326.     track(opRestoreLine,(void *)abs_line(),(void *)abs_row());
  327.  
  328.     int old_chr = chr_out(abs_line()->del_char(abs_col()));
  329.  
  330.     int _cur_col   = cur_col;
  331.     int _start_col = start_col;
  332.  
  333.     cur_col += abs_line()->ins_char(chr_in(chr), abs_col());
  334.  
  335.     if(cur_col >= rect.cols)
  336.     {
  337.         start_col += cur_col - rect.cols + 1;
  338.         cur_col    = rect.cols - 1;
  339.     }
  340.  
  341.     if(_cur_col != cur_col)
  342.         track(opCurCol,(void *)_cur_col);
  343.     if(_start_col != start_col)
  344.         track(opStartCol,(void *)_start_col);
  345.  
  346.     check_hiliting();
  347.  
  348.     return old_chr;
  349. }
  350.  
  351. void Buffer::del_word_right(Rect& rect)
  352. {
  353.     unmark();
  354.     changed = 1;
  355.  
  356.     if(abs_col() >= abs_line()->len())
  357.     {
  358.         del_char(rect); // 'del_char' check hiliting itself
  359.         return;
  360.     }
  361.     else
  362.     {
  363.         int chr;
  364.         int deleted = 0;
  365.         track(opRestoreLine,(void *)abs_line(),(void *)abs_row());
  366.  
  367. // Stage 1: delete characted in word
  368.         do
  369.         {   //Deleting characters in this way doesn't change screen appearance,
  370.             //but prevents merging lines
  371.             chr = chr_out(abs_line()->del_char(abs_col()));
  372.  
  373.             if(!deleted)
  374.                 deleted = chr;
  375.         }
  376.         while(__isic(chr));
  377.  
  378.         if(chr)
  379.         {
  380.             // Last char was not 'isascii', return it back
  381.             if(__isic(deleted))
  382.                 abs_line()->ins_char(chr_in(chr), abs_col());
  383.  
  384. // Stage 2: delete spaces after word
  385.             do
  386.             {
  387.                 chr = chr_out(abs_line()->del_char(abs_col()));
  388.             }
  389.             while(__issp(chr));
  390.  
  391.             if(chr)
  392.                 abs_line()->ins_char(chr_in(chr), abs_col());
  393.         }
  394.     }
  395.  
  396.     check_hiliting();
  397. }
  398.  
  399. void Buffer::del_word_left(Rect& rect)
  400. {
  401.     unmark();
  402.     changed = 1;
  403.  
  404.     if(abs_col() > abs_line()->len())
  405.         line_end(rect);
  406.     else
  407.     {
  408.         int chr;
  409.         int save_col;
  410.         int deleted = 0;
  411.  
  412.         track(opRestoreLine,(void *)abs_line(),(void *)abs_row());
  413. // Stage 1: delete characters in word
  414.         while(1)
  415.         {
  416.             save_col = abs_col();
  417.             cursor_left(rect);
  418.  
  419.             chr = chr_out(abs_line()->char_at(abs_col()));
  420.  
  421.             if(__isic(chr) && save_col != abs_col())
  422.             {
  423.                 deleted = chr_out(abs_line()->del_char(abs_col()));
  424.             }
  425.             else
  426.             {
  427.                 if(!__issp(chr) && !deleted && save_col != abs_col())
  428.                     abs_line()->del_char(abs_col());
  429.                 break;
  430.             }
  431.         }
  432.         if(save_col != abs_col())
  433.             cursor_right(rect);
  434.  
  435.         //deleted = 0;
  436.         while(1)
  437.         {
  438.             save_col = abs_col();
  439.             cursor_left(rect);
  440.  
  441.             chr = chr_out(abs_line()->char_at(abs_col()));
  442.  
  443.             if(__issp(chr) && save_col != abs_col())
  444.             {
  445.                 deleted = chr_out(abs_line()->del_char(abs_col()));
  446.             }
  447.             else
  448.             {
  449.                 if(deleted && save_col != abs_col())
  450.                     cursor_right(rect);
  451.                 break;
  452.             }
  453.         }
  454.     }
  455.  
  456.     check_hiliting();
  457. }
  458.  
  459. void Buffer::del_to_EOL(Rect& rect)
  460. {
  461.     unmark();
  462.     changed = 1;
  463.  
  464.     if(abs_col() > abs_line()->len())
  465.         line_end(rect);
  466.     else
  467.     {
  468.         int deleted = 0;
  469.  
  470.         track(opRestoreLine,(void *)abs_line(),(void *)abs_row());
  471.  
  472.         /*
  473.         do
  474.         {
  475.             deleted = chr_out(abs_line()->del_char(abs_col()));
  476.         }
  477.         while(deleted);
  478.         */
  479.         abs_line()->del_char(abs_col(), abs_line()->len() - abs_col());
  480.     }
  481.  
  482.     check_hiliting();
  483. }
  484.  
  485. void Buffer::dup_line(Rect&, int line_num)
  486. {
  487.     if(line_num >= Count())
  488.         return;
  489.  
  490.     changed = 1;
  491.  
  492.     PLine pLn = new Line(line(line_num));
  493.  
  494.     track(opDelLine,(void *)(line_num+1));
  495.     ins_line(pLn, line_num+1);
  496.     check_hiliting();
  497. }
  498.  
  499. Line* Buffer::del_line(Rect&, int line_num)
  500. {
  501.     changed = 1;
  502.  
  503.     if(line_num >= Count())
  504.         return 0;
  505.     if(line_num == Count() - 1 && line_num == abs_row())
  506.         return 0;
  507.  
  508.     track(opInsLine,(void *)line(line_num),(void *)line_num);
  509.  
  510.     if(hiliting)
  511.     {
  512.         // Removing this line may affect rest of lines in buffer
  513.  
  514.         // If start conditions in this and next line doesn't equal
  515.         // then recalculate rest of lines with new conditions
  516.  
  517.         PLine ln0 = line(line_num);
  518.         PLine ln1 = line(line_num + 1);
  519.  
  520.         //Use start conditions of line which will be removed,
  521.         //and recalculate rest of lines
  522.         if(ln0 && ln1 && ln0->state() != ln1->state())
  523.             fill_hiliting(line_num + 1, ln0->state());
  524.     }
  525.  
  526.     return PLine(Remove(line_num));
  527. }
  528.  
  529. void Buffer::back_space(Rect& rect)
  530. {
  531.     changed = 1;
  532.  
  533.     int pos = abs_col();
  534.     cursor_left(rect);
  535.  
  536.     if(chr_out(abs_line()->char_at(abs_col())) == ' ')
  537.     {
  538.         int is_eof = 1;
  539.         //Check if all chars up to end of line are spaces
  540.         for(int i = abs_col() + 1; i < abs_line()->len(); i++)
  541.         {
  542.             if(chr_out(abs_line()->char_at(i)) != ' ')
  543.             {
  544.                 is_eof = 0;
  545.                 break;
  546.             }
  547.         }
  548.  
  549.         if(is_eof)
  550.             return;
  551.     }
  552.  
  553.     if(abs_col() >= abs_line()->len())
  554.         return;
  555.  
  556.     if(pos != abs_col())
  557.         del_char(rect);
  558.     else
  559.     {
  560.         if(abs_row() > 0)
  561.         {
  562.             cursor_up(rect);
  563.             line_end(rect);
  564.             del_char(rect);
  565.         }
  566.     }
  567. }
  568.  
  569. //----------------------------------------------------------------------
  570. // Utility routines
  571. //
  572. //----------------------------------------------------------------------
  573.  
  574. Parser* Buffer::gen_parser(int i)
  575. {
  576.     Parser *res = Parser::GenParser(hiliting);
  577.  
  578.     if(!res)
  579.         res = new Parser;
  580.  
  581.     if(i)
  582.         res->SetXlat(cp_out);
  583.  
  584.     return res;
  585. }
  586.  
  587. void Buffer::mark()
  588. {
  589.     if(!mark_state)
  590.     {
  591.         track(opMarking,(void *)mark_state);
  592.         old_abs_col = abs_col();
  593.         old_abs_row = abs_row();
  594.     }
  595.     mark_state = 1;
  596. }
  597.  
  598. void Buffer::unmark()
  599. {
  600.     if(mark_state)
  601.     {
  602.         track(opMarking,(void *)mark_state);
  603.         track(opMarkPos,(void *)old_abs_col,(void *)old_abs_row);
  604.     }
  605.     mark_state = 0;
  606. }
  607.  
  608. void Buffer::set_column_block(int mode)
  609. {
  610.     track(opColBlock,(void *)col_block);
  611.     col_block = (mode) ? 1:0;
  612. }
  613.  
  614. void Buffer::set_hiliting(int mode)
  615. {
  616.     custom_syntax = 1;
  617.     track(opHiliting,(void *)hiliting);
  618.     hiliting = (mode <= HI_LAST && mode >= 0) ? mode : hiliting;
  619. }
  620.  
  621. void Buffer::set_ins_mode(int mode)
  622. {
  623.     track(opInsMode,(void *)ins_mode);
  624.     ins_mode = (mode) ? 1:0;
  625. }
  626.  
  627. void Buffer::set_changed(int i)
  628. {
  629.     changed = i;
  630. }
  631.  
  632. void Buffer::set_found(int h_row, int h_col, int h_len)
  633. {
  634.     found_row  = h_row;
  635.     found_len  = h_len;
  636.     found_col  = h_col;
  637.     found_show = 1;
  638. }
  639. void Buffer::ins_line(Line* ln, int line_num)
  640. {
  641.     if(!ln || line_num < 0 || line_num > Count() + 1)
  642.         return;
  643.  
  644.     At(ln, line_num);
  645. }
  646.  
  647. void Buffer::add_line(Line* ln0)
  648. {
  649.     if(Count())
  650.     {
  651.         PLine ln = line(Count() - 1);
  652.         int pos_state = ln->state();
  653.  
  654.         Parser* parser = gen_parser(1);
  655.  
  656.         ln0->set_hiliting(parser, pos_state);
  657.  
  658.         delete parser;
  659.     }
  660.     Add(ln0);
  661. }
  662.  
  663. void Buffer::fill_hiliting(int start, int pos_state)
  664. {
  665.     Parser* parser = gen_parser(1);
  666.  
  667.     for(int i = start;i < Count(); i++)
  668.     {
  669.         PLine ln = line(i);
  670.  
  671.         if(ln)
  672.             ln->set_hiliting(parser, pos_state);
  673.     }
  674.  
  675.     delete parser;
  676. }
  677.  
  678. void Buffer::check_hiliting()
  679. {
  680.     if(!hiliting)
  681.         return;
  682.  
  683.     PLine ln0 = abs_line();
  684.  
  685.     int pos_state = ln0->state();
  686.  
  687.     Parser* parser = gen_parser(1);
  688.  
  689.     ln0->set_hiliting(parser, pos_state);
  690.  
  691.     delete parser;
  692.  
  693.     PLine ln1 = line(abs_row() + 1);
  694.  
  695.     if(ln1 && ln1->state() != pos_state)
  696.         fill_hiliting(abs_row() + 1, pos_state);
  697. }
  698.  
  699. void Buffer::flip_hiliting()
  700. {
  701.     custom_syntax = 1;
  702.  
  703.     hiliting++;
  704.     if(hiliting > HI_LAST)
  705.         hiliting = 0;
  706.  
  707.     if(hiliting)
  708.         fill_hiliting(0, ST_INITIAL);
  709. }
  710.  
  711. void Buffer::set_xlate(char *cp)
  712. {
  713.     int i;
  714.  
  715.     cur_cp[0] = 0;
  716.  
  717.     for(i = 0; i < 256; i++)
  718.         cp_in[i] = cp_out[i] = i;
  719.  
  720.     if(!cp)
  721.         return;
  722.  
  723.     char cTmp[256];
  724.  
  725.     for(i = 0; i < 256; i++)
  726.         cTmp[i] = i;
  727.  
  728.     int rc1;
  729.     int rc2;
  730.  
  731.     rc1 = cp2cp(cp, "", &cTmp[1], &cp_out[1], 255);
  732.     rc2 = cp2cp("", cp, &cTmp[1], &cp_in [1], 255);
  733.  
  734.     if(rc1 || rc2)
  735.     {
  736.         for(i = 0; i < 256; i++)
  737.             cp_in[i] = cp_out[i] = i;
  738.     }
  739.     else
  740.     {
  741.         strcpy(cur_cp, cp);
  742.     }
  743. }
  744.  
  745. //-----------------------------------------
  746. // Word wrap
  747. //-----------------------------------------
  748.  
  749. int Buffer::ww_need(int i)
  750. {
  751.     PLine ln = line(i);
  752.  
  753.     if(!ln)
  754.         return 0;
  755.  
  756.     int slen = ln->len();
  757.  
  758.     while(slen && __issp(ln->char_at(slen - 1)))
  759.         slen--;
  760.  
  761.     if(slen > ww_width)
  762.         return 1;
  763.  
  764.     return 0;
  765. }
  766.  
  767. int Buffer::ww_perform(Rect& rect, int row)
  768. {
  769.     PLine ln = line(row);
  770.  
  771.     if(!ln)
  772.         return 0;
  773.  
  774.     int break_pos = 0;
  775.     int i = 0;
  776.     int slen = ln->len();
  777.  
  778.     while(slen && __issp(ln->char_at(slen - 1)))
  779.         slen--;
  780.  
  781.     if(slen < ww_width)
  782.         return 0;
  783.  
  784.     //Look for place where we will break line
  785.  
  786.     for(i = ww_width - 1; i > 0; i--)
  787.     {
  788.         if(__issp(chr_out(ln->char_at(i))))
  789.         {
  790.             break_pos = i + 1;
  791.             break;
  792.         }
  793.     }
  794.  
  795.     if(!break_pos) //Non-breakable line
  796.     {
  797.         //Try other way
  798.         for(i = ww_width; i < slen; i++)
  799.         {
  800.             if(__issp(chr_out(ln->char_at(i))))
  801.             {
  802.                 break_pos = i + 1;
  803.                 break;
  804.             }
  805.         }
  806.  
  807.         if(!break_pos)
  808.             return 0;
  809.     }
  810.  
  811.     int save_flags     = word_wrap;
  812.     int save_row       = abs_row();
  813.     int save_col       = abs_col();
  814.  
  815.     word_wrap = 0;
  816.  
  817.     goto_line(rect, row);
  818.     goto_col (rect, break_pos);
  819.  
  820.     if(save_flags & WW_MERGE)
  821.     {
  822.         line_end(rect);
  823.         del_char(rect);
  824.  
  825.         if(__issp(chr_out(abs_line()->char_at(abs_col()))))
  826.             del_word_right(rect);
  827.  
  828.         ins_char(rect,' ');
  829.         goto_col (rect, break_pos);
  830.         ins_char(rect, '\n');
  831.     }
  832.     else
  833.         ins_char(rect, '\n');
  834.  
  835.     word_wrap = save_flags;
  836.  
  837.     goto_line(rect, save_row);
  838.     goto_col(rect, save_col);
  839.  
  840.     return break_pos;
  841. }
  842.  
  843.