svgui  1.9
TextAbbrev.cpp
Go to the documentation of this file.
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
00002 
00003 /*
00004     Sonic Visualiser
00005     An audio file viewer and annotation editor.
00006     Centre for Digital Music, Queen Mary, University of London.
00007     This file copyright 2006-2007 Chris Cannam and QMUL.
00008     
00009     This program is free software; you can redistribute it and/or
00010     modify it under the terms of the GNU General Public License as
00011     published by the Free Software Foundation; either version 2 of the
00012     License, or (at your option) any later version.  See the file
00013     COPYING included with this distribution for more information.
00014 */
00015 
00016 #include "TextAbbrev.h"
00017 
00018 #include <QFontMetrics>
00019 #include <QApplication>
00020 
00021 #include <iostream>
00022 
00023 QString
00024 TextAbbrev::getDefaultEllipsis()
00025 {
00026     return "...";
00027 }
00028 
00029 int
00030 TextAbbrev::getFuzzLength(QString ellipsis)
00031 {
00032     int len = ellipsis.length();
00033     if (len < 3) return len + 3;
00034     else if (len > 5) return len + 5;
00035     else return len * 2;
00036 }
00037 
00038 int
00039 TextAbbrev::getFuzzWidth(const QFontMetrics &metrics, QString ellipsis)
00040 {
00041     int width = metrics.width(ellipsis);
00042     return width * 2;
00043 }
00044 
00045 QString
00046 TextAbbrev::abbreviateTo(QString text, int characters, Policy policy,
00047                          QString ellipsis)
00048 {
00049     switch (policy) {
00050 
00051     case ElideEnd:
00052     case ElideEndAndCommonPrefixes:
00053         text = text.left(characters) + ellipsis;
00054         break;
00055         
00056     case ElideStart:
00057         text = ellipsis + text.right(characters);
00058         break;
00059 
00060     case ElideMiddle:
00061         if (characters > 2) {
00062             text = text.left(characters/2 + 1) + ellipsis
00063                 + text.right(characters - (characters/2 + 1));
00064         } else {
00065             text = text.left(characters) + ellipsis;
00066         }
00067         break;
00068     }
00069 
00070     return text;
00071 }
00072 
00073 QString
00074 TextAbbrev::abbreviate(QString text, int maxLength, Policy policy, bool fuzzy,
00075                        QString ellipsis)
00076 {
00077     if (ellipsis == "") ellipsis = getDefaultEllipsis();
00078     int fl = (fuzzy ? getFuzzLength(ellipsis) : 0);
00079     if (maxLength <= ellipsis.length()) maxLength = ellipsis.length() + 1;
00080     if (text.length() <= maxLength + fl) return text;
00081 
00082     int truncated = maxLength - ellipsis.length();
00083     return abbreviateTo(text, truncated, policy, ellipsis);
00084 }
00085 
00086 QString
00087 TextAbbrev::abbreviate(QString text,
00088                        const QFontMetrics &metrics, int &maxWidth,
00089                        Policy policy, QString ellipsis)
00090 {
00091     if (ellipsis == "") ellipsis = getDefaultEllipsis();
00092 
00093     int tw = metrics.width(text);
00094 
00095     if (tw <= maxWidth) {
00096         maxWidth = tw;
00097         return text;
00098     }
00099 
00100     int truncated = text.length();
00101     QString original = text;
00102 
00103     while (tw > maxWidth && truncated > 1) {
00104 
00105         truncated--;
00106 
00107         if (truncated > ellipsis.length()) {
00108             text = abbreviateTo(original, truncated, policy, ellipsis);
00109         } else {
00110             break;
00111         }
00112 
00113         tw = metrics.width(text);
00114     }
00115 
00116     maxWidth = tw;
00117     return text;
00118 }
00119 
00120 QStringList
00121 TextAbbrev::abbreviate(const QStringList &texts, int maxLength,
00122                        Policy policy, bool fuzzy, QString ellipsis)
00123 {
00124     if (policy == ElideEndAndCommonPrefixes &&
00125         texts.size() > 1) {
00126 
00127         if (ellipsis == "") ellipsis = getDefaultEllipsis();
00128         int fl = (fuzzy ? getFuzzLength(ellipsis) : 0);
00129         if (maxLength <= ellipsis.length()) maxLength = ellipsis.length() + 1;
00130 
00131         int maxOrigLength = 0;
00132         for (int i = 0; i < texts.size(); ++i) {
00133             int len = texts[i].length();
00134             if (len > maxOrigLength) maxOrigLength = len;
00135         }
00136         if (maxOrigLength <= maxLength + fl) return texts;
00137 
00138         return abbreviate(elidePrefixes
00139                           (texts, maxOrigLength - maxLength, ellipsis),
00140                           maxLength, ElideEnd, fuzzy, ellipsis);
00141     }
00142 
00143     QStringList results;
00144     for (int i = 0; i < texts.size(); ++i) {
00145         results.push_back
00146             (abbreviate(texts[i], maxLength, policy, fuzzy, ellipsis));
00147     }
00148     return results;
00149 }
00150 
00151 QStringList
00152 TextAbbrev::abbreviate(const QStringList &texts, const QFontMetrics &metrics,
00153                        int &maxWidth, Policy policy, QString ellipsis)
00154 {
00155     if (policy == ElideEndAndCommonPrefixes &&
00156         texts.size() > 1) {
00157 
00158         if (ellipsis == "") ellipsis = getDefaultEllipsis();
00159 
00160         int maxOrigWidth = 0;
00161         for (int i = 0; i < texts.size(); ++i) {
00162             int w = metrics.width(texts[i]);
00163             if (w > maxOrigWidth) maxOrigWidth = w;
00164         }
00165 
00166         return abbreviate(elidePrefixes(texts, metrics,
00167                                         maxOrigWidth - maxWidth, ellipsis),
00168                           metrics, maxWidth, ElideEnd, ellipsis);
00169     }
00170 
00171     QStringList results;
00172     int maxAbbrWidth = 0;
00173     for (int i = 0; i < texts.size(); ++i) {
00174         int width = maxWidth;
00175         QString abbr = abbreviate(texts[i], metrics, width, policy, ellipsis);
00176         if (width > maxAbbrWidth) maxAbbrWidth = width;
00177         results.push_back(abbr);
00178     }
00179     maxWidth = maxAbbrWidth;
00180     return results;
00181 }
00182 
00183 QStringList
00184 TextAbbrev::elidePrefixes(const QStringList &texts,
00185                           int targetReduction,
00186                           QString ellipsis)
00187 {
00188     if (texts.empty()) return texts;
00189     int plen = getPrefixLength(texts);
00190     int fl = getFuzzLength(ellipsis);
00191     if (plen < fl) return texts;
00192 
00193     QString prefix = texts[0].left(plen);
00194     int truncated = plen;
00195     if (plen >= targetReduction + fl) {
00196         truncated = plen - targetReduction;
00197     } else {
00198         truncated = fl;
00199     }
00200     prefix = abbreviate(prefix, truncated, ElideEnd, false, ellipsis);
00201 
00202     QStringList results;
00203     for (int i = 0; i < texts.size(); ++i) {
00204         results.push_back
00205             (prefix + texts[i].right(texts[i].length() - plen));
00206     }
00207     return results;
00208 }
00209 
00210 QStringList
00211 TextAbbrev::elidePrefixes(const QStringList &texts,
00212                           const QFontMetrics &metrics,
00213                           int targetWidthReduction,
00214                           QString ellipsis)
00215 {
00216     if (texts.empty()) return texts;
00217     int plen = getPrefixLength(texts);
00218     int fl = getFuzzLength(ellipsis);
00219     if (plen < fl) return texts;
00220 
00221     QString prefix = texts[0].left(plen);
00222     int pwid = metrics.width(prefix);
00223     int twid = pwid - targetWidthReduction;
00224     if (twid < metrics.width(ellipsis) * 2) twid = metrics.width(ellipsis) * 2;
00225     prefix = abbreviate(prefix, metrics, twid, ElideEnd, ellipsis);
00226 
00227     QStringList results;
00228     for (int i = 0; i < texts.size(); ++i) {
00229         results.push_back
00230             (prefix + texts[i].right(texts[i].length() - plen));
00231     }
00232     return results;
00233 }
00234 
00235 static bool
00236 havePrefix(QString prefix, const QStringList &texts)
00237 {
00238     for (int i = 1; i < texts.size(); ++i) {
00239         if (!texts[i].startsWith(prefix)) return false;
00240     }
00241     return true;
00242 }
00243 
00244 int
00245 TextAbbrev::getPrefixLength(const QStringList &texts)
00246 {
00247     QString reference = texts[0];
00248 
00249     if (reference == "" || havePrefix(reference, texts)) {
00250         return reference.length();
00251     }
00252 
00253     int candidate = reference.length();
00254     QString splitChars(";:,./#-!()$_+=[]{}\\");
00255 
00256     while (--candidate > 1) {
00257         if (splitChars.contains(reference[candidate])) {
00258             if (havePrefix(reference.left(candidate), texts)) {
00259                 break;
00260             }
00261         }
00262     }
00263 
00264 //    SVDEBUG << "TextAbbrev::getPrefixLength: prefix length is " << candidate << endl;
00265 //    for (int i = 0; i < texts.size(); ++i) {
00266 //        cerr << texts[i].left(candidate) << "|" << texts[i].right(texts[i].length() - candidate) << endl;
00267 //    }
00268 
00269     return candidate;
00270 }
00271