tdf#137968 Added check for the headings order

Headings' levels order is important for readers and converters.
Check firstly finds out if the node is a TextNode, then gets it's OutlineLevel.
If it is greater than 0, i.e. the node is a heading, then it checks if
node's heading level is greater then previous one's by more than 1.
If so, warning is thrown.
So the first heading level is always 1 and no heading levels are greater then previous
by more than 1.

Change-Id: I39d230d747699eddd36610193bde9b23f348e257
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/105274
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
Tested-by: Jenkins
diff --git a/sw/inc/AccessibilityCheckStrings.hrc b/sw/inc/AccessibilityCheckStrings.hrc
index 73ac504..7470ab4 100644
--- a/sw/inc/AccessibilityCheckStrings.hrc
+++ b/sw/inc/AccessibilityCheckStrings.hrc
@@ -26,6 +26,7 @@
#define STR_NON_INTERACTIVE_FORMS       NC_("STR_NON_INTERACTIVE_FORMS", "An input form is not interactive.")
#define STR_FLOATING_TEXT               NC_("STR_FLOATING_TEXT", "Avoid floating text.")
#define STR_HEADING_IN_TABLE            NC_("STR_HEADING_IN_TABLE", "Tables must not contain headings.")
#define STR_HEADING_ORDER               NC_("STR_HEADING_ORDER", "Keep headings' levels ordered. Heading level %LEVEL_CURRENT% must not go after %LEVEL_PREV%.")

#define STR_DOCUMENT_DEFAULT_LANGUAGE   NC_("STR_DOCUMENT_DEFAULT_LANGUAGE", "Document default language is not set")
#define STR_STYLE_NO_LANGUAGE           NC_("STR_STYLE_NO_LANGUAGE", "Style '%STYLE_NAME%' has no language set")
diff --git a/sw/source/core/access/AccessibilityCheck.cxx b/sw/source/core/access/AccessibilityCheck.cxx
index 08e8f1d..3f97837 100644
--- a/sw/source/core/access/AccessibilityCheck.cxx
+++ b/sw/source/core/access/AccessibilityCheck.cxx
@@ -726,6 +726,48 @@ public:
    }
};

/// Checking if headings are ordered correctly.
class HeadingOrderCheck : public NodeCheck
{
public:
    HeadingOrderCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
        : NodeCheck(rIssueCollection)
    {
    }

    void check(SwNode* pCurrent) override
    {
        const SwTextNode* pTextNode = pCurrent->GetTextNode();
        if (!pTextNode)
            return;

        // If outline level stands for heading level...
        const int currentLevel = pTextNode->GetAttrOutlineLevel();
        if (currentLevel)
        {
            // ... and if is bigger than previous by more than 1, warn.
            if (currentLevel - m_prevLevel > 1)
            {
                // Preparing and posting a warning.
                OUString resultString = SwResId(STR_HEADING_ORDER);
                resultString
                    = resultString.replaceAll("%LEVEL_CURRENT%", OUString::number(currentLevel));
                resultString
                    = resultString.replaceAll("%LEVEL_PREV%", OUString::number(m_prevLevel));

                lclAddIssue(m_rIssueCollection, resultString);
            }

            // Updating previous level.
            m_prevLevel = currentLevel;
        }
    }

private:
    // Previous heading level to compare with.
    int m_prevLevel = 0;
};

class DocumentCheck : public BaseCheck
{
public:
@@ -879,6 +921,7 @@ void AccessibilityCheck::check()
    aNodeChecks.push_back(std::make_unique<NonInteractiveFormCheck>(m_aIssueCollection));
    aNodeChecks.push_back(std::make_unique<FloatingTextCheck>(m_aIssueCollection));
    aNodeChecks.push_back(std::make_unique<TableHeadingCheck>(m_aIssueCollection));
    aNodeChecks.push_back(std::make_unique<HeadingOrderCheck>(m_aIssueCollection));

    auto const& pNodes = m_pDoc->GetNodes();
    SwNode* pNode = nullptr;