Chris@286: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@286: Chris@286: /* Chris@286: Sonic Visualiser Chris@286: An audio file viewer and annotation editor. Chris@286: Centre for Digital Music, Queen Mary, University of London. Chris@286: This file copyright 2006-2007 Chris Cannam and QMUL. Chris@286: Chris@286: This program is free software; you can redistribute it and/or Chris@286: modify it under the terms of the GNU General Public License as Chris@286: published by the Free Software Foundation; either version 2 of the Chris@286: License, or (at your option) any later version. See the file Chris@286: COPYING included with this distribution for more information. Chris@286: */ Chris@286: Chris@286: #include "TextAbbrev.h" Chris@286: Chris@286: #include Chris@286: #include Chris@286: Chris@294: #include Chris@294: Chris@286: QString Chris@286: TextAbbrev::getDefaultEllipsis() Chris@286: { Chris@286: return "..."; Chris@286: } Chris@286: Chris@286: int Chris@286: TextAbbrev::getFuzzLength(QString ellipsis) Chris@286: { Chris@286: int len = ellipsis.length(); Chris@286: if (len < 3) return len + 3; Chris@286: else if (len > 5) return len + 5; Chris@286: else return len * 2; Chris@286: } Chris@286: Chris@286: int Chris@286: TextAbbrev::getFuzzWidth(const QFontMetrics &metrics, QString ellipsis) Chris@286: { Chris@286: int width = metrics.width(ellipsis); Chris@286: return width * 2; Chris@286: } Chris@286: Chris@286: QString Chris@286: TextAbbrev::abbreviateTo(QString text, int characters, Policy policy, Chris@286: QString ellipsis) Chris@286: { Chris@286: switch (policy) { Chris@286: Chris@286: case ElideEnd: Chris@286: case ElideEndAndCommonPrefixes: Chris@286: text = text.left(characters) + ellipsis; Chris@286: break; Chris@286: Chris@286: case ElideStart: Chris@286: text = ellipsis + text.right(characters); Chris@286: break; Chris@286: Chris@286: case ElideMiddle: Chris@286: if (characters > 2) { Chris@286: text = text.left(characters/2 + 1) + ellipsis Chris@286: + text.right(characters - (characters/2 + 1)); Chris@286: } else { Chris@286: text = text.left(characters) + ellipsis; Chris@286: } Chris@286: break; Chris@286: } Chris@286: Chris@286: return text; Chris@286: } Chris@286: Chris@286: QString Chris@286: TextAbbrev::abbreviate(QString text, int maxLength, Policy policy, bool fuzzy, Chris@286: QString ellipsis) Chris@286: { Chris@286: if (ellipsis == "") ellipsis = getDefaultEllipsis(); Chris@286: int fl = (fuzzy ? getFuzzLength(ellipsis) : 0); Chris@286: if (maxLength <= ellipsis.length()) maxLength = ellipsis.length() + 1; Chris@286: if (text.length() <= maxLength + fl) return text; Chris@286: Chris@286: int truncated = maxLength - ellipsis.length(); Chris@286: return abbreviateTo(text, truncated, policy, ellipsis); Chris@286: } Chris@286: Chris@286: QString Chris@286: TextAbbrev::abbreviate(QString text, Chris@286: const QFontMetrics &metrics, int &maxWidth, Chris@286: Policy policy, QString ellipsis) Chris@286: { Chris@286: if (ellipsis == "") ellipsis = getDefaultEllipsis(); Chris@286: Chris@286: int tw = metrics.width(text); Chris@286: Chris@286: if (tw <= maxWidth) { Chris@286: maxWidth = tw; Chris@286: return text; Chris@286: } Chris@286: Chris@286: int truncated = text.length(); Chris@286: QString original = text; Chris@286: Chris@286: while (tw > maxWidth && truncated > 1) { Chris@286: Chris@286: truncated--; Chris@286: Chris@286: if (truncated > ellipsis.length()) { Chris@286: text = abbreviateTo(original, truncated, policy, ellipsis); Chris@286: } else { Chris@286: break; Chris@286: } Chris@286: Chris@286: tw = metrics.width(text); Chris@286: } Chris@286: Chris@286: maxWidth = tw; Chris@286: return text; Chris@286: } Chris@286: Chris@286: QStringList Chris@286: TextAbbrev::abbreviate(const QStringList &texts, int maxLength, Chris@286: Policy policy, bool fuzzy, QString ellipsis) Chris@286: { Chris@286: if (policy == ElideEndAndCommonPrefixes && Chris@286: texts.size() > 1) { Chris@286: Chris@286: if (ellipsis == "") ellipsis = getDefaultEllipsis(); Chris@286: int fl = (fuzzy ? getFuzzLength(ellipsis) : 0); Chris@286: if (maxLength <= ellipsis.length()) maxLength = ellipsis.length() + 1; Chris@286: Chris@286: int maxOrigLength = 0; Chris@286: for (int i = 0; i < texts.size(); ++i) { Chris@286: int len = texts[i].length(); Chris@286: if (len > maxOrigLength) maxOrigLength = len; Chris@286: } Chris@286: if (maxOrigLength <= maxLength + fl) return texts; Chris@286: Chris@286: return abbreviate(elidePrefixes Chris@286: (texts, maxOrigLength - maxLength, ellipsis), Chris@286: maxLength, ElideEnd, fuzzy, ellipsis); Chris@286: } Chris@286: Chris@286: QStringList results; Chris@286: for (int i = 0; i < texts.size(); ++i) { Chris@286: results.push_back Chris@286: (abbreviate(texts[i], maxLength, policy, fuzzy, ellipsis)); Chris@286: } Chris@286: return results; Chris@286: } Chris@286: Chris@286: QStringList Chris@286: TextAbbrev::abbreviate(const QStringList &texts, const QFontMetrics &metrics, Chris@286: int &maxWidth, Policy policy, QString ellipsis) Chris@286: { Chris@286: if (policy == ElideEndAndCommonPrefixes && Chris@286: texts.size() > 1) { Chris@286: Chris@286: if (ellipsis == "") ellipsis = getDefaultEllipsis(); Chris@286: Chris@286: int maxOrigWidth = 0; Chris@286: for (int i = 0; i < texts.size(); ++i) { Chris@286: int w = metrics.width(texts[i]); Chris@286: if (w > maxOrigWidth) maxOrigWidth = w; Chris@286: } Chris@286: Chris@286: return abbreviate(elidePrefixes(texts, metrics, Chris@286: maxOrigWidth - maxWidth, ellipsis), Chris@286: metrics, maxWidth, ElideEnd, ellipsis); Chris@286: } Chris@286: Chris@286: QStringList results; Chris@286: int maxAbbrWidth = 0; Chris@286: for (int i = 0; i < texts.size(); ++i) { Chris@286: int width = maxWidth; Chris@286: QString abbr = abbreviate(texts[i], metrics, width, policy, ellipsis); Chris@286: if (width > maxAbbrWidth) maxAbbrWidth = width; Chris@286: results.push_back(abbr); Chris@286: } Chris@286: maxWidth = maxAbbrWidth; Chris@286: return results; Chris@286: } Chris@286: Chris@286: QStringList Chris@286: TextAbbrev::elidePrefixes(const QStringList &texts, Chris@286: int targetReduction, Chris@286: QString ellipsis) Chris@286: { Chris@286: if (texts.empty()) return texts; Chris@286: int plen = getPrefixLength(texts); Chris@286: int fl = getFuzzLength(ellipsis); Chris@286: if (plen < fl) return texts; Chris@286: Chris@286: QString prefix = texts[0].left(plen); Chris@286: int truncated = plen; Chris@286: if (plen >= targetReduction + fl) { Chris@286: truncated = plen - targetReduction; Chris@286: } else { Chris@286: truncated = fl; Chris@286: } Chris@286: prefix = abbreviate(prefix, truncated, ElideEnd, false, ellipsis); Chris@286: Chris@286: QStringList results; Chris@286: for (int i = 0; i < texts.size(); ++i) { Chris@286: results.push_back Chris@286: (prefix + texts[i].right(texts[i].length() - plen)); Chris@286: } Chris@286: return results; Chris@286: } Chris@286: Chris@286: QStringList Chris@286: TextAbbrev::elidePrefixes(const QStringList &texts, Chris@286: const QFontMetrics &metrics, Chris@286: int targetWidthReduction, Chris@286: QString ellipsis) Chris@286: { Chris@286: if (texts.empty()) return texts; Chris@286: int plen = getPrefixLength(texts); Chris@286: int fl = getFuzzLength(ellipsis); Chris@286: if (plen < fl) return texts; Chris@286: Chris@286: QString prefix = texts[0].left(plen); Chris@286: int pwid = metrics.width(prefix); Chris@286: int twid = pwid - targetWidthReduction; Chris@286: if (twid < metrics.width(ellipsis) * 2) twid = metrics.width(ellipsis) * 2; Chris@286: prefix = abbreviate(prefix, metrics, twid, ElideEnd, ellipsis); Chris@286: Chris@286: QStringList results; Chris@286: for (int i = 0; i < texts.size(); ++i) { Chris@286: results.push_back Chris@286: (prefix + texts[i].right(texts[i].length() - plen)); Chris@286: } Chris@286: return results; Chris@286: } Chris@286: Chris@286: static bool Chris@286: havePrefix(QString prefix, const QStringList &texts) Chris@286: { Chris@286: for (int i = 1; i < texts.size(); ++i) { Chris@286: if (!texts[i].startsWith(prefix)) return false; Chris@286: } Chris@286: return true; Chris@286: } Chris@286: Chris@286: int Chris@286: TextAbbrev::getPrefixLength(const QStringList &texts) Chris@286: { Chris@286: QString reference = texts[0]; Chris@286: Chris@286: if (reference == "" || havePrefix(reference, texts)) { Chris@286: return reference.length(); Chris@286: } Chris@286: Chris@286: int candidate = reference.length(); Chris@286: QString splitChars(";:,./#-!()$_+=[]{}\\"); Chris@286: Chris@286: while (--candidate > 1) { Chris@286: if (splitChars.contains(reference[candidate])) { Chris@294: if (havePrefix(reference.left(candidate), texts)) { Chris@286: break; Chris@286: } Chris@286: } Chris@286: } Chris@286: Chris@294: // std::cerr << "TextAbbrev::getPrefixLength: prefix length is " << candidate << std::endl; Chris@294: // for (int i = 0; i < texts.size(); ++i) { Chris@294: // std::cerr << texts[i].left(candidate).toStdString() << "|" << texts[i].right(texts[i].length() - candidate).toStdString() << std::endl; Chris@294: // } Chris@294: Chris@286: return candidate; Chris@286: } Chris@286: