bump product version to 4.1.6.2
[LibreOffice.git] / editeng / source / items / borderline.cxx
blob874f381defa9e36fe8c520415b92e5df5f9ebad9
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <basegfx/color/bcolor.hxx>
21 #include <basegfx/color/bcolortools.hxx>
23 #include <editeng/borderline.hxx>
24 #include <editeng/itemtype.hxx>
27 using namespace ::com::sun::star::table::BorderLineStyle;
29 // class SvxBorderLine --------------------------------------------------
31 namespace {
33 Color lcl_compute3DColor( Color aMain, int nLight, int nMedium, int nDark )
35 basegfx::BColor color = aMain.getBColor( );
36 basegfx::BColor hsl = basegfx::tools::rgb2hsl( color );
38 int nCoef = 0;
39 if ( hsl.getZ( ) >= 0.5 )
40 nCoef = nLight;
41 else if ( 0.5 > hsl.getZ() && hsl.getZ() >= 0.25 )
42 nCoef = nMedium;
43 else
44 nCoef = nDark;
46 double L = hsl.getZ() * 255.0 + nCoef;
47 hsl.setZ( L / 255.0 );
48 color = basegfx::tools::hsl2rgb( hsl );
50 return Color( color );
52 } // Anonymous namespace
54 namespace editeng {
56 Color SvxBorderLine::darkColor( Color aMain )
58 return aMain;
61 Color SvxBorderLine::lightColor( Color aMain )
64 // Divide Luminance by 2
65 basegfx::BColor color = aMain.getBColor( );
66 basegfx::BColor hsl = basegfx::tools::rgb2hsl( color );
67 hsl.setZ( hsl.getZ() * 0.5 );
68 color = basegfx::tools::hsl2rgb( hsl );
70 return Color( color );
74 Color SvxBorderLine::threeDLightColor( Color aMain )
76 // These values have been defined in an empirical way
77 return lcl_compute3DColor( aMain, 3, 40, 83 );
80 Color SvxBorderLine::threeDDarkColor( Color aMain )
82 // These values have been defined in an empirical way
83 return lcl_compute3DColor( aMain, -85, -43, -1 );
86 Color SvxBorderLine::threeDMediumColor( Color aMain )
88 // These values have been defined in an empirical way
89 return lcl_compute3DColor( aMain, -42, -0, 42 );
92 SvxBorderLine::SvxBorderLine( const Color *pCol, long nWidth,
93 SvxBorderStyle nStyle, bool bUseLeftTop,
94 Color (*pColorOutFn)( Color ), Color (*pColorInFn)( Color ),
95 Color (*pColorGapFn)( Color ) )
96 : m_nWidth( nWidth )
97 , m_bMirrorWidths( false )
98 , m_aWidthImpl( SvxBorderLine::getWidthImpl( nStyle ) )
99 , m_nMult( 1 )
100 , m_nDiv( 1 )
101 , m_nStyle( nStyle )
102 , m_bUseLeftTop( bUseLeftTop )
103 , m_pColorOutFn( pColorOutFn )
104 , m_pColorInFn( pColorInFn )
105 , m_pColorGapFn( pColorGapFn )
107 if ( pCol )
108 aColor = *pCol;
112 SvxBorderStyle
113 ConvertBorderStyleFromWord(int const nWordLineStyle)
115 switch (nWordLineStyle)
117 // First the single lines
118 case 1:
119 case 2: // thick line
120 case 5: // hairline
121 // and the unsupported special cases which we map to a single line
122 case 8:
123 case 9:
124 case 20:
125 return SOLID;
126 case 6:
127 return DOTTED;
128 case 7:
129 return DASHED;
130 case 22:
131 return FINE_DASHED;
132 // then the shading beams which we represent by a double line
133 case 23:
134 return DOUBLE;
135 // then the double lines, for which we have good matches
136 case 3:
137 case 10: // Don't have triple so use double
138 case 21: // Don't have double wave: use double instead
139 return DOUBLE;
140 case 11:
141 return THINTHICK_SMALLGAP;
142 case 12:
143 case 13: // Don't have thin thick thin, so use thick thin
144 return THICKTHIN_SMALLGAP;
145 case 14:
146 return THINTHICK_MEDIUMGAP;
147 case 15:
148 case 16: // Don't have thin thick thin, so use thick thin
149 return THICKTHIN_MEDIUMGAP;
150 case 17:
151 return THINTHICK_LARGEGAP;
152 case 18:
153 case 19: // Don't have thin thick thin, so use thick thin
154 return THICKTHIN_LARGEGAP;
155 case 24:
156 return EMBOSSED;
157 case 25:
158 return ENGRAVED;
159 case 26:
160 return OUTSET;
161 case 27:
162 return INSET;
163 default:
164 return NONE;
168 static const double THINTHICK_SMALLGAP_line2 = 15.0;
169 static const double THINTHICK_SMALLGAP_gap = 15.0;
170 static const double THINTHICK_LARGEGAP_line1 = 30.0;
171 static const double THINTHICK_LARGEGAP_line2 = 15.0;
172 static const double THICKTHIN_SMALLGAP_line1 = 15.0;
173 static const double THICKTHIN_SMALLGAP_gap = 15.0;
174 static const double THICKTHIN_LARGEGAP_line1 = 15.0;
175 static const double THICKTHIN_LARGEGAP_line2 = 30.0;
176 static const double OUTSET_line1 = 15.0;
177 static const double INSET_line2 = 15.0;
179 double
180 ConvertBorderWidthFromWord(SvxBorderStyle const eStyle, double const i_fWidth,
181 int const nWordLineStyle)
183 // fdo#68779: at least for RTF, 0.75pt is the default if width is missing
184 double const fWidth((i_fWidth == 0.0) ? 15.0 : i_fWidth);
185 switch (eStyle)
187 // Single lines
188 case SOLID:
189 switch (nWordLineStyle)
191 case 2:
192 return (fWidth * 2.0); // thick
193 case 5: // fdo#55526: map 0 hairline width to > 0
194 return (fWidth > 1.0) ? fWidth : 1.0;
195 default:
196 return fWidth;
198 break;
200 case DOTTED:
201 case DASHED:
202 return fWidth;
204 // Display a minimum effective border width of 1pt
205 case FINE_DASHED:
206 return (fWidth > 0 && fWidth < 20) ? 20 : fWidth;
208 // Double lines
209 case DOUBLE:
210 return fWidth * 3.0;
212 case THINTHICK_MEDIUMGAP:
213 case THICKTHIN_MEDIUMGAP:
214 case EMBOSSED:
215 case ENGRAVED:
216 return fWidth * 2.0;
218 case THINTHICK_SMALLGAP:
219 return fWidth + THINTHICK_SMALLGAP_line2 + THINTHICK_SMALLGAP_gap;
221 case THINTHICK_LARGEGAP:
222 return fWidth + THINTHICK_LARGEGAP_line1 + THINTHICK_LARGEGAP_line2;
224 case THICKTHIN_SMALLGAP:
225 return fWidth + THICKTHIN_SMALLGAP_line1 + THICKTHIN_SMALLGAP_gap;
227 case THICKTHIN_LARGEGAP:
228 return fWidth + THICKTHIN_LARGEGAP_line1 + THICKTHIN_LARGEGAP_line2;
230 case OUTSET:
231 return (fWidth * 2.0) + OUTSET_line1;
233 case INSET:
234 return (fWidth * 2.0) + INSET_line2;
236 default:
237 assert(false); // should only be called for known border style
238 return 0;
242 double
243 ConvertBorderWidthToWord(SvxBorderStyle const eStyle, double const fWidth)
245 switch (eStyle)
247 // Single lines
248 case SOLID:
249 case DOTTED:
250 case DASHED:
251 case FINE_DASHED:
252 return fWidth;
254 // Double lines
255 case DOUBLE:
256 return fWidth / 3.0;
258 case THINTHICK_MEDIUMGAP:
259 case THICKTHIN_MEDIUMGAP:
260 case EMBOSSED:
261 case ENGRAVED:
262 return fWidth / 2.0;
264 case THINTHICK_SMALLGAP:
265 return fWidth - THINTHICK_SMALLGAP_line2 - THINTHICK_SMALLGAP_gap;
267 case THINTHICK_LARGEGAP:
268 return fWidth - THINTHICK_LARGEGAP_line1 - THINTHICK_LARGEGAP_line2;
270 case THICKTHIN_SMALLGAP:
271 return fWidth - THICKTHIN_SMALLGAP_line1 - THICKTHIN_SMALLGAP_gap;
273 case THICKTHIN_LARGEGAP:
274 return fWidth - THICKTHIN_LARGEGAP_line1 - THICKTHIN_LARGEGAP_line2;
276 case OUTSET:
277 return (fWidth - OUTSET_line1) / 2.0;
279 case INSET:
280 return (fWidth - INSET_line2) / 2.0;
282 default:
283 assert(false); // should only be called for known border style
284 return 0;
288 /** Get the BorderWithImpl object corresponding to the given #nStyle, all the
289 units handled by the resulting object are Twips and the
290 BorderWidthImpl::GetLine1() corresponds to the Outer Line.
292 BorderWidthImpl SvxBorderLine::getWidthImpl( SvxBorderStyle nStyle )
294 BorderWidthImpl aImpl;
296 switch ( nStyle )
298 // No line: no width
299 case NONE:
300 aImpl = BorderWidthImpl( 0, 0.0 );
301 break;
303 // Single lines
304 case SOLID:
305 case DOTTED:
306 case DASHED:
307 case FINE_DASHED:
308 aImpl = BorderWidthImpl( CHANGE_LINE1, 1.0 );
309 break;
311 // Double lines
313 case DOUBLE:
314 aImpl = BorderWidthImpl(
315 CHANGE_LINE1 | CHANGE_LINE2 | CHANGE_DIST,
316 // fdo#46112 fdo#38542 fdo#43249:
317 // non-constant witdths must sum to 1
318 1.0/3.0, 1.0/3.0, 1.0/3.0 );
319 break;
321 case THINTHICK_SMALLGAP:
322 aImpl = BorderWidthImpl( CHANGE_LINE1, 1.0,
323 THINTHICK_SMALLGAP_line2, THINTHICK_SMALLGAP_gap );
324 break;
326 case THINTHICK_MEDIUMGAP:
327 aImpl = BorderWidthImpl(
328 CHANGE_LINE1 | CHANGE_LINE2 | CHANGE_DIST,
329 0.5, 0.25, 0.25 );
330 break;
332 case THINTHICK_LARGEGAP:
333 aImpl = BorderWidthImpl( CHANGE_DIST,
334 THINTHICK_LARGEGAP_line1, THINTHICK_LARGEGAP_line2, 1.0 );
335 break;
337 case THICKTHIN_SMALLGAP:
338 aImpl = BorderWidthImpl( CHANGE_LINE2, THICKTHIN_SMALLGAP_line1,
339 1.0, THICKTHIN_SMALLGAP_gap );
340 break;
342 case THICKTHIN_MEDIUMGAP:
343 aImpl = BorderWidthImpl(
344 CHANGE_LINE1 | CHANGE_LINE2 | CHANGE_DIST,
345 0.25, 0.5, 0.25 );
346 break;
348 case THICKTHIN_LARGEGAP:
349 aImpl = BorderWidthImpl( CHANGE_DIST, THICKTHIN_LARGEGAP_line1,
350 THICKTHIN_LARGEGAP_line2, 1.0 );
351 break;
353 // Engraved / Embossed
355 * Word compat: the lines widths are exactly following this rule, shouldbe:
356 * 0.75pt up to 3pt and then 3pt
359 case EMBOSSED:
360 case ENGRAVED:
361 aImpl = BorderWidthImpl(
362 CHANGE_LINE1 | CHANGE_LINE2 | CHANGE_DIST,
363 0.25, 0.25, 0.5 );
364 break;
366 // Inset / Outset
368 * Word compat: the gap width should be measured relatively to the biggest width for the
369 * row or column.
371 case OUTSET:
372 aImpl = BorderWidthImpl(
373 CHANGE_LINE2 | CHANGE_DIST,
374 OUTSET_line1, 0.5, 0.5 );
375 break;
377 case INSET:
378 aImpl = BorderWidthImpl(
379 CHANGE_LINE1 | CHANGE_DIST,
380 0.5, INSET_line2, 0.5 );
381 break;
384 return aImpl;
387 // -----------------------------------------------------------------------
389 SvxBorderLine::SvxBorderLine( const SvxBorderLine& r )
391 *this = r;
394 // -----------------------------------------------------------------------
396 SvxBorderLine& SvxBorderLine::operator=( const SvxBorderLine& r )
398 aColor = r.aColor;
399 m_nWidth = r.m_nWidth;
400 m_aWidthImpl = r.m_aWidthImpl;
401 m_bMirrorWidths = r.m_bMirrorWidths;
402 m_nMult = r.m_nMult;
403 m_nDiv = r.m_nDiv;
404 m_nStyle = r.m_nStyle;
405 m_bUseLeftTop = r.m_bUseLeftTop;
406 m_pColorOutFn = r.m_pColorOutFn;
407 m_pColorInFn = r.m_pColorInFn;
408 m_pColorGapFn = r.m_pColorGapFn;
409 return *this;
412 // -----------------------------------------------------------------------
414 void SvxBorderLine::ScaleMetrics( long nMult, long nDiv )
416 m_nMult = nMult;
417 m_nDiv = nDiv;
420 void SvxBorderLine::GuessLinesWidths( SvxBorderStyle nStyle, sal_uInt16 nOut, sal_uInt16 nIn, sal_uInt16 nDist )
422 if (NONE == nStyle)
424 nStyle = SOLID;
425 if ( nOut > 0 && nIn > 0 )
426 nStyle = DOUBLE;
429 if ( nStyle == DOUBLE )
431 static SvxBorderStyle aDoubleStyles[] =
433 DOUBLE,
434 THINTHICK_SMALLGAP,
435 THINTHICK_MEDIUMGAP,
436 THINTHICK_LARGEGAP,
437 THICKTHIN_SMALLGAP,
438 THICKTHIN_MEDIUMGAP,
439 THICKTHIN_LARGEGAP
442 size_t const len = SAL_N_ELEMENTS(aDoubleStyles);
443 long nWidth = 0;
444 SvxBorderStyle nTestStyle(NONE);
445 for (size_t i = 0; i < len && nWidth == 0; ++i)
447 nTestStyle = aDoubleStyles[i];
448 BorderWidthImpl aWidthImpl = getWidthImpl( nTestStyle );
449 nWidth = aWidthImpl.GuessWidth( nOut, nIn, nDist );
452 // If anything matched, then set it
453 if ( nWidth > 0 )
455 nStyle = nTestStyle;
456 SetBorderLineStyle(nStyle);
457 m_nWidth = nWidth;
459 else
461 // fdo#38542: not a known double, default to something custom...
462 SetBorderLineStyle(nStyle);
463 m_nWidth = nOut + nIn + nDist;
464 if (nOut + nIn + nDist)
466 m_aWidthImpl = BorderWidthImpl(
467 CHANGE_LINE1 | CHANGE_LINE2 | CHANGE_DIST,
468 static_cast<double>(nOut ) / static_cast<double>(m_nWidth),
469 static_cast<double>(nIn ) / static_cast<double>(m_nWidth),
470 static_cast<double>(nDist) / static_cast<double>(m_nWidth));
474 else
476 SetBorderLineStyle(nStyle);
477 if (nOut == 0 && nIn > 0)
479 // If only inner width is given swap inner and outer widths for
480 // single line styles, otherwise GuessWidth() marks this as invalid
481 // and returns a 0 width.
482 switch (nStyle)
484 case SOLID:
485 case DOTTED:
486 case DASHED:
487 case FINE_DASHED:
488 ::std::swap( nOut, nIn);
489 break;
490 default:
491 ; // nothing
494 m_nWidth = m_aWidthImpl.GuessWidth( nOut, nIn, nDist );
498 sal_uInt16 SvxBorderLine::GetOutWidth() const
500 sal_uInt16 nOut = (sal_uInt16)Scale( m_aWidthImpl.GetLine1( m_nWidth ), m_nMult, m_nDiv );
501 if ( m_bMirrorWidths )
502 nOut = (sal_uInt16)Scale( m_aWidthImpl.GetLine2( m_nWidth ), m_nMult, m_nDiv );
503 return nOut;
506 sal_uInt16 SvxBorderLine::GetInWidth() const
508 sal_uInt16 nIn = (sal_uInt16)Scale( m_aWidthImpl.GetLine2( m_nWidth ), m_nMult, m_nDiv );
509 if ( m_bMirrorWidths )
510 nIn = (sal_uInt16)Scale( m_aWidthImpl.GetLine1( m_nWidth ), m_nMult, m_nDiv );
511 return nIn;
514 sal_uInt16 SvxBorderLine::GetDistance() const
516 return (sal_uInt16)Scale( m_aWidthImpl.GetGap( m_nWidth ), m_nMult, m_nDiv );
519 // -----------------------------------------------------------------------
521 sal_Bool SvxBorderLine::operator==( const SvxBorderLine& rCmp ) const
523 return ( ( aColor == rCmp.aColor ) &&
524 ( m_nWidth == rCmp.m_nWidth ) &&
525 ( m_bMirrorWidths == rCmp.m_bMirrorWidths ) &&
526 ( m_aWidthImpl == rCmp.m_aWidthImpl ) &&
527 ( m_nStyle == rCmp.GetBorderLineStyle()) &&
528 ( m_bUseLeftTop == rCmp.m_bUseLeftTop ) &&
529 ( m_pColorOutFn == rCmp.m_pColorOutFn ) &&
530 ( m_pColorInFn == rCmp.m_pColorInFn ) &&
531 ( m_pColorGapFn == rCmp.m_pColorGapFn ) );
534 void SvxBorderLine::SetBorderLineStyle( SvxBorderStyle nNew )
536 m_nStyle = nNew;
537 m_aWidthImpl = getWidthImpl( m_nStyle );
539 switch ( nNew )
541 case EMBOSSED:
542 m_pColorOutFn = threeDLightColor;
543 m_pColorInFn = threeDDarkColor;
544 m_pColorGapFn = threeDMediumColor;
545 m_bUseLeftTop = true;
546 break;
547 case ENGRAVED:
548 m_pColorOutFn = threeDDarkColor;
549 m_pColorInFn = threeDLightColor;
550 m_pColorGapFn = threeDMediumColor;
551 m_bUseLeftTop = true;
552 break;
553 case OUTSET:
554 m_pColorOutFn = lightColor;
555 m_pColorInFn = darkColor;
556 m_bUseLeftTop = true;
557 m_pColorGapFn = NULL;
558 break;
559 case INSET:
560 m_pColorOutFn = darkColor;
561 m_pColorInFn = lightColor;
562 m_bUseLeftTop = true;
563 m_pColorGapFn = NULL;
564 break;
565 default:
566 m_pColorOutFn = darkColor;
567 m_pColorInFn = darkColor;
568 m_bUseLeftTop = false;
569 m_pColorGapFn = NULL;
570 break;
574 Color SvxBorderLine::GetColorOut( bool bLeftOrTop ) const
576 Color aResult = aColor;
578 if ( m_aWidthImpl.IsDouble() && m_pColorOutFn != NULL )
580 if ( !bLeftOrTop && m_bUseLeftTop )
581 aResult = (*m_pColorInFn)( aColor );
582 else
583 aResult = (*m_pColorOutFn)( aColor );
586 return aResult;
589 Color SvxBorderLine::GetColorIn( bool bLeftOrTop ) const
591 Color aResult = aColor;
593 if ( m_aWidthImpl.IsDouble() && m_pColorInFn != NULL )
595 if ( !bLeftOrTop && m_bUseLeftTop )
596 aResult = (*m_pColorOutFn)( aColor );
597 else
598 aResult = (*m_pColorInFn)( aColor );
601 return aResult;
604 Color SvxBorderLine::GetColorGap( ) const
606 Color aResult = aColor;
608 if ( m_aWidthImpl.IsDouble() && m_pColorGapFn != NULL )
610 aResult = (*m_pColorGapFn)( aColor );
613 return aResult;
616 // -----------------------------------------------------------------------
618 XubString SvxBorderLine::GetValueString( SfxMapUnit eSrcUnit,
619 SfxMapUnit eDestUnit,
620 const IntlWrapper* pIntl,
621 sal_Bool bMetricStr) const
623 static const sal_uInt16 aStyleIds[] =
625 RID_SOLID,
626 RID_DOTTED,
627 RID_DASHED,
628 RID_DOUBLE,
629 RID_THINTHICK_SMALLGAP,
630 RID_THINTHICK_MEDIUMGAP,
631 RID_THINTHICK_LARGEGAP,
632 RID_THICKTHIN_SMALLGAP,
633 RID_THICKTHIN_MEDIUMGAP,
634 RID_THICKTHIN_LARGEGAP,
635 RID_EMBOSSED,
636 RID_ENGRAVED,
637 RID_OUTSET,
638 RID_INSET
640 sal_uInt16 nResId = aStyleIds[m_nStyle];
641 String aStr;
642 aStr += sal_Unicode('(');
643 aStr += ::GetColorString( aColor );
644 aStr += cpDelim;
646 if ( nResId )
647 aStr += EE_RESSTR(nResId);
648 else
650 String sMetric = EE_RESSTR(GetMetricId( eDestUnit ));
651 aStr += GetMetricText( (long)GetInWidth(), eSrcUnit, eDestUnit, pIntl );
652 if ( bMetricStr )
653 aStr += sMetric;
654 aStr += cpDelim;
655 aStr += GetMetricText( (long)GetOutWidth(), eSrcUnit, eDestUnit, pIntl );
656 if ( bMetricStr )
657 aStr += sMetric;
658 aStr += cpDelim;
659 aStr += GetMetricText( (long)GetDistance(), eSrcUnit, eDestUnit, pIntl );
660 if ( bMetricStr )
661 aStr += sMetric;
663 aStr += sal_Unicode(')');
664 return aStr;
667 bool SvxBorderLine::HasPriority( const SvxBorderLine& rOtherLine ) const
669 const sal_uInt16 nThisSize = GetOutWidth() + GetDistance() + GetInWidth();
670 const sal_uInt16 nOtherSize = rOtherLine.GetOutWidth() + rOtherLine.GetDistance() + rOtherLine.GetInWidth();
672 if ( nThisSize > nOtherSize )
674 return true;
676 else if ( nThisSize < nOtherSize )
678 return false;
680 else if ( rOtherLine.GetInWidth() && !GetInWidth() )
682 return true;
685 return false;
688 bool operator!=( const SvxBorderLine& rLeft, const SvxBorderLine& rRight )
690 return !(rLeft == rRight);
693 } // namespace editeng
695 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */