diff --git a/addons/tabswitcher/tabswitcherfilesmodel.cpp b/addons/tabswitcher/tabswitcherfilesmodel.cpp index e62c5bc44..1cf81658a 100644 --- a/addons/tabswitcher/tabswitcherfilesmodel.cpp +++ b/addons/tabswitcher/tabswitcherfilesmodel.cpp @@ -1,208 +1,208 @@ /* This file is part of the KDE project Copyright (C) 2018 Gregor Mi This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "tabswitcherfilesmodel.h" #include #include #include #include #include #include namespace detail { static QIcon iconForDocument(KTextEditor::Document * doc) { return QIcon::fromTheme(QMimeDatabase().mimeTypeForUrl(doc->url()).iconName()); } FilenameListItem::FilenameListItem(KTextEditor::Document* doc) : document(doc) , icon(iconForDocument(doc)) , documentName(doc->documentName()) , fullPath(doc->url().toLocalFile()) { } /** * adapted from https://helloacm.com/c-coding-exercise-longest-common-prefix/ * see also http://www.cplusplus.com/forum/beginner/83540/ * Note that if strs contains the empty string, the result will be "" */ QString longestCommonPrefix(std::vector const & strs) { int n = INT_MAX; if (strs.size() <= 0) { return QString(); } if (strs.size() == 1) { return strs[0]; } // get the min length for (size_t i = 0; i < strs.size(); i++) { n = strs[i].length() < n ? strs[i].length() : n; } for (int c = 0; c < n; c++) { // check each character for (size_t i = 1; i < strs.size(); i++) { if (strs[i][c] != strs[i - 1][c]) { // we find a mis-match return QStringRef(&strs[0], 0, c).toString(); } } } // prefix is n-length return QStringRef(&strs[0], 0, n).toString(); } void post_process(FilenameList & data) { std::vector paths; std::for_each(data.begin(), data.end(), [&paths](FilenameListItem & item) { paths.push_back(item.fullPath); }); // Removes empty strings because Documents without file have no path and we would // otherwise in this case always get "" paths.erase( // erase-remove idiom, see https://en.cppreference.com/w/cpp/algorithm/remove std::remove_if(paths.begin(), paths.end(), [](QString s) { return s.isEmpty(); }), paths.end() ); QString prefix = longestCommonPrefix(paths); int prefix_length = prefix.length(); if (prefix_length == 1) { // if there is only the "/" at the beginning, then keep it prefix_length = 0; } std::for_each(data.begin(), data.end(), [prefix_length](FilenameListItem & item) { // Note that item.documentName can contain additional characters - e.g. "README.md (2)" - // so we cannot use that and have to parse the base filename by other means: QFileInfo fileinfo(item.fullPath); QString basename = fileinfo.fileName(); // e.g. "archive.tar.gz" // cut prefix (left side) and cut document name (plus slash) on the right side int len = item.fullPath.length() - prefix_length - basename.length() - 1; if (len > 0) { // only assign in case item.fullPath is not empty // "PREFIXPATH/REMAININGPATH/BASENAME" --> "REMAININGPATH" item.displayPathPrefix = QStringRef(&item.fullPath, prefix_length, len).toString(); } }); } } detail::TabswitcherFilesModel::TabswitcherFilesModel(QObject *parent) : QAbstractTableModel(parent) { } detail::TabswitcherFilesModel::TabswitcherFilesModel(const FilenameList & data) { data_ = data; post_process(data_); } bool detail::TabswitcherFilesModel::insertRow(int row, const FilenameListItem & item) { beginInsertRows(QModelIndex(), row, row + 1); data_.insert(data_.begin() + row, item); post_process(data_); endInsertRows(); return true; } bool detail::TabswitcherFilesModel::removeRow(int row) { if (data_.begin() + row == data_.end()) { return false; } - beginRemoveRows(QModelIndex(), row, row + 1); + beginRemoveRows(QModelIndex(), row, row); data_.erase(data_.begin() + row); post_process(data_); endRemoveRows(); return true; } void detail::TabswitcherFilesModel::clear() { if (data_.size() > 0) { beginRemoveRows(QModelIndex(), 0, data_.size() - 1); data_.clear(); endRemoveRows(); } } int detail::TabswitcherFilesModel::rowCount() const { return data_.size(); } detail::FilenameListItem * detail::TabswitcherFilesModel::item(int row) const { return const_cast(&data_[row]); } void detail::TabswitcherFilesModel::updateItem(FilenameListItem * item, QString const & documentName, QString const & fullPath) { item->documentName = documentName; item->fullPath = fullPath; post_process(data_); } int detail::TabswitcherFilesModel::columnCount(const QModelIndex & parent) const { Q_UNUSED(parent); return 2; } int detail::TabswitcherFilesModel::rowCount(const QModelIndex & parent) const { Q_UNUSED(parent); return data_.size(); } QVariant detail::TabswitcherFilesModel::data(const QModelIndex & index, int role) const { if (role == Qt::DisplayRole) { const auto & row = data_[index.row()]; if (index.column() == 0) return row.displayPathPrefix; else return row.documentName; } else if (role == Qt::DecorationRole) { if (index.column() == 1) { const auto & row = data_[index.row()]; return row.icon; } } else if (role == Qt::ToolTipRole) { const auto & row = data_[index.row()]; return row.fullPath; } else if (role == Qt::TextAlignmentRole) { if (index.column() == 0) return Qt::AlignRight + Qt::AlignVCenter; else return Qt::AlignVCenter; } else if (role == Qt::ForegroundRole) { if (index.column() == 0) return QBrush(Qt::darkGray); else return QVariant(); } return QVariant(); }