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