Various fixes in the multithreading code.
[vng.git] / hunks / HunksCursor.cpp
blob42ed74a7a45dffe442241f86e461b9fe08ee0e15
1 /*
2 * This file is part of the vng project
3 * Copyright (C) 2008 Thomas Zander <tzander@trolltech.com>
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program 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
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "HunksCursor.h"
20 #include "ChangeSet.h"
22 #include <QDebug>
24 HunksCursor::HunksCursor(ChangeSet &changeSet)
25 : m_changeSet(changeSet),
26 m_totalHunks(-1),
27 m_fileIndex(0),
28 m_hunkIndex(0),
29 m_subHunkIndex(0)
31 if (changeSet.count()) {
32 File file = changeSet.file(0);
33 for (int i=0; i < changeSet.count(); ++i) {
34 File file = changeSet.file(i);
35 if (file.fileName().isEmpty() || file.oldFileName().isEmpty())
36 file.setProtectionAcceptance(Vng::Accepted);
38 // make sure the first hunk is an actual change
39 if (file.fileName() == file.oldFileName())
40 forward(ItemScope, true);
44 HunksCursor::~HunksCursor()
48 int HunksCursor::forward(Scope scope, bool skipAnswered)
50 if (m_changeSet.count() == 0 || m_fileIndex >= m_changeSet.count())
51 return currentIndex();
53 struct Last {
54 Last(const ChangeSet &changeSet)
55 : hunk(0), subHunk(0)
57 file = changeSet.count() - 1;
58 if (! changeSet.hasAllHunks())
59 return;
60 File f = changeSet.file(file);
61 hunk = f.count() - 1;
62 if (hunk >= 0) {
63 Hunk h = f.hunks()[hunk];
64 hunk += 2;
65 subHunk = h.subHunkCount() - 1;
68 int file, hunk, subHunk;
70 Last last(m_changeSet);
71 if (m_fileIndex > last.file) {
72 m_fileIndex = last.file +1;
73 m_hunkIndex = 0;
74 m_subHunkIndex = 0;
75 return currentIndex(); // already at end.
78 switch(scope) {
79 case ItemScope: {
80 if (++m_subHunkIndex < currentSubHunkCount())
81 break;
83 --m_subHunkIndex;
84 File file = m_changeSet.file(m_fileIndex);
85 if (m_hunkIndex + 1 - 2 >= file.count())
86 return forward(BlockScope, skipAnswered);
87 else {
88 m_hunkIndex++;
89 m_subHunkIndex = 0;
91 break;
93 case BlockScope:
94 m_fileIndex++;
95 m_hunkIndex = 0;
96 m_subHunkIndex = 0;
97 if (m_fileIndex > last.file)
98 skipAnswered = false; // there is nothing left;
99 break;
100 case FullRange:
101 m_fileIndex = last.file + 1;
102 m_hunkIndex = 0;
103 m_subHunkIndex = 0;
104 break;
107 if (! isValid())
108 return currentIndex();
110 File file = m_changeSet.file(m_fileIndex);
111 const bool renamed = file.fileName() != file.oldFileName();
112 const bool protectionChanged = !renamed && file.protection() != file.oldProtection();
113 if (m_hunkIndex == 0 && !renamed)
114 m_hunkIndex++;
115 if (m_hunkIndex == 1 && !protectionChanged)
116 return forward(ItemScope, skipAnswered);
118 if (skipAnswered) {
119 if (m_hunkIndex == 0 && file.renameAcceptance() != Vng::Undecided
120 || m_hunkIndex == 1 && file.protectionAcceptance() != Vng::Undecided)
121 return forward(ItemScope, skipAnswered);
123 if (m_hunkIndex >= 2) { // a hunk;
124 Hunk hunk = file.hunks()[m_hunkIndex - 2];
125 Vng::Acceptance a = hunk.acceptance();
126 if (a == Vng::Accepted || a == Vng::Rejected || hunk.acceptance(m_subHunkIndex) != Vng::Undecided) {
127 if (hunk.subHunkCount() == m_subHunkIndex + 1)
128 return forward(BlockScope, skipAnswered);
129 return forward(ItemScope, skipAnswered);
133 return currentIndex();
136 int HunksCursor::back(Scope scope)
138 if (m_changeSet.count() == 0
139 || m_fileIndex == 0 && m_hunkIndex == 0 && m_subHunkIndex == 0)
140 return currentIndex();
141 switch(scope) {
142 case ItemScope:
143 if (--m_subHunkIndex >= 0)
144 break;
145 ++m_subHunkIndex;
146 if (--m_hunkIndex < 1) {
147 m_hunkIndex++;
148 return back(BlockScope);
150 m_subHunkIndex = currentSubHunkCount() - 1;
151 break;
152 case BlockScope:
153 if (--m_fileIndex < 0)
154 m_fileIndex = 0;
155 m_hunkIndex = m_changeSet.file(m_fileIndex).count() -1+2;
156 Q_ASSERT(m_hunkIndex >= 0);
157 m_subHunkIndex = currentSubHunkCount() - 1;
158 break;
159 case FullRange:
160 m_hunkIndex = 0;
161 m_fileIndex = 0;
162 m_subHunkIndex = 0;
163 break;
166 File file = m_changeSet.file(m_fileIndex);
167 const bool renamed = file.fileName() != file.oldFileName();
168 const bool protectionChanged = !renamed && file.protection() != file.oldProtection();
169 if (m_hunkIndex == 1 && !protectionChanged)
170 m_hunkIndex--;
171 if (m_hunkIndex == 0 && !renamed)
172 return back(BlockScope);
174 return currentIndex();
177 void HunksCursor::setResponse(bool response, Scope scope)
179 if (m_changeSet.count() == 0)
180 return;
181 const Vng::Acceptance accept = response ? Vng::Accepted : Vng::Rejected;
183 if (scope == ItemScope) {
184 File file = m_changeSet.file(m_fileIndex);
185 if (m_hunkIndex == 0) {
186 if (!response && file.oldFileName().isEmpty()
187 || response && file.fileName().isEmpty()) { // new file that is not added, removed file that we want added.
188 setResponse(response, BlockScope);
189 return;
191 file.setRenameAcceptance(accept);
193 else if (m_hunkIndex == 1)
194 file.setProtectionAcceptance(accept);
195 else {
196 Hunk hunk = file.hunks()[m_hunkIndex - 2];
197 hunk.setAcceptance(m_subHunkIndex, accept);
199 return;
202 int fileIndex = m_fileIndex;
203 int hunkIndex = m_hunkIndex;
205 do {
206 File file = m_changeSet.file(fileIndex);
207 QList<Hunk> hunks = file.hunks();
208 do {
209 if (hunkIndex == 0)
210 file.setRenameAcceptance(accept);
211 else if (hunkIndex == 1)
212 file.setProtectionAcceptance(accept);
213 else
214 hunks[hunkIndex - 2].setAcceptance(accept);
215 hunkIndex++;
216 } while(scope >= BlockScope && hunkIndex -2 < hunks.count());
217 hunkIndex = 0;
218 fileIndex++;
219 } while(scope == FullRange && fileIndex < m_changeSet.count());
222 int HunksCursor::count()
224 if (m_totalHunks == -1 && m_changeSet.hasAllHunks())
225 forceCount();
226 return m_totalHunks;
229 void HunksCursor::forceCount()
231 if (m_totalHunks < 0) {
232 int total = 0;
233 for (int i=0; i < m_changeSet.count(); ++i) {
234 File file = m_changeSet.file(i);
235 if (file.fileName() != file.oldFileName())
236 total++;
237 else if (file.protection() != file.oldProtection())
238 total++;
239 foreach(Hunk hunk, file.hunks())
240 total += hunk.subHunkCount();
242 m_totalHunks = total;
246 int HunksCursor::currentIndex() const
248 int answer = 1;
249 const int end = qMin(m_changeSet.count(), m_fileIndex+1);
250 int i=0;
251 while(i < end) {
252 File file = m_changeSet.file(i);
253 const bool renamed = file.fileName() != file.oldFileName();
254 const bool protectionChanged = !renamed && file.protection() != file.oldProtection();
255 QList<Hunk> hunks = file.hunks();
256 ++i;
257 if (i == end && m_fileIndex < m_changeSet.count()) {
258 if (m_hunkIndex > 0 && renamed)
259 answer++;
260 if (m_hunkIndex > 1 && protectionChanged)
261 answer++;
262 hunks = hunks.mid(0, m_hunkIndex - 2);
264 else {
265 if (renamed)
266 answer++;
267 if (protectionChanged)
268 answer++;
270 QList<Hunk>::Iterator iter2 = hunks.begin();
271 int index = 2;
272 while(iter2 != hunks.end()) {
273 if (isValid() && i == end && m_hunkIndex < index++)
274 break;
275 answer += (*iter2).subHunkCount();
276 ++iter2;
279 return answer + m_subHunkIndex;
282 QString HunksCursor::currentText() const
284 QByteArray bytes;
285 if (m_changeSet.count() <= m_fileIndex)
286 return QString();
287 File file = m_changeSet.file(m_fileIndex);
289 QString output;
290 QTextStream ts(&output);
291 if (m_config)
292 m_config->colorize(ts);
293 if (m_hunkIndex == 0) { // file rename
294 if (file.fileName().isEmpty())
295 ts << "remove ";
296 else if (file.oldFileName().isEmpty())
297 ts << "add ";
298 else
299 ts << "move ";
301 else if (m_hunkIndex == 1) // protections change
302 ts << "mode change ";
303 else
304 ts << "hunk ";
305 if (m_config)
306 m_config->normalColor(ts);
307 if (m_hunkIndex == 0) {
308 if (!file.oldFileName().isEmpty())
309 ts << "`" << file.oldFileName() << "' ";
310 if (! file.fileName().isEmpty())
311 ts << "`" << file.fileName() << "'";
313 else if (m_hunkIndex == 1)
314 ts << file.fileName() << " " << file.oldProtection() << " => " << file.protection() << endl;
316 if (m_hunkIndex < 2) {
317 ts << endl;
318 ts.flush();
319 return output;
322 if (file.count() <= m_hunkIndex - 2)
323 return "no change\n";
324 Hunk hunk = file.hunks()[m_hunkIndex - 2];
325 bytes.append(file.fileName());
326 bytes.append(" ");
327 bytes.append(QString::number(hunk.lineNumber(m_subHunkIndex)));
328 //bytes.append("[subhunk "+ QString::number(m_subHunkIndex + 1) +"/"+ QString::number(hunk.subHunkCount()) +"]");
329 bytes.append("\n");
331 QByteArray patch = hunk.subHunk(m_subHunkIndex);
332 //m_config.normalColor(out);
333 if (patch.contains((char) 0)) { // binary
334 //m_config.colorize(out);
335 bytes.append(QString("binary data\n"));
336 //m_config.normalColor(out);
338 else
339 bytes.append(patch);
341 ts.flush();
342 return output + QString(bytes);
345 QString HunksCursor::helpMessage() const
347 return QString(
348 "How to use revert...\n"
349 "y: revert this patch\n"
350 "n: don't revert it\n\n"
352 "s: don't revert the rest of the changes to this file\n"
353 "f: revert the rest of the changes to this file\n\n"
355 "d: revert selected patches, skipping all the remaining patches\n"
356 "a: revert all the remaining patches\n"
357 "q: cancel revert\n\n"
359 "j: skip to next patch\n"
360 "k: back up to previous patch\n"
361 "c: calculate number of patches\n"
362 "h or ?: show this help\n");
365 int HunksCursor::currentSubHunkCount()
367 File file = m_changeSet.file(m_fileIndex);
368 if (m_hunkIndex < 2)
369 return 1;
370 Hunk hunk = file.hunks()[m_hunkIndex - 2];
371 return hunk.subHunkCount();
374 bool HunksCursor::isValid() const
376 return m_changeSet.count() > 0 && m_changeSet.count() > m_fileIndex;
379 QString HunksCursor::allowedOptions() const
381 QString allowed = "ynsfqadjk";
382 if (m_totalHunks == -1)
383 allowed += "c";
384 return allowed;