Changeset View
Changeset View
Standalone View
Standalone View
addons/tabswitcher/tabswitcherfilesmodel.cpp
- This file was added.
1 | /* This file is part of the KDE project | ||||
---|---|---|---|---|---|
2 | | ||||
3 | Copyright (C) 2018 Gregor Mi <codestruct@posteo.org> | ||||
4 | | ||||
5 | This library is free software; you can redistribute it and/or | ||||
6 | modify it under the terms of the GNU Library General Public | ||||
7 | License as published by the Free Software Foundation; either | ||||
8 | version 2 of the License, or (at your option) any later version. | ||||
9 | | ||||
10 | This library is distributed in the hope that it will be useful, | ||||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||||
13 | Library General Public License for more details. | ||||
14 | | ||||
15 | You should have received a copy of the GNU Library General Public License | ||||
16 | along with this library; see the file COPYING.LIB. If not, write to | ||||
17 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||||
18 | Boston, MA 02110-1301, USA. | ||||
19 | */ | ||||
20 | | ||||
21 | #include "tabswitcherfilesmodel.h" | ||||
22 | | ||||
23 | #include <QDebug> | ||||
24 | | ||||
25 | #include <algorithm> | ||||
26 | | ||||
27 | namespace detail { | ||||
28 | /** | ||||
29 | * adpated from https://helloacm.com/c-coding-exercise-longest-common-prefix/ | ||||
30 | * see also http://www.cplusplus.com/forum/beginner/83540/ | ||||
31 | * Note that if strs contains the empty string, the result will be "" | ||||
32 | */ | ||||
33 | QString longestCommonPrefix(std::vector<QString>& strs) { | ||||
34 | int n = INT_MAX; | ||||
dhaumann: What license is this? Since we seem to reuse logs of code, this is important :-) | |||||
On https://helloacm.com/c-coding-exercise-longest-common-prefix/ the footer says "All rights Reserved © 2014~ 2018" "The programming examples are as ‘it is’. I do not hold any responsibility for damages/loss caused by running them." Does the 'as it is' already means we can use it? What if I reimplement it and still come up with a similar solution? Should I try to contact the site owner? gregormi: On https://helloacm.com/c-coding-exercise-longest-common-prefix/ the footer says "All rights… | |||||
35 | if (strs.size() <= 0) { | ||||
36 | return QString(); | ||||
37 | } | ||||
38 | if (strs.size() == 1) { | ||||
39 | return strs[0]; | ||||
40 | } | ||||
41 | // get the min length | ||||
42 | for (size_t i = 0; i < strs.size(); i++) { | ||||
43 | n = strs[i].length() < n ? strs[i].length() : n; | ||||
44 | } | ||||
45 | for (int c = 0; c < n; c++) { // check each character | ||||
46 | for (size_t i = 1; i < strs.size(); i++) { | ||||
47 | if (strs[i][c] != strs[i - 1][c]) { // we find a mis-match | ||||
48 | return QStringRef(&strs[0], 0, c).toString(); | ||||
49 | } | ||||
50 | } | ||||
51 | } | ||||
52 | // prefix is n-length | ||||
53 | return QStringRef(&strs[0], 0, n).toString(); | ||||
54 | } | ||||
55 | | ||||
56 | void post_process(FilenameList & data) | ||||
57 | { | ||||
58 | std::vector<QString> paths; | ||||
59 | std::for_each(data.begin(), data.end(), | ||||
60 | [&paths](FilenameListItem & item) { paths.push_back(item.fullPath); }); | ||||
61 | | ||||
62 | // Removes empty strings because Documents without file have no path and we would | ||||
63 | // otherwise in this case always get "" | ||||
64 | paths.erase( // erase-remove idiom, see https://en.cppreference.com/w/cpp/algorithm/remove | ||||
65 | std::remove_if(paths.begin(), paths.end(), [](QString s) { | ||||
66 | return s.isEmpty(); }), | ||||
67 | paths.end() | ||||
68 | ); | ||||
69 | QString prefix = longestCommonPrefix(paths); | ||||
70 | int prefix_length = prefix.length(); | ||||
71 | if (prefix_length == 1) { // if there is only the "/" at the beginning, then keep it | ||||
72 | prefix_length = 0; | ||||
73 | } | ||||
74 | | ||||
75 | std::for_each(data.begin(), data.end(), | ||||
76 | [prefix_length](FilenameListItem & item) { | ||||
77 | // cut prefix (left side) and cut document name (plus slash) on the right side | ||||
78 | int len = item.fullPath.length() - prefix_length - item.documentName.length() - 1; | ||||
79 | if (len > 0) { // only assign in case item.fullPath is not empty | ||||
80 | // "PREFIXPATH/REMAININGPATH/DOCUMENTNAME" --> "REMAININGPATH" | ||||
81 | item.displayPathPrefix = QStringRef(&item.fullPath, prefix_length, len).toString(); | ||||
82 | } | ||||
83 | }); | ||||
84 | | ||||
85 | } | ||||
86 | } | ||||
87 | | ||||
88 | detail::TabswitcherFilesModel::TabswitcherFilesModel(QObject *parent) : QAbstractTableModel(parent) | ||||
89 | { | ||||
90 | } | ||||
91 | | ||||
92 | detail::TabswitcherFilesModel::TabswitcherFilesModel(const FilenameList & data) | ||||
93 | { | ||||
94 | data_ = data; | ||||
95 | post_process(data_); | ||||
96 | } | ||||
97 | | ||||
98 | bool detail::TabswitcherFilesModel::insertRow(int row, FilenameListItem const * const item) | ||||
99 | { | ||||
100 | beginInsertRows(QModelIndex(), row, row + 1); | ||||
101 | data_.insert(data_.begin() + row, *item); | ||||
102 | post_process(data_); | ||||
103 | endInsertRows(); | ||||
104 | return true; | ||||
105 | } | ||||
106 | | ||||
107 | bool detail::TabswitcherFilesModel::removeRow(int row) | ||||
108 | { | ||||
109 | if (data_.begin() + row == data_.end()) { | ||||
110 | return false; | ||||
111 | } | ||||
112 | | ||||
113 | beginRemoveRows(QModelIndex(), row, row + 1); | ||||
114 | data_.erase(data_.begin() + row); | ||||
115 | post_process(data_); | ||||
116 | endRemoveRows(); | ||||
117 | return true; | ||||
118 | } | ||||
119 | | ||||
120 | int detail::TabswitcherFilesModel::rowCount() const | ||||
121 | { | ||||
122 | return data_.size(); | ||||
123 | } | ||||
124 | | ||||
125 | detail::FilenameListItem * detail::TabswitcherFilesModel::item(int row) const | ||||
126 | { | ||||
127 | return const_cast<detail::FilenameListItem *>(&data_[row]); | ||||
128 | } | ||||
129 | | ||||
130 | void detail::TabswitcherFilesModel::updateItem(FilenameListItem * item, QString const & documentName, QString const & fullPath) | ||||
131 | { | ||||
132 | item->documentName = documentName; | ||||
133 | item->fullPath = fullPath; | ||||
134 | post_process(data_); | ||||
135 | } | ||||
136 | | ||||
137 | int detail::TabswitcherFilesModel::columnCount(const QModelIndex & parent) const | ||||
138 | { | ||||
139 | Q_UNUSED(parent); | ||||
140 | return 2; | ||||
141 | } | ||||
142 | | ||||
143 | int detail::TabswitcherFilesModel::rowCount(const QModelIndex & parent) const | ||||
144 | { | ||||
145 | Q_UNUSED(parent); | ||||
146 | return data_.size(); | ||||
147 | } | ||||
148 | | ||||
149 | QVariant detail::TabswitcherFilesModel::data(const QModelIndex & index, int role) const | ||||
150 | { | ||||
151 | if (role == Qt::DisplayRole) { | ||||
152 | const auto & row = data_[index.row()]; | ||||
153 | if (index.column() == 0) | ||||
154 | return row.displayPathPrefix; | ||||
155 | else | ||||
156 | return row.documentName; | ||||
157 | } else if (role == Qt::DecorationRole) { | ||||
158 | if (index.column() == 1) { | ||||
159 | const auto & row = data_[index.row()]; | ||||
160 | return row.icon; | ||||
161 | } | ||||
162 | } else if (role == Qt::ToolTipRole) { | ||||
163 | const auto & row = data_[index.row()]; | ||||
164 | return row.fullPath; | ||||
165 | } else if (role == Qt::TextAlignmentRole) { | ||||
166 | if (index.column() == 0) | ||||
167 | return Qt::AlignRight + Qt::AlignVCenter; | ||||
168 | else | ||||
169 | return Qt::AlignVCenter; | ||||
170 | } else if (role == Qt::ForegroundRole) { | ||||
171 | if (index.column() == 0) | ||||
172 | return QBrush(Qt::darkGray); | ||||
173 | else | ||||
174 | return QVariant(); | ||||
175 | } | ||||
176 | return QVariant(); | ||||
177 | } |
What license is this? Since we seem to reuse logs of code, this is important :-)