From e22ceab9c9cbf3656922937d86c647ca7b701808 Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Fri, 23 Dec 2016 17:05:14 +0000 Subject: [PATCH] fpspreadsheet: Fix grid's Col/RowCount not being reduced if a sheet with less cols/rows than the current sheet is selected (http://forum.lazarus.freepascal.org/index.php/topic,35182.msg232477.html#msg232477). Introduce top/left cell indexes in worksheet to make sure that active cell remains at the row/col stored in the worksheet when changing worksheets. git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@5560 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- .../source/common/fpspreadsheet.pas | 19 +++ .../source/visual/fpspreadsheetctrls.pas | 7 +- .../source/visual/fpspreadsheetgrid.pas | 136 +++++++++++++----- 3 files changed, 126 insertions(+), 36 deletions(-) diff --git a/components/fpspreadsheet/source/common/fpspreadsheet.pas b/components/fpspreadsheet/source/common/fpspreadsheet.pas index b0170521b..fdbb04352 100644 --- a/components/fpspreadsheet/source/common/fpspreadsheet.pas +++ b/components/fpspreadsheet/source/common/fpspreadsheet.pas @@ -84,6 +84,8 @@ type FRows, FCols: TIndexedAVLTree; // This lists contain only rows or cols with styles different from default FActiveCellRow: Cardinal; FActiveCellCol: Cardinal; + FTopRow: Cardinal; + FLeftCol: Cardinal; FSelection: TsCellRangeArray; FLeftPaneWidth: Integer; FTopPaneHeight: Integer; @@ -477,6 +479,8 @@ type function GetSelectionRangeIndexOfActiveCell: Integer; procedure SetSelection(const ASelection: TsCellRangeArray); + procedure ScrollTo(ANewTopRow, ANewLeftCol: Cardinal); + // Comments function FindComment(ACell: PCell): PsComment; function HasComment(ACell: PCell): Boolean; @@ -582,6 +586,10 @@ type property ActiveCellCol: Cardinal read FActiveCellCol; {@@ Row index of the selected cell of this worksheet } property ActiveCellRow: Cardinal read FActiveCellRow; + {@@ Index of the left-most visible column in the grid - used by WorksheetGrid} + property LeftCol: Cardinal read FLeftCol; + {@@ Index of the top-most visible row in the grid - used by WorksheetGrid } + property TopRow: Cardinal read FTopRow; {@@ Number of frozen columns which do not scroll } property LeftPaneWidth: Integer read FLeftPaneWidth write FLeftPaneWidth; {@@ Number of frozen rows which do not scroll } @@ -4225,6 +4233,17 @@ begin FSelection[i] := ASelection[i]; end; +{@@ ---------------------------------------------------------------------------- + Uses the passed parameters a TopRow and LeftCol. These are used by the + TsWorksheetGrid to scroll the visible grid such that the corresponding cell + is at the top/left. +-------------------------------------------------------------------------------} +procedure TsWorksheet.ScrollTo(ANewTopRow, ANewLeftCol: Cardinal); +begin + FTopRow := ANewTopRow; + FLeftCol := ANewLeftCol; +end; + {@@ ---------------------------------------------------------------------------- Helper method to update internal caching variables -------------------------------------------------------------------------------} diff --git a/components/fpspreadsheet/source/visual/fpspreadsheetctrls.pas b/components/fpspreadsheet/source/visual/fpspreadsheetctrls.pas index d9bbe3f07..6921e0dae 100644 --- a/components/fpspreadsheet/source/visual/fpspreadsheetctrls.pas +++ b/components/fpspreadsheet/source/visual/fpspreadsheetctrls.pas @@ -1071,7 +1071,8 @@ begin AWorkbook.DisableNotifications; if AWorkbook <> FWorkbook then - InternalCreateNewWorkbook(AWorkbook); + InternalCreateNewWorkbook(AWorkbook) else + SetOptions(FOptions); WorkbookOpenedHandler(self); if AWorksheetIndex = -1 then @@ -1672,10 +1673,10 @@ begin FWorksheet.OnZoom := @WorksheetZoomHandler; NotifyListeners([lniWorksheet]); FWorksheet := AWorksheet; // !!!!! - if FWorksheet.ActiveCellRow = Cardinal(-1) then + if FWorksheet.ActiveCellRow = UNASSIGNED_ROW_COL_INDEX then r := FWorksheet.TopPaneHeight else r := FWorksheet.ActiveCellRow; - if FWorksheet.ActiveCellCol = Cardinal(-1) then + if FWorksheet.ActiveCellCol = UNASSIGNED_ROW_COL_INDEX then c := FWorksheet.LeftPaneWidth else c := FWorksheet.ActiveCellCol; SelectCell(r, c); diff --git a/components/fpspreadsheet/source/visual/fpspreadsheetgrid.pas b/components/fpspreadsheet/source/visual/fpspreadsheetgrid.pas index b7ae90799..8cff5c76f 100644 --- a/components/fpspreadsheet/source/visual/fpspreadsheetgrid.pas +++ b/components/fpspreadsheet/source/visual/fpspreadsheetgrid.pas @@ -15,6 +15,8 @@ unit fpspreadsheetgrid; {$mode objfpc}{$H+} {$I ..\fps.inc} +{.$DEFINE GRID_DEBUG} + { To do: - When Lazarus 1.4 comes out remove the workaround for the RGB2HLS bug in FindNearestPaletteIndex. @@ -247,6 +249,7 @@ type procedure SetEditText(ACol, ARow: Longint; const AValue: string); override; procedure Setup; procedure Sort(AColSorting: Boolean; AIndex, AIndxFrom, AIndxTo:Integer); override; + procedure TopLeftChanged; override; function TrimToCell(ACell: PCell): String; {@@ Automatically recalculate formulas whenever a cell value changes. } @@ -4081,45 +4084,100 @@ end; procedure TsCustomWorksheetGrid.ListenerNotification(AChangedItems: TsNotificationItems; AData: Pointer = nil); var + actgrow, actgcol: Integer; grow, gcol: Integer; srow, scol: Cardinal; cell: PCell; lRow: PRow; + + {$IFDEF GRID_DEBUG} + procedure DebugNotification(ACaption: String); + var + s: String; + begin + WriteLn(ACaption); + s := ''; + if (lniWorksheet in AChangedItems) then s := s + 'lniWorksheet, '; + if (lniCell in AChangedItems) then s := s + 'lniCell, '; + if (lniSelection in AChangedItems) then s := s + 'lniSelection, '; + if (lniAbortSelection in AChangedItems) then s := s + 'lniAbortSelection, '; + if (lniRow in AChangedItems) then s := s + 'lniRow, '; + if (lniCol in AChangedItems) then s := s + 'lniCol, '; + if (lniWorksheetZoom in AChangedItems) then s := s + 'lniWorksheetZoom, '; + if s <> '' then SetLength(s, Length(s) - 2); + WriteLn(' AChangedItems = [', s, ']'); + WriteLn(' ActiveCellRow: ', Worksheet.ActiveCellRow, ' ActiveCellCol: ', Worksheet.ActiveCellCol); + WriteLn(' TopRow: ', Worksheet.TopRow, ' LeftCol: ', Worksheet.LeftCol); + WriteLn; + end; + {$ENDIF} + begin Unused(AData); + {$IFDEF GRID_DEBUG} + if Worksheet <> nil then + DebugNotification('BEFORE ListenerNotification WorksheetGrid "' + Worksheet.Name + '":'); + {$ENDIF} + // Nothing to do for "workbook changed" because this is always combined with // "worksheet changed". // Worksheet changed if (lniWorksheet in AChangedItems) then begin - if (Worksheet <> nil) then - begin - inc(FLockSetup); - ShowHeaders := (soShowHeaders in Worksheet.Options); - ShowGridLines := (soShowGridLines in Worksheet.Options); - if (soHasFrozenPanes in Worksheet.Options) then begin - FrozenCols := Worksheet.LeftPaneWidth; - FrozenRows := Worksheet.TopPaneHeight; - end else begin - FrozenCols := 0; - FrozenRows := 0; + BeginUpdate; // avoid flicker... + try + if (Worksheet <> nil) then + begin + // remember indexes of top/left and active cell + grow := GetGridRow(Worksheet.TopRow); + gcol := GetGridCol(Worksheet.LeftCol); + actgrow := GetGridRow(Worksheet.ActiveCellRow); + actgcol := GetGridCol(Worksheet.ActiveCellCol); + AutoExpandToRow(grow, aeNavigation); + AutoExpandToCol(gcol, aeNavigation); + if (grow <> Row) or (gcol <> Col) then + MoveExtend(false, gcol, grow); + inc(FLockSetup); + // Setup grid headers and col/row count + ShowHeaders := (soShowHeaders in Worksheet.Options); + ShowGridLines := (soShowGridLines in Worksheet.Options); + if (soHasFrozenPanes in Worksheet.Options) then begin + FrozenCols := Worksheet.LeftPaneWidth; + FrozenRows := Worksheet.TopPaneHeight; + end else begin + FrozenCols := 0; + FrozenRows := 0; + end; + case Worksheet.BiDiMode of + bdDefault: ParentBiDiMode := true; + bdLTR : begin + ParentBiDiMode := false; + BiDiMode := bdLeftToRight; + end; + bdRTL : begin + ParentBiDiMode := false; + BiDiMode := bdRightToLeft; + end; + end; + dec(FLockSetup); end; - case Worksheet.BiDiMode of - bdDefault: ParentBiDiMode := true; - bdLTR : begin - ParentBiDiMode := false; - BiDiMode := bdLeftToRight; - end; - bdRTL : begin - ParentBiDiMode := false; - BiDiMode := bdRightToLeft; - end; + Setup; + // scroll the grid for top/left to be as stored in the sheet + if (grow <> TopRow) or (gcol <> LeftCol) then + begin + TopRow := gRow; + LeftCol := gCol; end; - dec(FLockSetup); + // Select active cell + AutoExpandToRow(actgrow, aeNavigation); + AutoExpandToCol(actgcol, aeNavigation); + if (actgrow <> Row) or (actgcol <> Col) then + MoveExtend(false, actgcol, actgrow); + finally + EndUpdate; end; - Setup; end; // Cell value or format changed @@ -4179,6 +4237,12 @@ begin // Worksheet zoom if (lniWorksheetZoom in AChangedItems) and (Worksheet <> nil) then AdaptToZoomFactor; // Reads value directly from Worksheet + + {$IFDEF GRID_DEBUG} + if Worksheet <> nil then + DebugNotification('AFTER ListenerNotification WorksheetGrid "' + Worksheet.Name + '":'); + {$ENDIF} + end; {@@ ---------------------------------------------------------------------------- @@ -4498,6 +4562,8 @@ end; initial column widths and row heights. -------------------------------------------------------------------------------} procedure TsCustomWorksheetGrid.Setup; +var + defColCount, defRowCount: Integer; begin if csLoading in ComponentState then exit; @@ -4520,15 +4586,10 @@ begin end; end else if Worksheet <> nil then begin - if FHeaderCount = 0 then - begin - ColCount := Max(GetGridCol(Worksheet.GetLastColIndex), ColCount-1); - RowCount := Max(GetGridRow(Worksheet.GetLastRowIndex), RowCount-1); - end else - begin - ColCount := Max(GetGridCol(Worksheet.GetLastColIndex) + 1, ColCount); - RowCount := Max(GetGridRow(Worksheet.GetLastRowIndex) + 1, RowCount); - end; + defColCount := DEFAULT_COL_COUNT; + defRowCount := DEFAULT_ROW_COUNT; + ColCount := Max(GetGridCol(Worksheet.GetLastColIndex)+1, defColCount) + FHeaderCount; + RowCount := max(GetGridRow(Worksheet.GetLastRowIndex)+1, defRowCount) + FHeaderCount; FixedCols := FFrozenCols + FHeaderCount; FixedRows := FFrozenRows + FHeaderCount; if ShowHeaders then begin @@ -4539,7 +4600,7 @@ begin end; UpdateColWidths; UpdateRowHeights; - Invalidate; + //Invalidate; // wp: really needed? Might cause flicker end; {@@ ---------------------------------------------------------------------------- @@ -4681,6 +4742,15 @@ begin ); end; +{@@ ---------------------------------------------------------------------------- + Store the value of the TopLeft cell in the worksheet +-------------------------------------------------------------------------------} +procedure TsCustomWorksheetGrid.TopLeftChanged; +begin + inherited; + Worksheet.ScrollTo(GetWorkSheetRow(TopRow), GetWorksheetCol(LeftCol)); +end; + {@@ ---------------------------------------------------------------------------- Modifies the text that is show for cells which are too narrow to hold the entire text. The method follows the behavior of Excel and Open/LibreOffice: