/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */
 
#include <sal/config.h>
 
#include <algorithm>
 
#include <basegfx/color/bcolor.hxx>
#include <basegfx/color/bcolortools.hxx>
 
#include <editeng/borderline.hxx>
#include <editeng/itemtype.hxx>
#include <editeng/editrids.hrc>
#include <editeng/eerdll.hxx>
 
using namespace ::com::sun::star::table::BorderLineStyle;
 
// class SvxBorderLine  --------------------------------------------------
 
namespace {
 
    Color lcl_compute3DColor( Color aMain, int nLight, int nMedium, int nDark )
    {
        basegfx::BColor color = aMain.getBColor( );
        basegfx::BColor hsl = basegfx::utils::rgb2hsl( color );
 
        int nCoef = 0;
        if ( hsl.getZ( ) >= 0.5 )
            nCoef = nLight;
        else if ( 0.5 > hsl.getZ() && hsl.getZ() >= 0.25 )
            nCoef = nMedium;
        else
            nCoef = nDark;
 
        double L = std::min(hsl.getZ() * 255.0 + nCoef, 255.0);
        hsl.setZ( L / 255.0 );
        color = basegfx::utils::hsl2rgb( hsl );
 
        return Color( color );
    }
} // Anonymous namespace
 
namespace editeng {
 
Color SvxBorderLine::darkColor( Color aMain )
{
    return aMain;
}
 
Color SvxBorderLine::lightColor( Color aMain )
{
 
    // Divide Luminance by 2
    basegfx::BColor color = aMain.getBColor( );
    basegfx::BColor hsl = basegfx::utils::rgb2hsl( color );
    hsl.setZ( hsl.getZ() * 0.5 );
    color = basegfx::utils::hsl2rgb( hsl );
 
    return Color( color );
}
 
 
Color SvxBorderLine::threeDLightColor( Color aMain )
{
    // These values have been defined in an empirical way
    return lcl_compute3DColor( aMain, 3, 40, 83 );
}
 
Color SvxBorderLine::threeDDarkColor( Color aMain )
{
    // These values have been defined in an empirical way
    return lcl_compute3DColor( aMain, -85, -43, -1 );
}
 
Color SvxBorderLine::threeDMediumColor( Color aMain )
{
    // These values have been defined in an empirical way
    return lcl_compute3DColor( aMain, -42, -0, 42 );
}
 
SvxBorderLine::SvxBorderLine( const Color *pCol, long nWidth,
       SvxBorderLineStyle nStyle,
       Color (*pColorOutFn)( Color ), Color (*pColorInFn)( Color ) )
: m_nWidth( nWidth )
, m_bMirrorWidths( false )
, m_aWidthImpl( SvxBorderLine::getWidthImpl( nStyle ) )
, m_nMult( 1 )
, m_nDiv( 1 )
, m_nStyle( nStyle )
, m_bUseLeftTop( false )
, m_pColorOutFn( pColorOutFn )
, m_pColorInFn( pColorInFn )
, m_pColorGapFn( nullptr )
{
    if ( pCol )
        aColor = *pCol;
}
 
 
SvxBorderLineStyle
ConvertBorderStyleFromWord(int const nWordLineStyle)
{
    switch (nWordLineStyle)
    {
        // First the single lines
        case  1:
        case  2: // thick line
        case  5: // hairline
        // and the unsupported special cases which we map to a single line
        case 20:
            return SvxBorderLineStyle::SOLID;
        case  6:
            return SvxBorderLineStyle::DOTTED;
        case  7:
            return SvxBorderLineStyle::DASHED;
        case 22:
            return SvxBorderLineStyle::FINE_DASHED;
        case 8:
            return SvxBorderLineStyle::DASH_DOT;
        case 9:
            return SvxBorderLineStyle::DASH_DOT_DOT;
        // then the shading beams which we represent by a double line
        case 23:
            return SvxBorderLineStyle::DOUBLE;
        // then the double lines, for which we have good matches
        case  3:
        case 10: // Don't have triple so use double
        case 21: // Don't have double wave: use double instead
            return SvxBorderLineStyle::DOUBLE;
        case 11:
            return SvxBorderLineStyle::THINTHICK_SMALLGAP;
        case 12:
        case 13: // Don't have thin thick thin, so use thick thin
            return SvxBorderLineStyle::THICKTHIN_SMALLGAP;
        case 14:
            return SvxBorderLineStyle::THINTHICK_MEDIUMGAP;
        case 15:
        case 16: // Don't have thin thick thin, so use thick thin
            return SvxBorderLineStyle::THICKTHIN_MEDIUMGAP;
        case 17:
            return SvxBorderLineStyle::THINTHICK_LARGEGAP;
        case 18:
        case 19: // Don't have thin thick thin, so use thick thin
            return SvxBorderLineStyle::THICKTHIN_LARGEGAP;
        case 24:
            return SvxBorderLineStyle::EMBOSSED;
        case 25:
            return SvxBorderLineStyle::ENGRAVED;
        case 26:
            return SvxBorderLineStyle::OUTSET;
        case 27:
            return SvxBorderLineStyle::INSET;
        default:
            return SvxBorderLineStyle::NONE;
    }
}
 
static const double THINTHICK_SMALLGAP_line2 = 15.0;
static const double THINTHICK_SMALLGAP_gap   = 15.0;
static const double THINTHICK_LARGEGAP_line1 = 30.0;
static const double THINTHICK_LARGEGAP_line2 = 15.0;
static const double THICKTHIN_SMALLGAP_line1 = 15.0;
static const double THICKTHIN_SMALLGAP_gap   = 15.0;
static const double THICKTHIN_LARGEGAP_line1 = 15.0;
static const double THICKTHIN_LARGEGAP_line2 = 30.0;
static const double OUTSET_line1 = 15.0;
static const double INSET_line2  = 15.0;
 
double
ConvertBorderWidthFromWord(SvxBorderLineStyle const eStyle, double const i_fWidth,
        int const nWordLineStyle)
{
    // fdo#68779: at least for RTF, 0.75pt is the default if width is missing
    double const fWidth((i_fWidth == 0.0) ? 15.0 : i_fWidth);
    switch (eStyle)
    {
        // Single lines
        case SvxBorderLineStyle::SOLID:
            switch (nWordLineStyle)
            {
                case 2:
                    return (fWidth * 2.0); // thick
                case 5: // fdo#55526: map 0 hairline width to > 0
                    return std::max(fWidth, 1.0);
                default:
                    return fWidth;
            }
            break;
 
        case SvxBorderLineStyle::DOTTED:
        case SvxBorderLineStyle::DASHED:
        case SvxBorderLineStyle::DASH_DOT:
        case SvxBorderLineStyle::DASH_DOT_DOT:
            return fWidth;
 
        // Display a minimum effective border width of 1pt
        case SvxBorderLineStyle::FINE_DASHED:
            return (fWidth > 0 && fWidth < 20) ? 20 : fWidth;
 
        // Double lines
        case SvxBorderLineStyle::DOUBLE:
            return fWidth * 3.0;
 
        case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
        case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
        case SvxBorderLineStyle::EMBOSSED:
        case SvxBorderLineStyle::ENGRAVED:
            return fWidth * 2.0;
 
        case SvxBorderLineStyle::THINTHICK_SMALLGAP:
            return fWidth + THINTHICK_SMALLGAP_line2 + THINTHICK_SMALLGAP_gap;
 
        case SvxBorderLineStyle::THINTHICK_LARGEGAP:
            return fWidth + THINTHICK_LARGEGAP_line1 + THINTHICK_LARGEGAP_line2;
 
        case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
            return fWidth + THICKTHIN_SMALLGAP_line1 + THICKTHIN_SMALLGAP_gap;
 
        case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
            return fWidth + THICKTHIN_LARGEGAP_line1 + THICKTHIN_LARGEGAP_line2;
 
        case SvxBorderLineStyle::OUTSET:
            return (fWidth * 2.0) + OUTSET_line1;
 
        case SvxBorderLineStyle::INSET:
            return (fWidth * 2.0) + INSET_line2;
 
        default:
            assert(false); // should only be called for known border style
    }
    return 0;
}
 
double
ConvertBorderWidthToWord(SvxBorderLineStyle const eStyle, double const fWidth)
{
    switch (eStyle)
    {
        // Single lines
        case SvxBorderLineStyle::SOLID:
        case SvxBorderLineStyle::DOTTED:
        case SvxBorderLineStyle::DASHED:
        case SvxBorderLineStyle::FINE_DASHED:
        case SvxBorderLineStyle::DASH_DOT:
        case SvxBorderLineStyle::DASH_DOT_DOT:
            return fWidth;
 
        // Double lines
        case SvxBorderLineStyle::DOUBLE:
        case SvxBorderLineStyle::DOUBLE_THIN:
            return fWidth / 3.0;
 
        case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
        case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
        case SvxBorderLineStyle::EMBOSSED:
        case SvxBorderLineStyle::ENGRAVED:
            return fWidth / 2.0;
 
        case SvxBorderLineStyle::THINTHICK_SMALLGAP:
            return fWidth - THINTHICK_SMALLGAP_line2 - THINTHICK_SMALLGAP_gap;
 
        case SvxBorderLineStyle::THINTHICK_LARGEGAP:
            return fWidth - THINTHICK_LARGEGAP_line1 - THINTHICK_LARGEGAP_line2;
 
        case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
            return fWidth - THICKTHIN_SMALLGAP_line1 - THICKTHIN_SMALLGAP_gap;
 
        case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
            return fWidth - THICKTHIN_LARGEGAP_line1 - THICKTHIN_LARGEGAP_line2;
 
        case SvxBorderLineStyle::OUTSET:
            return (fWidth - OUTSET_line1) / 2.0;
 
        case SvxBorderLineStyle::INSET:
            return (fWidth - INSET_line2) / 2.0;
 
        case SvxBorderLineStyle::NONE:
            return 0;
 
        default:
            assert(false); // should only be called for known border style
            return 0;
    }
}
 
/** Get the BorderWithImpl object corresponding to the given #nStyle, all the
    units handled by the resulting object are Twips and the
    BorderWidthImpl::GetLine1() corresponds to the Outer Line.
  */
BorderWidthImpl SvxBorderLine::getWidthImpl( SvxBorderLineStyle nStyle )
{
    BorderWidthImpl aImpl;
 
    switch ( nStyle )
    {
        // No line: no width
        case SvxBorderLineStyle::NONE:
            aImpl = BorderWidthImpl( BorderWidthImplFlags::FIXED, 0.0 );
            break;
 
        // Single lines
        case SvxBorderLineStyle::SOLID:
        case SvxBorderLineStyle::DOTTED:
        case SvxBorderLineStyle::DASHED:
        case SvxBorderLineStyle::FINE_DASHED:
        case SvxBorderLineStyle::DASH_DOT:
        case SvxBorderLineStyle::DASH_DOT_DOT:
            aImpl = BorderWidthImpl( BorderWidthImplFlags::CHANGE_LINE1, 1.0 );
            break;
 
        // Double lines
 
        case SvxBorderLineStyle::DOUBLE:
            aImpl = BorderWidthImpl(
                    BorderWidthImplFlags::CHANGE_LINE1 | BorderWidthImplFlags::CHANGE_LINE2 | BorderWidthImplFlags::CHANGE_DIST,
                    // fdo#46112 fdo#38542 fdo#43249:
                    // non-constant widths must sum to 1
                    1.0/3.0, 1.0/3.0, 1.0/3.0 );
            break;
 
        case SvxBorderLineStyle::DOUBLE_THIN:
            aImpl = BorderWidthImpl(BorderWidthImplFlags::CHANGE_DIST, 10.0, 10.0, 1.0);
            break;
 
        case SvxBorderLineStyle::THINTHICK_SMALLGAP:
            aImpl = BorderWidthImpl( BorderWidthImplFlags::CHANGE_LINE1, 1.0,
                    THINTHICK_SMALLGAP_line2, THINTHICK_SMALLGAP_gap );
            break;
 
        case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
            aImpl = BorderWidthImpl(
                    BorderWidthImplFlags::CHANGE_LINE1 | BorderWidthImplFlags::CHANGE_LINE2 | BorderWidthImplFlags::CHANGE_DIST,
                    0.5, 0.25, 0.25 );
            break;
 
        case SvxBorderLineStyle::THINTHICK_LARGEGAP:
            aImpl = BorderWidthImpl( BorderWidthImplFlags::CHANGE_DIST,
                    THINTHICK_LARGEGAP_line1, THINTHICK_LARGEGAP_line2, 1.0 );
            break;
 
        case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
            aImpl = BorderWidthImpl( BorderWidthImplFlags::CHANGE_LINE2, THICKTHIN_SMALLGAP_line1,
                    1.0, THICKTHIN_SMALLGAP_gap );
            break;
 
        case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
            aImpl = BorderWidthImpl(
                    BorderWidthImplFlags::CHANGE_LINE1 | BorderWidthImplFlags::CHANGE_LINE2 | BorderWidthImplFlags::CHANGE_DIST,
                    0.25, 0.5, 0.25 );
            break;
 
        case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
            aImpl = BorderWidthImpl( BorderWidthImplFlags::CHANGE_DIST, THICKTHIN_LARGEGAP_line1,
                    THICKTHIN_LARGEGAP_line2, 1.0 );
            break;
 
        // Engraved / Embossed
        /*
         *  Word compat: the lines widths are exactly following this rule, should be:
         *      0.75pt up to 3pt and then 3pt
         */
 
        case SvxBorderLineStyle::EMBOSSED:
        case SvxBorderLineStyle::ENGRAVED:
            aImpl = BorderWidthImpl(
                    BorderWidthImplFlags::CHANGE_LINE1 | BorderWidthImplFlags::CHANGE_LINE2 | BorderWidthImplFlags::CHANGE_DIST,
                    0.25, 0.25, 0.5 );
            break;
 
        // Inset / Outset
        /*
         * Word compat: the gap width should be measured relatively to the biggest width for the
         *      row or column.
         */
        case SvxBorderLineStyle::OUTSET:
            aImpl = BorderWidthImpl(
                    BorderWidthImplFlags::CHANGE_LINE2 | BorderWidthImplFlags::CHANGE_DIST,
                    OUTSET_line1, 0.5, 0.5 );
            break;
 
        case SvxBorderLineStyle::INSET:
            aImpl = BorderWidthImpl(
                    BorderWidthImplFlags::CHANGE_LINE1 | BorderWidthImplFlags::CHANGE_DIST,
                    0.5, INSET_line2, 0.5 );
            break;
    }
 
    return aImpl;
}
 
void SvxBorderLine::ScaleMetrics( long nMult, long nDiv )
{
    m_nMult = nMult;
    m_nDiv = nDiv;
}
 
void SvxBorderLine::GuessLinesWidths( SvxBorderLineStyle nStyle, sal_uInt16 nOut, sal_uInt16 nIn, sal_uInt16 nDist )
{
    if (SvxBorderLineStyle::NONE == nStyle)
    {
        nStyle = SvxBorderLineStyle::SOLID;
        if ( nOut > 0 && nIn > 0 )
            nStyle = SvxBorderLineStyle::DOUBLE;
    }
 
    if ( nStyle == SvxBorderLineStyle::DOUBLE )
    {
        static const SvxBorderLineStyle aDoubleStyles[] =
        {
            SvxBorderLineStyle::DOUBLE,
            SvxBorderLineStyle::DOUBLE_THIN,
            SvxBorderLineStyle::THINTHICK_SMALLGAP,
            SvxBorderLineStyle::THINTHICK_MEDIUMGAP,
            SvxBorderLineStyle::THINTHICK_LARGEGAP,
            SvxBorderLineStyle::THICKTHIN_SMALLGAP,
            SvxBorderLineStyle::THICKTHIN_MEDIUMGAP,
            SvxBorderLineStyle::THICKTHIN_LARGEGAP
        };
 
        static size_t const len = SAL_N_ELEMENTS(aDoubleStyles);
        long nWidth = 0;
        SvxBorderLineStyle nTestStyle(SvxBorderLineStyle::NONE);
        for (size_t i = 0; i < len && nWidth == 0; ++i)
        {
            nTestStyle = aDoubleStyles[i];
            BorderWidthImpl aWidthImpl = getWidthImpl( nTestStyle );
            nWidth = aWidthImpl.GuessWidth( nOut, nIn, nDist );
        }
 
        // If anything matched, then set it
        if ( nWidth > 0 )
        {
            nStyle = nTestStyle;
            SetBorderLineStyle(nStyle);
            m_nWidth = nWidth;
        }
        else
        {
            // fdo#38542: not a known double, default to something custom...
            SetBorderLineStyle(nStyle);
            m_nWidth = nOut + nIn + nDist;
            if (nOut + nIn + nDist)
            {
                m_aWidthImpl = BorderWidthImpl(
                    BorderWidthImplFlags::CHANGE_LINE1 | BorderWidthImplFlags::CHANGE_LINE2 | BorderWidthImplFlags::CHANGE_DIST,
                    static_cast<double>(nOut ) / static_cast<double>(m_nWidth),
                    static_cast<double>(nIn  ) / static_cast<double>(m_nWidth),
                    static_cast<double>(nDist) / static_cast<double>(m_nWidth));
            }
        }
    }
    else
    {
        SetBorderLineStyle(nStyle);
        if (nOut == 0 && nIn > 0)
        {
            // If only inner width is given swap inner and outer widths for
            // single line styles, otherwise GuessWidth() marks this as invalid
            // and returns a 0 width.
            switch (nStyle)
            {
                case SvxBorderLineStyle::SOLID:
                case SvxBorderLineStyle::DOTTED:
                case SvxBorderLineStyle::DASHED:
                case SvxBorderLineStyle::FINE_DASHED:
                case SvxBorderLineStyle::DASH_DOT:
                case SvxBorderLineStyle::DASH_DOT_DOT:
                    std::swap( nOut, nIn);
                    break;
                default:
                    ;   // nothing
            }
        }
        m_nWidth = m_aWidthImpl.GuessWidth( nOut, nIn, nDist );
    }
}
 
sal_uInt16 SvxBorderLine::GetOutWidth() const
{
    sal_uInt16 nOut = static_cast<sal_uInt16>(Scale( m_aWidthImpl.GetLine1( m_nWidth ), m_nMult, m_nDiv ));
    if ( m_bMirrorWidths )
        nOut = static_cast<sal_uInt16>(Scale( m_aWidthImpl.GetLine2( m_nWidth ), m_nMult, m_nDiv ));
    return nOut;
}
 
sal_uInt16 SvxBorderLine::GetInWidth() const
{
    sal_uInt16 nIn = static_cast<sal_uInt16>(Scale( m_aWidthImpl.GetLine2( m_nWidth ), m_nMult, m_nDiv ));
    if ( m_bMirrorWidths )
        nIn = static_cast<sal_uInt16>(Scale( m_aWidthImpl.GetLine1( m_nWidth ), m_nMult, m_nDiv ));
    return nIn;
}
 
sal_uInt16 SvxBorderLine::GetDistance() const
{
    return static_cast<sal_uInt16>(Scale( m_aWidthImpl.GetGap( m_nWidth ), m_nMult, m_nDiv ));
}
 
 
bool SvxBorderLine::operator==( const SvxBorderLine& rCmp ) const
{
    return ( ( aColor    == rCmp.aColor )            &&
             ( m_nWidth == rCmp.m_nWidth )           &&
             ( m_bMirrorWidths  == rCmp.m_bMirrorWidths )  &&
             ( m_aWidthImpl  == rCmp.m_aWidthImpl )  &&
             ( m_nStyle == rCmp.GetBorderLineStyle()) &&
             ( m_bUseLeftTop == rCmp.m_bUseLeftTop ) &&
             ( m_pColorOutFn == rCmp.m_pColorOutFn ) &&
             ( m_pColorInFn == rCmp.m_pColorInFn )   &&
             ( m_pColorGapFn == rCmp.m_pColorGapFn ) );
}
 
void SvxBorderLine::SetBorderLineStyle( SvxBorderLineStyle nNew )
{
    m_nStyle = nNew;
    m_aWidthImpl = getWidthImpl( m_nStyle );
 
    switch ( nNew )
    {
        case SvxBorderLineStyle::EMBOSSED:
            m_pColorOutFn = threeDLightColor;
            m_pColorInFn  = threeDDarkColor;
            m_pColorGapFn = threeDMediumColor;
            m_bUseLeftTop = true;
            break;
        case SvxBorderLineStyle::ENGRAVED:
            m_pColorOutFn = threeDDarkColor;
            m_pColorInFn  = threeDLightColor;
            m_pColorGapFn = threeDMediumColor;
            m_bUseLeftTop = true;
            break;
        case SvxBorderLineStyle::OUTSET:
            m_pColorOutFn = lightColor;
            m_pColorInFn  = darkColor;
            m_bUseLeftTop = true;
            m_pColorGapFn = nullptr;
            break;
        case SvxBorderLineStyle::INSET:
            m_pColorOutFn = darkColor;
            m_pColorInFn  = lightColor;
            m_bUseLeftTop = true;
            m_pColorGapFn = nullptr;
            break;
        default:
            m_pColorOutFn = darkColor;
            m_pColorInFn = darkColor;
            m_bUseLeftTop = false;
            m_pColorGapFn = nullptr;
            break;
    }
}
 
Color SvxBorderLine::GetColorOut( bool bLeftOrTop ) const
{
    Color aResult = aColor;
 
    if ( m_aWidthImpl.IsDouble() && m_pColorOutFn != nullptr )
    {
        if ( !bLeftOrTop && m_bUseLeftTop )
            aResult = (*m_pColorInFn)( aColor );
        else
            aResult = (*m_pColorOutFn)( aColor );
    }
 
    return aResult;
}
 
Color SvxBorderLine::GetColorIn( bool bLeftOrTop ) const
{
    Color aResult = aColor;
 
    if ( m_aWidthImpl.IsDouble() && m_pColorInFn != nullptr )
    {
        if ( !bLeftOrTop && m_bUseLeftTop )
            aResult = (*m_pColorOutFn)( aColor );
        else
            aResult = (*m_pColorInFn)( aColor );
    }
 
    return aResult;
}
 
Color SvxBorderLine::GetColorGap( ) const
{
    Color aResult = aColor;
 
    if ( m_aWidthImpl.IsDouble() && m_pColorGapFn != nullptr )
    {
        aResult = (*m_pColorGapFn)( aColor );
    }
 
    return aResult;
}
 
void SvxBorderLine::SetWidth( long nWidth )
{
    m_nWidth = nWidth;
}
 
OUString SvxBorderLine::GetValueString(MapUnit eSrcUnit,
                                       MapUnit eDestUnit,
                                       const IntlWrapper* pIntl,
                                       bool bMetricStr) const
{
    static const char* aStyleIds[] =
    {
        RID_SOLID,
        RID_DOTTED,
        RID_DASHED,
        RID_DOUBLE,
        RID_THINTHICK_SMALLGAP,
        RID_THINTHICK_MEDIUMGAP,
        RID_THINTHICK_LARGEGAP,
        RID_THICKTHIN_SMALLGAP,
        RID_THICKTHIN_MEDIUMGAP,
        RID_THICKTHIN_LARGEGAP,
        RID_EMBOSSED,
        RID_ENGRAVED,
        RID_OUTSET,
        RID_INSET,
        RID_FINE_DASHED,
        RID_DOUBLE_THIN,
        RID_DASH_DOT,
        RID_DASH_DOT_DOT
    };
    OUString aStr = "(" + ::GetColorString( aColor ) + OUString(cpDelim);
 
    if ( static_cast<int>(m_nStyle) < int(SAL_N_ELEMENTS(aStyleIds)) )
    {
        const char* pResId = aStyleIds[static_cast<int>(m_nStyle)];
        aStr += EditResId(pResId);
    }
    else
    {
        OUString sMetric = EditResId(GetMetricId( eDestUnit ));
        aStr += GetMetricText( static_cast<long>(GetInWidth()), eSrcUnit, eDestUnit, pIntl );
        if ( bMetricStr )
            aStr += sMetric;
        aStr += cpDelim;
        aStr += GetMetricText( static_cast<long>(GetOutWidth()), eSrcUnit, eDestUnit, pIntl );
        if ( bMetricStr )
            aStr += sMetric;
        aStr += cpDelim;
        aStr += GetMetricText( static_cast<long>(GetDistance()), eSrcUnit, eDestUnit, pIntl );
        if ( bMetricStr )
            aStr += sMetric;
    }
    aStr += ")";
    return aStr;
}
 
bool SvxBorderLine::HasPriority( const SvxBorderLine& rOtherLine ) const
{
    const sal_uInt16 nThisSize = GetScaledWidth();
    const sal_uInt16 nOtherSize = rOtherLine.GetScaledWidth();
 
    if ( nThisSize > nOtherSize )
    {
        return true;
    }
    else if ( nThisSize < nOtherSize )
    {
        return false;
    }
    else if ( rOtherLine.GetInWidth() && !GetInWidth() )
    {
        return true;
    }
 
    return false;
}
 
bool operator!=( const SvxBorderLine& rLeft, const SvxBorderLine& rRight )
{
    return !(rLeft == rRight);
}
 
} // namespace editeng
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V793 It is odd that the result of the 'nOut + nIn + nDist' statement is a part of the condition. Perhaps, this statement should have been compared with something else.