From ad8daec47cd630a51f8589a59de42ceca18941f7 Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Wed, 23 Sep 2015 21:40:32 +0000 Subject: [PATCH] fpspreadsheet: Add code for copying to clipboard (fpsCtrls), initial version only handles biff8 and biff5 formats. git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@4354 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- components/fpspreadsheet/fpspreadsheet.pas | 91 +++++++++++++++++-- .../fpspreadsheet/fpspreadsheetctrls.pas | 42 ++++++++- components/fpspreadsheet/fpsreaderwriter.pas | 11 ++- components/fpspreadsheet/xlsbiff5.pas | 2 +- components/fpspreadsheet/xlsbiff8.pas | 2 +- components/fpspreadsheet/xlscommon.pas | 7 ++ 6 files changed, 140 insertions(+), 15 deletions(-) diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas index a89759003..30eb7aaad 100755 --- a/components/fpspreadsheet/fpspreadsheet.pas +++ b/components/fpspreadsheet/fpspreadsheet.pas @@ -591,6 +591,9 @@ type {@@ The workbook contains the worksheets and provides methods for reading from and writing to file. } + + { TsWorkbook } + TsWorkbook = class private { Internal data } @@ -653,7 +656,8 @@ type class function GetFormatFromFileHeader(AStream: TStream; out SheetType: TsSpreadsheetFormat): Boolean; overload; function CreateSpreadReader(AFormat: TsSpreadsheetFormat): TsBasicSpreadReader; - function CreateSpreadWriter(AFormat: TsSpreadsheetFormat): TsBasicSpreadWriter; + function CreateSpreadWriter(AFormat: TsSpreadsheetFormat; + AClipboardMode: Boolean = false): TsBasicSpreadWriter; procedure ReadFromFile(AFileName: string; AFormat: TsSpreadsheetFormat); overload; procedure ReadFromFile(AFileName: string); overload; procedure ReadFromFileIgnoringExtension(AFileName: string); @@ -662,7 +666,7 @@ type const AFormat: TsSpreadsheetFormat; const AOverwriteExisting: Boolean = False); overload; procedure WriteToFile(const AFileName: String; const AOverwriteExisting: Boolean = False); overload; - procedure WriteToStream(AStream: TStream; AFormat: TsSpreadsheetFormat); + procedure WriteToStream(AStream: TStream; AFormat: TsSpreadsheetFormat; AClipboardMode: Boolean = false); { Worksheet list handling methods } function AddWorksheet(AName: string; @@ -720,6 +724,9 @@ type function AddNumberFormat(AFormatStr: String): Integer; function GetNumberFormat(AIndex: Integer): TsNumFormatParams; function GetNumberFormatCount: Integer; + + { Clipboard } + procedure CopyToClipboardStream(AStream: TStream; AFormat: TsSpreadsheetFormat); (* { Color handling } function FPSColorToHexString(AColor: TsColor; ARGBColor: TFPColor): String; @@ -810,6 +817,7 @@ type TsBasicSpreadReader = class(TsBasicSpreadReaderWriter) public { General writing methods } + procedure ReadFromClipboardStream(AStream: TStream); virtual; abstract; procedure ReadFromFile(AFileName: string); virtual; abstract; procedure ReadFromStream(AStream: TStream); virtual; abstract; procedure ReadFromStrings(AStrings: TStrings); virtual; abstract; @@ -821,6 +829,7 @@ type { Helpers } procedure CheckLimitations; virtual; { General writing methods } + procedure WriteToClipboardStream(AStream: TStream); virtual; abstract; procedure WriteToFile(const AFileName: string; const AOverwriteExisting: Boolean = False); virtual; abstract; procedure WriteToStream(AStream: TStream); virtual; abstract; @@ -6618,19 +6627,23 @@ end; Convenience method which creates the correct writer object for a given spreadsheet format. - @param AFormat File format to be used for writing the workbook + @param AFormat File format to be used for writing the workbook + @param AClipboardMode If TRUE then special data are written for usage in the + clipboard @return An instance of a TsCustomSpreadWriter descendent which is able to write the given file format. -------------------------------------------------------------------------------} -function TsWorkbook.CreateSpreadWriter(AFormat: TsSpreadsheetFormat): TsBasicSpreadWriter; +function TsWorkbook.CreateSpreadWriter(AFormat: TsSpreadsheetFormat; + AClipboardMode: Boolean = false): TsBasicSpreadWriter; var i: Integer; begin Result := nil; for i := 0 to Length(GsSpreadFormats) - 1 do - if GsSpreadFormats[i].Format = AFormat then + if (GsSpreadFormats[i].Format = AFormat) and + (AClipboardMode or GsSpreadFormats[i].CanWriteToClipboard) then begin Result := GsSpreadFormats[i].WriterClass.Create(self); Break; @@ -6890,19 +6903,23 @@ end; {@@ ---------------------------------------------------------------------------- Writes the document to a stream - @param AStream Instance of the stream being written to - @param AFormat File format to be written. + @param AStream Instance of the stream being written to + @param AFormat File format to be written. + @param AClipboardMode Stream will be used by calling method for clipboard access -------------------------------------------------------------------------------} -procedure TsWorkbook.WriteToStream(AStream: TStream; AFormat: TsSpreadsheetFormat); +procedure TsWorkbook.WriteToStream(AStream: TStream; AFormat: TsSpreadsheetFormat; + AClipboardMode: Boolean = false); var AWriter: TsBasicSpreadWriter; begin - AWriter := CreateSpreadWriter(AFormat); + AWriter := CreateSpreadWriter(AFormat, AClipboardMode); try PrepareBeforeSaving; AWriter.CheckLimitations; FReadWriteFlag := rwfWrite; - AWriter.WriteToStream(AStream); + if AClipboardMode then + AWriter.WriteToClipboardStream(AStream) else + AWriter.WriteToStream(AStream); finally FReadWriteFlag := rwfNormal; AWriter.Free; @@ -7715,6 +7732,60 @@ function TsWorkbook.GetNumberFormatCount: Integer; begin Result := FNumFormatList.Count; end; + +{@@ ---------------------------------------------------------------------------- + Writes the selected cells to a stream for usage in the clipboard. + Transfer to the clipboard has do be done by the calling routine since + fpspreadsheet does not "know" the system's clipboard. +-------------------------------------------------------------------------------} +procedure TsWorkbook.CopyToClipboardStream(AStream: TStream; + AFormat: TsSpreadsheetFormat); +var + clipbook: TsWorkbook; + clipsheet: TsWorksheet; + sel: TsCellRange; + r, c: Cardinal; + srccell, destcell: PCell; +begin + if AStream = nil then + exit; + + if ActiveWorksheet = nil then + exit; + + // Create workbook which will be written to clipboard stream + // Contains only the selected worksheet and the selected cells. + clipbook := TsWorkbook.Create; + try + clipsheet := clipbook.AddWorksheet(ActiveWorksheet.Name); + for sel in ActiveWorksheet.GetSelection do + begin + for r := sel.Row1 to sel.Row2 do + for c := sel.Col1 to sel.Col2 do + begin + srccell := ActiveWorksheet.FindCell(r, c); + if srccell <> nil then begin + destcell := clipsheet.AddCell(r, c); + clipsheet.CopyCell(srccell, destcell); + end; + end; + end; + // Select the same cells as in the source workbook. + clipsheet.SetSelection(ActiveWorksheet.GetSelection); + clipsheet.SelectCell(ActiveWorksheet.ActiveCellRow, ActiveWorksheet.ActiveCellCol); + + // Write this workbook to a stream. Set the last parameter (ClipboardMode) + // to TRUE to use the dedicated clipboard routine. + clipbook.WriteToStream(AStream, AFormat, true); + + // The calling routine which copies the stream to the clipboard requires + // the stream to be at its beginning. + AStream.Position := 0; + finally + clipbook.Free; + end; +end; + (* {@@ ---------------------------------------------------------------------------- Adds a color to the palette and returns its palette index, but only if the diff --git a/components/fpspreadsheet/fpspreadsheetctrls.pas b/components/fpspreadsheet/fpspreadsheetctrls.pas index 96a7b68e9..e85f2bf64 100644 --- a/components/fpspreadsheet/fpspreadsheetctrls.pas +++ b/components/fpspreadsheet/fpspreadsheetctrls.pas @@ -467,9 +467,13 @@ procedure Register; implementation uses - Types, Math, StrUtils, TypInfo, LCLType, LCLProc, Dialogs, Forms, + Types, Math, StrUtils, TypInfo, LCLType, LCLProc, Dialogs, Forms, Clipbrd, fpsStrings, fpsUtils, fpsNumFormat, fpsHTMLUtils; +var + cfBiff8Format: Integer = 0; + cfBiff5Format: Integer = 0; + {@@ ---------------------------------------------------------------------------- Registers the spreadsheet components in the Lazarus component palette, page "FPSpreadsheet". @@ -1145,7 +1149,43 @@ var r,c,i: Integer; sel: TsCellRangeArray; cell: PCell; + stream: TStream; begin + Clipboard.Open; + try + Clipboard.Clear; + + // Ensure the 'Biff8' clipboard format is registered + cfBiff8Format := Clipboard.FindFormatID('Biff8'); + If cfBiff8Format = 0 Then + cfBiff8Format := RegisterClipboardFormat('Biff8'); + + // Ensure that the 'Biff5' clipboard format is registered + cfBiff5Format := Clipboard.FindFormatID('Biff5'); + If cfBiff5Format = 0 Then + cfBiff5Format := RegisterClipboardFormat('Biff5'); + + stream := TMemoryStream.Create; + try + // At first write BIFF8 format + FWorkbook.CopyToClipboardStream(stream, sfExcel8); + Clipboard.AddFormat(cfBiff8Format, stream); + (stream as TMemoryStream).Clear; + + // Then write BIFF5 format + FWorkbook.CopyToClipboardStream(stream, sfExcel5); + Clipboard.AddFormat(cfBiff5Format, stream); + + // To do: HTML format, CSV format, XML format + // I don't know which format is written by xlsx and ods natively. + finally + stream.Free; + end; + finally + Clipboard.Close; + end; + + FCutPending := false; ClearCellClipboard; diff --git a/components/fpspreadsheet/fpsreaderwriter.pas b/components/fpspreadsheet/fpsreaderwriter.pas index 79d76fd6c..f481d9a80 100644 --- a/components/fpspreadsheet/fpsreaderwriter.pas +++ b/components/fpspreadsheet/fpsreaderwriter.pas @@ -141,13 +141,16 @@ type ReaderClass: TsSpreadReaderClass; WriterClass: TsSpreadWriterClass; Format: TsSpreadsheetFormat; + CanReadFromClipboard: Boolean; + CanWriteToClipboard: Boolean; end; var GsSpreadFormats: array of TsSpreadFormatData; procedure RegisterSpreadFormat(AReaderClass: TsSpreadReaderClass; - AWriterClass: TsSpreadWriterClass; AFormat: TsSpreadsheetFormat); + AWriterClass: TsSpreadWriterClass; AFormat: TsSpreadsheetFormat; + ACanReadFromClipboard: Boolean = false; ACanWriteToClipboard: Boolean = false); implementation @@ -162,7 +165,9 @@ uses procedure RegisterSpreadFormat( AReaderClass: TsSpreadReaderClass; AWriterClass: TsSpreadWriterClass; - AFormat: TsSpreadsheetFormat); + AFormat: TsSpreadsheetFormat; + ACanReadFromClipboard: Boolean = false; + ACanWriteToClipboard: Boolean = false); var len: Integer; begin @@ -172,6 +177,8 @@ begin GsSpreadFormats[len].ReaderClass := AReaderClass; GsSpreadFormats[len].WriterClass := AWriterClass; GsSpreadFormats[len].Format := AFormat; + GsSpreadFormats[len].CanReadFromClipboard := ACanReadFromClipboard; + GsSpreadFormats[len].CanWriteToClipboard := ACanWriteToClipboard; end; diff --git a/components/fpspreadsheet/xlsbiff5.pas b/components/fpspreadsheet/xlsbiff5.pas index abd636427..37b1e7d6d 100755 --- a/components/fpspreadsheet/xlsbiff5.pas +++ b/components/fpspreadsheet/xlsbiff5.pas @@ -1807,7 +1807,7 @@ initialization Excel5Settings.CodePage := GetDefaultTextEncoding; {$ENDIF} - RegisterSpreadFormat(TsSpreadBIFF5Reader, TsSpreadBIFF5Writer, sfExcel5); + RegisterSpreadFormat(TsSpreadBIFF5Reader, TsSpreadBIFF5Writer, sfExcel5, false, true); MakeLEPalette(PALETTE_BIFF5); end. diff --git a/components/fpspreadsheet/xlsbiff8.pas b/components/fpspreadsheet/xlsbiff8.pas index d9f001dd6..d0be69dbe 100755 --- a/components/fpspreadsheet/xlsbiff8.pas +++ b/components/fpspreadsheet/xlsbiff8.pas @@ -3443,7 +3443,7 @@ end; initialization // Registers this reader / writer in fpSpreadsheet - RegisterSpreadFormat(TsSpreadBIFF8Reader, TsSpreadBIFF8Writer, sfExcel8); + RegisterSpreadFormat(TsSpreadBIFF8Reader, TsSpreadBIFF8Writer, sfExcel8, false, true); // Converts the palette to litte-endian MakeLEPalette(PALETTE_BIFF8); diff --git a/components/fpspreadsheet/xlscommon.pas b/components/fpspreadsheet/xlscommon.pas index 8439b474b..54c9af12c 100644 --- a/components/fpspreadsheet/xlscommon.pas +++ b/components/fpspreadsheet/xlscommon.pas @@ -463,6 +463,8 @@ type FCodePage: String; // in a format prepared for lconvencoding.ConvertEncoding FFirstNumFormatIndexInFile: Integer; FPalette: TsPalette; + FClipboardMode: Boolean; + procedure AddBuiltinNumFormats; override; function FindXFIndex(ACell: PCell): Integer; virtual; function FixLineEnding(const AText: String): String; @@ -571,6 +573,7 @@ type constructor Create(AWorkbook: TsWorkbook); override; destructor Destroy; override; procedure CheckLimitations; override; + procedure WriteToClipboardStream(AStream: TStream); override; end; procedure AddBuiltinBiffFormats(AList: TStringList; @@ -3945,6 +3948,10 @@ begin AStream.WriteWord(WordToLE(w)); end; +procedure TsSpreadBIFFWriter.WriteToClipboardStream(AStream: TStream); +begin + WriteToStream(AStream); +end; procedure TsSpreadBIFFWriter.WriteVirtualCells(AStream: TStream); var