diff --git a/components/fpspreadsheet/examples/fpsctrls/main.lfm b/components/fpspreadsheet/examples/fpsctrls/main.lfm index 4a12d1a0d..5602be100 100644 --- a/components/fpspreadsheet/examples/fpsctrls/main.lfm +++ b/components/fpspreadsheet/examples/fpsctrls/main.lfm @@ -28,7 +28,7 @@ object Form1: TForm1 end object CellIndicator: TsCellIndicator Left = 95 - Height = 28 + Height = 23 Top = 9 Width = 80 TabOrder = 1 @@ -37,7 +37,7 @@ object Form1: TForm1 end object CellEdit: TsCellEdit Left = 184 - Height = 28 + Height = 23 Top = 9 Width = 731 Anchors = [akTop, akLeft, akRight] @@ -60,12 +60,13 @@ object Form1: TForm1 WorkbookSource = WorkbookSource object WorksheetGrid: TsWorksheetGrid Left = 2 - Height = 528 - Top = 28 + Height = 533 + Top = 23 Width = 668 FrozenCols = 0 FrozenRows = 0 ReadFormulas = False + TextOverflow = True WorkbookSource = WorkbookSource Align = alClient AutoAdvance = aaDown @@ -127,8 +128,8 @@ object Form1: TForm1 TabOrder = 2 object Inspector: TsSpreadsheetInspector Left = 2 - Height = 528 - Top = 28 + Height = 533 + Top = 23 Width = 244 Align = alClient RowCount = 24 @@ -136,10 +137,10 @@ object Form1: TForm1 Strings.Strings = ( 'FileName=' 'FileFormat=sfExcel8' - 'Options=' + 'Options=boAutoCalc, boCalcBeforeSaving, boReadFormulas' 'FormatSettings=' ' ThousandSeparator=.' - ' DecimalSeparator=,' + ' DecimalSeparator=.' ' ListSeparator=;' ' DateSeparator=.' ' TimeSeparator=:' @@ -165,8 +166,8 @@ object Form1: TForm1 WorkbookSource = WorkbookSource Mode = imWorkbook ColWidths = ( - 109 - 110 + 120 + 120 ) end end diff --git a/components/fpspreadsheet/examples/fpsctrls_no_install/main.lfm b/components/fpspreadsheet/examples/fpsctrls_no_install/main.lfm index d53b034cc..2435ee64d 100644 --- a/components/fpspreadsheet/examples/fpsctrls_no_install/main.lfm +++ b/components/fpspreadsheet/examples/fpsctrls_no_install/main.lfm @@ -125,11 +125,11 @@ object Form1: TForm1 end object CbLoader: TComboBox Left = 104 - Height = 28 + Height = 23 Top = 8 Width = 148 - ItemHeight = 20 - ItemIndex = 0 + ItemHeight = 15 + ItemIndex = 1 Items.Strings = ( 'Workbook' 'WorkbookSource' @@ -137,13 +137,13 @@ object Form1: TForm1 ) Style = csDropDownList TabOrder = 1 - Text = 'Workbook' + Text = 'WorkbookSource' end object Label1: TLabel Left = 13 - Height = 20 + Height = 15 Top = 11 - Width = 73 + Width = 58 Caption = 'Loaded by:' ParentColor = False end diff --git a/components/fpspreadsheet/examples/fpsctrls_no_install/main.pas b/components/fpspreadsheet/examples/fpsctrls_no_install/main.pas index 154531afa..0219582b1 100644 --- a/components/fpspreadsheet/examples/fpsctrls_no_install/main.pas +++ b/components/fpspreadsheet/examples/fpsctrls_no_install/main.pas @@ -157,18 +157,18 @@ begin if MessageDlg('Do you really want to delete this worksheet?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then - WorkbookSource.Workbook.RemoveWorksheet(WorkbookSource.SelectedWorksheet); + WorkbookSource.Workbook.RemoveWorksheet(WorkbookSource.Worksheet); end; procedure TForm1.SpeedButton3Click(Sender: TObject); var s: String; begin - s := WorkbookSource.SelectedWorksheet.Name; + s := WorkbookSource.Worksheet.Name; if InputQuery('Edit worksheet name', 'New name', s) then begin if WorkbookSource.Workbook.ValidWorksheetName(s) then - WorkbookSource.SelectedWorksheet.Name := s + WorkbookSource.Worksheet.Name := s else MessageDlg('Invalid worksheet name.', mtError, [mbOK], 0); end; diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas index 166762b7e..d52b2bd75 100755 --- a/components/fpspreadsheet/fpspreadsheet.pas +++ b/components/fpspreadsheet/fpspreadsheet.pas @@ -512,6 +512,7 @@ type FRows, FCols: TIndexedAVLTree; // This lists contain only rows or cols with styles different from default FActiveCellRow: Cardinal; FActiveCellCol: Cardinal; + FSelection: TsCellRangeArray; FLeftPaneWidth: Integer; FTopPaneHeight: Integer; FOptions: TsSheetOptions; @@ -794,8 +795,13 @@ type ARowFrom, AColFrom, ARowTo, AColTo: Cardinal); overload; procedure Sort(ASortParams: TsSortParams; ARange: String); overload; - // Selected cell + // Selected cell and ranges procedure SelectCell(ARow, ACol: Cardinal); + procedure ClearSelection; + function GetSelection: TsCellRangeArray; + function GetSelectionAsString: String; + function GetSelectionCount: Integer; + procedure SetSelection(const ASelection: TsCellRangeArray); { Properties } @@ -907,11 +913,13 @@ type FWriting: Boolean; FCalculationLock: Integer; FOptions: TsWorkbookOptions; + FActiveWorksheet: TsWorksheet; FOnWriteCellData: TsWorkbookWriteCellDataEvent; FOnReadCellData: TsWorkbookReadCellDataEvent; FOnChangeWorksheet: TsWorksheetEvent; FOnAddWorksheet: TsWorksheetEvent; FOnRemoveWorksheet: TsRemoveWorksheetEvent; + FOnSelectWorksheet: TsWorksheetEvent; FFileName: String; FLog: TStringList; @@ -964,6 +972,7 @@ type function GetWorksheetIndex(AWorksheet: TsWorksheet): Integer; procedure RemoveAllWorksheets; procedure RemoveWorksheet(AWorksheet: TsWorksheet); + procedure SelectWorksheet(AWorksheet: TsWorksheet); function ValidWorksheetName(var AName: String; ReplaceDuplicateName: Boolean = false): Boolean; @@ -1005,6 +1014,8 @@ type procedure AddErrorMsg(const AMsg: String; const Args: array of const); overload; procedure ClearErrorList; + {@@ Identifies the "active" worksheet (only for visual controls)} + property ActiveWorksheet: TsWorksheet read FActiveWorksheet; {@@ This property is only used for formats which don't support unicode and support a single encoding for the whole document, like Excel 2 to 5 } property Encoding: TsEncoding read FEncoding write FEncoding; @@ -1023,6 +1034,8 @@ type property OnChangeWorksheet: TsWorksheetEvent read FOnChangeWorksheet write FOnChangeWorksheet; {@@ This event fires when a worksheet is deleted } property OnRemoveWorksheet: TsRemoveWorksheetEvent read FOnRemoveWorksheet write FOnRemoveWorksheet; + {@@ This event fires when a worksheet is made "active"} + property OnSelectWorksheet: TsWorksheetEvent read FOnSelectWorksheet write FOnSelectWorksheet; {@@ This event allows to provide external cell data for writing to file, standard cells are ignored. Intended for converting large database files to a spreadsheet format. Requires Option boVirtualMode to be set. } @@ -3532,6 +3545,70 @@ begin end; end; +{@@ ---------------------------------------------------------------------------- + Clears the list of seleccted cell ranges + Only needed by the visual controls. +-------------------------------------------------------------------------------} +procedure TsWorksheet.ClearSelection; +begin +SetLength(FSelection, 0); +end; + +{@@ ---------------------------------------------------------------------------- + Returns the list of selected cell ranges +-------------------------------------------------------------------------------} +function TsWorksheet.GetSelection: TsCellRangeArray; +var + i: Integer; +begin + SetLength(Result, Length(FSelection)); + for i:=0 to High(FSelection) do + Result[i] := FSelection[i]; +end; + +{@@ ---------------------------------------------------------------------------- + Returns all selection ranges as an Excel string +-------------------------------------------------------------------------------} +function TsWorksheet.GetSelectionAsString: String; +const + RELATIVE = [rfRelRow, rfRelCol, rfRelRow2, rfRelCol2]; +var + i: Integer; + L: TStringList; +begin + L := TStringList.Create; + try + for i:=0 to Length(FSelection)-1 do + with FSelection[i] do + L.Add(GetCellRangeString(Row1, Col1, Row2, Col2, RELATIVE, true)); + L.Delimiter := DefaultFormatSettings.ListSeparator; + L.StrictDelimiter := true; + Result := L.DelimitedText; + finally + L.Free; + end; +end; + +{@@ ---------------------------------------------------------------------------- + Returns the number of selected cell ranges +-------------------------------------------------------------------------------} +function TsWorksheet.GetSelectionCount: Integer; +begin + Result := Length(FSelection); +end; + +{@@ ---------------------------------------------------------------------------- + Marks an array of cell ranges as "selected". Only needed for visual controls +-------------------------------------------------------------------------------} +procedure TsWorksheet.SetSelection(const ASelection: TsCellRangeArray); +var + i: Integer; +begin + SetLength(FSelection, Length(ASelection)); + for i:=0 to High(FSelection) do + FSelection[i] := ASelection[i]; +end; + {@@ ---------------------------------------------------------------------------- Helper method to update internal caching variables -------------------------------------------------------------------------------} @@ -6574,6 +6651,19 @@ begin end; end; +{@@ ---------------------------------------------------------------------------- + Makes the specified worksheet "active". Only needed for visual controls. + The active worksheet is displayed in a TsWorksheetGrid and in the selected + tab of a TsWorkbookTabControl. +-------------------------------------------------------------------------------} +procedure TsWorkbook.SelectWorksheet(AWorksheet: TsWorksheet); +begin + if (AWorksheet <> nil) and (FWorksheets.IndexOf(AWorksheet) = -1) then + raise Exception.Create('[TsWorkbook.SelectSheet] Worksheet does not belong to the workbook'); + FActiveWorksheet := AWorksheet; + if Assigned(FOnSelectWorksheet) then FOnSelectWorksheet(self, AWorksheet); +end; + {@@ ---------------------------------------------------------------------------- Checks whether the passed string is a valid worksheet name according to Excel (ODS seems to be a bit less restrictive, but if we follow Excel's convention diff --git a/components/fpspreadsheet/fpspreadsheetctrls.pas b/components/fpspreadsheet/fpspreadsheetctrls.pas index 98a7f0bfb..9b391b185 100644 --- a/components/fpspreadsheet/fpspreadsheetctrls.pas +++ b/components/fpspreadsheet/fpspreadsheetctrls.pas @@ -38,6 +38,7 @@ type procedure WorksheetAddedHandler(Sender: TObject; ASheet: TsWorksheet); procedure WorksheetChangedHandler(Sender: TObject; ASheet: TsWorksheet); procedure WorksheetRemovedHandler(Sender: TObject; ASheetIndex: Integer); + procedure WorksheetSelectedHandler(Sender: TObject; AWorksheet: TsWorksheet); protected procedure DoShowError(const AErrorMsg: String); @@ -67,7 +68,7 @@ type public property Workbook: TsWorkbook read FWorkbook; - property SelectedWorksheet: TsWorksheet read FWorksheet; + property Worksheet: TsWorksheet read FWorksheet; published property AutoDetectFormat: Boolean read FAutoDetectFormat write FAutoDetectFormat; @@ -165,10 +166,10 @@ type protected procedure DoUpdate; virtual; procedure Notification(AComponent: TComponent; Operation: TOperation); override; - procedure UpdateCellValue(ACell: PCell); virtual; - procedure UpdateCellProperties(ACell: PCell); virtual; - procedure UpdateWorkbook(AWorkbook: TsWorkbook); virtual; - procedure UpdateWorksheet(ASheet: TsWorksheet); virtual; + procedure UpdateCellValue(ACell: PCell; AStrings: TStrings); virtual; + procedure UpdateCellProperties(ACell: PCell; AStrings: TStrings); virtual; + procedure UpdateWorkbook(AWorkbook: TsWorkbook; AStrings: TStrings); virtual; + procedure UpdateWorksheet(ASheet: TsWorksheet; AStrings: TStrings); virtual; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; @@ -292,7 +293,7 @@ begin FWorksheet := FWorkbook.AddWorksheet('Sheet1'); SelectWorksheet(FWorksheet); - // notify dependent controls + // notify listeners NotifyListeners([lniWorkbook, lniWorksheet, lniSelection]); end; @@ -318,6 +319,7 @@ begin FWorkbook.OnAddWorksheet := @WorksheetAddedHandler; FWorkbook.OnChangeWorksheet := @WorksheetChangedHandler; FWorkbook.OnRemoveWorksheet := @WorksheetRemovedHandler; + FWorkbook.OnSelectWorksheet := @WorksheetSelectedHandler; // Pass options to workbook SetOptions(FOptions); end; @@ -521,7 +523,7 @@ end; -------------------------------------------------------------------------------} procedure TsWorkbookSource.SelectCell(ASheetRow, ASheetCol: Cardinal); begin - if SelectedWorksheet <> nil then + if FWorksheet <> nil then FWorksheet.SelectCell(ASheetRow, ASheetCol); NotifyListeners([lniSelection]); end; @@ -534,6 +536,10 @@ end; -------------------------------------------------------------------------------} procedure TsWorkbookSource.SelectWorksheet(AWorkSheet: TsWorksheet); begin + FWorksheet := AWorksheet; + if (FWorkbook <> nil) then + FWorkbook.SelectWorksheet(AWorksheet); +{ if AWorksheet = nil then exit; FWorksheet := AWorkSheet; @@ -541,6 +547,7 @@ begin FWorksheet.OnSelectCell := @CellSelectedHandler; NotifyListeners([lniWorksheet]); SelectCell(FWorksheet.ActiveCellRow, FWorksheet.ActiveCellCol); + } end; {@@ ---------------------------------------------------------------------------- @@ -633,6 +640,23 @@ begin SelectWorksheet(sheet); end; +{@@ ---------------------------------------------------------------------------- + Event handler called whenever a the workbook makes a worksheet "active". +-------------------------------------------------------------------------------} +procedure TsWorkbookSource.WorksheetSelectedHandler(Sender: TObject; + AWorksheet: TsWorksheet); +begin + FWorksheet := AWorksheet; + if FWorksheet <> nil then + begin + FWorksheet.OnChangeCell := @CellChangedHandler; + FWorksheet.OnSelectCell := @CellSelectedHandler; + NotifyListeners([lniWorksheet]); + SelectCell(FWorksheet.ActiveCellRow, FWorksheet.ActiveCellCol); + end else + NotifyListeners([lniWorksheet]); +end; + {------------------------------------------------------------------------------} { TsWorkbookTabControl } @@ -663,6 +687,16 @@ end; Creates a (string) list containing the names of the workbook's sheet names -------------------------------------------------------------------------------} procedure TsWorkbookTabControl.GetSheetList(AList: TStrings); +var + i: Integer; +begin + AList.Clear; + if Workbook <> nil then + for i:=0 to Workbook.GetWorksheetCount-1 do + AList.Add(Workbook.GetWorksheetByIndex(i).Name); +end; +{ +procedure TsWorkbookTabControl.GetSheetList(AList: TStrings); var i: Integer; oldTabIndex: Integer; @@ -680,7 +714,7 @@ begin TabIndex := oldTabIndex; end; end; - + } {@@ ---------------------------------------------------------------------------- Getter method for property "Workbook" -------------------------------------------------------------------------------} @@ -698,7 +732,7 @@ end; function TsWorkbookTabControl.GetWorksheet: TsWorksheet; begin if FWorkbookSource <> nil then - Result := FWorkbookSource.SelectedWorksheet + Result := FWorkbookSource.Worksheet else Result := nil; end; @@ -714,9 +748,39 @@ procedure TsWorkbookTabControl.ListenerNotification( AChangedItems: TsNotificationItems; AData: Pointer = nil); var i: Integer; + oldTabIndex: Integer; + list: TStringList; begin Unused(AData); + // Workbook changed + if (lniWorkbook in AChangedItems) then + begin + oldTabIndex := TabIndex; + list := TStringList.Create; + Tabs.BeginUpdate; + try + GetSheetList(list); + Tabs.Assign(list); + if (oldTabIndex = -1) and (Tabs.Count > 0) then + TabIndex := 0 + else + if oldTabIndex < Tabs.Count then + TabIndex := oldTabIndex; + finally + list.Free; + Tabs.EndUpdate; + end; + end; + + // Worksheet changed + if (lniWorksheet in AChangedItems) and (Worksheet <> nil) then + begin + i := Tabs.IndexOf(Worksheet.Name); + if i <> TabIndex then + TabIndex := i; + end; +{ // Workbook changed if (lniWorkbook in AChangedItems) then GetSheetList(Tabs); @@ -728,6 +792,7 @@ begin if i <> TabIndex then TabIndex := i; end; + } end; {@@ ---------------------------------------------------------------------------- @@ -840,7 +905,7 @@ end; function TsCellEdit.GetWorksheet: TsWorksheet; begin if FWorkbookSource <> nil then - Result := FWorkbookSource.SelectedWorksheet + Result := FWorkbookSource.Worksheet else Result := nil; end; @@ -963,7 +1028,7 @@ end; function TsCellIndicator.GetWorksheet: TsWorksheet; begin if FWorkbookSource <> nil then - Result := FWorkbookSource.SelectedWorksheet + Result := FWorkbookSource.Worksheet else Result := nil; end; @@ -1038,6 +1103,38 @@ end; setting (workbook, worksheet, cell values, cell properties). -------------------------------------------------------------------------------} procedure TsSpreadsheetInspector.DoUpdate; +var + cell: PCell; + sheet: TsWorksheet; + book: TsWorkbook; + list: TStringList; +begin + cell := nil; + sheet := nil; + book := nil; + if FWorkbookSource <> nil then + begin + book := FWorkbookSource.Workbook; + sheet := FWorkbookSource.Worksheet; + if sheet <> nil then + cell := sheet.FindCell(sheet.ActiveCellRow, sheet.ActiveCellCol); + end; + + list := TStringList.Create; + try + case FMode of + imCellValue : UpdateCellValue(cell, list); + imCellProperties : UpdateCellProperties(cell, list); + imWorksheet : UpdateWorksheet(sheet, list); + imWorkbook : UpdateWorkbook(book, list); + end; + Strings.Assign(list); + finally + list.Free; + end; +end; +(* +procedure TsSpreadsheetInspector.DoUpdate; var cell: PCell; sheet: TsWorksheet; @@ -1051,7 +1148,7 @@ begin if FWorkbookSource <> nil then begin book := FWorkbookSource.Workbook; - sheet := FWorkbookSource.SelectedWorksheet; + sheet := FWorkbookSource.Worksheet; if sheet <> nil then cell := sheet.FindCell(sheet.ActiveCellRow, sheet.ActiveCellCol); end; @@ -1063,6 +1160,7 @@ begin imWorkbook : UpdateWorkbook(book); end; end; + *) function TsSpreadsheetInspector.GetWorkbook: TsWorkbook; begin @@ -1075,7 +1173,7 @@ end; function TsSpreadsheetInspector.GetWorksheet: TsWorksheet; begin if FWorkbookSource <> nil then - Result := FWorkbookSource.SelectedWorksheet + Result := FWorkbookSource.Worksheet else Result := nil; end; @@ -1084,6 +1182,15 @@ procedure TsSpreadsheetInspector.ListenerNotification( AChangedItems: TsNotificationItems; AData: Pointer = nil); begin Unused(AData); + case FMode of + imWorkbook: + if ([lniWorkbook, lniWorksheet]*AChangedItems <> []) then DoUpdate; + imWorksheet: + if ([lniWorksheet, lniSelection]*AChangedItems <> []) then DoUpdate; + imCellValue, imCellProperties: + if ([lniCell, lniSelection]*AChangedItems <> []) then DoUpdate; + end; +{ case FMode of imWorkbook: if lniWorkbook in AChangedItems then DoUpdate; @@ -1093,6 +1200,7 @@ begin imCellProperties: if ([lniCell, lniSelection]*AChangedItems <> []) then DoUpdate; end; + } end; procedure TsSpreadsheetInspector.Notification(AComponent: TComponent; @@ -1123,39 +1231,40 @@ begin ListenerNotification([lniWorkbook, lniWorksheet, lniSelection]); end; -procedure TsSpreadsheetInspector.UpdateCellProperties(ACell: PCell); +procedure TsSpreadsheetInspector.UpdateCellProperties(ACell: PCell; + AStrings: TStrings); var s: String; cb: TsCellBorder; r1, r2, c1, c2: Cardinal; begin if (ACell = nil) or not (uffFont in ACell^.UsedFormattingFields) - then Strings.Add('FontIndex=') - else Strings.Add(Format('FontIndex=%d (%s)', [ + then AStrings.Add('FontIndex=') + else AStrings.Add(Format('FontIndex=%d (%s)', [ ACell^.FontIndex, Workbook.GetFontAsString(ACell^.FontIndex) ])); if (ACell=nil) or not (uffTextRotation in ACell^.UsedFormattingFields) - then Strings.Add('TextRotation=') - else Strings.Add(Format('TextRotation=%s', [ + then AStrings.Add('TextRotation=') + else AStrings.Add(Format('TextRotation=%s', [ GetEnumName(TypeInfo(TsTextRotation), ord(ACell^.TextRotation)) ])); if (ACell=nil) or not (uffHorAlign in ACell^.UsedFormattingFields) - then Strings.Add('HorAlignment=') - else Strings.Add(Format('HorAlignment=%s', [ + then AStrings.Add('HorAlignment=') + else AStrings.Add(Format('HorAlignment=%s', [ GetEnumName(TypeInfo(TsHorAlignment), ord(ACell^.HorAlignment)) ])); if (ACell=nil) or not (uffVertAlign in ACell^.UsedFormattingFields) - then Strings.Add('VertAlignment=') - else Strings.Add(Format('VertAlignment=%s', [ + then AStrings.Add('VertAlignment=') + else AStrings.Add(Format('VertAlignment=%s', [ GetEnumName(TypeInfo(TsVertAlignment), ord(ACell^.VertAlignment)) ])); if (ACell=nil) or not (uffBorder in ACell^.UsedFormattingFields) then - Strings.Add('Borders=') + AStrings.Add('Borders=') else begin s := ''; @@ -1163,84 +1272,85 @@ begin if cb in ACell^.Border then s := s + ', ' + GetEnumName(TypeInfo(TsCellBorder), ord(cb)); if s <> '' then Delete(s, 1, 2); - Strings.Add('Borders='+s); + AStrings.Add('Borders='+s); end; for cb in TsCellBorder do if ACell = nil then - Strings.Add(Format('BorderStyles[%s]=', [ + AStrings.Add(Format('BorderStyles[%s]=', [ GetEnumName(TypeInfo(TsCellBorder), ord(cb))])) else - Strings.Add(Format('BorderStyles[%s]=%s, %s', [ + AStrings.Add(Format('BorderStyles[%s]=%s, %s', [ GetEnumName(TypeInfo(TsCellBorder), ord(cb)), GetEnumName(TypeInfo(TsLineStyle), ord(ACell^.BorderStyles[cbEast].LineStyle)), Workbook.GetColorName(ACell^.BorderStyles[cbEast].Color)])); if (ACell = nil) or not (uffBackgroundColor in ACell^.UsedformattingFields) - then Strings.Add('BackgroundColor=') - else Strings.Add(Format('BackgroundColor=%d (%s)', [ + then AStrings.Add('BackgroundColor=') + else AStrings.Add(Format('BackgroundColor=%d (%s)', [ ACell^.BackgroundColor, Workbook.GetColorName(ACell^.BackgroundColor)])); if (ACell = nil) or not (uffNumberFormat in ACell^.UsedFormattingFields) then begin - Strings.Add('NumberFormat='); - Strings.Add('NumberFormatStr='); + AStrings.Add('NumberFormat='); + AStrings.Add('NumberFormatStr='); end else begin - Strings.Add(Format('NumberFormat=%s', [ + AStrings.Add(Format('NumberFormat=%s', [ GetEnumName(TypeInfo(TsNumberFormat), ord(ACell^.NumberFormat))])); - Strings.Add('NumberFormatStr=' + ACell^.NumberFormatStr); + AStrings.Add('NumberFormatStr=' + ACell^.NumberFormatStr); end; if (Worksheet = nil) or not Worksheet.IsMerged(ACell) then - Strings.Add('Merged range=') + AStrings.Add('Merged range=') else begin Worksheet.FindMergedRange(ACell, r1, c1, r2, c2); - Strings.Add('Merged range=' + GetCellRangeString(r1, c1, r2, c2)); + AStrings.Add('Merged range=' + GetCellRangeString(r1, c1, r2, c2)); end; end; -procedure TsSpreadsheetInspector.UpdateCellValue(ACell: PCell); +procedure TsSpreadsheetInspector.UpdateCellValue(ACell: PCell; AStrings: TStrings); begin if ACell = nil then begin if Worksheet <> nil then begin - Strings.Add(Format('Row=%d', [Worksheet.ActiveCellRow])); - Strings.Add(Format('Col=%d', [Worksheet.ActiveCellCol])); + AStrings.Add(Format('Row=%d', [Worksheet.ActiveCellRow])); + AStrings.Add(Format('Col=%d', [Worksheet.ActiveCellCol])); end else begin - Strings.Add('Row='); - Strings.Add('Col='); + AStrings.Add('Row='); + AStrings.Add('Col='); end; - Strings.Add('ContentType=(none)'); + AStrings.Add('ContentType=(none)'); end else begin - Strings.Add(Format('Row=%d', [ACell^.Row])); - Strings.Add(Format('Col=%d', [ACell^.Col])); - Strings.Add(Format('ContentType=%s', [ + AStrings.Add(Format('Row=%d', [ACell^.Row])); + AStrings.Add(Format('Col=%d', [ACell^.Col])); + AStrings.Add(Format('ContentType=%s', [ GetEnumName(TypeInfo(TCellContentType), ord(ACell^.ContentType)) ])); - Strings.Add(Format('NumberValue=%g', [ACell^.NumberValue])); - Strings.Add(Format('DateTimeValue=%g', [ACell^.DateTimeValue])); - Strings.Add(Format('UTF8StringValue=%s', [ACell^.UTF8StringValue])); - Strings.Add(Format('BoolValue=%s', [BoolToStr(ACell^.BoolValue)])); - Strings.Add(Format('ErrorValue=%s', [ + AStrings.Add(Format('NumberValue=%g', [ACell^.NumberValue])); + AStrings.Add(Format('DateTimeValue=%g', [ACell^.DateTimeValue])); + AStrings.Add(Format('UTF8StringValue=%s', [ACell^.UTF8StringValue])); + AStrings.Add(Format('BoolValue=%s', [BoolToStr(ACell^.BoolValue)])); + AStrings.Add(Format('ErrorValue=%s', [ GetEnumName(TypeInfo(TsErrorValue), ord(ACell^.ErrorValue)) ])); - Strings.Add(Format('FormulaValue=%s', [Worksheet.ReadFormulaAsString(ACell, true)])); //^.FormulaValue])); + AStrings.Add(Format('FormulaValue=%s', [Worksheet.ReadFormulaAsString(ACell, true)])); //^.FormulaValue])); if ACell^.SharedFormulaBase = nil then - Strings.Add('SharedFormulaBase=') + AStrings.Add('SharedFormulaBase=') else - Strings.Add(Format('SharedFormulaBase=%s', [GetCellString( + AStrings.Add(Format('SharedFormulaBase=%s', [GetCellString( ACell^.SharedFormulaBase^.Row, ACell^.SharedFormulaBase^.Col) ])); end; end; -procedure TsSpreadsheetInspector.UpdateWorkbook(AWorkbook: TsWorkbook); +procedure TsSpreadsheetInspector.UpdateWorkbook(AWorkbook: TsWorkbook; + AStrings: TStrings); var bo: TsWorkbookOption; s: String; @@ -1248,75 +1358,87 @@ var begin if AWorkbook = nil then begin - Strings.Add('FileName='); - Strings.Add('FileFormat='); - Strings.Add('Options='); - Strings.Add('FormatSettings='); + AStrings.Add('FileName='); + AStrings.Add('FileFormat='); + AStrings.Add('Options='); + AStrings.Add('ActiveWorksheet='); + AStrings.Add('FormatSettings='); end else begin - Strings.Add(Format('FileName=%s', [AWorkbook.FileName])); - Strings.Add(Format('FileFormat=%s', [ + AStrings.Add(Format('FileName=%s', [AWorkbook.FileName])); + AStrings.Add(Format('FileFormat=%s', [ GetEnumName(TypeInfo(TsSpreadsheetFormat), ord(AWorkbook.FileFormat)) ])); + if AWorkbook.ActiveWorksheet <> nil then + AStrings.Add('ActiveWorksheet=' + AWorkbook.ActiveWorksheet.Name) + else + AStrings.Add('ActiveWorksheet='); + s := ''; for bo in TsWorkbookOption do if bo in AWorkbook.Options then s := s + ', ' + GetEnumName(TypeInfo(TsWorkbookOption), ord(bo)); if s <> '' then Delete(s, 1, 2); - Strings.Add('Options='+s); + AStrings.Add('Options='+s); - Strings.Add('FormatSettings='); - Strings.Add(' ThousandSeparator='+AWorkbook.FormatSettings.ThousandSeparator); - Strings.Add(' DecimalSeparator='+AWorkbook.FormatSettings.DecimalSeparator); - Strings.Add(' ListSeparator='+AWorkbook.FormatSettings.ListSeparator); - Strings.Add(' DateSeparator='+AWorkbook.FormatSettings.DateSeparator); - Strings.Add(' TimeSeparator='+AWorkbook.FormatSettings.TimeSeparator); - Strings.Add(' ShortDateFormat='+AWorkbook.FormatSettings.ShortDateFormat); - Strings.Add(' LongDateFormat='+AWorkbook.FormatSettings.LongDateFormat); - Strings.Add(' ShortTimeFormat='+AWorkbook.FormatSettings.ShortTimeFormat); - Strings.Add(' LongTimeFormat='+AWorkbook.FormatSettings.LongTimeFormat); - Strings.Add(' TimeAMString='+AWorkbook.FormatSettings.TimeAMString); - Strings.Add(' TimePMString='+AWorkbook.FormatSettings.TimePMString); + AStrings.Add('FormatSettings='); + AStrings.Add(' ThousandSeparator='+AWorkbook.FormatSettings.ThousandSeparator); + AStrings.Add(' DecimalSeparator='+AWorkbook.FormatSettings.DecimalSeparator); + AStrings.Add(' ListSeparator='+AWorkbook.FormatSettings.ListSeparator); + AStrings.Add(' DateSeparator='+AWorkbook.FormatSettings.DateSeparator); + AStrings.Add(' TimeSeparator='+AWorkbook.FormatSettings.TimeSeparator); + AStrings.Add(' ShortDateFormat='+AWorkbook.FormatSettings.ShortDateFormat); + AStrings.Add(' LongDateFormat='+AWorkbook.FormatSettings.LongDateFormat); + AStrings.Add(' ShortTimeFormat='+AWorkbook.FormatSettings.ShortTimeFormat); + AStrings.Add(' LongTimeFormat='+AWorkbook.FormatSettings.LongTimeFormat); + AStrings.Add(' TimeAMString='+AWorkbook.FormatSettings.TimeAMString); + AStrings.Add(' TimePMString='+AWorkbook.FormatSettings.TimePMString); s := AWorkbook.FormatSettings.ShortMonthNames[1]; for i:=2 to 12 do s := s + ', ' + AWorkbook.FormatSettings.ShortMonthNames[i]; - Strings.Add(' ShortMonthNames='+s); + AStrings.Add(' ShortMonthNames='+s); s := AWorkbook.FormatSettings.LongMonthnames[1]; for i:=2 to 12 do s := s +', ' + AWorkbook.FormatSettings.LongMonthNames[i]; - Strings.Add(' LongMontNames='+s); + AStrings.Add(' LongMontNames='+s); s := AWorkbook.FormatSettings.ShortDayNames[1]; for i:=2 to 7 do s := s + ', ' + AWorkbook.FormatSettings.ShortDayNames[i]; - Strings.Add(' ShortMonthNames='+s); + AStrings.Add(' ShortMonthNames='+s); s := AWorkbook.FormatSettings.LongDayNames[1]; for i:=2 to 7 do s := s +', ' + AWorkbook.FormatSettings.LongDayNames[i]; - Strings.Add(' LongMontNames='+s); - Strings.Add(' CurrencyString='+AWorkbook.FormatSettings.CurrencyString); - Strings.Add(' PosCurrencyFormat='+IntToStr(AWorkbook.FormatSettings.CurrencyFormat)); - Strings.Add(' NegCurrencyFormat='+IntToStr(AWorkbook.FormatSettings.NegCurrFormat)); - Strings.Add(' TwoDigitYearCenturyWindow='+IntToStr(AWorkbook.FormatSettings.TwoDigitYearCenturyWindow)); + AStrings.Add(' LongMontNames='+s); + AStrings.Add(' CurrencyString='+AWorkbook.FormatSettings.CurrencyString); + AStrings.Add(' PosCurrencyFormat='+IntToStr(AWorkbook.FormatSettings.CurrencyFormat)); + AStrings.Add(' NegCurrencyFormat='+IntToStr(AWorkbook.FormatSettings.NegCurrFormat)); + AStrings.Add(' TwoDigitYearCenturyWindow='+IntToStr(AWorkbook.FormatSettings.TwoDigitYearCenturyWindow)); end; end; -procedure TsSpreadsheetInspector.UpdateWorksheet(ASheet: TsWorksheet); +procedure TsSpreadsheetInspector.UpdateWorksheet(ASheet: TsWorksheet; + AStrings: TStrings); begin if ASheet = nil then begin - Strings.Add('First row='); - Strings.Add('Last row='); - Strings.Add('First column='); - Strings.Add('Last column='); + AStrings.Add('Name='); + AStrings.Add('First row='); + AStrings.Add('Last row='); + AStrings.Add('First column='); + AStrings.Add('Last column='); + AStrings.Add('Active cell='); + AStrings.Add('Selection='); end else begin - Strings.Add(Format('First row=%d', [Integer(ASheet.GetFirstRowIndex)])); - Strings.Add(Format('Last row=%d', [ASheet.GetLastRowIndex])); - Strings.Add(Format('First column=%d', [Integer(ASheet.GetFirstColIndex)])); - Strings.Add(Format('Last column=%d', [ASheet.GetLastColIndex])); + AStrings.Add(Format('Name=%s', [ASheet.Name])); + AStrings.Add(Format('First row=%d', [Integer(ASheet.GetFirstRowIndex)])); + AStrings.Add(Format('Last row=%d', [ASheet.GetLastRowIndex])); + AStrings.Add(Format('First column=%d', [Integer(ASheet.GetFirstColIndex)])); + AStrings.Add(Format('Last column=%d', [ASheet.GetLastColIndex])); + AStrings.Add(Format('Active cell=%s', [GetCellString(ASheet.ActiveCellRow, ASheet.ActiveCellCol)])); + AStrings.Add(Format('Selection=%s', [ASheet.GetSelectionAsString])); end; end; - end. diff --git a/components/fpspreadsheet/fpspreadsheetgrid.pas b/components/fpspreadsheet/fpspreadsheetgrid.pas index b31080f62..9c81cf387 100644 --- a/components/fpspreadsheet/fpspreadsheetgrid.pas +++ b/components/fpspreadsheet/fpspreadsheetgrid.pas @@ -17,6 +17,8 @@ unit fpspreadsheetgrid; {$mode objfpc}{$H+} +{.$DEFINE ENABLE_MULTI_SELECT} // requires Laz trunk younger than r...??? (containing grid multisel patch) + interface uses @@ -325,6 +327,12 @@ type by the rectangle is activated. } property Wordwraps[ARect: TGridRect]: Boolean read GetWordwraps write SetWordwraps; + + // inherited + {$IFDEF ENABLE_MULTI_SELECT} + {@@ Allow multiple selections} + property RangeSelectMode default rsmMulti; + {$ENDIF} end; { TsWorksheetGrid } @@ -760,6 +768,9 @@ begin FInitRowCount := 100; FCellFont := TFont.Create; FOwnsWorkbook := true; + {$IFDEF ENABLE_MULTI_SELECT} + RangeSelectMode := rsmMulti; + {$ENDIF} end; {@@ ---------------------------------------------------------------------------- @@ -3212,9 +3223,45 @@ end; old thick selection border. -------------------------------------------------------------------------------} procedure TsCustomWorksheetGrid.MoveSelection; +var + sel: TsCellRangeArray; + {$IFDEF ENABLE_MULTI_SELECT} + i: Integer; + {$ENDIF} begin - if (WorkbookSource <> nil) then - WorkbookSource.SelectCell(GetWorksheetRow(Row), GetWorksheetCol(Col)); + if Worksheet <> nil then + begin + {$IFDEF ENABLE_MULTI_SELECT} + if HasMultiSelection then + begin + SetLength(sel, SelectedRangeCount); + for i:=0 to High(sel) do + with SelectedRange[i] do + begin + sel[i].Row1 := GetWorksheetRow(Top); + sel[i].Col1 := GetWorksheetCol(Left); + sel[i].Row2 := GetWorksheetRow(Bottom); + sel[i].Col2 := GetWorksheetCol(Right); + end; + end else + begin + SetLength(sel, 1); + sel[0].Row1 := GetWorksheetRow(Selection.Top); + sel[0].Col1 := GetWorksheetCol(Selection.Left); + sel[0].Row2 := GetWorksheetRow(Selection.Bottom); + sel[0].Col2 := GetWorksheetRow(Selection.Right); + end; + {$ELSE} + SetLength(sel, 1); + sel[0].Row1 := GetWorksheetRow(Selection.Top); + sel[0].Col1 := GetWorksheetCol(Selection.Left); + sel[0].Row2 := GetWorksheetRow(Selection.Bottom); + sel[0].Col2 := GetWorksheetRow(Selection.Right); + {$ENDIF} + Worksheet.SetSelection(sel); + + Worksheet.SelectCell(GetWorksheetRow(Row), GetWorksheetCol(Col)); + end; //Refresh; inherited; Refresh; @@ -3743,7 +3790,7 @@ end; function TsCustomWorksheetGrid.GetWorksheet: TsWorksheet; begin if FWorkbookSource <> nil then - Result := FWorkbooksource.SelectedWorksheet + Result := FWorkbooksource.Worksheet else Result := FOwnedWorksheet; end; diff --git a/components/fpspreadsheet/fpsutils.pas b/components/fpspreadsheet/fpsutils.pas index 7529de0a1..e5c5b9215 100644 --- a/components/fpspreadsheet/fpsutils.pas +++ b/components/fpspreadsheet/fpsutils.pas @@ -83,7 +83,8 @@ function ParseCellColString(const AStr: string; function GetColString(AColIndex: Integer): String; function GetCellString(ARow,ACol: Cardinal; AFlags: TsRelFlags = [rfRelRow, rfRelCol]): String; function GetCellRangeString(ARow1, ACol1, ARow2, ACol2: Cardinal; - AFlags: TsRelFlags = [rfRelRow, rfRelCol, rfRelRow2, rfRelCol2]): String; + AFlags: TsRelFlags = [rfRelRow, rfRelCol, rfRelRow2, rfRelCol2]; + Compact: Boolean = false): String; function GetErrorValueStr(AErrorValue: TsErrorValue): String; @@ -737,14 +738,18 @@ end; --> $A1:$B3 -------------------------------------------------------------------------------} function GetCellRangeString(ARow1, ACol1, ARow2, ACol2: Cardinal; - AFlags: TsRelFlags = [rfRelRow, rfRelCol, rfRelRow2, rfRelCol2]): String; + AFlags: TsRelFlags = [rfRelRow, rfRelCol, rfRelRow2, rfRelCol2]; + Compact: Boolean = false): String; begin - Result := Format('%s%s%s%d:%s%s%s%d', [ - RELCHAR[rfRelCol in AFlags], GetColString(ACol1), - RELCHAR[rfRelRow in AFlags], ARow1 + 1, - RELCHAR[rfRelCol2 in AFlags], GetColString(ACol2), - RELCHAR[rfRelRow2 in AFlags], ARow2 + 1 - ]); + if Compact and (ARow1 = ARow2) and (ACol1 = ACol2) then + Result := GetCellString(ARow1, ACol1, AFlags) + else + Result := Format('%s%s%s%d:%s%s%s%d', [ + RELCHAR[rfRelCol in AFlags], GetColString(ACol1), + RELCHAR[rfRelRow in AFlags], ARow1 + 1, + RELCHAR[rfRelCol2 in AFlags], GetColString(ACol2), + RELCHAR[rfRelRow2 in AFlags], ARow2 + 1 + ]); end; {@@ ----------------------------------------------------------------------------