[comp.lang.pascal] Looking for a cut/paste procedure

defaria@hpclapd.HP.COM (Andy DeFaria) (01/03/91)

I have been playing around  with my mouse  and I have  gotten a mouse  unit
that let's me track mouse events and the  like.  What I'm  trying to do now
is  implement a  cut and paste  procedure  so  that  a button  1  drag will
highlight text on the screen and place the text into  a cut buffer.  Button
2 will paste any  text.  This is  turning out  to be  difficult and I would
appreciate any help that I can get.

One of the problems that I have is that there can  be up to 2000 characters
cut and  TP only likes strings  < 256  characters.  Another problem is that
currently my highlight and cut procedure isn't smart enough to  be  able to
cut only  what  is highlighted and   tends  to  cut  the  same character(s)
multiple times.  I know that given more time, debugging  and coding I could
work through it but I would appreciate any  pointers (and/or code)  to help
me complete this task.

Thanks in advance.

CDCKAB%EMUVM1.BITNET@cunyvm.cuny.edu ( Karl Brendel) (01/07/91)

In article <950048@hpclapd.HP.COM>, defaria@hpclapd.hp.com
  (Andy DeFaria) wrote:

[...deleted...]

>One of the problems that I have is that there can  be up to 2000
>characters cut and  TP only likes strings  < 256  characters.
>Another problem is that currently my highlight and cut procedure
>isn't smart enough to  be  able to cut only  what  is highlighted
>and   tends  to  cut  the  same character(s) multiple times.  I know
>that given more time, debugging  and coding I could work through it
>but I would appreciate any  pointers (and/or code)  to help me
>complete this task.

Don't cut to a string: cut to an array of char (or byte or word). (Be
sure to note that the simplest cut (a Move from screen memory to
your array) will pick up the attribute bytes as well as the
character bytes.) The array will not be limited to 255 char.

When pasting, you will have to consider issues related to blanks at
the end of lines (if you are cutting a rectangle from the screen,
and some lines in it are shorter than others, like text with a
ragged right margin) and implicit carriage returns (when text runs
all the way to the right margin), etc.

As for the cutting of characters multiple times, I don't think we
can really give you a clue where you're going wrong without seeing
some code. Perhaps your drag algorithm is off? It seems to me that
you should be picking off an upper left corner and a lower right
corner (begin and end of the drag), then doing some kind of action
that boils down to

        i := 0;
        for y := ytl to ylr do for x := xtl to xlr do
        begin
          buffer[i] := charAt(x,y);
          Inc(i);
        end;

(where you've got an appropriate charAt function).

+--------------------------------------------------------------------+
| Karl Brendel                           Centers for Disease Control |
| Internet: CDCKAB@EMUVM1.BITNET         Epidemiology Program Office |
| Bitnet: CDCKAB@EMUVM1                  Atlanta, GA, USA            |
|                        Home of Epi Info 5.0                        |
+--------------------------------------------------------------------+

defaria@hpclapd.HP.COM (Andy DeFaria) (01/08/91)

I have cutting working now, however I still have problems with highlighting
selected text:

My problems are not with actually  cutting the  text from the screen buffer
itself but  more highlighting the  text  that  is   to be cut  in a  smooth
interactive fashion.  It is not as easy as you  might imagine.  Roughly you
need to mark where selection begins (CutRegion).  Then as the pointer moves
you   want   to  highlight  the selected  region.   Plus you   may  need to
unhighlight some region (think   of the  pointer  moving backward).   Brute
force   algorithm of  unhighlighting   all of   the  previous  region   and
re-highlighting  the  new region produces  flicker  as  the  update  of the
display is not that fast or  optimized so  the following algorithm attempts
to highlight (and unhighlight) only that which is necessary.  You must also
take into consideration when the pointer has moved up or down a row and the
highlighting that needs to be done.  This is sort of handled when I finally
figured out that it  would  be better to  think  of  video memory as  a one
dimensional array of characters instead of  a two dimensional array of rows
and   columns.  Still my  HighlightText   routine  does not  handle  things
accurately.

Another problem is that mouse events are not always  reported.  You may get
an mouse event that  says the mouse  has  moved to pixel (120, 80)  (screen
position 15, 10) and its prior position was pixel (80, 80) (screen position
10, 10).   This  in and of itself  is not difficult  to handle  but picture
this: You have  a ten character block  currently selected (row 10 column 10
thru row 10 column 20)  and you  get a mouse  event that says the mouse has
moved to row 10 column 15.  You cannot unhighlight the proper region unless
you know which *direction* that mouse moved from.  (i.e. did it go from 10,
10 to 10, 15 or from 10, 20?).  I think  that this can  easily be solved by
keeping track of the last known pointer position but it  was at  this point
that I said "Boy, this is starting to get difficult.   And all this code to
execute for a mouse interrupt!  Maybe I'm just  going  about this the wrong
way?" and decided to ask the net.

Here is an excerpt of my code:

{ HighlightText: Highlights text from CutRegion to NewStop.  Main problems are }
{                are here.  This procedure does not always highlight the text  }
{                properly.  Consider when NewStop goes < CutRegion.Start       }
Procedure HighlightText (NewStop : Integer);

Var 
   I : Integer;

Begin
   If NewStop > CutRegion.Stop then
      Begin
      For I := CutRegion.Stop to NewStop do
         ScreenAddress^.Position [I].Attribute := (Red Shl 4) + Black;
      CutRegion.Stop := NewStop;
      End
   else If NewStop < CutRegion.Start then
      Begin
      For I := CutRegion.Start to NewStop do
         ScreenAddress^.Position [I].Attribute := (Red Shl 4) + Black;
      CutRegion.Start := NewStop;
      End
   else 
      Begin
      For I := NewStop to CutRegion.Stop do
         ScreenAddress^.Position [I].Attribute := (Black Shl 4) + LightGray;
      CutRegion.Stop := NewStop;
      End;
      
End;

{ CutText : This interrupt procedure is called by the mouse driver whenever    }
{           Mouse events occur.  It will handle LeftDown (start cut), LeftUp   }
{           (end cur), move while LeftDown (select and highlight text) and     }
{           RightDown (paiste).                                                }
{$F+}
Procedure CutText (Flags, CS, IP, AX, BX,CX,DX,SE, DI, DS, ES, BP : Word);
   Interrupt;
   
Var
   NewStop : Integer;
   I       : Integer;
   
Begin
   If AX = LeftDown then
      Begin { Unhighlight any old CutRegion }
      For I := CutRegion.Start to CutRegion.Stop do
         ScreenAddress^.Position [I].Attribute := (Black Shl 4) + LightGray;
      { Set Start and Stop to current character.  Note formula converts the }
      { pixel offsets in DX and CX to an offset from the beginning of video }
      { memory.                                                             }
      CutRegion.Start := (DX * 10) + (CX div 8) + 1;
      CutRegion.Stop  := CutRegion.Start;
      SetLength (CutBuffer, 0);
      End
   else If AX = LeftUp then
      Begin { Turn off pointer and cut the CutRegion.  Note that      }
            { CutBuffer is defined as a BigString and ConCat will add }
	    { a character to the end of a BigString.                  }
      DisplayPointer (False);
      For I := CutRegion.Start to CutRegion.Stop do
         ConCat (CutBuffer, ScreenAddress^.Position [I].Character);
      End
   else If AX = RightDown then
      Begin { Dumb paiste routine for now.  Simply write CutBuffer on line 25. }
      GotoXY (1, 25);
      ClrEOL;
      Write ('CutBuffer: ');
      PrintBigString (Output, CutBuffer);
      End
   else If AX = Move then
      Begin 
      DisplayPointer (True);  { Turn on pointer on movement. }
      If ButtonType (BX) = Button1 then
         Begin { Button 1 drag }
	 NewStop := (DX * 10) + (CX div 8) + 1;
	 { Events are generated for every pixel so we check to see if we have  }
	 { left the current character.  At last count that was CutRegion.Stop. }
	 { So if the NewStop is <> CutRegion.Stop then it must have moved out- }
	 { the last character position and we need to HighlightText.           }
	 If NewStop <> CutRegion.Stop then
	    HighlightText (NewStop);
	 End;
      End;
      
{------------------------------------------------------------------------------}
{ This inline code will pop all registers off the stack and then does a "far"  }
{ return.  This is useful in interrupt procedures that need to do a far return }
{ instead of executing jthe IRET that Turbo Pascal inserts for all Interrupt   }
{ procedures.                                                                  }
{------------------------------------------------------------------------------}
   Inline ($8B/$E5/
           $5D/
	   $07/
	   $1F/
	   $5F/
	   $5E/
	   $5A/
	   $59/
	   $5B/
	   $58/$CB);
	   
End;

{ EnableCutText: Simply sets mouse interrupt handler to CutText }
Procedure EnableCutText;

Begin { EnableCutText }
   If Not MousePresent then
      Exit;
      
   EnableEvents (@CutText, LeftDown + LeftUp + Move + RightDown);
   
End; { EnableCutText }