From 95704110ce388f1861c128aee2a50193f983e6a5 Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Mon, 28 Sep 2015 20:23:28 +0000 Subject: [PATCH] fpspreadsheet: Improved cooperation of visual controls with clipboard. git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@4363 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- .../examples/visual/fpsctrls/main.pas | 6 ++ components/fpspreadsheet/fpsactions.pas | 64 +++++++++++------ components/fpspreadsheet/fpscsv.pas | 37 +++++++--- components/fpspreadsheet/fpspreadsheet.pas | 52 ++++++++------ .../fpspreadsheet/fpspreadsheetchart.pas | 4 +- .../fpspreadsheet/fpspreadsheetctrls.pas | 72 ++++++++----------- .../fpspreadsheet/fpspreadsheetgrid.pas | 20 +++++- 7 files changed, 157 insertions(+), 98 deletions(-) diff --git a/components/fpspreadsheet/examples/visual/fpsctrls/main.pas b/components/fpspreadsheet/examples/visual/fpsctrls/main.pas index 7cd4e1187..9acc0e5d0 100644 --- a/components/fpspreadsheet/examples/visual/fpsctrls/main.pas +++ b/components/fpspreadsheet/examples/visual/fpsctrls/main.pas @@ -348,6 +348,7 @@ type procedure AcShowGridLinesExecute(Sender: TObject); procedure AcShowGridLinesUpdate(Sender: TObject); procedure AcViewInspectorExecute(Sender: TObject); + procedure EditCut1Execute(Sender: TObject); procedure HyperlinkHandler(Sender: TObject; ACaption: String; var AHyperlink: TsHyperlink); procedure InspectorTabControlChange(Sender: TObject); @@ -581,6 +582,11 @@ begin InspectorSplitter.Left := 0; // Make sure that the splitter is always at the left of the inspector end; +procedure TMainForm.EditCut1Execute(Sender: TObject); +begin + // +end; + { Event handler for hyperlinks: it only has to provide the hyperlink data which are applied to the active cell by the TsCellHyperlinkAction. Is called by the "new hyperlink" and "edit hyperlink" actions. diff --git a/components/fpspreadsheet/fpsactions.pas b/components/fpspreadsheet/fpsactions.pas index f53324535..1f7f8e648 100644 --- a/components/fpspreadsheet/fpsactions.pas +++ b/components/fpspreadsheet/fpsactions.pas @@ -118,6 +118,7 @@ type FCopyMode: TsCopyMode; public procedure ExecuteTarget(Target: TObject); override; + function HandlesTarget(Target: TObject): Boolean; override; procedure UpdateTarget(Target: TObject); override; published property Caption; @@ -525,7 +526,7 @@ implementation uses StdCtrls, ExtCtrls, Buttons, Forms, - fpsUtils, fpsNumFormat, fpsVisualUtils; + fpsUtils, fpsNumFormat, fpsVisualUtils, fpSpreadsheetGrid; procedure Register; begin @@ -767,28 +768,47 @@ const coCopyFormat, coCopyValue, coCopyFormula, coCopyCell ); begin - Unused(Target); + if Target is TsCustomWorksheetGrid then + case FCopyMode of + cmBrush: + begin + Checked := true; + WorkbookSource.SetPendingOperation(OPERATIONS[FCopyItem], Worksheet.GetSelection); + end; + cmCopy: + begin + Checked := false; + WorkbookSource.CopyCellsToClipboard; + end; + cmCut: + begin + Checked := false; + WorkbookSource.CutCellsToClipboard; + end; + cmPaste: + begin + Checked := false; + WorkbookSource.PasteCellsFromClipboard(OPERATIONS[FCopyItem]); + end; + end + else + if Target is TCustomEdit then + case FCopyMode of + cmBrush : ; + cmCopy : (Target as TCustomEdit).CopyToClipboard; + cmCut : (Target as TCustomEdit).CutToClipboard; + cmPaste : (Target as TCustomEdit).PasteFromClipboard; + end; +end; + +function TsCopyAction.HandlesTarget(Target: TObject): Boolean; +begin case FCopyMode of - cmBrush: - begin - Checked := true; - WorkbookSource.SetPendingOperation(OPERATIONS[FCopyItem], Worksheet.GetSelection); - end; - cmCopy: - begin - Checked := false; - WorkbookSource.CopyCellsToClipboard; - end; - cmCut: - begin - Checked := false; - WorkbookSource.CutCellsToClipboard; - end; - cmPaste: - begin - Checked := false; - WorkbookSource.PasteCellsFromClipboard(OPERATIONS[FCopyItem]); - end; + cmBrush: Result := inherited HandlesTarget(Target); + cmCopy, + cmCut, + cmPaste: Result := (Target <> nil) and + ( (Target is TsCustomWorksheetGrid) or (Target is TCustomEdit) ); end; end; diff --git a/components/fpspreadsheet/fpscsv.pas b/components/fpspreadsheet/fpscsv.pas index ad1541388..8a086eb90 100644 --- a/components/fpspreadsheet/fpscsv.pas +++ b/components/fpspreadsheet/fpscsv.pas @@ -22,6 +22,7 @@ type procedure ReadNumber(AStream: TStream); override; public constructor Create(AWorkbook: TsWorkbook); override; + procedure ReadFromClipboardStream(AStream: TStream); override; procedure ReadFromFile(AFileName: String); override; procedure ReadFromStream(AStream: TStream); override; procedure ReadFromStrings(AStrings: TStrings); override; @@ -200,6 +201,11 @@ begin Unused(AStream); end; +procedure TsCSVReader.ReadFromClipboardStream(AStream: TStream); +begin + ReadFromStream(AStream); +end; + procedure TsCSVReader.ReadFromFile(AFileName: String); begin FWorksheetName := ChangeFileExt(ExtractFileName(AFileName), ''); @@ -372,9 +378,10 @@ end; procedure TsCSVWriter.WriteSheet(AStream: TStream; AWorksheet: TsWorksheet); var r: Cardinal; - FirstRow: Cardinal; - LastRow: Cardinal; + firstRow: Cardinal; + lastRow: Cardinal; cell: PCell; + n: Integer; begin FWorksheet := AWorksheet; @@ -385,15 +392,23 @@ begin FCSVBuilder.QuoteChar := CSVParams.QuoteChar; FCSVBuilder.SetOutput(AStream); - if FClipboardMode then - FirstRow := FWorksheet.GetFirstRowIndex else - FirstRow := 0; - LastRow := FWorksheet.GetLastOccupiedRowIndex; - for r := FirstRow to LastRow do + n := FWorksheet.GetCellCount; + if FClipboardMode and (n = 1) then begin - for cell in FWorksheet.Cells.GetRowEnumerator(r) do - WriteCellToStream(AStream, cell); - FCSVBuilder.AppendRow; + cell := FWorksheet.Cells.GetFirstCell; + WriteCellToStream(AStream, cell); + end else + begin + if FClipboardMode then + firstRow := FWorksheet.GetFirstRowIndex else + firstRow := 0; + lastRow := FWorksheet.GetLastOccupiedRowIndex; + for r := firstRow to lastRow do + begin + for cell in FWorksheet.Cells.GetRowEnumerator(r) do + WriteCellToStream(AStream, cell); + FCSVBuilder.AppendRow; + end; end; finally FreeAndNil(FCSVBuilder); @@ -433,7 +448,7 @@ end; initialization InitFormatSettings(CSVParams.FormatSettings); - RegisterSpreadFormat(TsCSVReader, TsCSVWriter, sfCSV, false, true); + RegisterSpreadFormat(TsCSVReader, TsCSVWriter, sfCSV, true, true); end. diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas index d9d6a9ab1..0bc8ab21e 100755 --- a/components/fpspreadsheet/fpspreadsheet.pas +++ b/components/fpspreadsheet/fpspreadsheet.pas @@ -1601,6 +1601,10 @@ begin toRow := AToCell^.Row; toCol := AToCell^.Col; + // Avoid misplaced notifications during the copy operations when things could + // not yet be in place. + FWorkbook.DisableNotifications; + // Copy cell values AToCell^ := AFromCell^; @@ -1648,6 +1652,11 @@ begin end; end; + FWorkbook.EnableNotifications; + + // Notify visual controls of changes + ChangedCell(AToCell^.Row, AToCell^.Col); + // Notify visual controls of possibly changed row heights. ChangedFont(AToCell^.Row, AToCell^.Col); end; @@ -7876,27 +7885,30 @@ begin end; // Select the same cells as in the clipboard n := clipsheet.GetSelectionCount; - SetLength(selArray, n); - for i := 0 to n-1 do - begin - sel := clipsheet.GetSelection[i]; - selArray[i].Row1 := sel.Row1 + dr; - selArray[i].Col1 := sel.Col1 + dc; - selArray[i].Row2 := sel.Row2 + dr; - selArray[i].Col2 := sel.Col2 + dc; + if n > 0 then + begin + SetLength(selArray, n); + for i := 0 to n-1 do + begin + sel := clipsheet.GetSelection[i]; + selArray[i].Row1 := sel.Row1 + dr; + selArray[i].Col1 := sel.Col1 + dc; + selArray[i].Row2 := sel.Row2 + dr; + selArray[i].Col2 := sel.Col2 + dc; + end; + ActiveWorksheet.SetSelection(selArray); + // Select active cell. If not found in the file, let's use the last cell of the selections + if (clipsheet.ActiveCellRow <> 0) and (clipsheet.ActiveCellCol <> 0) then + begin + r := clipsheet.ActiveCellRow; + c := clipsheet.ActiveCellCol; + end else + begin + r := sel.Row2; + c := sel.Col2; + end; + ActiveWorksheet.SelectCell(r + dr, c + dc); end; - ActiveWorksheet.SetSelection(selArray); - // Select active cell. If not found in the file, let's use the last cell of the selections - if (clipsheet.ActiveCellRow <> 0) and (clipsheet.ActiveCellCol <> 0) then - begin - r := clipsheet.ActiveCellRow; - c := clipsheet.ActiveCellCol; - end else - begin - r := sel.Row2; - c := sel.Col2; - end; - ActiveWorksheet.SelectCell(r + dr, c + dc); finally clipbook.Free; end; diff --git a/components/fpspreadsheet/fpspreadsheetchart.pas b/components/fpspreadsheet/fpspreadsheetchart.pas index 838b10716..95e5034e2 100644 --- a/components/fpspreadsheet/fpspreadsheetchart.pas +++ b/components/fpspreadsheet/fpspreadsheetchart.pas @@ -237,7 +237,7 @@ begin end else if cell^.ContentType = cctUTF8String then begin FCurItem.X := AIndex; - FCurItem.Text := FDataWorksheet.ReadAsUTF8Text(cell); + FCurItem.Text := FDataWorksheet.ReadAsText(cell); end else begin FCurItem.X := FDataWorksheet.ReadAsNumber(cell); @@ -473,7 +473,7 @@ begin end else if cell^.ContentType = cctUTF8String then begin ANumber := APointIndex; - AText := FWorksheets[rngX].ReadAsUTF8Text(cell); + AText := FWorksheets[rngX].ReadAsText(cell); end else begin ANumber := FWorksheets[rngX].ReadAsNumber(cell); diff --git a/components/fpspreadsheet/fpspreadsheetctrls.pas b/components/fpspreadsheet/fpspreadsheetctrls.pas index 14e9a81e4..3d609284d 100644 --- a/components/fpspreadsheet/fpspreadsheetctrls.pas +++ b/components/fpspreadsheet/fpspreadsheetctrls.pas @@ -270,6 +270,7 @@ type { TsCellFormatItem } + TsCellFormatItem = (cfiFontName, cfiFontSize, cfiFontColor, cfiBackgroundColor, cfiBorderColor); @@ -498,6 +499,7 @@ function SpreadsheetFormatInClipboard: Boolean; begin Result := Clipboard.HasFormat(cfBiff8Format) or Clipboard.HasFormat(cfBiff5Format) or + Clipboard.HasFormat(cfOpenDocumentFormat) or Clipboard.HasFormat(cfHTMLFormat) or Clipboard.HasFormat(cfTextHTMLFormat) or Clipboard.HasFormat(cfCSVFormat) or @@ -955,23 +957,12 @@ var I: IsSpreadsheetControl; C: TComponent; begin - { - // Select worksheet in tab control first - if lniWorksheet in AChangedItems then - for j:=0 to FListeners.Count-1 do begin - C := TComponent(FListeners[j]); - if C is TsWorkbookTabControl then begin - C.GetInterface(GUID_SpreadsheetControl, I); - I.ListenerNotification(AChangedItems, AData); - end; - end; - } for j:=0 to FListeners.Count-1 do begin C := TComponent(FListeners[j]); if C.GetInterface(GUID_SpreadsheetControl, I) then I.ListenerNotification(AChangedItems, AData) else - raise Exception.CreateFmt('Class %s is not prepared to be a spreadsheet listener.', + raise Exception.CreateFmt('[TsWorkbookSource.NotifyListeners] Class %s is not prepared to be a spreadsheet listener.', [C.ClassName]); end; end; @@ -1258,40 +1249,37 @@ var cf: Integer; fmt: TsSpreadsheetFormat; stream: TStream; + s: String; begin - Clipboard.Open; + stream := TMemoryStream.Create; try - stream := TMemoryStream.Create; - try - // Check whether the clipboard content is suitable for fpspreadsheet - if Clipboard.GetFormat(cfOpenDocumentFormat, stream) then - fmt := sfOpenDocument - else if Clipboard.GetFormat(cfBiff8Format, stream) then - fmt := sfExcel8 - else if Clipboard.GetFormat(cfBiff5Format, stream) then - fmt := sfExcel5 - else if Clipboard.GetFormat(cfHTMLFormat, stream) or Clipboard.GetFormat(cfTextHTMLFormat, stream) then - fmt := sfHTML - else if Clipboard.GetFormat(cfCSVFormat, stream) then //or Clipboard.GetFormat(CF_TEXT, stream) then - fmt := sfCSV - else if Clipboard.GetFormat(PredefinedClipboardFormat(pcfText), stream) then - fmt := sfCSV - else begin - // Exit if there are no spreadsheet data in clipboard - MessageDlg('No appropriate spreadsheet data in clipboard', mtError, [mbOk], 0); - exit; - end; - - // Paste stream into workbook - FWorkbook.PasteFromClipboardStream(stream, fmt, AItem); - - // To do: XML format - // I don't know which format is written by xlsx and ods natively. - finally - stream.Free; + // Check whether the clipboard content is suitable for fpspreadsheet + if Clipboard.GetFormat(cfOpenDocumentFormat, stream) then + fmt := sfOpenDocument + else if Clipboard.GetFormat(cfBiff8Format, stream) then + fmt := sfExcel8 + else if Clipboard.GetFormat(cfBiff5Format, stream) then + fmt := sfExcel5 + else if Clipboard.GetFormat(cfHTMLFormat, stream) or Clipboard.GetFormat(cfTextHTMLFormat, stream) then + fmt := sfHTML + else if Clipboard.GetFormat(cfCSVFormat, stream) then + fmt := sfCSV + else if Clipboard.GetFormat(CF_TEXT, stream) then + fmt := sfCSV + else begin + // Exit if there are no spreadsheet data in clipboard + MessageDlg('No appropriate spreadsheet data in clipboard', mtError, [mbOk], 0); + exit; end; + + // Paste stream into workbook + stream.Position := 0; + FWorkbook.PasteFromClipboardStream(stream, fmt, AItem); + + // To do: XML format + // I don't know which format is written by xlsx and ods natively. finally - Clipboard.Close; + stream.Free; end; (* diff --git a/components/fpspreadsheet/fpspreadsheetgrid.pas b/components/fpspreadsheet/fpspreadsheetgrid.pas index ccbc9f6a6..75afbe14c 100644 --- a/components/fpspreadsheet/fpspreadsheetgrid.pas +++ b/components/fpspreadsheet/fpspreadsheetgrid.pas @@ -151,6 +151,9 @@ type procedure CreateNewWorkbook; procedure DblClick; override; procedure DefineProperties(Filer: TFiler); override; + procedure DoCopyToClipboard; override; + procedure DoCutToClipboard; override; + procedure DoPasteFromClipboard; override; procedure DoOnResize; override; procedure DoPrepareCanvas(ACol, ARow: Integer; AState: TGridDrawState); override; procedure DrawAllRows; override; @@ -1329,6 +1332,21 @@ begin Unused(Filer); end; +procedure TsCustomWorksheetGrid.DoCopyToClipboard; +begin + WorkbookSource.CopyCellsToClipboard; +end; + +procedure TsCustomWorksheetGrid.DoCutToClipboard; +begin + WorkbookSource.CutCellsToClipboard; +end; + +procedure TsCustomWorksheetGrid.DoPasteFromClipboard; +begin + WorkbookSource.PasteCellsFromClipboard(coCopyCell); +end; + procedure TsCustomWorksheetGrid.DoOnResize; begin if (csDesigning in ComponentState) and (Worksheet = nil) then @@ -3989,7 +4007,7 @@ var nfs: String; isGeneralFmt: Boolean; begin - Result := Worksheet.ReadAsUTF8Text(ACell); + Result := Worksheet.ReadAsText(ACell); if (Result = '') or ((ACell <> nil) and (ACell^.ContentType = cctUTF8String)) then exit;