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"
24 HunksCursor::HunksCursor(ChangeSet
&changeSet
)
25 : m_changeSet(changeSet
),
31 changeSet
.waitForFinishFirstFile();
32 if (changeSet
.count()) {
33 // make sure the first hunk is an actual change
34 File file
= changeSet
.file(0);
35 if (file
.fileName() == file
.oldFileName())
36 forward(ItemScope
, true);
43 HunksCursor::~HunksCursor()
47 int HunksCursor::forward(Scope scope
, bool skipAnswered
)
49 if (m_changeSet
.count() == 0 || m_fileIndex
>= m_changeSet
.count())
50 return currentIndex();
53 Last(const ChangeSet
&changeSet
)
56 file
= changeSet
.count() - 1;
57 if (! changeSet
.hasAllHunks())
59 File f
= changeSet
.file(file
);
62 Hunk h
= f
.hunks()[hunk
];
64 subHunk
= h
.subHunkCount() - 1;
67 int file
, hunk
, subHunk
;
69 Last
last(m_changeSet
);
70 if (m_fileIndex
> last
.file
) {
71 m_fileIndex
= last
.file
+1;
74 return currentIndex(); // already at end.
79 if (++m_subHunkIndex
< currentSubHunkCount())
83 File file
= m_changeSet
.file(m_fileIndex
);
84 if (m_hunkIndex
+ 1 - 2 >= file
.count() && !(m_hunkIndex
== 1 && file
.isBinary()))
85 return forward(BlockScope
, skipAnswered
);
96 if (m_fileIndex
> last
.file
)
97 skipAnswered
= false; // there is nothing left;
100 m_fileIndex
= last
.file
+ 1;
107 return currentIndex();
109 File file
= m_changeSet
.file(m_fileIndex
);
110 const bool renamed
= file
.fileName() != file
.oldFileName();
111 const bool protectionChanged
= !renamed
&& file
.protection() != file
.oldProtection();
112 if (m_hunkIndex
== 0 && !renamed
)
114 if (m_hunkIndex
== 1 && !protectionChanged
)
115 return forward(ItemScope
, skipAnswered
);
118 if ((m_hunkIndex
== 0 && file
.renameAcceptance() != Vng::Undecided
)
119 || (m_hunkIndex
== 1 && file
.protectionAcceptance() != Vng::Undecided
))
120 return forward(ItemScope
, skipAnswered
);
122 if (m_hunkIndex
== 2 && file
.isBinary())
124 else if (m_hunkIndex
>= 2) { // a hunk;
125 Hunk hunk
= file
.hunks()[m_hunkIndex
- 2];
126 Vng::Acceptance a
= hunk
.acceptance();
127 if (a
== Vng::Accepted
|| a
== Vng::Rejected
|| hunk
.acceptance(m_subHunkIndex
) != Vng::Undecided
) {
128 if (hunk
.subHunkCount() == m_subHunkIndex
+ 1)
129 return forward(BlockScope
, skipAnswered
);
130 return forward(ItemScope
, skipAnswered
);
134 return currentIndex();
137 int HunksCursor::back(Scope scope
)
139 if (m_changeSet
.count() == 0
140 || (m_fileIndex
== 0 && m_hunkIndex
== 0 && m_subHunkIndex
== 0))
141 return currentIndex();
144 if (--m_subHunkIndex
>= 0)
147 if (--m_hunkIndex
< 1) {
149 return back(BlockScope
);
151 m_subHunkIndex
= currentSubHunkCount() - 1;
154 if (--m_fileIndex
< 0)
156 m_hunkIndex
= m_changeSet
.file(m_fileIndex
).count() -1+2;
157 Q_ASSERT(m_hunkIndex
>= 0);
158 m_subHunkIndex
= currentSubHunkCount() - 1;
167 File file
= m_changeSet
.file(m_fileIndex
);
168 const bool renamed
= file
.fileName() != file
.oldFileName();
169 const bool protectionChanged
= !renamed
&& file
.protection() != file
.oldProtection();
170 if (m_hunkIndex
== 1 && !protectionChanged
)
172 if (m_hunkIndex
== 0 && !renamed
)
173 return back(BlockScope
);
175 return currentIndex();
178 void HunksCursor::setResponse(bool response
, Scope scope
)
180 if (m_changeSet
.count() == 0)
182 const Vng::Acceptance accept
= response
? Vng::Accepted
: Vng::Rejected
;
184 if (scope
== ItemScope
) {
185 File file
= m_changeSet
.file(m_fileIndex
);
186 if (m_hunkIndex
== 0) {
187 if ((!response
&& file
.oldFileName().isEmpty())
188 || (response
&& file
.fileName().isEmpty())) { // new file that is not added, removed file that we want added.
189 setResponse(response
, BlockScope
);
192 file
.setRenameAcceptance(accept
);
194 else if (m_hunkIndex
== 1)
195 file
.setProtectionAcceptance(accept
);
196 else if (m_hunkIndex
== 2 && file
.isBinary())
197 file
.setBinaryChangeAcceptance(accept
);
199 Hunk hunk
= file
.hunks()[m_hunkIndex
- 2];
200 hunk
.setAcceptance(m_subHunkIndex
, accept
);
205 int fileIndex
= m_fileIndex
;
206 int hunkIndex
= m_hunkIndex
;
208 bool firstSubHunk
= m_subHunkIndex
> 0;
210 File file
= m_changeSet
.file(fileIndex
);
211 QList
<Hunk
> hunks
= file
.hunks();
214 file
.setRenameAcceptance(accept
);
215 else if (hunkIndex
== 1)
216 file
.setProtectionAcceptance(accept
);
217 else if (firstSubHunk
) { // if this is the first hunk, we have to accept only the appropriate subhunks.
218 Hunk hunk
= hunks
[hunkIndex
-2];
219 for (int i
= m_subHunkIndex
; i
< hunk
.subHunkCount(); ++i
)
220 hunk
.setAcceptance(i
, accept
);
223 hunks
[hunkIndex
- 2].setAcceptance(accept
);
225 firstSubHunk
= false;
226 } while(scope
>= BlockScope
&& hunkIndex
-2 < hunks
.count());
229 } while(scope
== FullRange
&& fileIndex
< m_changeSet
.count());
232 int HunksCursor::count()
234 if (m_totalHunks
== -1 && m_changeSet
.hasAllHunks())
239 void HunksCursor::forceCount()
241 if (m_totalHunks
< 0) {
243 for (int i
=0; i
< m_changeSet
.count(); ++i
) {
244 File file
= m_changeSet
.file(i
);
245 if (file
.fileName() != file
.oldFileName()
246 || file
.protection() != file
.oldProtection()
249 foreach(Hunk hunk
, file
.hunks())
250 total
+= hunk
.subHunkCount();
252 m_totalHunks
= total
;
256 int HunksCursor::currentIndex() const
259 const int end
= qMin(m_changeSet
.count(), m_fileIndex
+1);
262 File file
= m_changeSet
.file(i
);
263 const bool renamed
= file
.fileName() != file
.oldFileName();
264 const bool protectionChanged
= !renamed
&& file
.protection() != file
.oldProtection();
265 QList
<Hunk
> hunks
= file
.hunks();
267 if (i
== end
&& m_fileIndex
< m_changeSet
.count()) {
268 if (m_hunkIndex
> 0 && renamed
)
270 if (m_hunkIndex
> 1 && protectionChanged
)
272 hunks
= hunks
.mid(0, m_hunkIndex
- 2);
277 if (protectionChanged
)
280 QList
<Hunk
>::Iterator iter2
= hunks
.begin();
282 while(iter2
!= hunks
.end()) {
283 if (isValid() && i
== end
&& m_hunkIndex
< index
++)
285 answer
+= (*iter2
).subHunkCount();
289 return answer
+ m_subHunkIndex
;
292 QString
HunksCursor::currentText() const
295 if (m_changeSet
.count() <= m_fileIndex
)
297 File file
= m_changeSet
.file(m_fileIndex
);
300 QTextStream
ts(&output
);
302 m_config
->colorize(ts
);
303 if (m_hunkIndex
== 0) { // file rename
304 if (file
.fileName().isEmpty())
306 else if (file
.oldFileName().isEmpty())
311 else if (m_hunkIndex
== 1) // protections change
312 ts
<< "mode change ";
316 m_config
->normalColor(ts
);
317 if (m_hunkIndex
== 0) {
318 if (!file
.oldFileName().isEmpty())
319 ts
<< "`" << QString::fromUtf8(file
.oldFileName()) << "' ";
320 if (! file
.fileName().isEmpty())
321 ts
<< "`" << QString::fromUtf8(file
.fileName()) << "'";
323 else if (m_hunkIndex
== 1)
324 ts
<< QString::fromUtf8(file
.fileName()) << " " << file
.oldProtection() << " => " << file
.protection() << endl
;
326 if (m_hunkIndex
< 2) {
332 if (file
.count() <= m_hunkIndex
- 2) {
334 return "binary data\n";
336 return "no change\n";
338 Hunk hunk
= file
.hunks()[m_hunkIndex
- 2];
339 bytes
.append(QString::fromUtf8(file
.fileName()));
341 bytes
.append(QString::number(hunk
.lineNumber(m_subHunkIndex
)));
342 //bytes.append("[subhunk "+ QString::number(m_subHunkIndex + 1) +"/"+ QString::number(hunk.subHunkCount()) +"]");
345 QByteArray patch
= hunk
.subHunk(m_subHunkIndex
);
346 //m_config.normalColor(out);
347 if (patch
.contains((char) 0)) { // binary
348 //m_config.colorize(out);
349 bytes
.append(QString("binary data\n"));
350 //m_config.normalColor(out);
356 return output
+ QString::fromLocal8Bit(bytes
);
359 QString
HunksCursor::helpMessage() const
362 "How to use revert...\n"
363 "y: revert this patch\n"
364 "n: don't revert it\n\n"
366 "s: don't revert the rest of the changes to this file\n"
367 "f: revert the rest of the changes to this file\n\n"
369 "d: revert selected patches, skipping all the remaining patches\n"
370 "a: revert all the remaining patches\n"
371 "q: cancel revert\n\n"
373 "j: skip to next patch\n"
374 "k: back up to previous patch\n"
375 "c: calculate number of patches\n"
376 "h or ?: show this help\n");
379 int HunksCursor::currentSubHunkCount()
381 File file
= m_changeSet
.file(m_fileIndex
);
382 if (m_hunkIndex
< 2 || file
.isBinary())
384 Hunk hunk
= file
.hunks()[m_hunkIndex
- 2];
385 return hunk
.subHunkCount();
388 bool HunksCursor::isValid() const
390 return m_changeSet
.count() > 0 && m_changeSet
.count() > m_fileIndex
;
393 QString
HunksCursor::allowedOptions() const
395 QString allowed
= "ynsfqadjk";
396 if (m_totalHunks
== -1)