]> git.lyx.org Git - features.git/commitdiff
* Some more clever elide mode for the LyX buffer tabs. In trunk
authorStefan Schimanski <sts@lyx.org>
Tue, 29 Apr 2008 15:44:07 +0000 (15:44 +0000)
committerStefan Schimanski <sts@lyx.org>
Tue, 29 Apr 2008 15:44:07 +0000 (15:44 +0000)
  currently the whole path is (possibly with some unmotivated ... in
  the middle) used which is usually far too long.

  The algorithm implemented here will start with absolute paths. From
  left to right path segments are added to the display string if they
  help to make the display strings more unique. Otherwise nothing is
  added, or if some middle path segments are omitted otherwise, three
  dots ... are used.

  The result is that we get just the base filename without extension if
  they are unique in the tabbar.

  The patch is open for discussion. If there is demand we can create
  yet another preference option to get back the old behaviour.

git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@24555 a592a061-630c-0410-9148-cb99ea01b6c8

src/frontends/qt4/GuiWorkArea.cpp
src/frontends/qt4/GuiWorkArea.h
src/support/FileName.cpp
src/support/FileName.h

index 04f853fe8e4559d28cf9f67a8b6f7f25d5f7e2a0..3794296b6715db5e3a016311f28ae3f912cd6a3f 100644 (file)
@@ -1313,13 +1313,15 @@ GuiWorkArea * TabWorkArea::addWorkArea(Buffer & buffer, GuiView & view)
                showBar(count() > 0);
        addTab(wa, wa->windowTitle());
        QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
-               this, SLOT(updateTabText(GuiWorkArea *)));
+               this, SLOT(updateTabTexts()));
        if (currentWorkArea() && currentWorkArea()->isFullScreen())
                setFullScreen(true);
        else
                // Hide tabbar if there's only one tab.
                showBar(count() > 1);
 
+       updateTabTexts();
+       
        return wa;
 }
 
@@ -1347,6 +1349,8 @@ bool TabWorkArea::removeWorkArea(GuiWorkArea * work_area)
                lastWorkAreaRemoved();
        }
 
+       updateTabTexts();
+
        return true;
 }
 
@@ -1392,13 +1396,216 @@ void TabWorkArea::closeCurrentTab()
        }
 }
 
+///
+class DisplayPath {
+public:
+       /// make vector happy
+       DisplayPath() {}
+       ///
+       DisplayPath(int tab, FileName const & filename)
+               : tab_(tab)
+       {
+               filename_ = toqstr(filename.onlyFileNameWithoutExt());
+               postfix_ = toqstr(filename.absoluteFilePath()).split("/", QString::SkipEmptyParts);
+               postfix_.pop_back();
+               abs_ = toqstr(filename.absoluteFilePath());
+               dottedPrefix_ = false;
+       }
+       
+       /// Absolute path for debugging.
+       QString abs() const
+       {
+               return abs_;
+       }
+       /// Add the first segment from the postfix or three dots to the prefix.
+       /// Merge multiple dot tripples. In fact dots are added lazily, i.e. only
+       /// when really needed.
+       void shiftPathSegment(bool dotted)
+       {
+               if (postfix_.count() > 0) {
+                       if (!dotted) {
+                               if (dottedPrefix_ && !prefix_.isEmpty())
+                                       prefix_ += ".../";
+                               prefix_ += postfix_.front() + "/";
+                       }
+                       dottedPrefix_ = dotted && !prefix_.isEmpty();
+                       postfix_.pop_front();
+               }
+       }
+       ///
+       QString displayString() const
+       {
+               if (prefix_.isEmpty())
+                       return filename_;
+               else {
+                       bool dots = dottedPrefix_ || !postfix_.isEmpty();
+                       return prefix_
+                               + (dots ? ".../" : "")
+                               + filename_;
+               }
+       }
+       ///
+       QString forecastDisplayString() const
+       {
+               if (postfix_.count() == 0)
+                       return displayString();
+               
+               bool postfixLeft = postfix_.count() > 1;
+               return prefix_
+                       + (dottedPrefix_ ? ".../" : "")
+                       + postfix_.front() + "/"
+                       + (postfixLeft ? ".../" : "")
+                       + filename_;
+       }
+       ///
+       bool final() const
+       {
+               return postfix_.empty();
+       }
+       ///
+       int tab() const
+       {
+               return tab_;
+       }
+       
+private:
+       ///
+       QString prefix_;
+       ///
+       QStringList postfix_;
+       ///
+       QString filename_;
+       ///
+       QString abs_;
+       ///
+       int tab_;
+       ///
+       bool dottedPrefix_;
+};
 
