Bump version to 4.1-6
[LibreOffice.git] / editeng / source / items / borderline.cxx
blob53d98459c911802a1f3b4a9de5599aa6c5d7e246
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 fWidth,
181 int const nWordLineStyle)
183 switch (eStyle)
185 // Single lines
186 case SOLID:
187 switch (nWordLineStyle)
189 case 2:
190 return (fWidth * 2.0); // thick
191 case 5: // fdo#55526: map 0 hairline width to > 0
192 return (fWidth > 1.0) ? fWidth : 1.0;
193 default:
194 return fWidth;
196 break;
198 case DOTTED:
199 case DASHED:
200 return fWidth;
202 // Display a minimum effective border width of 1pt
203 case FINE_DASHED:
204 return (fWidth > 0 && fWidth < 20) ? 20 : fWidth;
206 // Double lines
207 case DOUBLE:
208 return fWidth * 3.0;
210 case THINTHICK_MEDIUMGAP:
211 case THICKTHIN_MEDIUMGAP:
212 case EMBOSSED:
213 case ENGRAVED:
214 return fWidth * 2.0;
216 case THINTHICK_SMALLGAP:
217 return fWidth + THINTHICK_SMALLGAP_line2 + THINTHICK_SMALLGAP_gap;
219 case THINTHICK_LARGEGAP:
220 return fWidth + THINTHICK_LARGEGAP_line1 + THINTHICK_LARGEGAP_line2;
222 case THICKTHIN_SMALLGAP:
223 return fWidth + THICKTHIN_SMALLGAP_line1 + THICKTHIN_SMALLGAP_gap;
225 case THICKTHIN_LARGEGAP:
226 return fWidth + THICKTHIN_LARGEGAP_line1 + THICKTHIN_LARGEGAP_line2;
228 case OUTSET:
229 return (fWidth * 2.0) + OUTSET_line1;
231 case INSET:
232 return (fWidth * 2.0) + INSET_line2;
234 default:
235 assert(false); // should only be called for known border style
236 return 0;
240 double
241 ConvertBorderWidthToWord(SvxBorderStyle const eStyle, double const fWidth)
243 switch (eStyle)
245 // Single lines
246 case SOLID:
247 case DOTTED:
248 case DASHED:
249 case FINE_DASHED:
250 return fWidth;
252 // Double lines
253 case DOUBLE:
254 return fWidth / 3.0;
256 case THINTHICK_MEDIUMGAP:
257 case THICKTHIN_MEDIUMGAP:
258 case EMBOSSED:
259 case ENGRAVED:
260 return fWidth / 2.0;
262 case THINTHICK_SMALLGAP:
263 return fWidth - THINTHICK_SMALLGAP_line2 - THINTHICK_SMALLGAP_gap;
265 case THINTHICK_LARGEGAP:
266 return fWidth - THINTHICK_LARGEGAP_line1 - THINTHICK_LARGEGAP_line2;
268 case THICKTHIN_SMALLGAP:
269 return fWidth - THICKTHIN_SMALLGAP_line1 - THICKTHIN_SMALLGAP_gap;
271 case THICKTHIN_LARGEGAP:
272 return fWidth - THICKTHIN_LARGEGAP_line1 - THICKTHIN_LARGEGAP_line2;
274 case OUTSET:
275 return (fWidth - OUTSET_line1) / 2.0;
277 case INSET:
278 return (fWidth - INSET_line2) / 2.0;
280 default:
281 assert(false); // should only be called for known border style
282 return 0;
286 /** Get the BorderWithImpl object corresponding to the given #nStyle, all the
287 units handled by the resulting object are Twips and the
288 BorderWidthImpl::GetLine1() corresponds to the Outer Line.
290 BorderWidthImpl SvxBorderLine::getWidthImpl( SvxBorderStyle nStyle )
292 BorderWidthImpl aImpl;
294 switch ( nStyle )
296 // No line: no width
297 case NONE:
298 aImpl = BorderWidthImpl( 0, 0.0 );
299 break;
301 // Single lines
302 case SOLID:
303 case DOTTED:
304 case DASHED:
305 case FINE_DASHED:
306 aImpl = BorderWidthImpl( CHANGE_LINE1, 1.0 );
307 break;
309 // Double lines
311 case DOUBLE:
312 aImpl = BorderWidthImpl(
313 CHANGE_LINE1 | CHANGE_LINE2 | CHANGE_DIST,
314 // fdo#46112 fdo#38542 fdo#43249:
315 // non-constant witdths must sum to 1
316 1.0/3.0, 1.0/3.0, 1.0/3.0 );
317 break;
319 case DOUBLE_THIN:
320 aImpl = BorderWidthImpl(CHANGE_DIST, 10.0, 10.0, 1.0);
321 break;
323 case THINTHICK_SMALLGAP:
324 aImpl = BorderWidthImpl( CHANGE_LINE1, 1.0,
325 THINTHICK_SMALLGAP_line2, THINTHICK_SMALLGAP_gap );
326 break;
328 case THINTHICK_MEDIUMGAP:
329 aImpl = BorderWidthImpl(
330 CHANGE_LINE1 | CHANGE_LINE2 | CHANGE_DIST,
331 0.5, 0.25, 0.25 );
332 break;
334 case THINTHICK_LARGEGAP:
335 aImpl = BorderWidthImpl( CHANGE_DIST,
336 THINTHICK_LARGEGAP_line1, THINTHICK_LARGEGAP_line2, 1.0 );
337 break;
339 case THICKTHIN_SMALLGAP:
340 aImpl = BorderWidthImpl( CHANGE_LINE2, THICKTHIN_SMALLGAP_line1,
341 1.0, THICKTHIN_SMALLGAP_gap );
342 break;
344 case THICKTHIN_MEDIUMGAP:
345 aImpl = BorderWidthImpl(
346 CHANGE_LINE1 | CHANGE_LINE2 | CHANGE_DIST,
347 0.25, 0.5, 0.25 );
348 break;
350 case THICKTHIN_LARGEGAP:
351 aImpl = BorderWidthImpl( CHANGE_DIST, THICKTHIN_LARGEGAP_line1,
352 THICKTHIN_LARGEGAP_line2, 1.0 );
353 break;
355 // Engraved / Embossed
357 * Word compat: the lines widths are exactly following this rule, shouldbe:
358 * 0.75pt up to 3pt and then 3pt
361 case EMBOSSED:
362 case ENGRAVED:
363 aImpl = BorderWidthImpl(
364 CHANGE_LINE1 | CHANGE_LINE2 | CHANGE_DIST,
365 0.25, 0.25, 0.5 );
366 break;
368 // Inset / Outset
370 * Word compat: the gap width should be measured relatively to the biggest width for the
371 * row or column.
373 case OUTSET:
374 aImpl = BorderWidthImpl(
375 CHANGE_LINE2 | CHANGE_DIST,
376 OUTSET_line1, 0.5, 0.5 );
377 break;
379 case INSET:
380 aImpl = BorderWidthImpl(
381 CHANGE_LINE1 | CHANGE_DIST,
382 0.5, INSET_line2, 0.5 );
383 break;
386 return aImpl;
389 // -----------------------------------------------------------------------
391 SvxBorderLine::SvxBorderLine( const SvxBorderLine& r )
393 *this = r;
396 // -----------------------------------------------------------------------
398 SvxBorderLine& SvxBorderLine::operator=( const SvxBorderLine& r )
400 aColor = r.aColor;
401 m_nWidth = r.m_nWidth;
402 m_aWidthImpl = r.m_aWidthImpl;
403 m_bMirrorWidths = r.m_bMirrorWidths;
404 m_nMult = r.m_nMult;
405 m_nDiv = r.m_nDiv;
406 m_nStyle = r.m_nStyle;
407 m_bUseLeftTop = r.m_bUseLeftTop;
408 m_pColorOutFn = r.m_pColorOutFn;
409 m_pColorInFn = r.m_pColorInFn;
410 m_pColorGapFn = r.m_pColorGapFn;
411 return *this;
414 // -----------------------------------------------------------------------
416 void SvxBorderLine::ScaleMetrics( long nMult, long nDiv )
418 m_nMult = nMult;
419 m_nDiv = nDiv;
422 void SvxBorderLine::GuessLinesWidths( SvxBorderStyle nStyle, sal_uInt16 nOut, sal_uInt16 nIn, sal_uInt16 nDist )
424 if (NONE == nStyle)
426 nStyle = SOLID;
427 if ( nOut > 0 && nIn > 0 )
428 nStyle = DOUBLE;
431 if ( nStyle == DOUBLE )
433 static SvxBorderStyle aDoubleStyles[] =
435 DOUBLE,
436 DOUBLE_THIN,
437 THINTHICK_SMALLGAP,
438 THINTHICK_MEDIUMGAP,
439 THINTHICK_LARGEGAP,
440 THICKTHIN_SMALLGAP,
441 THICKTHIN_MEDIUMGAP,
442 THICKTHIN_LARGEGAP
445 size_t const len = SAL_N_ELEMENTS(aDoubleStyles);
446 long nWidth = 0;
447 SvxBorderStyle nTestStyle(NONE);
448 for (size_t i = 0; i < len && nWidth == 0; ++i)
450 nTestStyle = aDoubleStyles[i];
451 BorderWidthImpl aWidthImpl = getWidthImpl( nTestStyle );
452 nWidth = aWidthImpl.GuessWidth( nOut, nIn, nDist );
455 // If anything matched, then set it
456 if ( nWidth > 0 )
458 nStyle = nTestStyle;
459 SetBorderLineStyle(nStyle);
460 m_nWidth = nWidth;
462 else
464 // fdo#38542: not a known double, default to something custom...
465 SetBorderLineStyle(nStyle);
466 m_nWidth = nOut + nIn + nDist;
467 if (nOut + nIn + nDist)
469 m_aWidthImpl = BorderWidthImpl(
470 CHANGE_LINE1 | CHANGE_LINE2 | CHANGE_DIST,
471 static_cast<double>(nOut ) / static_cast<double>(m_nWidth),
472 static_cast<double>(nIn ) / static_cast<double>(m_nWidth),
473 static_cast<double>(nDist) / static_cast<double>(m_nWidth));
477 else
479 SetBorderLineStyle(nStyle);
480 if (nOut == 0 && nIn > 0)
482 // If only inner width is given swap inner and outer widths for
483 // single line styles, otherwise GuessWidth() marks this as invalid
484 // and returns a 0 width.
485 switch (nStyle)
487 case SOLID:
488 case DOTTED:
489 case DASHED:
490 case FINE_DASHED:
491 ::std::swap( nOut, nIn);
492 break;
493 default:
494 ; // nothing
497 m_nWidth = m_aWidthImpl.GuessWidth( nOut, nIn, nDist );
501 sal_uInt16 SvxBorderLine::GetOutWidth() const
503 sal_uInt16 nOut = (sal_uInt16)Scale( m_aWidthImpl.GetLine1( m_nWidth ), m_nMult, m_nDiv );
504 if ( m_bMirrorWidths )
505 nOut = (sal_uInt16)Scale( m_aWidthImpl.GetLine2( m_nWidth ), m_nMult, m_nDiv );
506 return nOut;
509 sal_uInt16 SvxBorderLine::GetInWidth() const
511 sal_uInt16 nIn = (sal_uInt16)Scale( m_aWidthImpl.GetLine2( m_nWidth ), m_nMult, m_nDiv );
512 if ( m_bMirrorWidths )
513 nIn = (sal_uInt16)Scale( m_aWidthImpl.GetLine1( m_nWidth ), m_nMult, m_nDiv );
514 return nIn;
517 sal_uInt16 SvxBorderLine::GetDistance() const
519 return (sal_uInt16)Scale( m_aWidthImpl.GetGap( m_nWidth ), m_nMult, m_nDiv );
522 // -----------------------------------------------------------------------
524 sal_Bool SvxBorderLine::operator==( const SvxBorderLine& rCmp ) const
526 return ( ( aColor == rCmp.aColor ) &&
527 ( m_nWidth == rCmp.m_nWidth ) &&
528 ( m_bMirrorWidths == rCmp.m_bMirrorWidths ) &&
529 ( m_aWidthImpl == rCmp.m_aWidthImpl ) &&
530 ( m_nStyle == rCmp.GetBorderLineStyle()) &&
531 ( m_bUseLeftTop == rCmp.m_bUseLeftTop ) &&
532 ( m_pColorOutFn == rCmp.m_pColorOutFn ) &&
533 ( m_pColorInFn == rCmp.m_pColorInFn ) &&
534 ( m_pColorGapFn == rCmp.m_pColorGapFn ) );
537 void SvxBorderLine::SetBorderLineStyle( SvxBorderStyle nNew )
539 m_nStyle = nNew;
540 m_aWidthImpl = getWidthImpl( m_nStyle );
542 switch ( nNew )
544 case EMBOSSED:
545 m_pColorOutFn = threeDLightColor;
546 m_pColorInFn = threeDDarkColor;
547 m_pColorGapFn = threeDMediumColor;
548 m_bUseLeftTop = true;
549 break;
550 case ENGRAVED:
551 m_pColorOutFn = threeDDarkColor;
552 m_pColorInFn = threeDLightColor;
553 m_pColorGapFn = threeDMediumColor;
554 m_bUseLeftTop = true;
555 break;
556 case OUTSET:
557 m_pColorOutFn = lightColor;
558 m_pColorInFn = darkColor;
559 m_bUseLeftTop = true;
560 m_pColorGapFn = NULL;
561 break;
562 case INSET:
563 m_pColorOutFn = darkColor;
564 m_pColorInFn = lightColor;
565 m_bUseLeftTop = true;
566 m_pColorGapFn = NULL;
567 break;
568 default:
569 m_pColorOutFn = darkColor;
570 m_pColorInFn = darkColor;
571 m_bUseLeftTop = false;
572 m_pColorGapFn = NULL;
573 break;
577 Color SvxBorderLine::GetColorOut( bool bLeftOrTop ) const
579 Color aResult = aColor;
581 if ( m_aWidthImpl.IsDouble() && m_pColorOutFn != NULL )
583 if ( !bLeftOrTop && m_bUseLeftTop )
584 aResult = (*m_pColorInFn)( aColor );
585 else
586 aResult = (*m_pColorOutFn)( aColor );
589 return aResult;
592 Color SvxBorderLine::GetColorIn( bool bLeftOrTop ) const
594 Color aResult = aColor;
596 if ( m_aWidthImpl.IsDouble() && m_pColorInFn != NULL )
598 if ( !bLeftOrTop && m_bUseLeftTop )
599 aResult = (*m_pColorOutFn)( aColor );
600 else
601 aResult = (*m_pColorInFn)( aColor );
604 return aResult;
607 Color SvxBorderLine::GetColorGap( ) const
609 Color aResult = aColor;
611 if ( m_aWidthImpl.IsDouble() && m_pColorGapFn != NULL )
613 aResult = (*m_pColorGapFn)( aColor );
616 return aResult;
619 // -----------------------------------------------------------------------
621 XubString SvxBorderLine::GetValueString( SfxMapUnit eSrcUnit,
622 SfxMapUnit eDestUnit,
623 const IntlWrapper* pIntl,
624 sal_Bool bMetricStr) const
626 static const sal_uInt16 aStyleIds[] =
628 RID_SOLID,
629 RID_DOTTED,
630 RID_DASHED,
631 RID_DOUBLE,
632 RID_THINTHICK_SMALLGAP,
633 RID_THINTHICK_MEDIUMGAP,
634 RID_THINTHICK_LARGEGAP,
635 RID_THICKTHIN_SMALLGAP,
636 RID_THICKTHIN_MEDIUMGAP,
637 RID_THICKTHIN_LARGEGAP,
638 RID_EMBOSSED,
639 RID_ENGRAVED,
640 RID_OUTSET,
641 RID_INSET
643 sal_uInt16 nResId = aStyleIds[m_nStyle];
644 String aStr;
645 aStr += sal_Unicode('(');
646 aStr += ::GetColorString( aColor );
647 aStr += cpDelim;
649 if ( nResId )
650 aStr += EE_RESSTR(nResId);
651 else
653 String sMetric = EE_RESSTR(GetMetricId( eDestUnit ));
654 aStr += GetMetricText( (long)GetInWidth(), eSrcUnit, eDestUnit, pIntl );
655 if ( bMetricStr )
656 aStr += sMetric;
657 aStr += cpDelim;
658 aStr += GetMetricText( (long)GetOutWidth(), eSrcUnit, eDestUnit, pIntl );
659 if ( bMetricStr )
660 aStr += sMetric;
661 aStr += cpDelim;
662 aStr += GetMetricText( (long)GetDistance(), eSrcUnit, eDestUnit, pIntl );
663 if ( bMetricStr )
664 aStr += sMetric;
666 aStr += sal_Unicode(')');
667 return aStr;
670 bool SvxBorderLine::HasPriority( const SvxBorderLine& rOtherLine ) const
672 const sal_uInt16 nThisSize = GetOutWidth() + GetDistance() + GetInWidth();
673 const sal_uInt16 nOtherSize = rOtherLine.GetOutWidth() + rOtherLine.GetDistance() + rOtherLine.GetInWidth();
675 if ( nThisSize > nOtherSize )
677 return true;
679 else if ( nThisSize < nOtherSize )
681 return false;
683 else if ( rOtherLine.GetInWidth() && !GetInWidth() )
685 return true;
688 return false;
691 bool operator!=( const SvxBorderLine& rLeft, const SvxBorderLine& rRight )
693 return !(rLeft == rRight);
696 } // namespace editeng
698 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */