xref: /core/sc/source/ui/vba/vbaborders.cxx (revision a73494cf)
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 #include "vbaborders.hxx"
20 
21 #include <sal/macros.h>
22 #include <cppuhelper/implbase.hxx>
23 #include <ooo/vba/excel/XlBordersIndex.hpp>
24 #include <ooo/vba/excel/XlBorderWeight.hpp>
25 #include <ooo/vba/excel/XlLineStyle.hpp>
26 #include <ooo/vba/excel/XlColorIndex.hpp>
27 #include <com/sun/star/beans/XPropertySet.hpp>
28 #include <com/sun/star/table/TableBorder.hpp>
29 #include <com/sun/star/table/XColumnRowRange.hpp>
30 
31 #include "vbapalette.hxx"
32 
33 using namespace ::com::sun::star;
34 using namespace ::ooo::vba;
35 using namespace ::ooo::vba::excel;
36 
37 typedef ::cppu::WeakImplHelper<container::XIndexAccess > RangeBorders_Base;
38 typedef InheritedHelperInterfaceWeakImpl<excel::XBorder > ScVbaBorder_Base;
39 
40 // #TODO sort these indexes to match the order in which Excel iterates over the
41 // borders, the enumeration will match the order in this list
42 static const sal_Int16 supportedIndexTable[] = {  XlBordersIndex::xlEdgeLeft, XlBordersIndex::xlEdgeTop, XlBordersIndex::xlEdgeBottom, XlBordersIndex::xlEdgeRight, XlBordersIndex::xlDiagonalDown, XlBordersIndex::xlDiagonalUp, XlBordersIndex::xlInsideVertical, XlBordersIndex::xlInsideHorizontal };
43 
44 static const char sTableBorder[] = "TableBorder";
45 
46 //  Equiv widths in 1/100 mm
47 const static sal_Int32 OOLineThin = 35;
48 const static sal_Int32 OOLineMedium = 88;
49 const static sal_Int32 OOLineThick = 141;
50 const static sal_Int32 OOLineHairline = 2;
51 
52 class ScVbaBorder : public ScVbaBorder_Base
53 {
54 private:
55     uno::Reference< beans::XPropertySet > m_xProps;
56     sal_Int32 const m_LineType;
57     ScVbaPalette m_Palette;
58     void setBorderLine( const table::BorderLine& rBorderLine )
59     {
60         table::TableBorder aTableBorder;
61         m_xProps->getPropertyValue( sTableBorder ) >>= aTableBorder;
62 
63         switch ( m_LineType )
64         {
65             case XlBordersIndex::xlEdgeLeft:
66                 aTableBorder.IsLeftLineValid = true;
67                 aTableBorder.LeftLine= rBorderLine;
68                 break;
69             case XlBordersIndex::xlEdgeTop:
70                 aTableBorder.IsTopLineValid = true;
71                 aTableBorder.TopLine = rBorderLine;
72                 break;
73 
74             case XlBordersIndex::xlEdgeBottom:
75                 aTableBorder.IsBottomLineValid = true;
76                 aTableBorder.BottomLine = rBorderLine;
77                 break;
78             case XlBordersIndex::xlEdgeRight:
79                 aTableBorder.IsRightLineValid = true;
80                 aTableBorder.RightLine = rBorderLine;
81                 break;
82             case XlBordersIndex::xlInsideVertical:
83                 aTableBorder.IsVerticalLineValid = true;
84                 aTableBorder.VerticalLine = rBorderLine;
85                 break;
86             case XlBordersIndex::xlInsideHorizontal:
87                 aTableBorder.IsHorizontalLineValid = true;
88                 aTableBorder.HorizontalLine = rBorderLine;
89                 break;
90             case XlBordersIndex::xlDiagonalDown:
91             case XlBordersIndex::xlDiagonalUp:
92                 // #TODO have to ignore at the momement, would be
93                 // nice to investigate what we can do here
94                 break;
95             default:
96                     return;
97         }
98         m_xProps->setPropertyValue( sTableBorder, uno::makeAny(aTableBorder) );
99     }
100 
101     bool getBorderLine( table::BorderLine& rBorderLine )
102     {
103         table::TableBorder aTableBorder;
104         m_xProps->getPropertyValue( sTableBorder ) >>= aTableBorder;
105         switch ( m_LineType )
106         {
107             case XlBordersIndex::xlEdgeLeft:
108                 if ( aTableBorder.IsLeftLineValid )
109                     rBorderLine = aTableBorder.LeftLine;
110                 break;
111             case XlBordersIndex::xlEdgeTop:
112                 if ( aTableBorder.IsTopLineValid )
113                     rBorderLine = aTableBorder.TopLine;
114                 break;
115 
116             case XlBordersIndex::xlEdgeBottom:
117                 if ( aTableBorder.IsBottomLineValid )
118                     rBorderLine = aTableBorder.BottomLine;
119                 break;
120             case XlBordersIndex::xlEdgeRight:
121                 if ( aTableBorder.IsRightLineValid )
122                     rBorderLine = aTableBorder.RightLine;
123                 break;
124             case XlBordersIndex::xlInsideVertical:
125                 if ( aTableBorder.IsVerticalLineValid )
126                     rBorderLine = aTableBorder.VerticalLine;
127                 break;
128             case XlBordersIndex::xlInsideHorizontal:
129                 if ( aTableBorder.IsHorizontalLineValid )
130                     rBorderLine = aTableBorder.HorizontalLine;
131                 break;
132 
133             case XlBordersIndex::xlDiagonalDown:
134             case XlBordersIndex::xlDiagonalUp:
135                 // #TODO have to ignore at the momement, would be
136                 // nice to investigate what we can do here
137                 break;
138             default:
139                     return false;
140         }
141         return true;
142     }
143 
144 protected:
145     virtual OUString getServiceImplName() override
146     {
147         return OUString("ScVbaBorder");
148     }
149     virtual css::uno::Sequence<OUString> getServiceNames() override
150     {
151         static uno::Sequence< OUString > const aServiceNames
152         {
153             "ooo.vba.excel.Border"
154         };
155         return aServiceNames;
156     }
157 public:
158     ScVbaBorder( const uno::Reference< beans::XPropertySet > & xProps, const uno::Reference< uno::XComponentContext >& xContext, sal_Int32 lineType, const ScVbaPalette& rPalette) : ScVbaBorder_Base( uno::Reference< XHelperInterface >( xProps, uno::UNO_QUERY ), xContext ), m_xProps( xProps ), m_LineType( lineType ), m_Palette( rPalette ) {}
159 
160     // XBorder
161     uno::Any SAL_CALL getColor() override
162     {
163         table::BorderLine aBorderLine;
164         if ( getBorderLine( aBorderLine ) )
165             return uno::makeAny( OORGBToXLRGB( Color(aBorderLine.Color) ) );
166         throw uno::RuntimeException("No Implementation available" );
167     }
168     void SAL_CALL setColor( const uno::Any& _color ) override
169     {
170         sal_Int32 nColor = 0;
171         _color >>= nColor;
172         table::BorderLine aBorderLine;
173         if ( !getBorderLine( aBorderLine ) )
174             throw uno::RuntimeException("No Implementation available" );
175 
176         aBorderLine.Color = XLRGBToOORGB( nColor );
177         setBorderLine( aBorderLine );
178 
179     }
180 
181     uno::Any SAL_CALL getColorIndex() override
182     {
183         sal_Int32 nColor = 0;
184         XLRGBToOORGB( getColor() ) >>= nColor;
185         uno::Reference< container::XIndexAccess > xIndex = m_Palette.getPalette();
186         sal_Int32 nElems = xIndex->getCount();
187         sal_Int32 nIndex = -1;
188         for ( sal_Int32 count=0; count<nElems; ++count )
189         {
190             sal_Int32 nPaletteColor = 0;
191             xIndex->getByIndex( count ) >>= nPaletteColor;
192             if ( nPaletteColor == nColor )
193             {
194                 nIndex = count + 1;
195                 break;
196             }
197         }
198         return uno::makeAny(nIndex);
199     }
200 
201     void SAL_CALL setColorIndex( const uno::Any& _colorindex ) override
202     {
203         sal_Int32 nColor = 0;
204         _colorindex >>= nColor;
205         if ( !nColor || nColor == XlColorIndex::xlColorIndexAutomatic )
206             nColor = 1;
207         setColor( OORGBToXLRGB( m_Palette.getPalette()->getByIndex( --nColor )  ) );
208     }
209     uno::Any SAL_CALL getWeight() override
210     {
211         table::BorderLine aBorderLine;
212         if ( getBorderLine( aBorderLine ) )
213         {
214             switch ( aBorderLine.OuterLineWidth )
215             {
216                 case 0: // Thin = default OO thickness
217                 case OOLineThin:
218                     return uno::makeAny( XlBorderWeight::xlThin );
219                 case OOLineMedium:
220                     return uno::makeAny( XlBorderWeight::xlMedium );
221                 case OOLineThick:
222                     return uno::makeAny( XlBorderWeight::xlThick );
223                 case OOLineHairline:
224                     return uno::makeAny( XlBorderWeight::xlHairline );
225                 default:
226                     break;
227             }
228         }
229         throw uno::RuntimeException("Method failed" );
230     }
231     void SAL_CALL setWeight( const uno::Any& _weight ) override
232     {
233         sal_Int32 nWeight = 0;
234         _weight >>= nWeight;
235         table::BorderLine aBorderLine;
236         if ( !getBorderLine( aBorderLine ) )
237                     throw uno::RuntimeException("Method failed" );
238 
239         switch ( nWeight )
240         {
241             case XlBorderWeight::xlThin:
242                 aBorderLine.OuterLineWidth = OOLineThin;
243                 break;
244             case XlBorderWeight::xlMedium:
245                 aBorderLine.OuterLineWidth = OOLineMedium;
246                 break;
247             case XlBorderWeight::xlThick:
248                 aBorderLine.OuterLineWidth = OOLineThick;
249                 break;
250             case XlBorderWeight::xlHairline:
251                 aBorderLine.OuterLineWidth = OOLineHairline;
252                 break;
253             default:
254                 throw uno::RuntimeException("Bad param" );
255         }
256         setBorderLine( aBorderLine );
257 
258     }
259 
260     void SAL_CALL setTintAndShade( const uno::Any& /*rAny*/ ) override
261     {
262         // TODO implement
263     }
264     uno::Any SAL_CALL getTintAndShade() override
265     {
266         // TODO implement
267         return uno::makeAny(static_cast<double>(0));
268     }
269 
270     uno::Any SAL_CALL getLineStyle() override
271     {
272         // always return xlContinuous;
273         return uno::makeAny( XlLineStyle::xlContinuous );
274     }
275     void SAL_CALL setLineStyle( const uno::Any& _linestyle ) override
276     {
277         // Urk no choice but to silently ignore we don't support this attribute
278         // #TODO would be nice to support the excel line styles
279         sal_Int32 nLineStyle = 0;
280         _linestyle >>= nLineStyle;
281         table::BorderLine aBorderLine;
282         if ( !getBorderLine( aBorderLine ) )
283             throw uno::RuntimeException("Method failed" );
284 
285         switch ( nLineStyle )
286         {
287             case XlLineStyle::xlContinuous:
288             case XlLineStyle::xlDash:
289             case XlLineStyle::xlDashDot:
290             case XlLineStyle::xlDashDotDot:
291             case XlLineStyle::xlDot:
292             case XlLineStyle::xlDouble:
293             case XlLineStyle::xlLineStyleNone:
294             case XlLineStyle::xlSlantDashDot:
295                 break;
296             default:
297                 throw uno::RuntimeException("Bad param" );
298         }
299         setBorderLine( aBorderLine );
300 
301     }
302 };
303 
304 class RangeBorders : public RangeBorders_Base
305 {
306 private:
307     uno::Reference< table::XCellRange > m_xRange;
308     uno::Reference< uno::XComponentContext > m_xContext;
309     ScVbaPalette m_Palette;
310     sal_Int32 getTableIndex( sal_Int32 nConst )
311     {
312         // okay return position of the index in the table
313         sal_Int32 nIndexes = getCount();
314         sal_Int32 realIndex = 0;
315         const sal_Int16* pTableEntry = supportedIndexTable;
316         for ( ; realIndex < nIndexes; ++realIndex, ++pTableEntry )
317         {
318             if ( *pTableEntry == nConst )
319                 return realIndex;
320         }
321         return getCount(); // error condition
322     }
323 public:
324     RangeBorders(  const uno::Reference< table::XCellRange >& xRange,  const uno::Reference< uno::XComponentContext > & xContext, const ScVbaPalette& rPalette ) : m_xRange( xRange ), m_xContext( xContext ), m_Palette( rPalette )
325     {
326     }
327     // XIndexAccess
328     virtual ::sal_Int32 SAL_CALL getCount(  ) override
329     {
330         return SAL_N_ELEMENTS( supportedIndexTable );
331     }
332     virtual uno::Any SAL_CALL getByIndex( ::sal_Int32 Index ) override
333     {
334 
335         sal_Int32 nIndex = getTableIndex( Index );
336         if ( nIndex >= 0 && nIndex < getCount() )
337         {
338             uno::Reference< beans::XPropertySet > xProps( m_xRange, uno::UNO_QUERY_THROW );
339             return uno::makeAny( uno::Reference< excel::XBorder >( new ScVbaBorder( xProps, m_xContext, supportedIndexTable[ nIndex ], m_Palette )) );
340         }
341         throw lang::IndexOutOfBoundsException();
342     }
343     virtual uno::Type SAL_CALL getElementType(  ) override
344     {
345         return  cppu::UnoType<excel::XBorder>::get();
346     }
347     virtual sal_Bool SAL_CALL hasElements(  ) override
348     {
349         return true;
350     }
351 };
352 
353 static uno::Reference< container::XIndexAccess >
354 rangeToBorderIndexAccess( const uno::Reference< table::XCellRange >& xRange,  const uno::Reference< uno::XComponentContext > & xContext, const ScVbaPalette& rPalette )
355 {
356     return new RangeBorders( xRange, xContext, rPalette );
357 }
358 
359 class RangeBorderEnumWrapper : public EnumerationHelper_BASE
360 {
361     uno::Reference<container::XIndexAccess > m_xIndexAccess;
362     sal_Int32 nIndex;
363 public:
364     explicit RangeBorderEnumWrapper( const uno::Reference< container::XIndexAccess >& xIndexAccess ) : m_xIndexAccess( xIndexAccess ), nIndex( 0 ) {}
365     virtual sal_Bool SAL_CALL hasMoreElements(  ) override
366     {
367         return ( nIndex < m_xIndexAccess->getCount() );
368     }
369 
370     virtual uno::Any SAL_CALL nextElement(  ) override
371     {
372         if ( nIndex < m_xIndexAccess->getCount() )
373             return m_xIndexAccess->getByIndex( nIndex++ );
374         throw container::NoSuchElementException();
375     }
376 };
377 
378 ScVbaBorders::ScVbaBorders( const uno::Reference< XHelperInterface >& xParent,
379                             const uno::Reference< uno::XComponentContext > & xContext,
380                             const uno::Reference< table::XCellRange >& xRange,
381                             const ScVbaPalette& rPalette  )
382     :  ScVbaBorders_BASE( xParent, xContext, rangeToBorderIndexAccess( xRange ,xContext, rPalette ) ), bRangeIsSingleCell( false )
383 {
384     uno::Reference< table::XColumnRowRange > xColumnRowRange(xRange, uno::UNO_QUERY_THROW );
385     if ( xColumnRowRange->getRows()->getCount() == 1 && xColumnRowRange->getColumns()->getCount() == 1 )
386         bRangeIsSingleCell = true;
387     m_xProps.set( xRange, uno::UNO_QUERY_THROW );
388 }
389 
390 uno::Reference< container::XEnumeration >
391 ScVbaBorders::createEnumeration()
392 {
393     return new RangeBorderEnumWrapper( m_xIndexAccess );
394 }
395 
396 uno::Any
397 ScVbaBorders::createCollectionObject( const css::uno::Any& aSource )
398 {
399     return aSource; // it's already a Border object
400 }
401 
402 uno::Type
403 ScVbaBorders::getElementType()
404 {
405     return cppu::UnoType<excel::XBorders>::get();
406 }
407 
408 uno::Any
409 ScVbaBorders::getItemByIntIndex( const sal_Int32 nIndex )
410 {
411     return createCollectionObject( m_xIndexAccess->getByIndex( nIndex ) );
412 }
413 
414 uno::Any SAL_CALL ScVbaBorders::getColor()
415 {
416     sal_Int32 count = getCount();
417     uno::Any color;
418     for( sal_Int32 i = 0; i < count ; i++ )
419     {
420         if( XlBordersIndex::xlDiagonalDown != supportedIndexTable[i] && XlBordersIndex::xlDiagonalUp != supportedIndexTable[i] )
421         {
422             uno::Reference< XBorder > xBorder( getItemByIntIndex( supportedIndexTable[i] ), uno::UNO_QUERY_THROW );
423             if( color.hasValue() )
424             {
425                 if( color != xBorder->getColor() )
426                     return uno::makeAny( uno::Reference< uno::XInterface >() );
427             }
428             else
429                 color = xBorder->getColor();
430         }
431     }
432     return  color;
433 }
434 void SAL_CALL ScVbaBorders::setColor( const uno::Any& _color )
435 {
436     sal_Int32 count = getCount();
437     for( sal_Int32 i = 0; i < count ; i++ )
438     {
439         uno::Reference< XBorder > xBorder( getItemByIntIndex( supportedIndexTable[i] ), uno::UNO_QUERY_THROW );
440         xBorder->setColor( _color );
441     }
442 }
443 uno::Any SAL_CALL ScVbaBorders::getColorIndex()
444 {
445     sal_Int32 count = getCount();
446     uno::Any nColorIndex;
447     for( sal_Int32 i = 0; i < count ; i++ )
448     {
449         if( XlBordersIndex::xlDiagonalDown != supportedIndexTable[i] && XlBordersIndex::xlDiagonalUp != supportedIndexTable[i] )
450         {
451             uno::Reference< XBorder > xBorder( getItemByIntIndex( supportedIndexTable[i] ), uno::UNO_QUERY_THROW );
452             if( nColorIndex.hasValue() )
453             {
454                 if( nColorIndex != xBorder->getColorIndex() )
455                     return uno::makeAny( uno::Reference< uno::XInterface >() );
456             }
457             else
458                 nColorIndex = xBorder->getColorIndex();
459         }
460     }
461     return  nColorIndex;
462 }
463 void SAL_CALL ScVbaBorders::setColorIndex( const uno::Any& _colorindex )
464 {
465     sal_Int32 count = getCount();
466     for( sal_Int32 i = 0; i < count ; i++ )
467     {
468         uno::Reference< XBorder > xBorder( getItemByIntIndex( supportedIndexTable[i] ), uno::UNO_QUERY_THROW );
469         xBorder->setColorIndex( _colorindex );
470     }
471 }
472 
473 static bool
474 lcl_areAllLineWidthsSame( const table::TableBorder& maTableBorder, bool bIsCell )
475 {
476 
477     bool bRes = false;
478     if (bIsCell)
479     {
480         bRes = ((maTableBorder.TopLine.OuterLineWidth == maTableBorder.BottomLine.OuterLineWidth) &&
481 (maTableBorder.TopLine.OuterLineWidth == maTableBorder.LeftLine.OuterLineWidth) &&
482 (maTableBorder.TopLine.OuterLineWidth == maTableBorder.RightLine.OuterLineWidth));
483     }
484     else
485     {
486         bRes = ((maTableBorder.TopLine.OuterLineWidth == maTableBorder.BottomLine.OuterLineWidth) &&
487 (maTableBorder.TopLine.OuterLineWidth == maTableBorder.LeftLine.OuterLineWidth) &&
488 (maTableBorder.TopLine.OuterLineWidth == maTableBorder.HorizontalLine.OuterLineWidth) &&
489 (maTableBorder.TopLine.OuterLineWidth == maTableBorder.VerticalLine.OuterLineWidth) &&
490 (maTableBorder.TopLine.OuterLineWidth == maTableBorder.RightLine.OuterLineWidth));
491     }
492     return bRes;
493 }
494 
495 uno::Any SAL_CALL ScVbaBorders::getLineStyle()
496 {
497     table::TableBorder aTableBorder;
498     m_xProps->getPropertyValue( sTableBorder ) >>= aTableBorder;
499 
500     sal_Int32 aLinestyle =  XlLineStyle::xlLineStyleNone;
501 
502     if ( lcl_areAllLineWidthsSame( aTableBorder, bRangeIsSingleCell ))
503     {
504         if (aTableBorder.TopLine.LineDistance != 0)
505         {
506             aLinestyle = XlLineStyle::xlDouble;
507         }
508         else if ( aTableBorder.TopLine.OuterLineWidth != 0 )
509         {
510             aLinestyle = XlLineStyle::xlContinuous;
511         }
512     }
513     return uno::makeAny( aLinestyle );
514 }
515 void SAL_CALL ScVbaBorders::setLineStyle( const uno::Any& _linestyle )
516 {
517     sal_Int32 count = getCount();
518     for( sal_Int32 i = 0; i < count ; i++ )
519     {
520         uno::Reference< XBorder > xBorder( getItemByIntIndex( supportedIndexTable[i] ), uno::UNO_QUERY_THROW );
521         xBorder->setLineStyle( _linestyle );
522     }
523 }
524 uno::Any SAL_CALL ScVbaBorders::getWeight()
525 {
526     sal_Int32 count = getCount();
527     uno::Any weight;
528     for( sal_Int32 i = 0; i < count ; i++ )
529     {
530         if( XlBordersIndex::xlDiagonalDown != supportedIndexTable[i] && XlBordersIndex::xlDiagonalUp != supportedIndexTable[i] )
531         {
532             uno::Reference< XBorder > xBorder( getItemByIntIndex( supportedIndexTable[i] ), uno::UNO_QUERY_THROW );
533             if( weight.hasValue() )
534             {
535                 if( weight != xBorder->getWeight() )
536                     return uno::makeAny( uno::Reference< uno::XInterface >() );
537             }
538             else
539                 weight = xBorder->getWeight();
540         }
541     }
542     return  weight;
543 }
544 
545 uno::Any SAL_CALL ScVbaBorders::getTintAndShade()
546 {
547     // TODO implement
548     return uno::makeAny(static_cast<double>(0));
549 }
550 
551 void SAL_CALL ScVbaBorders::setTintAndShade(const uno::Any& /*rAny*/)
552 {
553     // TODO implement
554 }
555 
556 void SAL_CALL ScVbaBorders::setWeight( const uno::Any& _weight )
557 {
558     sal_Int32 count = getCount();
559     for( sal_Int32 i = 0; i < count ; i++ )
560     {
561         uno::Reference< XBorder > xBorder( getItemByIntIndex( supportedIndexTable[i] ), uno::UNO_QUERY_THROW );
562         xBorder->setWeight( _weight );
563     }
564 }
565 
566 OUString
567 ScVbaBorders::getServiceImplName()
568 {
569     return OUString("ScVbaBorders");
570 }
571 
572 uno::Sequence< OUString >
573 ScVbaBorders::getServiceNames()
574 {
575     static uno::Sequence< OUString > const aServiceNames
576     {
577         "ooo.vba.excel.Borders"
578     };
579     return aServiceNames;
580 }
581 
582 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
583