-void TabWorkArea::updateTabText(GuiWorkArea * wa)
+
+///
+bool operator<(DisplayPath const & a, DisplayPath const & b)
 {
-       int const i = indexOf(wa);
-       if (i < 0)
+       return a.displayString() < b.displayString();
+}
+
+///
+bool operator==(DisplayPath const & a, DisplayPath const & b)
+{
+       return a.displayString() == b.displayString();
+}
+
+
+void TabWorkArea::updateTabTexts()
+{
+       size_t n = count();
+       if (n == 0)
                return;
-       setTabText(i, wa->windowTitle());
+       std::list<DisplayPath> paths;
+       typedef std::list<DisplayPath>::iterator It;
+       
+       // collect full names first: path into postfix, empty prefix and 
+       // filename without extension
+       for (size_t i = 0; i < n; ++i) {
+               GuiWorkArea * i_wa = dynamic_cast<GuiWorkArea *>(widget(i)); 
+               FileName const fn = i_wa->bufferView().buffer().fileName();
+               paths.push_back(DisplayPath(i, fn));
+       }
+       
+       // go through path segments and see if it helps to make the path more unique
+       bool somethingChanged = true;
+       bool allFinal = false;
+       while (somethingChanged && !allFinal) {
+               // adding path segments changes order
+               paths.sort();
+               
+               LYXERR(Debug::GUI, "updateTabTexts() iteration start");
+               somethingChanged = false;
+               allFinal = true;
+               
+               // find segments which are not unique (i.e. non-atomic)
+               It it = paths.begin();
+               It segStart = it;
+               QString segString = it->displayString();
+               for (; it != paths.end(); ++it) {
+                       // look to the next item
+                       It next = it;
+                       ++next;
+                       
+                       // final?
+                       allFinal = allFinal && it->final();
+                       
+                       LYXERR(Debug::GUI, "it = " << fromqstr(it->abs())
+                              << " => " << fromqstr(it->displayString()));
+                       
+                       // still the same segment?
+                       QString nextString;
+                       if ((next != paths.end()
+                            && (nextString = next->displayString()) == segString))
+                               continue;
+                       LYXERR(Debug::GUI, "segment ended");
+                       
+                       // only a trivial one with one element?
+                       if (it == segStart) {
+                               // start new segment
+                               segStart = next;
+                               segString = nextString;
+                               continue;
+                       }
+                       
+                       // we found a non-atomic segment segStart <= sit <= it < next.
+                       // Shift path segments and hope for the best
+                       // that it makes the path more unique.
+                       somethingChanged = true;
+                       It sit = segStart;
+                       QString dspString = sit->forecastDisplayString();
+                       LYXERR(Debug::GUI, "first forecast found for "
+                              << fromqstr(sit->abs())
+                              << " => " << fromqstr(dspString));
+                       ++sit;
+                       bool moreUnique = false;
+                       for (; sit != next; ++sit) {
+                               if (sit->forecastDisplayString() != dspString) {
+                                       LYXERR(Debug::GUI, "different forecast found for "
+                                              << fromqstr(sit->abs())
+                                              << " => "
+                                              << fromqstr(sit->forecastDisplayString()));
+                                       moreUnique = true;
+                                       break;
+                               } else {
+                                       LYXERR(Debug::GUI, "same forecast found for "
+                                              << fromqstr(sit->abs())
+                                              << " => "
+                                              << fromqstr(sit->forecastDisplayString()));
+                               }
+                       }
+                       
+                       // if the path segment helped, add it. Otherwise add dots
+                       bool dots = !moreUnique;
+                       LYXERR(Debug::GUI, "using dots = " << dots);
+                       for (sit = segStart; sit != next; ++sit) {
+                               sit->shiftPathSegment(dots);
+                               LYXERR(Debug::GUI, "shifting " << fromqstr(sit->abs())
+                                      << " => "
+                                      << fromqstr(sit->displayString()));
+                       }
+
+                       // start new segment
+                       segStart = next;
+                       segString = nextString;
+               }
+       }
+       
+       // set new tab titles
+       for (It it = paths.begin(); it != paths.end(); ++it) {
+               GuiWorkArea * i_wa = dynamic_cast<GuiWorkArea *>(widget(it->tab())); 
+               Buffer & buf = i_wa->bufferView().buffer();
+               if (!buf.fileName().empty() && !buf.isClean())
+                       setTabText(it->tab(), it->displayString() + "*");
+               else
+                       setTabText(it->tab(), it->displayString());
+       }
 }
 
 
index b86835356325a724d326522270a67349e04a1ac2..4d531b9ec3fdfff31d15e16a14938bf3f9dbfb1f 100644 (file)
@@ -270,7 +270,7 @@ public Q_SLOTS:
        /// close current tab, or the one given by \c clicked_tab_
        void closeCurrentTab();
        ///
-       void updateTabText(GuiWorkArea *);
+       void updateTabTexts();
        
 private Q_SLOTS:
        ///
index b7c3b49b56c1b521f17d92d21367851f5bfe935a..f9d27ca8bc22740ecf7445ee49d5b6d0cfbd7d1b 100644 (file)
@@ -265,6 +265,12 @@ string FileName::onlyFileName() const
 }
 
 
+string FileName::onlyFileNameWithoutExt() const
+{
+       return fromqstr(d->fi.baseName());
+}
+
+
 FileName FileName::onlyPath() const
 {
        FileName path;
index f35354a01f79148ffbaf3bd71b7d11768ffd56b6..ee644f575f7f3865f1bc8377953397247ff0449b 100644 (file)
@@ -162,6 +162,8 @@ public:
 
        /// filename without path
        std::string onlyFileName() const;
+        /// filename without path and without extension
+        std::string onlyFileNameWithoutExt() const;
        /// path without file name
        FileName onlyPath() const;
        /// used for display in the Gui