1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 #include "oox/xls/viewsettings.hxx"
25
26 #include <com/sun/star/awt/Point.hpp>
27 #include <com/sun/star/awt/Size.hpp>
28 #include <com/sun/star/beans/PropertyValue.hpp>
29 #include <com/sun/star/container/XIndexContainer.hpp>
30 #include <com/sun/star/container/XNameContainer.hpp>
31 #include <com/sun/star/document/XViewDataSupplier.hpp>
32 #include <comphelper/mediadescriptor.hxx>
33 #include "oox/core/filterbase.hxx"
34 #include "oox/helper/attributelist.hxx"
35 #include "oox/helper/containerhelper.hxx"
36 #include "oox/helper/propertymap.hxx"
37 #include "oox/helper/propertyset.hxx"
38 #include "oox/xls/addressconverter.hxx"
39 #include "oox/xls/biffinputstream.hxx"
40 #include "oox/xls/unitconverter.hxx"
41 #include "oox/xls/workbooksettings.hxx"
42 #include "oox/xls/worksheetbuffer.hxx"
43
44 namespace oox {
45 namespace xls {
46
47 // ============================================================================
48
49 using namespace ::com::sun::star::awt;
50 using namespace ::com::sun::star::container;
51 using namespace ::com::sun::star::document;
52 using namespace ::com::sun::star::table;
53 using namespace ::com::sun::star::uno;
54
55 using ::oox::core::FilterBase;
56 using ::rtl::OUString;
57
58 // ============================================================================
59
60 namespace {
61
62 const sal_Int32 OOX_BOOKVIEW_TABBARRATIO_DEF = 600; /// Default tabbar ratio.
63 const sal_Int32 OOX_SHEETVIEW_NORMALZOOM_DEF = 100; /// Default zoom for normal view.
64 const sal_Int32 OOX_SHEETVIEW_SHEETLAYZOOM_DEF = 60; /// Default zoom for pagebreak preview.
65 const sal_Int32 OOX_SHEETVIEW_PAGELAYZOOM_DEF = 100; /// Default zoom for page layout view.
66
67 const sal_uInt8 BIFF12_PANE_FROZEN = 0x01;
68 const sal_uInt8 BIFF12_PANE_FROZENNOSPLIT = 0x02;
69
70 const sal_uInt16 BIFF12_SHEETVIEW_WINPROTECTED = 0x0001;
71 const sal_uInt16 BIFF12_SHEETVIEW_SHOWFORMULAS = 0x0002;
72 const sal_uInt16 BIFF12_SHEETVIEW_SHOWGRID = 0x0004;
73 const sal_uInt16 BIFF12_SHEETVIEW_SHOWHEADINGS = 0x0008;
74 const sal_uInt16 BIFF12_SHEETVIEW_SHOWZEROS = 0x0010;
75 const sal_uInt16 BIFF12_SHEETVIEW_RIGHTTOLEFT = 0x0020;
76 const sal_uInt16 BIFF12_SHEETVIEW_SELECTED = 0x0040;
77 const sal_uInt16 BIFF12_SHEETVIEW_SHOWRULER = 0x0080;
78 const sal_uInt16 BIFF12_SHEETVIEW_SHOWOUTLINE = 0x0100;
79 const sal_uInt16 BIFF12_SHEETVIEW_DEFGRIDCOLOR = 0x0200;
80 const sal_uInt16 BIFF12_SHEETVIEW_SHOWWHITESPACE = 0x0400;
81
82 const sal_uInt16 BIFF12_CHARTSHEETVIEW_SELECTED = 0x0001;
83 const sal_uInt16 BIFF12_CHARTSHEETVIEW_ZOOMTOFIT = 0x0002;
84
85 const sal_uInt8 BIFF12_WBVIEW_HIDDEN = 0x01;
86 const sal_uInt8 BIFF12_WBVIEW_MINIMIZED = 0x02;
87 const sal_uInt8 BIFF12_WBVIEW_SHOWHORSCROLL = 0x08;
88 const sal_uInt8 BIFF12_WBVIEW_SHOWVERSCROLL = 0x10;
89 const sal_uInt8 BIFF12_WBVIEW_SHOWTABBAR = 0x20;
90 const sal_uInt8 BIFF12_WBVIEW_AUTOFILTERGROUP = 0x40;
91
92 const sal_uInt8 BIFF_PANE_BOTTOMRIGHT = 0; /// Bottom-right pane.
93 const sal_uInt8 BIFF_PANE_TOPRIGHT = 1; /// Right, or top-right pane.
94 const sal_uInt8 BIFF_PANE_BOTTOMLEFT = 2; /// Bottom, or bottom-left pane.
95 const sal_uInt8 BIFF_PANE_TOPLEFT = 3; /// Single, top, left, or top-left pane.
96
97 const sal_uInt16 BIFF_WINDOW1_HIDDEN = 0x0001;
98 const sal_uInt16 BIFF_WINDOW1_MINIMIZED = 0x0002;
99 const sal_uInt16 BIFF_WINDOW1_SHOWHORSCROLL = 0x0008;
100 const sal_uInt16 BIFF_WINDOW1_SHOWVERSCROLL = 0x0010;
101 const sal_uInt16 BIFF_WINDOW1_SHOWTABBAR = 0x0020;
102
103 const sal_uInt16 BIFF_WINDOW2_SHOWFORMULAS = 0x0001;
104 const sal_uInt16 BIFF_WINDOW2_SHOWGRID = 0x0002;
105 const sal_uInt16 BIFF_WINDOW2_SHOWHEADINGS = 0x0004;
106 const sal_uInt16 BIFF_WINDOW2_FROZEN = 0x0008;
107 const sal_uInt16 BIFF_WINDOW2_SHOWZEROS = 0x0010;
108 const sal_uInt16 BIFF_WINDOW2_DEFGRIDCOLOR = 0x0020;
109 const sal_uInt16 BIFF_WINDOW2_RIGHTTOLEFT = 0x0040;
110 const sal_uInt16 BIFF_WINDOW2_SHOWOUTLINE = 0x0080;
111 const sal_uInt16 BIFF_WINDOW2_FROZENNOSPLIT = 0x0100;
112 const sal_uInt16 BIFF_WINDOW2_SELECTED = 0x0200;
113 const sal_uInt16 BIFF_WINDOW2_DISPLAYED = 0x0400;
114 const sal_uInt16 BIFF_WINDOW2_PAGEBREAKMODE = 0x0800;
115
116 // Attention: view settings in Calc do not use com.sun.star.view.DocumentZoomType!
117 const sal_Int16 API_ZOOMTYPE_PERCENT = 0; /// Zoom value in percent.
118
119 const sal_Int32 API_ZOOMVALUE_MIN = 20; /// Minimum zoom in Calc.
120 const sal_Int32 API_ZOOMVALUE_MAX = 400; /// Maximum zoom in Calc.
121
122 // no predefined constants for split mode
123 const sal_Int16 API_SPLITMODE_NONE = 0; /// No splits in window.
124 const sal_Int16 API_SPLITMODE_SPLIT = 1; /// Window is split.
125 const sal_Int16 API_SPLITMODE_FREEZE = 2; /// Window has frozen panes.
126
127 // no predefined constants for pane idetifiers
128 const sal_Int16 API_SPLITPANE_TOPLEFT = 0; /// Top-left, or top pane.
129 const sal_Int16 API_SPLITPANE_TOPRIGHT = 1; /// Top-right pane.
130 const sal_Int16 API_SPLITPANE_BOTTOMLEFT = 2; /// Bottom-left, bottom, left, or single pane.
131 const sal_Int16 API_SPLITPANE_BOTTOMRIGHT = 3; /// Bottom-right, or right pane.
132
133 // ----------------------------------------------------------------------------
134
135 /** Returns the OOXML pane identifier from the passed BIFF pane id. */
lclGetOoxPaneId(sal_Int32 nBiffPaneId,sal_Int32 nDefaultPaneId)136 sal_Int32 lclGetOoxPaneId( sal_Int32 nBiffPaneId, sal_Int32 nDefaultPaneId )
137 {
138 static const sal_Int32 spnPaneIds[] = { XML_bottomRight, XML_topRight, XML_bottomLeft, XML_topLeft };
139 return STATIC_ARRAY_SELECT( spnPaneIds, nBiffPaneId, nDefaultPaneId );
140 }
141
142 } // namespace
143
144 // ============================================================================
145
PaneSelectionModel()146 PaneSelectionModel::PaneSelectionModel() :
147 mnActiveCellId( 0 )
148 {
149 }
150
151 // ----------------------------------------------------------------------------
152
SheetViewModel()153 SheetViewModel::SheetViewModel() :
154 mnWorkbookViewId( 0 ),
155 mnViewType( XML_normal ),
156 mnActivePaneId( XML_topLeft ),
157 mnPaneState( XML_split ),
158 mfSplitX( 0.0 ),
159 mfSplitY( 0.0 ),
160 mnCurrentZoom( 0 ),
161 mnNormalZoom( 0 ),
162 mnSheetLayoutZoom( 0 ),
163 mnPageLayoutZoom( 0 ),
164 mbSelected( false ),
165 mbRightToLeft( false ),
166 mbDefGridColor( true ),
167 mbShowFormulas( false ),
168 mbShowGrid( true ),
169 mbShowHeadings( true ),
170 mbShowZeros( true ),
171 mbShowOutline( true ),
172 mbZoomToFit( false )
173 {
174 maGridColor.setIndexed( OOX_COLOR_WINDOWTEXT );
175 }
176
isPageBreakPreview() const177 bool SheetViewModel::isPageBreakPreview() const
178 {
179 return mnViewType == XML_pageBreakPreview;
180 }
181
getNormalZoom() const182 sal_Int32 SheetViewModel::getNormalZoom() const
183 {
184 const sal_Int32& rnZoom = isPageBreakPreview() ? mnNormalZoom : mnCurrentZoom;
185 sal_Int32 nZoom = (rnZoom > 0) ? rnZoom : OOX_SHEETVIEW_NORMALZOOM_DEF;
186 return getLimitedValue< sal_Int32 >( nZoom, API_ZOOMVALUE_MIN, API_ZOOMVALUE_MAX );
187 }
188
getPageBreakZoom() const189 sal_Int32 SheetViewModel::getPageBreakZoom() const
190 {
191 const sal_Int32& rnZoom = isPageBreakPreview() ? mnCurrentZoom : mnSheetLayoutZoom;
192 sal_Int32 nZoom = (rnZoom > 0) ? rnZoom : OOX_SHEETVIEW_SHEETLAYZOOM_DEF;
193 return getLimitedValue< sal_Int32 >( nZoom, API_ZOOMVALUE_MIN, API_ZOOMVALUE_MAX );
194 }
195
getGridColor(const FilterBase & rFilter) const196 sal_Int32 SheetViewModel::getGridColor( const FilterBase& rFilter ) const
197 {
198 return mbDefGridColor ? API_RGB_TRANSPARENT : maGridColor.getColor( rFilter.getGraphicHelper() );
199 }
200
getPaneSelection(sal_Int32 nPaneId) const201 const PaneSelectionModel* SheetViewModel::getPaneSelection( sal_Int32 nPaneId ) const
202 {
203 return maPaneSelMap.get( nPaneId ).get();
204 }
205
getActiveSelection() const206 const PaneSelectionModel* SheetViewModel::getActiveSelection() const
207 {
208 return getPaneSelection( mnActivePaneId );
209 }
210
createPaneSelection(sal_Int32 nPaneId)211 PaneSelectionModel& SheetViewModel::createPaneSelection( sal_Int32 nPaneId )
212 {
213 PaneSelectionModelMap::mapped_type& rxPaneSel = maPaneSelMap[ nPaneId ];
214 if( !rxPaneSel )
215 rxPaneSel.reset( new PaneSelectionModel );
216 return *rxPaneSel;
217 }
218
219 // ----------------------------------------------------------------------------
220
SheetViewSettings(const WorksheetHelper & rHelper)221 SheetViewSettings::SheetViewSettings( const WorksheetHelper& rHelper ) :
222 WorksheetHelper( rHelper )
223 {
224 }
225
importSheetView(const AttributeList & rAttribs)226 void SheetViewSettings::importSheetView( const AttributeList& rAttribs )
227 {
228 SheetViewModel& rModel = *createSheetView();
229 rModel.maGridColor.setIndexed( rAttribs.getInteger( XML_colorId, OOX_COLOR_WINDOWTEXT ) );
230 rModel.maFirstPos = getAddressConverter().createValidCellAddress( rAttribs.getString( XML_topLeftCell, OUString() ), getSheetIndex(), false );
231 rModel.mnWorkbookViewId = rAttribs.getToken( XML_workbookViewId, 0 );
232 rModel.mnViewType = rAttribs.getToken( XML_view, XML_normal );
233 rModel.mnCurrentZoom = rAttribs.getInteger( XML_zoomScale, 100 );
234 rModel.mnNormalZoom = rAttribs.getInteger( XML_zoomScaleNormal, 0 );
235 rModel.mnSheetLayoutZoom = rAttribs.getInteger( XML_zoomScaleSheetLayoutView, 0 );
236 rModel.mnPageLayoutZoom = rAttribs.getInteger( XML_zoomScalePageLayoutView, 0 );
237 rModel.mbSelected = rAttribs.getBool( XML_tabSelected, false );
238 rModel.mbRightToLeft = rAttribs.getBool( XML_rightToLeft, false );
239 rModel.mbDefGridColor = rAttribs.getBool( XML_defaultGridColor, true );
240 rModel.mbShowFormulas = rAttribs.getBool( XML_showFormulas, false );
241 rModel.mbShowGrid = rAttribs.getBool( XML_showGridLines, true );
242 rModel.mbShowHeadings = rAttribs.getBool( XML_showRowColHeaders, true );
243 rModel.mbShowZeros = rAttribs.getBool( XML_showZeros, true );
244 rModel.mbShowOutline = rAttribs.getBool( XML_showOutlineSymbols, true );
245 }
246
importPane(const AttributeList & rAttribs)247 void SheetViewSettings::importPane( const AttributeList& rAttribs )
248 {
249 OSL_ENSURE( !maSheetViews.empty(), "SheetViewSettings::importPane - missing sheet view model" );
250 if( !maSheetViews.empty() )
251 {
252 SheetViewModel& rModel = *maSheetViews.back();
253 rModel.maSecondPos = getAddressConverter().createValidCellAddress( rAttribs.getString( XML_topLeftCell, OUString() ), getSheetIndex(), false );
254 rModel.mnActivePaneId = rAttribs.getToken( XML_activePane, XML_topLeft );
255 rModel.mnPaneState = rAttribs.getToken( XML_state, XML_split );
256 rModel.mfSplitX = rAttribs.getDouble( XML_xSplit, 0.0 );
257 rModel.mfSplitY = rAttribs.getDouble( XML_ySplit, 0.0 );
258 }
259 }
260
importSelection(const AttributeList & rAttribs)261 void SheetViewSettings::importSelection( const AttributeList& rAttribs )
262 {
263 OSL_ENSURE( !maSheetViews.empty(), "SheetViewSettings::importSelection - missing sheet view model" );
264 if( !maSheetViews.empty() )
265 {
266 // pane this selection belongs to
267 sal_Int32 nPaneId = rAttribs.getToken( XML_pane, XML_topLeft );
268 PaneSelectionModel& rSelData = maSheetViews.back()->createPaneSelection( nPaneId );
269 // cursor position
270 rSelData.maActiveCell = getAddressConverter().createValidCellAddress( rAttribs.getString( XML_activeCell, OUString() ), getSheetIndex(), false );
271 rSelData.mnActiveCellId = rAttribs.getInteger( XML_activeCellId, 0 );
272 // selection
273 rSelData.maSelection.clear();
274 getAddressConverter().convertToCellRangeList( rSelData.maSelection, rAttribs.getString( XML_sqref, OUString() ), getSheetIndex(), false );
275 }
276 }
277
importChartSheetView(const AttributeList & rAttribs)278 void SheetViewSettings::importChartSheetView( const AttributeList& rAttribs )
279 {
280 SheetViewModel& rModel = *createSheetView();
281 rModel.mnWorkbookViewId = rAttribs.getToken( XML_workbookViewId, 0 );
282 rModel.mnCurrentZoom = rAttribs.getInteger( XML_zoomScale, 100 );
283 rModel.mbSelected = rAttribs.getBool( XML_tabSelected, false );
284 rModel.mbZoomToFit = rAttribs.getBool( XML_zoomToFit, false );
285 }
286
importSheetView(SequenceInputStream & rStrm)287 void SheetViewSettings::importSheetView( SequenceInputStream& rStrm )
288 {
289 SheetViewModel& rModel = *createSheetView();
290 sal_uInt16 nFlags;
291 sal_Int32 nViewType;
292 BinAddress aFirstPos;
293 rStrm >> nFlags >> nViewType >> aFirstPos;
294 rModel.maGridColor.importColorId( rStrm );
295 rModel.mnCurrentZoom = rStrm.readuInt16();
296 rModel.mnNormalZoom = rStrm.readuInt16();
297 rModel.mnSheetLayoutZoom = rStrm.readuInt16();
298 rModel.mnPageLayoutZoom = rStrm.readuInt16();
299 rStrm >> rModel.mnWorkbookViewId;
300
301 rModel.maFirstPos = getAddressConverter().createValidCellAddress( aFirstPos, getSheetIndex(), false );
302 static const sal_Int32 spnViewTypes[] = { XML_normal, XML_pageBreakPreview, XML_pageLayout };
303 rModel.mnViewType = STATIC_ARRAY_SELECT( spnViewTypes, nViewType, XML_normal );
304 rModel.mbSelected = getFlag( nFlags, BIFF12_SHEETVIEW_SELECTED );
305 rModel.mbRightToLeft = getFlag( nFlags, BIFF12_SHEETVIEW_RIGHTTOLEFT );
306 rModel.mbDefGridColor = getFlag( nFlags, BIFF12_SHEETVIEW_DEFGRIDCOLOR );
307 rModel.mbShowFormulas = getFlag( nFlags, BIFF12_SHEETVIEW_SHOWFORMULAS );
308 rModel.mbShowGrid = getFlag( nFlags, BIFF12_SHEETVIEW_SHOWGRID );
309 rModel.mbShowHeadings = getFlag( nFlags, BIFF12_SHEETVIEW_SHOWHEADINGS );
310 rModel.mbShowZeros = getFlag( nFlags, BIFF12_SHEETVIEW_SHOWZEROS );
311 rModel.mbShowOutline = getFlag( nFlags, BIFF12_SHEETVIEW_SHOWOUTLINE );
312 }
313
importPane(SequenceInputStream & rStrm)314 void SheetViewSettings::importPane( SequenceInputStream& rStrm )
315 {
316 OSL_ENSURE( !maSheetViews.empty(), "SheetViewSettings::importPane - missing sheet view model" );
317 if( !maSheetViews.empty() )
318 {
319 SheetViewModel& rModel = *maSheetViews.back();
320
321 BinAddress aSecondPos;
322 sal_Int32 nActivePaneId;
323 sal_uInt8 nFlags;
324 rStrm >> rModel.mfSplitX >> rModel.mfSplitY >> aSecondPos >> nActivePaneId >> nFlags;
325
326 rModel.maSecondPos = getAddressConverter().createValidCellAddress( aSecondPos, getSheetIndex(), false );
327 rModel.mnActivePaneId = lclGetOoxPaneId( nActivePaneId, XML_topLeft );
328 rModel.mnPaneState = getFlagValue( nFlags, BIFF12_PANE_FROZEN, getFlagValue( nFlags, BIFF12_PANE_FROZENNOSPLIT, XML_frozen, XML_frozenSplit ), XML_split );
329 }
330 }
331
importSelection(SequenceInputStream & rStrm)332 void SheetViewSettings::importSelection( SequenceInputStream& rStrm )
333 {
334 OSL_ENSURE( !maSheetViews.empty(), "SheetViewSettings::importSelection - missing sheet view model" );
335 if( !maSheetViews.empty() )
336 {
337 // pane this selection belongs to
338 sal_Int32 nPaneId = rStrm.readInt32();
339 PaneSelectionModel& rPaneSel = maSheetViews.back()->createPaneSelection( lclGetOoxPaneId( nPaneId, -1 ) );
340 // cursor position
341 BinAddress aActiveCell;
342 rStrm >> aActiveCell >> rPaneSel.mnActiveCellId;
343 rPaneSel.maActiveCell = getAddressConverter().createValidCellAddress( aActiveCell, getSheetIndex(), false );
344 // selection
345 BinRangeList aSelection;
346 rStrm >> aSelection;
347 rPaneSel.maSelection.clear();
348 getAddressConverter().convertToCellRangeList( rPaneSel.maSelection, aSelection, getSheetIndex(), false );
349 }
350 }
351
importChartSheetView(SequenceInputStream & rStrm)352 void SheetViewSettings::importChartSheetView( SequenceInputStream& rStrm )
353 {
354 SheetViewModel& rModel = *createSheetView();
355 sal_uInt16 nFlags;
356 rStrm >> nFlags >> rModel.mnCurrentZoom >> rModel.mnWorkbookViewId;
357
358 rModel.mbSelected = getFlag( nFlags, BIFF12_CHARTSHEETVIEW_SELECTED );
359 rModel.mbZoomToFit = getFlag( nFlags, BIFF12_CHARTSHEETVIEW_ZOOMTOFIT );
360 }
361
importWindow2(BiffInputStream & rStrm)362 void SheetViewSettings::importWindow2( BiffInputStream& rStrm )
363 {
364 OSL_ENSURE( maSheetViews.empty(), "SheetViewSettings::importWindow2 - multiple WINDOW2 records" );
365 SheetViewModel& rModel = *createSheetView();
366 if( getBiff() == BIFF2 )
367 {
368 rModel.mbShowFormulas = rStrm.readuInt8() != 0;
369 rModel.mbShowGrid = rStrm.readuInt8() != 0;
370 rModel.mbShowHeadings = rStrm.readuInt8() != 0;
371 rModel.mnPaneState = (rStrm.readuInt8() == 0) ? XML_split : XML_frozen;
372 rModel.mbShowZeros = rStrm.readuInt8() != 0;
373 BinAddress aFirstPos;
374 rStrm >> aFirstPos;
375 rModel.maFirstPos = getAddressConverter().createValidCellAddress( aFirstPos, getSheetIndex(), false );
376 rModel.mbDefGridColor = rStrm.readuInt8() != 0;
377 rModel.maGridColor.importColorRgb( rStrm );
378 }
379 else
380 {
381 sal_uInt16 nFlags;
382 BinAddress aFirstPos;
383 rStrm >> nFlags >> aFirstPos;
384
385 rModel.maFirstPos = getAddressConverter().createValidCellAddress( aFirstPos, getSheetIndex(), false );
386 rModel.mnPaneState = getFlagValue( nFlags, BIFF_WINDOW2_FROZEN, getFlagValue( nFlags, BIFF_WINDOW2_FROZENNOSPLIT, XML_frozen, XML_frozenSplit ), XML_split );
387 rModel.mbSelected = getFlag( nFlags, BIFF_WINDOW2_SELECTED );
388 rModel.mbRightToLeft = getFlag( nFlags, BIFF_WINDOW2_RIGHTTOLEFT );
389 rModel.mbDefGridColor = getFlag( nFlags, BIFF_WINDOW2_DEFGRIDCOLOR );
390 rModel.mbShowFormulas = getFlag( nFlags, BIFF_WINDOW2_SHOWFORMULAS );
391 rModel.mbShowGrid = getFlag( nFlags, BIFF_WINDOW2_SHOWGRID );
392 rModel.mbShowHeadings = getFlag( nFlags, BIFF_WINDOW2_SHOWHEADINGS );
393 rModel.mbShowZeros = getFlag( nFlags, BIFF_WINDOW2_SHOWZEROS );
394 rModel.mbShowOutline = getFlag( nFlags, BIFF_WINDOW2_SHOWOUTLINE );
395
396 if( getBiff() == BIFF8 )
397 {
398 rModel.mnViewType = getFlagValue( nFlags, BIFF_WINDOW2_PAGEBREAKMODE, XML_pageBreakPreview, XML_normal );
399
400 rModel.maGridColor.importColorId( rStrm );
401 // zoom data not included in chart sheets
402 if( (getSheetType() != SHEETTYPE_CHARTSHEET) && (rStrm.getRemaining() >= 6) )
403 {
404 rStrm.skip( 2 );
405 sal_uInt16 nPageZoom, nNormalZoom;
406 rStrm >> nPageZoom >> nNormalZoom;
407 rModel.mnSheetLayoutZoom = nPageZoom;
408 rModel.mnNormalZoom = nNormalZoom;
409 }
410 }
411 else
412 {
413 rModel.maGridColor.importColorRgb( rStrm );
414 }
415 }
416 }
417
importPane(BiffInputStream & rStrm)418 void SheetViewSettings::importPane( BiffInputStream& rStrm )
419 {
420 OSL_ENSURE( !maSheetViews.empty(), "SheetViewSettings::importPane - missing leading WINDOW2 record" );
421 if( !maSheetViews.empty() )
422 {
423 sal_uInt8 nActivePaneId;
424 sal_uInt16 nSplitX, nSplitY;
425 BinAddress aSecondPos;
426 rStrm >> nSplitX >> nSplitY >> aSecondPos >> nActivePaneId;
427
428 SheetViewModel& rModel = *maSheetViews.back();
429 rModel.mfSplitX = nSplitX;
430 rModel.mfSplitY = nSplitY;
431 rModel.maSecondPos = getAddressConverter().createValidCellAddress( aSecondPos, getSheetIndex(), false );
432 rModel.mnActivePaneId = lclGetOoxPaneId( nActivePaneId, XML_topLeft );
433 }
434 }
435
importScl(BiffInputStream & rStrm)436 void SheetViewSettings::importScl( BiffInputStream& rStrm )
437 {
438 OSL_ENSURE( !maSheetViews.empty(), "SheetViewSettings::importScl - missing leading WINDOW2 record" );
439 if( !maSheetViews.empty() )
440 {
441 sal_uInt16 nNum, nDenom;
442 rStrm >> nNum >> nDenom;
443 OSL_ENSURE( nDenom > 0, "SheetViewSettings::importScl - invalid denominator" );
444 if( nDenom > 0 )
445 maSheetViews.back()->mnCurrentZoom = getLimitedValue< sal_Int32, sal_uInt16 >( (nNum * 100) / nDenom, 10, 400 );
446 }
447 }
448
importSelection(BiffInputStream & rStrm)449 void SheetViewSettings::importSelection( BiffInputStream& rStrm )
450 {
451 OSL_ENSURE( !maSheetViews.empty(), "SheetViewSettings::importPane - missing leading WINDOW2 record" );
452 if( !maSheetViews.empty() )
453 {
454 // pane this selection belongs to
455 sal_uInt8 nPaneId = rStrm.readuInt8();
456 PaneSelectionModel& rPaneSel = maSheetViews.back()->createPaneSelection( lclGetOoxPaneId( nPaneId, -1 ) );
457 // cursor position
458 BinAddress aActiveCell;
459 sal_uInt16 nActiveCellId;
460 rStrm >> aActiveCell >> nActiveCellId;
461 rPaneSel.maActiveCell = getAddressConverter().createValidCellAddress( aActiveCell, getSheetIndex(), false );
462 rPaneSel.mnActiveCellId = nActiveCellId;
463 // selection
464 rPaneSel.maSelection.clear();
465 BinRangeList aSelection;
466 aSelection.read( rStrm, false );
467 getAddressConverter().convertToCellRangeList( rPaneSel.maSelection, aSelection, getSheetIndex(), false );
468 }
469 }
470
finalizeImport()471 void SheetViewSettings::finalizeImport()
472 {
473 // force creation of sheet view model to get the Excel defaults
474 SheetViewModelRef xModel = maSheetViews.empty() ? createSheetView() : maSheetViews.front();
475
476 // #i59590# #158194# special handling for chart sheets (Excel ignores some settings in chart sheets)
477 if( getSheetType() == SHEETTYPE_CHARTSHEET )
478 {
479 xModel->maPaneSelMap.clear();
480 xModel->maFirstPos = xModel->maSecondPos = CellAddress( getSheetIndex(), 0, 0 );
481 xModel->mnViewType = XML_normal;
482 xModel->mnActivePaneId = XML_topLeft;
483 xModel->mnPaneState = XML_split;
484 xModel->mfSplitX = xModel->mfSplitY = 0.0;
485 xModel->mbRightToLeft = false;
486 xModel->mbDefGridColor = true;
487 xModel->mbShowFormulas = false;
488 xModel->mbShowGrid = true;
489 xModel->mbShowHeadings = true;
490 xModel->mbShowZeros = true;
491 xModel->mbShowOutline = true;
492 }
493
494 // sheet selected (active sheet must be selected)
495 bool bSelected = xModel->mbSelected || (getSheetIndex() == getViewSettings().getActiveCalcSheet());
496
497 // visible area and current cursor position (selection not supported via API)
498 CellAddress aFirstPos = xModel->maFirstPos;
499 const PaneSelectionModel* pPaneSel = xModel->getActiveSelection();
500 CellAddress aCursor = pPaneSel ? pPaneSel->maActiveCell : aFirstPos;
501
502 // freeze/split position default
503 sal_Int16 nHSplitMode = API_SPLITMODE_NONE;
504 sal_Int16 nVSplitMode = API_SPLITMODE_NONE;
505 sal_Int32 nHSplitPos = 0;
506 sal_Int32 nVSplitPos = 0;
507 // active pane default
508 sal_Int16 nActivePane = API_SPLITPANE_BOTTOMLEFT;
509
510 // freeze/split position
511 if( (xModel->mnPaneState == XML_frozen) || (xModel->mnPaneState == XML_frozenSplit) )
512 {
513 /* Frozen panes: handle split position as row/column positions.
514 #i35812# Excel uses number of visible rows/columns in the
515 frozen area (rows/columns scolled outside are not incuded),
516 Calc uses absolute position of first unfrozen row/column. */
517 const CellAddress& rMaxApiPos = getAddressConverter().getMaxApiAddress();
518 if( (xModel->mfSplitX >= 1.0) && (xModel->maFirstPos.Column + xModel->mfSplitX <= rMaxApiPos.Column) )
519 nHSplitPos = static_cast< sal_Int32 >( xModel->maFirstPos.Column + xModel->mfSplitX );
520 nHSplitMode = (nHSplitPos > 0) ? API_SPLITMODE_FREEZE : API_SPLITMODE_NONE;
521 if( (xModel->mfSplitY >= 1.0) && (xModel->maFirstPos.Row + xModel->mfSplitY <= rMaxApiPos.Row) )
522 nVSplitPos = static_cast< sal_Int32 >( xModel->maFirstPos.Row + xModel->mfSplitY );
523 nVSplitMode = (nVSplitPos > 0) ? API_SPLITMODE_FREEZE : API_SPLITMODE_NONE;
524 }
525 else if( xModel->mnPaneState == XML_split )
526 {
527 // split window: view settings API uses twips...
528 nHSplitPos = getLimitedValue< sal_Int32, double >( xModel->mfSplitX + 0.5, 0, SAL_MAX_INT32 );
529 nHSplitMode = (nHSplitPos > 0) ? API_SPLITMODE_SPLIT : API_SPLITMODE_NONE;
530 nVSplitPos = getLimitedValue< sal_Int32, double >( xModel->mfSplitY + 0.5, 0, SAL_MAX_INT32 );
531 nVSplitMode = (nVSplitPos > 0) ? API_SPLITMODE_SPLIT : API_SPLITMODE_NONE;
532 }
533
534 // active pane
535 switch( xModel->mnActivePaneId )
536 {
537 // no horizontal split -> always use left panes
538 // no vertical split -> always use *bottom* panes
539 case XML_topLeft:
540 nActivePane = (nVSplitMode == API_SPLITMODE_NONE) ? API_SPLITPANE_BOTTOMLEFT : API_SPLITPANE_TOPLEFT;
541 break;
542 case XML_topRight:
543 nActivePane = (nHSplitMode == API_SPLITMODE_NONE) ?
544 ((nVSplitMode == API_SPLITMODE_NONE) ? API_SPLITPANE_BOTTOMLEFT : API_SPLITPANE_TOPLEFT) :
545 ((nVSplitMode == API_SPLITMODE_NONE) ? API_SPLITPANE_BOTTOMRIGHT : API_SPLITPANE_TOPRIGHT);
546 break;
547 case XML_bottomLeft:
548 nActivePane = API_SPLITPANE_BOTTOMLEFT;
549 break;
550 case XML_bottomRight:
551 nActivePane = (nHSplitMode == API_SPLITMODE_NONE) ? API_SPLITPANE_BOTTOMLEFT : API_SPLITPANE_BOTTOMRIGHT;
552 break;
553 }
554
555 // write the sheet view settings into the property sequence
556 PropertyMap aPropMap;
557 aPropMap[ PROP_TableSelected ] <<= bSelected;
558 aPropMap[ PROP_CursorPositionX ] <<= aCursor.Column;
559 aPropMap[ PROP_CursorPositionY ] <<= aCursor.Row;
560 aPropMap[ PROP_HorizontalSplitMode ] <<= nHSplitMode;
561 aPropMap[ PROP_VerticalSplitMode ] <<= nVSplitMode;
562 aPropMap[ PROP_HorizontalSplitPositionTwips ] <<= nHSplitPos;
563 aPropMap[ PROP_VerticalSplitPositionTwips ] <<= nVSplitPos;
564 aPropMap[ PROP_ActiveSplitRange ] <<= nActivePane;
565 aPropMap[ PROP_PositionLeft ] <<= aFirstPos.Column;
566 aPropMap[ PROP_PositionTop ] <<= aFirstPos.Row;
567 aPropMap[ PROP_PositionRight ] <<= xModel->maSecondPos.Column;
568 aPropMap[ PROP_PositionBottom ] <<= ((nVSplitPos > 0) ? xModel->maSecondPos.Row : xModel->maFirstPos.Row);
569 aPropMap[ PROP_ZoomType ] <<= API_ZOOMTYPE_PERCENT;
570 aPropMap[ PROP_ZoomValue ] <<= static_cast< sal_Int16 >( xModel->getNormalZoom() );
571 aPropMap[ PROP_PageViewZoomValue ] <<= static_cast< sal_Int16 >( xModel->getPageBreakZoom() );
572 aPropMap[ PROP_GridColor ] <<= xModel->getGridColor( getBaseFilter() );
573 aPropMap[ PROP_ShowPageBreakPreview ] <<= xModel->isPageBreakPreview();
574 aPropMap[ PROP_ShowFormulas ] <<= xModel->mbShowFormulas;
575 aPropMap[ PROP_ShowGrid ] <<= xModel->mbShowGrid;
576 aPropMap[ PROP_HasColumnRowHeaders ] <<= xModel->mbShowHeadings;
577 aPropMap[ PROP_ShowZeroValues ] <<= xModel->mbShowZeros;
578 aPropMap[ PROP_IsOutlineSymbolsSet ] <<= xModel->mbShowOutline;
579
580 // store sheet view settings in global view settings object
581 getViewSettings().setSheetViewSettings( getSheetIndex(), xModel, Any( aPropMap.makePropertyValueSequence() ) );
582 }
583
isSheetRightToLeft() const584 bool SheetViewSettings::isSheetRightToLeft() const
585 {
586 return !maSheetViews.empty() && maSheetViews.front()->mbRightToLeft;
587 }
588
589 // private --------------------------------------------------------------------
590
createSheetView()591 SheetViewModelRef SheetViewSettings::createSheetView()
592 {
593 SheetViewModelRef xModel( new SheetViewModel );
594 maSheetViews.push_back( xModel );
595 return xModel;
596 }
597
598 // ============================================================================
599
WorkbookViewModel()600 WorkbookViewModel::WorkbookViewModel() :
601 mnWinX( 0 ),
602 mnWinY( 0 ),
603 mnWinWidth( 0 ),
604 mnWinHeight( 0 ),
605 mnActiveSheet( 0 ),
606 mnFirstVisSheet( 0 ),
607 mnTabBarWidth( OOX_BOOKVIEW_TABBARRATIO_DEF ),
608 mnVisibility( XML_visible ),
609 mbShowTabBar( true ),
610 mbShowHorScroll( true ),
611 mbShowVerScroll( true ),
612 mbMinimized( false )
613 {
614 }
615
616 // ----------------------------------------------------------------------------
617
ViewSettings(const WorkbookHelper & rHelper)618 ViewSettings::ViewSettings( const WorkbookHelper& rHelper ) :
619 WorkbookHelper( rHelper ),
620 mbValidOleSize( false )
621 {
622 }
623
importWorkbookView(const AttributeList & rAttribs)624 void ViewSettings::importWorkbookView( const AttributeList& rAttribs )
625 {
626 WorkbookViewModel& rModel = createWorkbookView();
627 rModel.mnWinX = rAttribs.getInteger( XML_xWindow, 0 );
628 rModel.mnWinY = rAttribs.getInteger( XML_yWindow, 0 );
629 rModel.mnWinWidth = rAttribs.getInteger( XML_windowWidth, 0 );
630 rModel.mnWinHeight = rAttribs.getInteger( XML_windowHeight, 0 );
631 rModel.mnActiveSheet = rAttribs.getInteger( XML_activeTab, 0 );
632 rModel.mnFirstVisSheet = rAttribs.getInteger( XML_firstSheet, 0 );
633 rModel.mnTabBarWidth = rAttribs.getInteger( XML_tabRatio, 600 );
634 rModel.mnVisibility = rAttribs.getToken( XML_visibility, XML_visible );
635 rModel.mbShowTabBar = rAttribs.getBool( XML_showSheetTabs, true );
636 rModel.mbShowHorScroll = rAttribs.getBool( XML_showHorizontalScroll, true );
637 rModel.mbShowVerScroll = rAttribs.getBool( XML_showVerticalScroll, true );
638 rModel.mbMinimized = rAttribs.getBool( XML_minimized, false );
639 }
640
importOleSize(const AttributeList & rAttribs)641 void ViewSettings::importOleSize( const AttributeList& rAttribs )
642 {
643 OUString aRange = rAttribs.getString( XML_ref, OUString() );
644 mbValidOleSize = getAddressConverter().convertToCellRange( maOleSize, aRange, 0, true, false );
645 }
646
importWorkbookView(SequenceInputStream & rStrm)647 void ViewSettings::importWorkbookView( SequenceInputStream& rStrm )
648 {
649 WorkbookViewModel& rModel = createWorkbookView();
650 sal_uInt8 nFlags;
651 rStrm >> rModel.mnWinX >> rModel.mnWinY >> rModel.mnWinWidth >> rModel.mnWinHeight >> rModel.mnTabBarWidth >> rModel.mnFirstVisSheet >> rModel.mnActiveSheet >> nFlags;
652 rModel.mnVisibility = getFlagValue( nFlags, BIFF12_WBVIEW_HIDDEN, XML_hidden, XML_visible );
653 rModel.mbShowTabBar = getFlag( nFlags, BIFF12_WBVIEW_SHOWTABBAR );
654 rModel.mbShowHorScroll = getFlag( nFlags, BIFF12_WBVIEW_SHOWHORSCROLL );
655 rModel.mbShowVerScroll = getFlag( nFlags, BIFF12_WBVIEW_SHOWVERSCROLL );
656 rModel.mbMinimized = getFlag( nFlags, BIFF12_WBVIEW_MINIMIZED );
657 }
658
importOleSize(SequenceInputStream & rStrm)659 void ViewSettings::importOleSize( SequenceInputStream& rStrm )
660 {
661 BinRange aBinRange;
662 rStrm >> aBinRange;
663 mbValidOleSize = getAddressConverter().convertToCellRange( maOleSize, aBinRange, 0, true, false );
664 }
665
importWindow1(BiffInputStream & rStrm)666 void ViewSettings::importWindow1( BiffInputStream& rStrm )
667 {
668 sal_uInt16 nWinX, nWinY, nWinWidth, nWinHeight;
669 rStrm >> nWinX >> nWinY >> nWinWidth >> nWinHeight;
670
671 // WINDOW1 record occures in every sheet in BIFF4W
672 OSL_ENSURE( maBookViews.empty() || ((getBiff() == BIFF4) && isWorkbookFile()),
673 "ViewSettings::importWindow1 - multiple WINDOW1 records" );
674 WorkbookViewModel& rModel = createWorkbookView();
675 rModel.mnWinX = nWinX;
676 rModel.mnWinY = nWinY;
677 rModel.mnWinWidth = nWinWidth;
678 rModel.mnWinHeight = nWinHeight;
679
680 if( getBiff() <= BIFF4 )
681 {
682 sal_uInt8 nHidden;
683 rStrm >> nHidden;
684 rModel.mnVisibility = (nHidden == 0) ? XML_visible : XML_hidden;
685 }
686 else
687 {
688 sal_uInt16 nFlags, nActiveTab, nFirstVisTab, nSelectCnt, nTabBarWidth;
689 rStrm >> nFlags >> nActiveTab >> nFirstVisTab >> nSelectCnt >> nTabBarWidth;
690
691 rModel.mnActiveSheet = nActiveTab;
692 rModel.mnFirstVisSheet = nFirstVisTab;
693 rModel.mnTabBarWidth = nTabBarWidth;
694 rModel.mnVisibility = getFlagValue( nFlags, BIFF_WINDOW1_HIDDEN, XML_hidden, XML_visible );
695 rModel.mbMinimized = getFlag( nFlags, BIFF_WINDOW1_MINIMIZED );
696 rModel.mbShowHorScroll = getFlag( nFlags, BIFF_WINDOW1_SHOWHORSCROLL );
697 rModel.mbShowVerScroll = getFlag( nFlags, BIFF_WINDOW1_SHOWVERSCROLL );
698 rModel.mbShowTabBar = getFlag( nFlags, BIFF_WINDOW1_SHOWTABBAR );
699 }
700 }
701
importOleSize(BiffInputStream & rStrm)702 void ViewSettings::importOleSize( BiffInputStream& rStrm )
703 {
704 rStrm.skip( 2 );
705 BinRange aBinRange;
706 aBinRange.read( rStrm, false );
707 mbValidOleSize = getAddressConverter().convertToCellRange( maOleSize, aBinRange, 0, true, false );
708 }
709
setSheetViewSettings(sal_Int16 nSheet,const SheetViewModelRef & rxSheetView,const Any & rProperties)710 void ViewSettings::setSheetViewSettings( sal_Int16 nSheet, const SheetViewModelRef& rxSheetView, const Any& rProperties )
711 {
712 maSheetViews[ nSheet ] = rxSheetView;
713 maSheetProps[ nSheet ] = rProperties;
714 }
715
setSheetUsedArea(const CellRangeAddress & rUsedArea)716 void ViewSettings::setSheetUsedArea( const CellRangeAddress& rUsedArea )
717 {
718 maSheetUsedAreas[ rUsedArea.Sheet ] = rUsedArea;
719 }
720
finalizeImport()721 void ViewSettings::finalizeImport()
722 {
723 const WorksheetBuffer& rWorksheets = getWorksheets();
724 if( rWorksheets.getWorksheetCount() <= 0 ) return;
725
726 // force creation of workbook view model to get the Excel defaults
727 const WorkbookViewModel& rModel = maBookViews.empty() ? createWorkbookView() : *maBookViews.front();
728
729 // show object mode is part of workbook settings
730 sal_Int16 nShowMode = getWorkbookSettings().getApiShowObjectMode();
731
732 // view settings for all sheets
733 Reference< XNameContainer > xSheetsNC = ContainerHelper::createNameContainer( getBaseFilter().getComponentContext() );
734 if( !xSheetsNC.is() ) return;
735 for( SheetPropertiesMap::const_iterator aIt = maSheetProps.begin(), aEnd = maSheetProps.end(); aIt != aEnd; ++aIt )
736 ContainerHelper::insertByName( xSheetsNC, rWorksheets.getCalcSheetName( aIt->first ), aIt->second );
737
738 // use active sheet to set sheet properties that are document-global in Calc
739 sal_Int16 nActiveSheet = getActiveCalcSheet();
740 SheetViewModelRef& rxActiveSheetView = maSheetViews[ nActiveSheet ];
741 OSL_ENSURE( rxActiveSheetView.get(), "ViewSettings::finalizeImport - missing active sheet view settings" );
742 if( !rxActiveSheetView )
743 rxActiveSheetView.reset( new SheetViewModel );
744
745 Reference< XIndexContainer > xContainer = ContainerHelper::createIndexContainer( getBaseFilter().getComponentContext() );
746 if( xContainer.is() ) try
747 {
748 PropertyMap aPropMap;
749 aPropMap[ PROP_Tables ] <<= xSheetsNC;
750 aPropMap[ PROP_ActiveTable ] <<= rWorksheets.getCalcSheetName( nActiveSheet );
751 aPropMap[ PROP_HasHorizontalScrollBar ] <<= rModel.mbShowHorScroll;
752 aPropMap[ PROP_HasVerticalScrollBar ] <<= rModel.mbShowVerScroll;
753 aPropMap[ PROP_HasSheetTabs ] <<= rModel.mbShowTabBar;
754 aPropMap[ PROP_RelativeHorizontalTabbarWidth ] <<= double( rModel.mnTabBarWidth / 1000.0 );
755 aPropMap[ PROP_ShowObjects ] <<= nShowMode;
756 aPropMap[ PROP_ShowCharts ] <<= nShowMode;
757 aPropMap[ PROP_ShowDrawing ] <<= nShowMode;
758 aPropMap[ PROP_GridColor ] <<= rxActiveSheetView->getGridColor( getBaseFilter() );
759 aPropMap[ PROP_ShowPageBreakPreview ] <<= rxActiveSheetView->isPageBreakPreview();
760 aPropMap[ PROP_ShowFormulas ] <<= rxActiveSheetView->mbShowFormulas;
761 aPropMap[ PROP_ShowGrid ] <<= rxActiveSheetView->mbShowGrid;
762 aPropMap[ PROP_HasColumnRowHeaders ] <<= rxActiveSheetView->mbShowHeadings;
763 aPropMap[ PROP_ShowZeroValues ] <<= rxActiveSheetView->mbShowZeros;
764 aPropMap[ PROP_IsOutlineSymbolsSet ] <<= rxActiveSheetView->mbShowOutline;
765
766 xContainer->insertByIndex( 0, Any( aPropMap.makePropertyValueSequence() ) );
767 Reference< XIndexAccess > xIAccess( xContainer, UNO_QUERY_THROW );
768 Reference< XViewDataSupplier > xViewDataSuppl( getDocument(), UNO_QUERY_THROW );
769 xViewDataSuppl->setViewData( xIAccess );
770 }
771 catch( Exception& )
772 {
773 OSL_ENSURE( false, "ViewSettings::finalizeImport - cannot create document view settings" );
774 }
775
776 /* Set visible area to be used if this document is an embedded OLE object.
777 #i44077# If a new OLE object is inserted from file, there is no OLESIZE
778 record in the Excel file. In this case, use the used area calculated
779 from file contents (used cells and drawing objects). */
780 maOleSize.Sheet = nActiveSheet;
781 const CellRangeAddress* pVisibleArea = mbValidOleSize ?
782 &maOleSize : ContainerHelper::getMapElement( maSheetUsedAreas, nActiveSheet );
783 if( pVisibleArea )
784 {
785 // calculate the visible area in units of 1/100 mm
786 PropertySet aRangeProp( getCellRangeFromDoc( *pVisibleArea ) );
787 Point aPos;
788 Size aSize;
789 if( aRangeProp.getProperty( aPos, PROP_Position ) && aRangeProp.getProperty( aSize, PROP_Size ) )
790 {
791 // set the visible area as sequence of long at the media descriptor
792 Sequence< sal_Int32 > aWinExtent( 4 );
793 aWinExtent[ 0 ] = aPos.X;
794 aWinExtent[ 1 ] = aPos.Y;
795 aWinExtent[ 2 ] = aPos.X + aSize.Width;
796 aWinExtent[ 3 ] = aPos.Y + aSize.Height;
797 getBaseFilter().getMediaDescriptor()[ CREATE_OUSTRING( "WinExtent" ) ] <<= aWinExtent;
798 }
799 }
800 }
801
getActiveCalcSheet() const802 sal_Int16 ViewSettings::getActiveCalcSheet() const
803 {
804 return maBookViews.empty() ? 0 : ::std::max< sal_Int16 >( getWorksheets().getCalcSheetIndex( maBookViews.front()->mnActiveSheet ), 0 );
805 }
806
807 // private --------------------------------------------------------------------
808
createWorkbookView()809 WorkbookViewModel& ViewSettings::createWorkbookView()
810 {
811 WorkbookViewModelRef xModel( new WorkbookViewModel );
812 maBookViews.push_back( xModel );
813 return *xModel;
814 }
815
816 // ============================================================================
817
818 } // namespace xls
819 } // namespace oox
820