Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include "drawingml/table/tablecell.hxx"
21 : #include "drawingml/table/tableproperties.hxx"
22 : #include <basegfx/color/bcolor.hxx>
23 : #include "oox/drawingml/shapepropertymap.hxx"
24 : #include "drawingml/textbody.hxx"
25 : #include "oox/drawingml/theme.hxx"
26 : #include "oox/core/xmlfilterbase.hxx"
27 : #include "oox/helper/propertyset.hxx"
28 : #include <tools/color.hxx>
29 : #include <com/sun/star/container/XNameContainer.hpp>
30 : #include <com/sun/star/beans/XMultiPropertySet.hpp>
31 : #include <com/sun/star/table/XTable.hpp>
32 : #include <com/sun/star/table/XMergeableCellRange.hpp>
33 : #include <com/sun/star/table/BorderLine2.hpp>
34 : #include <com/sun/star/drawing/LineStyle.hpp>
35 : #include <com/sun/star/drawing/TextVerticalAdjust.hpp>
36 : #include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
37 : #include <com/sun/star/text/XText.hpp>
38 : #include <com/sun/star/text/WritingMode.hpp>
39 :
40 : using namespace ::oox::core;
41 : using namespace ::com::sun::star;
42 : using namespace ::com::sun::star::uno;
43 : using namespace ::com::sun::star::beans;
44 : using ::com::sun::star::table::BorderLine2;
45 :
46 : namespace oox { namespace drawingml { namespace table {
47 :
48 77 : TableCell::TableCell()
49 0 : : mpTextBody( new TextBody() )
50 : , mnRowSpan ( 1 )
51 : , mnGridSpan( 1 )
52 : , mbhMerge( false )
53 : , mbvMerge( false )
54 : , mnMarL( 91440 )
55 : , mnMarR( 91440 )
56 : , mnMarT( 45720 )
57 : , mnMarB( 45720 )
58 : , mnVertToken( XML_horz )
59 : , mnAnchorToken( XML_t )
60 : , mbAnchorCtr( false )
61 77 : , mnHorzOverflowToken( XML_clip )
62 : {
63 77 : }
64 196 : TableCell::~TableCell()
65 : {
66 196 : }
67 :
68 462 : void applyLineAttributes( const ::oox::core::XmlFilterBase& rFilterBase,
69 : Reference< XPropertySet >& rxPropSet, oox::drawingml::LineProperties& rLineProperties,
70 : sal_Int32 nPropId )
71 : {
72 462 : BorderLine2 aBorderLine;
73 462 : if( rLineProperties.maLineFill.moFillType.differsFrom( XML_noFill ))
74 : {
75 287 : Color aColor = rLineProperties.maLineFill.getBestSolidColor();
76 287 : aBorderLine.Color = aColor.getColor( rFilterBase.getGraphicHelper() );
77 287 : aBorderLine.OuterLineWidth = static_cast< sal_Int16 >( GetCoordinate( rLineProperties.moLineWidth.get( 0 ) ) / 4 );
78 287 : aBorderLine.InnerLineWidth = static_cast< sal_Int16 >( GetCoordinate( rLineProperties.moLineWidth.get( 0 ) ) / 4 );
79 287 : aBorderLine.LineWidth = static_cast< sal_Int16 >( GetCoordinate( rLineProperties.moLineWidth.get( 0 ) ) / 2 );
80 287 : aBorderLine.LineDistance = 0;
81 : }
82 175 : else if ( rLineProperties.moLineWidth.get(0)!=0 )
83 : {
84 : // Default color of Line is black.
85 5 : rLineProperties.maLineFill.maFillColor.setSrgbClr( 0 );
86 5 : aBorderLine.OuterLineWidth = static_cast< sal_Int16 >( GetCoordinate( rLineProperties.moLineWidth.get( 0 ) ) / 4 );
87 5 : aBorderLine.InnerLineWidth = static_cast< sal_Int16 >( GetCoordinate( rLineProperties.moLineWidth.get( 0 ) ) / 4 );
88 5 : aBorderLine.LineWidth = static_cast< sal_Int16 >( GetCoordinate( rLineProperties.moLineWidth.get( 0 ) ) / 2 );
89 5 : aBorderLine.LineDistance = 0;
90 : }
91 :
92 462 : PropertySet aPropSet( rxPropSet );
93 462 : aPropSet.setProperty( nPropId, aBorderLine );
94 462 : }
95 :
96 1140 : void applyBorder( const ::oox::core::XmlFilterBase& rFilterBase, TableStylePart& rTableStylePart, sal_Int32 nLineType, oox::drawingml::LineProperties& rLineProperties )
97 : {
98 1140 : std::map < sal_Int32, ::oox::drawingml::LinePropertiesPtr >& rPartLineBorders( rTableStylePart.getLineBorders() );
99 1140 : ::oox::drawingml::ShapeStyleRef& rLineStyleRef = rTableStylePart.getStyleRefs()[ nLineType ];
100 1140 : std::map < sal_Int32, ::oox::drawingml::LinePropertiesPtr >::const_iterator aIter( rPartLineBorders.find( nLineType ) );
101 1140 : if ( ( aIter != rPartLineBorders.end() ) && aIter->second.get() )
102 169 : rLineProperties.assignUsed( *aIter->second );
103 971 : else if (rLineStyleRef.mnThemedIdx != 0)
104 : {
105 110 : if (const Theme* pTheme = rFilterBase.getCurrentTheme())
106 : {
107 110 : rLineProperties.assignUsed( *pTheme->getLineStyle(rLineStyleRef.mnThemedIdx) );
108 110 : sal_Int32 nPhClr = rLineStyleRef.maPhClr.getColor( rFilterBase.getGraphicHelper() );
109 110 : rLineProperties.maLineFill.maFillColor.setSrgbClr( nPhClr );
110 : }
111 : }
112 1140 : }
113 :
114 190 : void applyTableStylePart( const ::oox::core::XmlFilterBase& rFilterBase,
115 : oox::drawingml::FillProperties& rFillProperties,
116 : TextCharacterProperties& aTextCharProps,
117 : oox::drawingml::LineProperties& rLeftBorder,
118 : oox::drawingml::LineProperties& rRightBorder,
119 : oox::drawingml::LineProperties& rTopBorder,
120 : oox::drawingml::LineProperties& rBottomBorder,
121 : oox::drawingml::LineProperties& rTopLeftToBottomRightBorder,
122 : oox::drawingml::LineProperties& rBottomLeftToTopRightBorder,
123 : TableStylePart& rTableStylePart )
124 : {
125 190 : ::oox::drawingml::FillPropertiesPtr& rPartFillPropertiesPtr( rTableStylePart.getFillProperties() );
126 190 : if ( rPartFillPropertiesPtr.get() )
127 109 : rFillProperties.assignUsed( *rPartFillPropertiesPtr );
128 : else
129 : {
130 81 : ::oox::drawingml::ShapeStyleRef& rFillStyleRef = rTableStylePart.getStyleRefs()[ XML_fillRef ];
131 81 : const Theme* pTheme = rFilterBase.getCurrentTheme();
132 81 : if (pTheme && rFillStyleRef.mnThemedIdx != 0 )
133 : {
134 1 : rFillProperties.assignUsed( *pTheme->getFillStyle( rFillStyleRef.mnThemedIdx ) );
135 1 : sal_Int32 nPhClr = rFillStyleRef.maPhClr.getColor( rFilterBase.getGraphicHelper() );
136 1 : rFillProperties.maFillColor.setSrgbClr( nPhClr );
137 : }
138 : }
139 :
140 190 : applyBorder( rFilterBase, rTableStylePart, XML_left, rLeftBorder );
141 190 : applyBorder( rFilterBase, rTableStylePart, XML_right, rRightBorder );
142 190 : applyBorder( rFilterBase, rTableStylePart, XML_top, rTopBorder );
143 190 : applyBorder( rFilterBase, rTableStylePart, XML_bottom, rBottomBorder );
144 190 : applyBorder( rFilterBase, rTableStylePart, XML_tl2br, rTopLeftToBottomRightBorder );
145 190 : applyBorder( rFilterBase, rTableStylePart, XML_tr2bl, rBottomLeftToTopRightBorder );
146 :
147 190 : aTextCharProps.maLatinFont = rTableStylePart.getLatinFont();
148 190 : aTextCharProps.maAsianFont = rTableStylePart.getAsianFont();
149 190 : aTextCharProps.maComplexFont = rTableStylePart.getComplexFont();
150 190 : aTextCharProps.maSymbolFont = rTableStylePart.getSymbolFont();
151 190 : if (rTableStylePart.getTextColor().isUsed())
152 88 : aTextCharProps.maCharColor = rTableStylePart.getTextColor();
153 190 : if( rTableStylePart.getTextBoldStyle().is_initialized() )
154 23 : aTextCharProps.moBold = *rTableStylePart.getTextBoldStyle();
155 190 : if( rTableStylePart.getTextItalicStyle().is_initialized() )
156 0 : aTextCharProps.moItalic = *rTableStylePart.getTextItalicStyle();
157 190 : }
158 :
159 77 : void applyTableCellProperties( const Reference < ::com::sun::star::table::XCell >& rxCell, const TableCell& rTableCell )
160 : {
161 77 : Reference< XPropertySet > xPropSet( rxCell, UNO_QUERY_THROW );
162 77 : xPropSet->setPropertyValue( "TextUpperDistance", Any( static_cast< sal_Int32 >( rTableCell.getTopMargin() / 360 ) ) );
163 77 : xPropSet->setPropertyValue( "TextRightDistance", Any( static_cast< sal_Int32 >( rTableCell.getRightMargin() / 360 ) ) );
164 77 : xPropSet->setPropertyValue( "TextLeftDistance", Any( static_cast< sal_Int32 >( rTableCell.getLeftMargin() / 360 ) ) );
165 77 : xPropSet->setPropertyValue( "TextLowerDistance", Any( static_cast< sal_Int32 >( rTableCell.getBottomMargin() / 360 ) ) );
166 :
167 : drawing::TextVerticalAdjust eVA;
168 77 : switch( rTableCell.getAnchorToken() )
169 : {
170 3 : case XML_ctr: eVA = drawing::TextVerticalAdjust_CENTER; break;
171 0 : case XML_b: eVA = drawing::TextVerticalAdjust_BOTTOM; break;
172 : case XML_just:
173 : case XML_dist:
174 : default:
175 74 : case XML_t: eVA = drawing::TextVerticalAdjust_TOP; break;
176 : }
177 77 : xPropSet->setPropertyValue( "TextVerticalAdjust", Any( eVA ) );
178 77 : }
179 :
180 77 : void TableCell::pushToXCell( const ::oox::core::XmlFilterBase& rFilterBase, ::oox::drawingml::TextListStylePtr pMasterTextListStyle,
181 : const ::com::sun::star::uno::Reference < ::com::sun::star::table::XCell >& rxCell, const TableProperties& rTableProperties,
182 : const TableStyle& rTableStyle, sal_Int32 nColumn, sal_Int32 nMaxColumn, sal_Int32 nRow, sal_Int32 nMaxRow )
183 : {
184 77 : TableStyle& rTable( const_cast< TableStyle& >( rTableStyle ) );
185 77 : TableProperties& rProperties( const_cast< TableProperties& >( rTableProperties ) );
186 :
187 77 : Reference< text::XText > xText( rxCell, UNO_QUERY_THROW );
188 154 : Reference< text::XTextCursor > xAt = xText->createTextCursor();
189 :
190 77 : applyTableCellProperties( rxCell, *this );
191 154 : TextCharacterProperties aTextStyleProps;
192 77 : xAt->gotoStart( sal_True );
193 154 : Reference< text::XTextRange > xStart( xAt, UNO_QUERY );
194 77 : xAt->gotoEnd( sal_True );
195 :
196 154 : Reference< XPropertySet > xPropSet( rxCell, UNO_QUERY_THROW );
197 154 : oox::drawingml::FillProperties aFillProperties;
198 154 : oox::drawingml::LineProperties aLinePropertiesLeft;
199 154 : oox::drawingml::LineProperties aLinePropertiesRight;
200 154 : oox::drawingml::LineProperties aLinePropertiesTop;
201 154 : oox::drawingml::LineProperties aLinePropertiesBottom;
202 154 : oox::drawingml::LineProperties aLinePropertiesTopLeftToBottomRight;
203 154 : oox::drawingml::LineProperties aLinePropertiesBottomLeftToTopRight;
204 :
205 : applyTableStylePart( rFilterBase, aFillProperties, aTextStyleProps,
206 : aLinePropertiesLeft,
207 : aLinePropertiesRight,
208 : aLinePropertiesTop,
209 : aLinePropertiesBottom,
210 : aLinePropertiesTopLeftToBottomRight,
211 : aLinePropertiesBottomLeftToTopRight,
212 77 : rTable.getWholeTbl() );
213 :
214 77 : if ( rProperties.isFirstRow() && ( nRow == 0 ) )
215 : {
216 : applyTableStylePart( rFilterBase, aFillProperties, aTextStyleProps,
217 : aLinePropertiesLeft,
218 : aLinePropertiesRight,
219 : aLinePropertiesTop,
220 : aLinePropertiesBottom,
221 : aLinePropertiesTopLeftToBottomRight,
222 : aLinePropertiesBottomLeftToTopRight,
223 9 : rTable.getFirstRow() );
224 : }
225 77 : if ( rProperties.isLastRow() && ( nRow == nMaxRow ) )
226 : {
227 : applyTableStylePart( rFilterBase, aFillProperties, aTextStyleProps,
228 : aLinePropertiesLeft,
229 : aLinePropertiesRight,
230 : aLinePropertiesTop,
231 : aLinePropertiesBottom,
232 : aLinePropertiesTopLeftToBottomRight,
233 : aLinePropertiesBottomLeftToTopRight,
234 4 : rTable.getLastRow() );
235 : }
236 77 : if ( rProperties.isFirstCol() && ( nColumn == 0 ) )
237 : {
238 : applyTableStylePart( rFilterBase, aFillProperties, aTextStyleProps,
239 : aLinePropertiesLeft,
240 : aLinePropertiesRight,
241 : aLinePropertiesTop,
242 : aLinePropertiesBottom,
243 : aLinePropertiesTopLeftToBottomRight,
244 : aLinePropertiesBottomLeftToTopRight,
245 5 : rTable.getFirstCol() );
246 : }
247 77 : if ( rProperties.isLastCol() && ( nColumn == nMaxColumn ) )
248 : {
249 : applyTableStylePart( rFilterBase, aFillProperties, aTextStyleProps,
250 : aLinePropertiesLeft,
251 : aLinePropertiesRight,
252 : aLinePropertiesTop,
253 : aLinePropertiesBottom,
254 : aLinePropertiesTopLeftToBottomRight,
255 : aLinePropertiesBottomLeftToTopRight,
256 5 : rTable.getLastCol() );
257 : }
258 77 : if ( rProperties.isBandRow() )
259 : {
260 211 : if ( ( !rProperties.isFirstRow() || ( nRow != 0 ) ) &&
261 116 : ( !rProperties.isLastRow() || ( nRow != nMaxRow ) ) &&
262 208 : ( !rProperties.isFirstCol() || ( nColumn != 0 ) ) &&
263 54 : ( !rProperties.isLastCol() || ( nColumn != nMaxColumn ) ) )
264 : {
265 42 : sal_Int32 nBand = nRow;
266 42 : if ( rProperties.isFirstRow() )
267 18 : nBand++;
268 42 : if ( nBand & 1 )
269 : {
270 : applyTableStylePart( rFilterBase, aFillProperties, aTextStyleProps,
271 : aLinePropertiesLeft,
272 : aLinePropertiesRight,
273 : aLinePropertiesTop,
274 : aLinePropertiesBottom,
275 : aLinePropertiesTopLeftToBottomRight,
276 : aLinePropertiesBottomLeftToTopRight,
277 19 : rTable.getBand2H() );
278 : }
279 : else
280 : {
281 : applyTableStylePart( rFilterBase, aFillProperties, aTextStyleProps,
282 : aLinePropertiesLeft,
283 : aLinePropertiesRight,
284 : aLinePropertiesTop,
285 : aLinePropertiesBottom,
286 : aLinePropertiesTopLeftToBottomRight,
287 : aLinePropertiesBottomLeftToTopRight,
288 23 : rTable.getBand1H() );
289 : }
290 : }
291 : }
292 77 : if ( ( nRow == 0 ) && ( nColumn == 0 ) )
293 : {
294 : applyTableStylePart( rFilterBase, aFillProperties, aTextStyleProps,
295 : aLinePropertiesLeft,
296 : aLinePropertiesRight,
297 : aLinePropertiesTop,
298 : aLinePropertiesBottom,
299 : aLinePropertiesTopLeftToBottomRight,
300 : aLinePropertiesBottomLeftToTopRight,
301 12 : rTable.getNwCell() );
302 : }
303 77 : if ( ( nRow == nMaxRow ) && ( nColumn == 0 ) )
304 : {
305 : applyTableStylePart( rFilterBase, aFillProperties, aTextStyleProps,
306 : aLinePropertiesLeft,
307 : aLinePropertiesRight,
308 : aLinePropertiesTop,
309 : aLinePropertiesBottom,
310 : aLinePropertiesTopLeftToBottomRight,
311 : aLinePropertiesBottomLeftToTopRight,
312 12 : rTable.getSwCell() );
313 : }
314 77 : if ( ( nRow == 0 ) && ( nColumn == nMaxColumn ) )
315 : {
316 : applyTableStylePart( rFilterBase, aFillProperties, aTextStyleProps,
317 : aLinePropertiesLeft,
318 : aLinePropertiesRight,
319 : aLinePropertiesTop,
320 : aLinePropertiesBottom,
321 : aLinePropertiesTopLeftToBottomRight,
322 : aLinePropertiesBottomLeftToTopRight,
323 12 : rTable.getNeCell() );
324 : }
325 77 : if ( ( nRow == nMaxRow ) && ( nColumn == nMaxColumn ) )
326 : {
327 : applyTableStylePart( rFilterBase, aFillProperties, aTextStyleProps,
328 : aLinePropertiesLeft,
329 : aLinePropertiesRight,
330 : aLinePropertiesTop,
331 : aLinePropertiesBottom,
332 : aLinePropertiesTopLeftToBottomRight,
333 : aLinePropertiesBottomLeftToTopRight,
334 12 : rTable.getSeCell() );
335 : }
336 77 : if ( rProperties.isBandCol() )
337 : {
338 0 : if ( ( !rProperties.isFirstRow() || ( nRow != 0 ) ) &&
339 0 : ( !rProperties.isLastRow() || ( nRow != nMaxRow ) ) &&
340 0 : ( !rProperties.isFirstCol() || ( nColumn != 0 ) ) &&
341 0 : ( !rProperties.isLastCol() || ( nColumn != nMaxColumn ) ) )
342 : {
343 0 : sal_Int32 nBand = nColumn;
344 0 : if ( rProperties.isFirstCol() )
345 0 : nBand++;
346 0 : if ( nBand & 1 )
347 : {
348 : applyTableStylePart( rFilterBase, aFillProperties, aTextStyleProps,
349 : aLinePropertiesLeft,
350 : aLinePropertiesRight,
351 : aLinePropertiesTop,
352 : aLinePropertiesBottom,
353 : aLinePropertiesTopLeftToBottomRight,
354 : aLinePropertiesBottomLeftToTopRight,
355 0 : rTable.getBand2V() );
356 : }
357 : else
358 : {
359 : applyTableStylePart( rFilterBase, aFillProperties, aTextStyleProps,
360 : aLinePropertiesLeft,
361 : aLinePropertiesRight,
362 : aLinePropertiesTop,
363 : aLinePropertiesBottom,
364 : aLinePropertiesTopLeftToBottomRight,
365 : aLinePropertiesBottomLeftToTopRight,
366 0 : rTable.getBand1V() );
367 : }
368 : }
369 : }
370 77 : aLinePropertiesLeft.assignUsed( maLinePropertiesLeft );
371 77 : aLinePropertiesRight.assignUsed( maLinePropertiesRight );
372 77 : aLinePropertiesTop.assignUsed( maLinePropertiesTop );
373 77 : aLinePropertiesBottom.assignUsed( maLinePropertiesBottom );
374 77 : aLinePropertiesTopLeftToBottomRight.assignUsed( maLinePropertiesTopLeftToBottomRight );
375 77 : aLinePropertiesBottomLeftToTopRight.assignUsed( maLinePropertiesBottomLeftToTopRight );
376 77 : applyLineAttributes( rFilterBase, xPropSet, aLinePropertiesLeft, PROP_LeftBorder );
377 77 : applyLineAttributes( rFilterBase, xPropSet, aLinePropertiesRight, PROP_RightBorder );
378 77 : applyLineAttributes( rFilterBase, xPropSet, aLinePropertiesTop, PROP_TopBorder );
379 77 : applyLineAttributes( rFilterBase, xPropSet, aLinePropertiesBottom, PROP_BottomBorder );
380 77 : applyLineAttributes( rFilterBase, xPropSet, aLinePropertiesTopLeftToBottomRight, PROP_DiagonalTLBR );
381 77 : applyLineAttributes( rFilterBase, xPropSet, aLinePropertiesBottomLeftToTopRight, PROP_DiagonalBLTR );
382 :
383 77 : aFillProperties.assignUsed( maFillProperties );
384 154 : ShapePropertyMap aPropMap( rFilterBase.getModelObjectHelper() );
385 :
386 154 : Color aBgColor;
387 77 : sal_Int32 nPhClr = API_RGB_TRANSPARENT;
388 77 : std::shared_ptr< ::oox::drawingml::FillProperties >& rBackgroundFillPropertiesPtr( rTable.getBackgroundFillProperties() );
389 77 : ::oox::drawingml::ShapeStyleRef& rBackgroundFillStyle( rTable.getBackgroundFillStyleRef() );
390 77 : if (rBackgroundFillPropertiesPtr.get())
391 0 : aBgColor = rBackgroundFillPropertiesPtr->getBestSolidColor();
392 77 : else if (rBackgroundFillStyle.mnThemedIdx != 0)
393 : {
394 24 : if (const Theme* pTheme = rFilterBase.getCurrentTheme())
395 : {
396 24 : aBgColor = pTheme->getFillStyle(rBackgroundFillStyle.mnThemedIdx)->getBestSolidColor();
397 24 : nPhClr = rBackgroundFillStyle.maPhClr.getColor(rFilterBase.getGraphicHelper());
398 : }
399 : }
400 77 : if (aBgColor.isUsed())
401 : {
402 24 : const Color& rCellColor = aFillProperties.getBestSolidColor();
403 24 : const double fTransparency = rCellColor.isUsed() ? 0.01 * rCellColor.getTransparency() : 1.0;
404 24 : ::Color nBgColor( aBgColor.getColor(rFilterBase.getGraphicHelper(), nPhClr) );
405 24 : ::Color nCellColor( rCellColor.getColor(rFilterBase.getGraphicHelper()) );
406 24 : ::Color aResult( basegfx::interpolate(nBgColor.getBColor(), nCellColor.getBColor(), 1.0 - fTransparency) );
407 24 : aFillProperties.maFillColor.clearTransformations();
408 24 : aFillProperties.maFillColor.setSrgbClr(aResult.GetRGBColor());
409 24 : aFillProperties.moFillType.set(XML_solidFill);
410 : }
411 :
412 : // TODO: phClr?
413 77 : aFillProperties.pushToPropMap( aPropMap, rFilterBase.getGraphicHelper() );
414 77 : PropertySet( xPropSet ).setProperties( aPropMap );
415 :
416 77 : if ( getVertToken() == XML_eaVert )
417 : {
418 0 : xPropSet->setPropertyValue("TextWritingMode", Any(com::sun::star::text::WritingMode_TB_RL));
419 : }
420 :
421 154 : getTextBody()->insertAt( rFilterBase, xText, xAt, aTextStyleProps, pMasterTextListStyle );
422 77 : }
423 :
424 246 : } } }
425 :
426 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|