bump product version to 5.0.4.1
[LibreOffice.git] / editeng / source / items / borderline.cxx
blob2a132dfc94da162f7779c9d6968da9ad6be578d1
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 20:
123 return SOLID;
124 case 6:
125 return DOTTED;
126 case 7:
127 return DASHED;
128 case 22:
129 return FINE_DASHED;
130 case 8:
131 return DASH_DOT;
132 case 9:
133 return DASH_DOT_DOT;
134 // then the shading beams which we represent by a double line
135 case 23:
136 return DOUBLE;
137 // then the double lines, for which we have good matches
138 case 3:
139 case 10: // Don't have triple so use double
140 case 21: // Don't have double wave: use double instead
141 return DOUBLE;
142 case 11:
143 return THINTHICK_SMALLGAP;
144 case 12:
145 case 13: // Don't have thin thick thin, so use thick thin
146 return THICKTHIN_SMALLGAP;
147 case 14:
148 return THINTHICK_MEDIUMGAP;
149 case 15:
150 case 16: // Don't have thin thick thin, so use thick thin
151 return THICKTHIN_MEDIUMGAP;
152 case 17:
153 return THINTHICK_LARGEGAP;
154 case 18:
155 case 19: // Don't have thin thick thin, so use thick thin
156 return THICKTHIN_LARGEGAP;
157 case 24:
158 return EMBOSSED;
159 case 25:
160 return ENGRAVED;
161 case 26:
162 return OUTSET;
163 case 27:
164 return INSET;
165 default:
166 return css::table::BorderLineStyle::NONE;
170 static const double THINTHICK_SMALLGAP_line2 = 15.0;
171 static const double THINTHICK_SMALLGAP_gap = 15.0;
172 static const double THINTHICK_LARGEGAP_line1 = 30.0;
173 static const double THINTHICK_LARGEGAP_line2 = 15.0;
174 static const double THICKTHIN_SMALLGAP_line1 = 15.0;
175 static const double THICKTHIN_SMALLGAP_gap = 15.0;
176 static const double THICKTHIN_LARGEGAP_line1 = 15.0;
177 static const double THICKTHIN_LARGEGAP_line2 = 30.0;
178 static const double OUTSET_line1 = 15.0;
179 static const double INSET_line2 = 15.0;
181 double
182 ConvertBorderWidthFromWord(SvxBorderStyle const eStyle, double const i_fWidth,
183 int const nWordLineStyle)
185 // fdo#68779: at least for RTF, 0.75pt is the default if width is missing
186 double const fWidth((i_fWidth == 0.0) ? 15.0 : i_fWidth);
187 switch (eStyle)
189 // Single lines
190 case SOLID:
191 switch (nWordLineStyle)
193 case 2:
194 return (fWidth * 2.0); // thick
195 case 5: // fdo#55526: map 0 hairline width to > 0
196 return (fWidth > 1.0) ? fWidth : 1.0;
197 default:
198 return fWidth;
200 break;
202 case DOTTED:
203 case DASHED:
204 case DASH_DOT:
205 case DASH_DOT_DOT:
206 return fWidth;
208 // Display a minimum effective border width of 1pt
209 case FINE_DASHED:
210 return (fWidth > 0 && fWidth < 20) ? 20 : fWidth;
212 // Double lines
213 case DOUBLE:
214 return fWidth * 3.0;
216 case THINTHICK_MEDIUMGAP:
217 case THICKTHIN_MEDIUMGAP:
218 case EMBOSSED:
219 case ENGRAVED:
220 return fWidth * 2.0;
222 case THINTHICK_SMALLGAP:
223 return fWidth + THINTHICK_SMALLGAP_line2 + THINTHICK_SMALLGAP_gap;
225 case THINTHICK_LARGEGAP:
226 return fWidth + THINTHICK_LARGEGAP_line1 + THINTHICK_LARGEGAP_line2;
228 case THICKTHIN_SMALLGAP:
229 return fWidth + THICKTHIN_SMALLGAP_line1 + THICKTHIN_SMALLGAP_gap;
231 case THICKTHIN_LARGEGAP:
232 return fWidth + THICKTHIN_LARGEGAP_line1 + THICKTHIN_LARGEGAP_line2;
234 case OUTSET:
235 return (fWidth * 2.0) + OUTSET_line1;
237 case INSET:
238 return (fWidth * 2.0) + INSET_line2;
240 default:
241 assert(false); // should only be called for known border style
242 return 0;
246 double
247 ConvertBorderWidthToWord(SvxBorderStyle const eStyle, double const fWidth)
249 switch (eStyle)
251 // Single lines
252 case SOLID:
253 case DOTTED:
254 case DASHED:
255 case FINE_DASHED:
256 case DASH_DOT:
257 case DASH_DOT_DOT:
258 return fWidth;
260 // Double lines
261 case DOUBLE:
262 case DOUBLE_THIN:
263 return fWidth / 3.0;
265 case THINTHICK_MEDIUMGAP:
266 case THICKTHIN_MEDIUMGAP:
267 case EMBOSSED:
268 case ENGRAVED:
269 return fWidth / 2.0;
271 case THINTHICK_SMALLGAP:
272 return fWidth - THINTHICK_SMALLGAP_line2 - THINTHICK_SMALLGAP_gap;
274 case THINTHICK_LARGEGAP:
275 return fWidth - THINTHICK_LARGEGAP_line1 - THINTHICK_LARGEGAP_line2;
277 case THICKTHIN_SMALLGAP:
278 return fWidth - THICKTHIN_SMALLGAP_line1 - THICKTHIN_SMALLGAP_gap;
280 case THICKTHIN_LARGEGAP:
281 return fWidth - THICKTHIN_LARGEGAP_line1 - THICKTHIN_LARGEGAP_line2;
283 case OUTSET:
284 return (fWidth - OUTSET_line1) / 2.0;
286 case INSET:
287 return (fWidth - INSET_line2) / 2.0;
289 case css::table::BorderLineStyle::NONE:
290 return 0;
292 default:
293 assert(false); // should only be called for known border style
294 return 0;
298 /** Get the BorderWithImpl object corresponding to the given #nStyle, all the
299 units handled by the resulting object are Twips and the
300 BorderWidthImpl::GetLine1() corresponds to the Outer Line.
302 BorderWidthImpl SvxBorderLine::getWidthImpl( SvxBorderStyle nStyle )
304 BorderWidthImpl aImpl;
306 switch ( nStyle )
308 // No line: no width
309 case css::table::BorderLineStyle::NONE:
310 aImpl = BorderWidthImpl( BorderWidthImplFlags::FIXED, 0.0 );
311 break;
313 // Single lines
314 case SOLID:
315 case DOTTED:
316 case DASHED:
317 case FINE_DASHED:
318 case DASH_DOT:
319 case DASH_DOT_DOT:
320 aImpl = BorderWidthImpl( BorderWidthImplFlags::CHANGE_LINE1, 1.0 );
321 break;
323 // Double lines
325 case DOUBLE:
326 aImpl = BorderWidthImpl(
327 BorderWidthImplFlags::CHANGE_LINE1 | BorderWidthImplFlags::CHANGE_LINE2 | BorderWidthImplFlags::CHANGE_DIST,
328 // fdo#46112 fdo#38542 fdo#43249:
329 // non-constant witdths must sum to 1
330 1.0/3.0, 1.0/3.0, 1.0/3.0 );
331 break;
333 case DOUBLE_THIN:
334 aImpl = BorderWidthImpl(BorderWidthImplFlags::CHANGE_DIST, 10.0, 10.0, 1.0);
335 break;
337 case THINTHICK_SMALLGAP:
338 aImpl = BorderWidthImpl( BorderWidthImplFlags::CHANGE_LINE1, 1.0,
339 THINTHICK_SMALLGAP_line2, THINTHICK_SMALLGAP_gap );
340 break;
342 case THINTHICK_MEDIUMGAP:
343 aImpl = BorderWidthImpl(
344 BorderWidthImplFlags::CHANGE_LINE1 | BorderWidthImplFlags::CHANGE_LINE2 | BorderWidthImplFlags::CHANGE_DIST,
345 0.5, 0.25, 0.25 );
346 break;
348 case THINTHICK_LARGEGAP:
349 aImpl = BorderWidthImpl( BorderWidthImplFlags::CHANGE_DIST,
350 THINTHICK_LARGEGAP_line1, THINTHICK_LARGEGAP_line2, 1.0 );
351 break;
353 case THICKTHIN_SMALLGAP:
354 aImpl = BorderWidthImpl( BorderWidthImplFlags::CHANGE_LINE2, THICKTHIN_SMALLGAP_line1,
355 1.0, THICKTHIN_SMALLGAP_gap );
356 break;
358 case THICKTHIN_MEDIUMGAP:
359 aImpl = BorderWidthImpl(
360 BorderWidthImplFlags::CHANGE_LINE1 | BorderWidthImplFlags::CHANGE_LINE2 | BorderWidthImplFlags::CHANGE_DIST,
361 0.25, 0.5, 0.25 );
362 break;
364 case THICKTHIN_LARGEGAP:
365 aImpl = BorderWidthImpl( BorderWidthImplFlags::CHANGE_DIST, THICKTHIN_LARGEGAP_line1,
366 THICKTHIN_LARGEGAP_line2, 1.0 );
367 break;
369 // Engraved / Embossed
371 * Word compat: the lines widths are exactly following this rule, shouldbe:
372 * 0.75pt up to 3pt and then 3pt
375 case EMBOSSED:
376 case ENGRAVED:
377 aImpl = BorderWidthImpl(
378 BorderWidthImplFlags::CHANGE_LINE1 | BorderWidthImplFlags::CHANGE_LINE2 | BorderWidthImplFlags::CHANGE_DIST,
379 0.25, 0.25, 0.5 );
380 break;
382 // Inset / Outset
384 * Word compat: the gap width should be measured relatively to the biggest width for the
385 * row or column.
387 case OUTSET:
388 aImpl = BorderWidthImpl(
389 BorderWidthImplFlags::CHANGE_LINE2 | BorderWidthImplFlags::CHANGE_DIST,
390 OUTSET_line1, 0.5, 0.5 );
391 break;
393 case INSET:
394 aImpl = BorderWidthImpl(
395 BorderWidthImplFlags::CHANGE_LINE1 | BorderWidthImplFlags::CHANGE_DIST,
396 0.5, INSET_line2, 0.5 );
397 break;
400 return aImpl;
405 SvxBorderLine::SvxBorderLine( const SvxBorderLine& r )
407 *this = r;
412 SvxBorderLine& SvxBorderLine::operator=( const SvxBorderLine& r )
414 aColor = r.aColor;
415 m_nWidth = r.m_nWidth;
416 m_aWidthImpl = r.m_aWidthImpl;
417 m_bMirrorWidths = r.m_bMirrorWidths;
418 m_nMult = r.m_nMult;
419 m_nDiv = r.m_nDiv;
420 m_nStyle = r.m_nStyle;
421 m_bUseLeftTop = r.m_bUseLeftTop;
422 m_pColorOutFn = r.m_pColorOutFn;
423 m_pColorInFn = r.m_pColorInFn;
424 m_pColorGapFn = r.m_pColorGapFn;
425 return *this;
430 void SvxBorderLine::ScaleMetrics( long nMult, long nDiv )
432 m_nMult = nMult;
433 m_nDiv = nDiv;
436 void SvxBorderLine::GuessLinesWidths( SvxBorderStyle nStyle, sal_uInt16 nOut, sal_uInt16 nIn, sal_uInt16 nDist )
438 if (css::table::BorderLineStyle::NONE == nStyle)
440 nStyle = SOLID;
441 if ( nOut > 0 && nIn > 0 )
442 nStyle = DOUBLE;
445 if ( nStyle == DOUBLE )
447 static const SvxBorderStyle aDoubleStyles[] =
449 DOUBLE,
450 DOUBLE_THIN,
451 THINTHICK_SMALLGAP,
452 THINTHICK_MEDIUMGAP,
453 THINTHICK_LARGEGAP,
454 THICKTHIN_SMALLGAP,
455 THICKTHIN_MEDIUMGAP,
456 THICKTHIN_LARGEGAP
459 static size_t const len = SAL_N_ELEMENTS(aDoubleStyles);
460 long nWidth = 0;
461 SvxBorderStyle nTestStyle(css::table::BorderLineStyle::NONE);
462 for (size_t i = 0; i < len && nWidth == 0; ++i)
464 nTestStyle = aDoubleStyles[i];
465 BorderWidthImpl aWidthImpl = getWidthImpl( nTestStyle );
466 nWidth = aWidthImpl.GuessWidth( nOut, nIn, nDist );
469 // If anything matched, then set it
470 if ( nWidth > 0 )
472 nStyle = nTestStyle;
473 SetBorderLineStyle(nStyle);
474 m_nWidth = nWidth;
476 else
478 // fdo#38542: not a known double, default to something custom...
479 SetBorderLineStyle(nStyle);
480 m_nWidth = nOut + nIn + nDist;
481 if (nOut + nIn + nDist)
483 m_aWidthImpl = BorderWidthImpl(
484 BorderWidthImplFlags::CHANGE_LINE1 | BorderWidthImplFlags::CHANGE_LINE2 | BorderWidthImplFlags::CHANGE_DIST,
485 static_cast<double>(nOut ) / static_cast<double>(m_nWidth),
486 static_cast<double>(nIn ) / static_cast<double>(m_nWidth),
487 static_cast<double>(nDist) / static_cast<double>(m_nWidth));
491 else
493 SetBorderLineStyle(nStyle);
494 if (nOut == 0 && nIn > 0)
496 // If only inner width is given swap inner and outer widths for
497 // single line styles, otherwise GuessWidth() marks this as invalid
498 // and returns a 0 width.
499 switch (nStyle)
501 case SOLID:
502 case DOTTED:
503 case DASHED:
504 case FINE_DASHED:
505 case DASH_DOT:
506 case DASH_DOT_DOT:
507 ::std::swap( nOut, nIn);
508 break;
509 default:
510 ; // nothing
513 m_nWidth = m_aWidthImpl.GuessWidth( nOut, nIn, nDist );
517 sal_uInt16 SvxBorderLine::GetOutWidth() const
519 sal_uInt16 nOut = (sal_uInt16)Scale( m_aWidthImpl.GetLine1( m_nWidth ), m_nMult, m_nDiv );
520 if ( m_bMirrorWidths )
521 nOut = (sal_uInt16)Scale( m_aWidthImpl.GetLine2( m_nWidth ), m_nMult, m_nDiv );
522 return nOut;
525 sal_uInt16 SvxBorderLine::GetInWidth() const
527 sal_uInt16 nIn = (sal_uInt16)Scale( m_aWidthImpl.GetLine2( m_nWidth ), m_nMult, m_nDiv );
528 if ( m_bMirrorWidths )
529 nIn = (sal_uInt16)Scale( m_aWidthImpl.GetLine1( m_nWidth ), m_nMult, m_nDiv );
530 return nIn;
533 sal_uInt16 SvxBorderLine::GetDistance() const
535 return (sal_uInt16)Scale( m_aWidthImpl.GetGap( m_nWidth ), m_nMult, m_nDiv );
540 bool SvxBorderLine::operator==( const SvxBorderLine& rCmp ) const
542 return ( ( aColor == rCmp.aColor ) &&
543 ( m_nWidth == rCmp.m_nWidth ) &&
544 ( m_bMirrorWidths == rCmp.m_bMirrorWidths ) &&
545 ( m_aWidthImpl == rCmp.m_aWidthImpl ) &&
546 ( m_nStyle == rCmp.GetBorderLineStyle()) &&
547 ( m_bUseLeftTop == rCmp.m_bUseLeftTop ) &&
548 ( m_pColorOutFn == rCmp.m_pColorOutFn ) &&
549 ( m_pColorInFn == rCmp.m_pColorInFn ) &&
550 ( m_pColorGapFn == rCmp.m_pColorGapFn ) );
553 void SvxBorderLine::SetBorderLineStyle( SvxBorderStyle nNew )
555 m_nStyle = nNew;
556 m_aWidthImpl = getWidthImpl( m_nStyle );
558 switch ( nNew )
560 case EMBOSSED:
561 m_pColorOutFn = threeDLightColor;
562 m_pColorInFn = threeDDarkColor;
563 m_pColorGapFn = threeDMediumColor;
564 m_bUseLeftTop = true;
565 break;
566 case ENGRAVED:
567 m_pColorOutFn = threeDDarkColor;
568 m_pColorInFn = threeDLightColor;
569 m_pColorGapFn = threeDMediumColor;
570 m_bUseLeftTop = true;
571 break;
572 case OUTSET:
573 m_pColorOutFn = lightColor;
574 m_pColorInFn = darkColor;
575 m_bUseLeftTop = true;
576 m_pColorGapFn = NULL;
577 break;
578 case INSET:
579 m_pColorOutFn = darkColor;
580 m_pColorInFn = lightColor;
581 m_bUseLeftTop = true;
582 m_pColorGapFn = NULL;
583 break;
584 default:
585 m_pColorOutFn = darkColor;
586 m_pColorInFn = darkColor;
587 m_bUseLeftTop = false;
588 m_pColorGapFn = NULL;
589 break;
593 Color SvxBorderLine::GetColorOut( bool bLeftOrTop ) const
595 Color aResult = aColor;
597 if ( m_aWidthImpl.IsDouble() && m_pColorOutFn != NULL )
599 if ( !bLeftOrTop && m_bUseLeftTop )
600 aResult = (*m_pColorInFn)( aColor );
601 else
602 aResult = (*m_pColorOutFn)( aColor );
605 return aResult;
608 Color SvxBorderLine::GetColorIn( bool bLeftOrTop ) const
610 Color aResult = aColor;
612 if ( m_aWidthImpl.IsDouble() && m_pColorInFn != NULL )
614 if ( !bLeftOrTop && m_bUseLeftTop )
615 aResult = (*m_pColorOutFn)( aColor );
616 else
617 aResult = (*m_pColorInFn)( aColor );
620 return aResult;
623 Color SvxBorderLine::GetColorGap( ) const
625 Color aResult = aColor;
627 if ( m_aWidthImpl.IsDouble() && m_pColorGapFn != NULL )
629 aResult = (*m_pColorGapFn)( aColor );
632 return aResult;
635 void SvxBorderLine::SetWidth( long nWidth )
637 m_nWidth = nWidth;
640 OUString SvxBorderLine::GetValueString( SfxMapUnit eSrcUnit,
641 SfxMapUnit eDestUnit,
642 const IntlWrapper* pIntl,
643 bool bMetricStr) const
645 static const sal_uInt16 aStyleIds[] =
647 RID_SOLID,
648 RID_DOTTED,
649 RID_DASHED,
650 RID_DOUBLE,
651 RID_THINTHICK_SMALLGAP,
652 RID_THINTHICK_MEDIUMGAP,
653 RID_THINTHICK_LARGEGAP,
654 RID_THICKTHIN_SMALLGAP,
655 RID_THICKTHIN_MEDIUMGAP,
656 RID_THICKTHIN_LARGEGAP,
657 RID_EMBOSSED,
658 RID_ENGRAVED,
659 RID_OUTSET,
660 RID_INSET,
661 RID_FINE_DASHED,
662 RID_DOUBLE_THIN,
663 RID_DASH_DOT,
664 RID_DASH_DOT_DOT
666 OUString aStr = "(" + ::GetColorString( aColor ) + OUString(cpDelim);
668 if ( m_nStyle < int(SAL_N_ELEMENTS(aStyleIds)) )
670 sal_uInt16 nResId = aStyleIds[m_nStyle];
671 aStr += EE_RESSTR(nResId);
673 else
675 OUString sMetric = EE_RESSTR(GetMetricId( eDestUnit ));
676 aStr += GetMetricText( (long)GetInWidth(), eSrcUnit, eDestUnit, pIntl );
677 if ( bMetricStr )
678 aStr += sMetric;
679 aStr += cpDelim;
680 aStr += GetMetricText( (long)GetOutWidth(), eSrcUnit, eDestUnit, pIntl );
681 if ( bMetricStr )
682 aStr += sMetric;
683 aStr += cpDelim;
684 aStr += GetMetricText( (long)GetDistance(), eSrcUnit, eDestUnit, pIntl );
685 if ( bMetricStr )
686 aStr += sMetric;
688 aStr += ")";
689 return aStr;
692 bool SvxBorderLine::HasPriority( const SvxBorderLine& rOtherLine ) const
694 const sal_uInt16 nThisSize = GetScaledWidth();
695 const sal_uInt16 nOtherSize = rOtherLine.GetScaledWidth();
697 if ( nThisSize > nOtherSize )
699 return true;
701 else if ( nThisSize < nOtherSize )
703 return false;
705 else if ( rOtherLine.GetInWidth() && !GetInWidth() )
707 return true;
710 return false;
713 bool operator!=( const SvxBorderLine& rLeft, const SvxBorderLine& rRight )
715 return !(rLeft == rRight);
718 } // namespace editeng
720 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */