I set the goRowSelect property to true and I want
to copy the selected rows from my StringGrid to another StringGrid.
How do I do that?
Sincerely,
Jeff
see Unit below. Apologies to any unsuspecting lurkers for the size of this
post!
Peter Below (TeamB) 10011...@compuserve.com)
{+------------------------------------------------------------
| Unit GridClip
|
| Version: 1.0 Last modified: 04.11.97, 10:58:10
| Author : P. Below
| Project: Common utilities
| Description:
| This unit provides clipboard operations for stringgrids.
| We use a custom clipboard format but also provide CF_TEXT
| representation of data copied to the clipboard for the
| benefit of other programs. The text representation uses
| rows separated by CRLF combinations, with column cell data
| separated by tab characters.
| The custom format is binary. Note that we only copy the
| text in cells, not any attached objects.
+------------------------------------------------------------}
Unit GridClip;
Interface
Uses Classes, Grids;
Var
GridClipboardFormat: Cardinal;
Procedure CopySelectionToClipboard( aStringgrid: TStringGrid );
Procedure CutSelectionToClipboard( aStringgrid: TStringGrid;
DeleteEmptyLines: Boolean );
Procedure DeleteSelection( aStringgrid: TStringGrid;
DeleteEmptyLines: Boolean );
Procedure PasteFromClipboard( aStringgrid: TStringGrid; atCell:
TGridCoord;
InsertNewRows: Boolean );
Implementation
Uses Wintypes, Messages, WinProcs, SysUtils, Clipbrd;
Type
TGridInfoHeader = Record
numCols, numRows: Integer;
End;
TGridCracker = Class( TStringGrid )
End;
{+------------------------------------------------------------
| Procedure CopyStreamAs
|
| Parameters:
| stream: stream to copy to the clipboard
| format: format to use
| Call method:
| static
| Description:
| Allocates a global memory block, copies the streams contents
| into it and sets this block into the clipboard with the
| requested format. The clipboard is assumed to be already open.
| Error Conditions:
| May run out of memory, which we signal by an EOutOfMemory exception.
|
|Created: 04.11.97 12:21:58 by P. Below
+------------------------------------------------------------}
Procedure CopyStreamAs( stream: TStream; format: Integer );
Var
hMem: Thandle;
pMem: PChar;
size: LongInt;
oldpos: LongInt;
Begin
size := stream.size;
hmem := GlobalAlloc( GMEM_SHARE or GHND, size );
If hMem <> 0 Then Begin
pMem := GlobalLock( hMem );
If pMem <> Nil Then Begin
try
oldpos := stream.position;
stream.Position := 0;
stream.readBuffer( pMem^, size );
stream.Position := oldpos;
GlobalUnlock( hMem );
Clipboard.SetAshandle( format, hMem );
except
{ Operation failed somewhere, free the memory block and
reraise the exception. }
GlobalUnlock( hMem );
GlobalFree( hMem );
raise;
end; { try ... except }
End { If }
Else Begin
{ Lock error, very improbable, we treat it as an out of memory
error. }
GlobalFree( hMem );
OutOfMemoryError;
End; { Else }
End { If }
Else { Allocation failed. }
OutOfMemoryError;
End; { CopyStreamAs }
{+------------------------------------------------------------
| Procedure PasteStreamfrom
|
| Parameters:
| stream: stream to copy the clipboard data to
| format: clipboard format to fetch
| Call method:
| static
| Description:
| Gets the global memory handle for the requested format out of
| the clipboard, locks it and copies the data to the stream,
| starting at the current stream position. The position is
| left at the end of the read data.
| Error Conditions:
| If the clipboard does not contain the requested format or if
| the handle could not be obtained or locked we simply do
| nothing. No exception is raised unless the stream.WriteBuffer
| method runs into an error.
|
|Created: 04.11.97 12:39:36 by P. Below
+------------------------------------------------------------}
Procedure PasteStreamfrom( stream: TStream; format: Integer );
Var
hMem: Thandle;
pMem: PChar;
size: LongInt;
Begin
If Clipboard.HasFormat( format ) Then Begin
hMem := Clipboard.GetAsHandle( format );
If hMem <> 0 Then Begin
size := GlobalSize( hMem );
If size > 0 Then Begin
pMem := GlobalLock( hMem );
If pMem <> Nil Then Begin
try
stream.WriteBuffer( pMem^, size );
finally
GlobalUnlock( hMem );
end;
End; { If }
End; { If }
End; { If }
End; { If }
End; { PasteStreamfrom }
{+------------------------------------------------------------
| Procedure CopySelectionToClipboard
|
| Parameters:
| aStringGrid: grid who's selection we have to copy to the
| clipboard.
| Call method:
| static
| Description:
| If the grid has a non-empty selection we produce a custom
| binary image of the cells strings in a memory stream and put
| it on the clipboard. In addition we produce a string representation
| also in a memory stream and put that onto the clipboard as CF_TEXT
| format.
| The custom format consists of a header containing the number of
| columns and rows in the data block, plus a sequence of strings
| in the format used by TWriter.
| Error Conditions:
| We may run out of memory when creating or writing to the memory
| stream. This will raise exceptions.
|
|Created: 04.11.97 11:07:22 by P. Below
+------------------------------------------------------------}
Procedure CopySelectionToClipboard( aStringgrid: TStringGrid );
Var
SelRect: TGridRect;
ms: TMemoryStream;
writer: TWriter;
header: TGridInfoHeader;
i, j: Integer;
ch: Char;
S: String;
Begin
Selrect := aStringGrid.Selection;
If ( Selrect.Left > -1) and ( SelRect.Top > -1 ) Then Begin
{ We have a selection, prepare the header for the clipboard
data. }
header.numCols := Selrect.Right - Selrect.Left + 1;
header.numRows := Selrect.Bottom - Selrect.Top + 1;
{ Create memory stream and writer to access it. Make sure
both objects are properly freed in case of exceptions. }
ms:= TMemoryStream.Create;
try
writer := TWriter.Create( ms, 4096 );
try
{ Write the custom data format. }
With Writer Do Begin
Write( header, sizeof( header ));
For i := SelRect.Top To Selrect.Bottom Do
For j := SelRect.Left To Selrect.Right Do
WriteString( aStringGrid.Cells[ j, i ] );
FlushBuffer;
End; { With }
With Clipboard Do Begin
{ Open clipboard and copy the custom format into it. Make
sure the clipboard is properly closed in case of exceptions.
}
Open;
try
CopyStreamAs( ms, GridClipboardFormat );
{ Now build the text format of the data. }
ms.Clear;
For i := SelRect.Top To Selrect.Bottom Do Begin
For j := SelRect.Left To Selrect.Right Do Begin
S:= aStringGrid.Cells[ j,i ];
If Length( S ) > 0 Then
ms.WriteBuffer( S[ 1 ], Length( S ));
If j = Selrect.right Then
S:= #13#10
Else
S:= #9;
ms.WriteBuffer( S[ 1 ], Length( S ));
End; { For }
End; { For }
{ Make sure it is zero-terminated. }
ch:= #0;
ms.WriteBuffer( ch, sizeof( ch ));
{ Copy the data to the clipboard. }
CopyStreamAs( ms, CF_TEXT );
finally
{ Close the clipboard. }
Close;
end;
End; { With }
finally
writer.free;
end;
finally
ms.Free;
end;
End; { If }
End; { CopySelectionToClipboard }
{+------------------------------------------------------------
| Procedure CutSelectionToClipboard
|
| Parameters:
| aStringGrid: grid who's selection we have to copy to the
| clipboard.
| DeleteEmptyLines: true if empty grid lines should be deleted.
| Call method:
| static
| Description:
| Copies the selection (if any) of the grid to clipboard and
| then deletes it.
| Error Conditions:
| Can run into out of memory errors and stream errors in the
| called functions.
|
|Created: 04.11.97 13:21:38 by P. Below
+------------------------------------------------------------}
Procedure CutSelectionToClipboard( aStringgrid: TStringGrid;
DeleteEmptyLines: Boolean );
Begin
CopySelectionToClipboard( aStringGrid );
DeleteSelection( aStringGrid, DeleteEmptyLines );
End; { CutSelectionToClipboard }
{+------------------------------------------------------------
| Procedure ConvertTextToGridformat
|
| Parameters:
| ms: memory stream containing the data to convert. It also
| receives the converted data.
| Call method:
| static
| Description:
| Tries to parse the text into a semblance of grid data.
| Error Conditions:
|
|
|Created: 04.11.97 15:15:08 by P. Below
+------------------------------------------------------------}
Procedure ConvertTextToGridformat( ms: TMemoryStream );
Var
header: TGridInfoHeader;
pBuf, pScan, pStart : PChar;
writer: TWriter;
stringsWritten, diff: Integer;
Begin
header.numCols := 1;
header.numRows := 1;
stringsWritten := 0;
pBuf := StrAlloc( ms.size );
try
ms.Position := 0;
ms.ReadBuffer( pBuf^, ms.size );
ms.Clear;
writer:= TWriter.Create( ms, 4096 );
try
writer.write( header, sizeof( header ));
pScan:= pBuf;
pStart := pBuf;
While pStart^ <> #0 Do Begin
pScan := pStart;
While ( pScan^ <> #9 ) and ( pScan^ <> #13 ) and ( pScan^<> #0 )
Do
Inc( pScan );
If pScan^ = #9 Then Begin
If header.numRows = 1 Then
Inc( header.numCols );
End { If }
Else If pScan^ = #13 Then Begin
Inc( header.numRows );
End; { If }
pScan^:= #0;
Writer.WriteString( StrPas( pStart ));
Inc( stringsWritten );
pStart := pScan;
If pStart^ <> #0 Then Begin
Inc( pStart );
If pStart^ = #10 Then
Inc( pStart );
End; { If }
End; { While }
Writer.FlushBuffer;
finally
writer.free;
end;
ms.Position := 0;
If ( header.numCols * header.numRows ) <> stringsWritten Then Begin
diff := header.numCols * header.numRows - stringsWritten;
If diff = header.numCols Then Begin
Dec( header.numCols )
End { If }
Else Begin
ms.Clear;
Exit;
End; { Else }
End; { If }
ms.WriteBuffer( header, Sizeof( header ));
ms.Position := 0;
finally
StrDispose( pBuf );
end;
End; { ConvertTextToGridformat }
{+------------------------------------------------------------
| Procedure PasteFromClipboard
|
| Parameters:
| aStringgrid: grid to paste into
| atCell : coordinates of first cell to paste to
| InsertNewRows: true if new rows should be inserted for the data,
| false if existing cells should be overwritten.
| Call method:
| static
| Description:
| Pastes data from the clipboard into the passed grid. It will
| first look for our custom clipboard format and use that, if
| found. Otherwise it looks for a CF_TEXT clipboard item and
| tries to interpret that as a tab and CRLF separated representation
| of a grids contents. If the clipboard contains neither of these
| formats we do nothing.
| The layout of the clipboard data ( number of columns and rows ) may
| not match the grid we paste into. Data that would have to go in
| columns beyond the right edge of the current grid is discarded.
| However, addiditional lines are added to the grid as needed.
| If the InsertNewRows parameter is true pasted lines are inserted
| into the grids and existing lines move down.
| Calculations are a wee bit complicated by the fact that we can
| paste starting at an arbitrary cell in the grid.
| Error Conditions:
| May run out of memory or into stream I/O errors.
|
|Created: 04.11.97 13:44:04 by P. Below
+------------------------------------------------------------}
Procedure PasteFromClipboard( aStringgrid: TStringGrid; atCell: TGridCoord;
InsertNewRows: Boolean );
Var
ms: TmemoryStream;
reader: TReader;
header: TGridInfoHeader;
S: String;
i, j, orgRowcount: Integer;
Begin
If atCell.X >= aStringgrid.ColCount Then
Exit; { We don't allow paste beyond the right edge of the grid. }
If atCell.Y > aStringgrid.RowCount Then
atCell.Y := aStringgrid.RowCount; { This appends to the grid. }
ms:= TMemoryStream.Create;
try
{ Fetch data from clipboard into the memory stream. Prefer
custom format, accept CF_TEXT as alternative. }
If Clipboard.HasFormat( GridClipboardFormat ) Then Begin
PasteStreamFrom( ms, GridClipboardFormat );
End { If }
Else
If Clipboard.HasFormat( CF_TEXT ) Then Begin
PasteStreamFrom( ms, CF_TEXT );
ConvertTextToGridformat( ms );
End; { If }
If ms.Size > 0 Then Begin
{ OK, we have some data. Create a reader, block visual update
of the grid to reduce flicker, then read the header from
the data stream. The header tells us how many columns and
rows there are to paste. }
ms.Position := 0;
reader:= TReader.Create( ms, 4096 );
aStringgrid.Perform( WM_SETREDRAW, 0, 0 );
try
With reader Do Begin
Read( header, sizeof( header ));
{ If we are asked to insert the data we need to add a
bunch of empty lines and then move existing lines
down to get the empty lines to the row we are asked
to start the insertion. Even if we overwrite existing
cells we may have to add empty lines if there are more
rows to paste than are in the grid below the insertion
point.
Since the MoveRow method is protected in TCustomGrid
we need a cracker class. }
If InsertNewRows and ( header.numRows > 0 ) Then Begin
orgRowcount:= aStringGrid.RowCount;
aStringGrid.RowCount := orgRowcount + header.numRows;
For j := 1 To orgRowcount - atCell.Y Do
TGridCracker( aStringGrid ).MoveRow(
orgRowcount-j, orgRowcount-j+header.numRows );
End { If }