~ubuntu-branches/ubuntu/lucid/kdebase/lucid

« back to all changes in this revision

Viewing changes to apps/dolphin/src/versioncontrol/fileviewsvnplugin.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jonathan Thomas
  • Date: 2009-12-02 13:28:20 UTC
  • mto: This revision was merged to the branch mainline in revision 258.
  • Revision ID: james.westby@ubuntu.com-20091202132820-tpxn348l9frx5zd5
Tags: upstream-4.3.80
ImportĀ upstreamĀ versionĀ 4.3.80

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***************************************************************************
 
2
 *   Copyright (C) 2009 by Peter Penz <peter.penz@gmx.at>                  *
 
3
 *                                                                         *
 
4
 *   This program is free software; you can redistribute it and/or modify  *
 
5
 *   it under the terms of the GNU General Public License as published by  *
 
6
 *   the Free Software Foundation; either version 2 of the License, or     *
 
7
 *   (at your option) any later version.                                   *
 
8
 *                                                                         *
 
9
 *   This program is distributed in the hope that it will be useful,       *
 
10
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 
11
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 
12
 *   GNU General Public License for more details.                          *
 
13
 *                                                                         *
 
14
 *   You should have received a copy of the GNU General Public License     *
 
15
 *   along with this program; if not, write to the                         *
 
16
 *   Free Software Foundation, Inc.,                                       *
 
17
 *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA            *
 
18
 ***************************************************************************/
 
19
 
 
20
#include "fileviewsvnplugin.h"
 
21
 
 
22
#include <kaction.h>
 
23
#include <kdemacros.h>
 
24
#include <kdialog.h>
 
25
#include <kfileitem.h>
 
26
#include <kicon.h>
 
27
#include <klocale.h>
 
28
#include <krun.h>
 
29
#include <kshell.h>
 
30
#include <kvbox.h>
 
31
#include <QDir>
 
32
#include <QLabel>
 
33
#include <QProcess>
 
34
#include <QString>
 
35
#include <QStringList>
 
36
#include <QTextEdit>
 
37
#include <QTextStream>
 
38
 
 
39
#include <KPluginFactory>
 
40
#include <KPluginLoader>
 
41
K_PLUGIN_FACTORY(FileViewSvnPluginFactory, registerPlugin<FileViewSvnPlugin>();)
 
42
K_EXPORT_PLUGIN(FileViewSvnPluginFactory("fileviewsvnplugin"))
 
43
 
 
44
FileViewSvnPlugin::FileViewSvnPlugin(QObject* parent, const QList<QVariant>& args) :
 
45
    KVersionControlPlugin(parent),
 
46
    m_versionInfoHash(),
 
47
    m_versionInfoKeys(),
 
48
    m_updateAction(0),
 
49
    m_showLocalChangesAction(0),
 
50
    m_commitAction(0),
 
51
    m_addAction(0),
 
52
    m_removeAction(0),
 
53
    m_command(),
 
54
    m_errorMsg(),
 
55
    m_operationCompletedMsg(),
 
56
    m_contextDir(),
 
57
    m_contextItems(),
 
58
    m_tempFile()
 
59
{
 
60
    Q_UNUSED(args);
 
61
 
 
62
    m_updateAction = new KAction(this);
 
63
    m_updateAction->setIcon(KIcon("view-refresh"));
 
64
    m_updateAction->setText(i18nc("@item:inmenu", "SVN Update"));
 
65
    connect(m_updateAction, SIGNAL(triggered()),
 
66
            this, SLOT(updateFiles()));
 
67
 
 
68
    m_showLocalChangesAction = new KAction(this);
 
69
    m_showLocalChangesAction->setIcon(KIcon("view-split-left-right"));
 
70
    m_showLocalChangesAction->setText(i18nc("@item:inmenu", "Show Local SVN Changes"));
 
71
    connect(m_showLocalChangesAction, SIGNAL(triggered()),
 
72
            this, SLOT(showLocalChanges()));
 
73
 
 
74
    m_commitAction = new KAction(this);
 
75
    m_commitAction->setText(i18nc("@item:inmenu", "SVN Commit..."));
 
76
    connect(m_commitAction, SIGNAL(triggered()),
 
77
            this, SLOT(commitFiles()));
 
78
 
 
79
    m_addAction = new KAction(this);
 
80
    m_addAction->setIcon(KIcon("list-add"));
 
81
    m_addAction->setText(i18nc("@item:inmenu", "SVN Add"));
 
82
    connect(m_addAction, SIGNAL(triggered()),
 
83
            this, SLOT(addFiles()));
 
84
 
 
85
    m_removeAction = new KAction(this);
 
86
    m_removeAction->setIcon(KIcon("list-remove"));
 
87
    m_removeAction->setText(i18nc("@item:inmenu", "SVN Delete"));
 
88
    connect(m_removeAction, SIGNAL(triggered()),
 
89
            this, SLOT(removeFiles()));
 
90
}
 
91
 
 
92
FileViewSvnPlugin::~FileViewSvnPlugin()
 
93
{
 
94
}
 
95
 
 
96
QString FileViewSvnPlugin::fileName() const
 
97
{
 
98
    return ".svn";
 
99
}
 
100
 
 
101
bool FileViewSvnPlugin::beginRetrieval(const QString& directory)
 
102
{
 
103
    Q_ASSERT(directory.endsWith('/'));
 
104
 
 
105
    QStringList arguments;
 
106
    arguments << "status" << "--show-updates" << directory;
 
107
 
 
108
    QProcess process;
 
109
    process.start("svn", arguments);
 
110
    while (process.waitForReadyRead()) {
 
111
        char buffer[1024];
 
112
        while (process.readLine(buffer, sizeof(buffer)) > 0)  {
 
113
            VersionState state = NormalVersion;
 
114
            QString filePath(buffer);
 
115
 
 
116
            switch (buffer[0]) {
 
117
            case '?': state = UnversionedVersion; break;
 
118
            case 'M': state = LocallyModifiedVersion; break;
 
119
            case 'A': state = AddedVersion; break;
 
120
            case 'D': state = RemovedVersion; break;
 
121
            case 'C': state = ConflictingVersion; break;
 
122
            default:
 
123
                if (filePath.contains('*')) {
 
124
                    state = UpdateRequiredVersion;
 
125
                }
 
126
                break;
 
127
            }
 
128
 
 
129
            int pos = filePath.indexOf('/');
 
130
            const int length = filePath.length() - pos - 1;
 
131
            filePath = filePath.mid(pos, length);
 
132
            if (!filePath.isEmpty()) {
 
133
                m_versionInfoHash.insert(filePath, state);
 
134
            }
 
135
        }
 
136
    }
 
137
 
 
138
    m_versionInfoKeys = m_versionInfoHash.keys();
 
139
    return true;
 
140
}
 
141
 
 
142
void FileViewSvnPlugin::endRetrieval()
 
143
{
 
144
}
 
145
 
 
146
KVersionControlPlugin::VersionState FileViewSvnPlugin::versionState(const KFileItem& item)
 
147
{
 
148
    const QString itemUrl = item.localPath();
 
149
    if (m_versionInfoHash.contains(itemUrl)) {
 
150
        return m_versionInfoHash.value(itemUrl);
 
151
    }
 
152
 
 
153
    if (!item.isDir()) {
 
154
        // files that have not been listed by 'svn status' (= m_versionInfoHash)
 
155
        // are under version control per definition
 
156
        return NormalVersion;
 
157
    }
 
158
 
 
159
    // The item is a directory. Check whether an item listed by 'svn status' (= m_versionInfoHash)
 
160
    // is part of this directory. In this case a local modification should be indicated in the
 
161
    // directory already.
 
162
    foreach (const QString& key, m_versionInfoKeys) {
 
163
        if (key.startsWith(itemUrl)) {
 
164
            const VersionState state = m_versionInfoHash.value(key);
 
165
            if (state == LocallyModifiedVersion) {
 
166
                return LocallyModifiedVersion;
 
167
            }
 
168
        }
 
169
    }
 
170
 
 
171
    return NormalVersion;
 
172
}
 
173
 
 
174
QList<QAction*> FileViewSvnPlugin::contextMenuActions(const KFileItemList& items)
 
175
{
 
176
    Q_ASSERT(!items.isEmpty());
 
177
    foreach (const KFileItem& item, items) {
 
178
        m_contextItems.append(item);
 
179
    }
 
180
    m_contextDir.clear();
 
181
 
 
182
    // iterate all items and check the version state to know which
 
183
    // actions can be enabled
 
184
    const int itemsCount = items.count();
 
185
    int versionedCount = 0;
 
186
    int editingCount = 0;
 
187
    foreach (const KFileItem& item, items) {
 
188
        const VersionState state = versionState(item);
 
189
        if (state != UnversionedVersion) {
 
190
            ++versionedCount;
 
191
        }
 
192
 
 
193
        switch (state) {
 
194
        case LocallyModifiedVersion:
 
195
        case ConflictingVersion:
 
196
            ++editingCount;
 
197
            break;
 
198
        default:
 
199
            break;
 
200
        }
 
201
    }
 
202
    m_commitAction->setEnabled(editingCount > 0);
 
203
    m_addAction->setEnabled(versionedCount == 0);
 
204
    m_removeAction->setEnabled(versionedCount == itemsCount);
 
205
 
 
206
    QList<QAction*> actions;
 
207
    actions.append(m_updateAction);
 
208
    actions.append(m_commitAction);
 
209
    actions.append(m_addAction);
 
210
    actions.append(m_removeAction);
 
211
    return actions;
 
212
}
 
213
 
 
214
QList<QAction*> FileViewSvnPlugin::contextMenuActions(const QString& directory)
 
215
{
 
216
    const bool enabled = m_contextItems.isEmpty();
 
217
    if (enabled) {
 
218
        m_contextDir = directory;
 
219
    }
 
220
 
 
221
    // Only enable the SVN actions if no SVN commands are
 
222
    // executed currently (see slotOperationCompleted() and
 
223
    // startSvnCommandProcess()).
 
224
    m_updateAction->setEnabled(enabled);
 
225
    m_showLocalChangesAction->setEnabled(enabled);
 
226
    m_commitAction->setEnabled(enabled);
 
227
 
 
228
    QList<QAction*> actions;
 
229
    actions.append(m_updateAction);
 
230
    actions.append(m_showLocalChangesAction);
 
231
    actions.append(m_commitAction);
 
232
    return actions;
 
233
}
 
234
 
 
235
void FileViewSvnPlugin::updateFiles()
 
236
{
 
237
    execSvnCommand("update",
 
238
                   i18nc("@info:status", "Updating SVN repository..."),
 
239
                   i18nc("@info:status", "Update of SVN repository failed."),
 
240
                   i18nc("@info:status", "Updated SVN repository."));
 
241
}
 
242
 
 
243
void FileViewSvnPlugin::showLocalChanges()
 
244
{
 
245
    Q_ASSERT(!m_contextDir.isEmpty());
 
246
    Q_ASSERT(m_contextItems.isEmpty());
 
247
 
 
248
    const QString command = "mkfifo /tmp/fifo; svn diff " +
 
249
                            KShell::quoteArg(m_contextDir) +
 
250
                            " > /tmp/fifo & kompare /tmp/fifo; rm /tmp/fifo";
 
251
    KRun::runCommand(command, 0);
 
252
}
 
253
 
 
254
void FileViewSvnPlugin::commitFiles()
 
255
{
 
256
    KDialog dialog(0, Qt::Dialog);
 
257
 
 
258
    KVBox* box = new KVBox(&dialog);
 
259
    new QLabel(i18nc("@label", "Description:"), box);
 
260
    QTextEdit* editor = new QTextEdit(box);
 
261
 
 
262
    dialog.setMainWidget(box);
 
263
    dialog.setCaption(i18nc("@title:window", "SVN Commit"));
 
264
    dialog.setButtons(KDialog::Ok | KDialog::Cancel);
 
265
    dialog.setDefaultButton(KDialog::Ok);
 
266
    dialog.setButtonText(KDialog::Ok, i18nc("@action:button", "Commit"));
 
267
 
 
268
    KConfigGroup dialogConfig(KSharedConfig::openConfig("dolphinrc"),
 
269
                              "SvnCommitDialog");
 
270
    dialog.restoreDialogSize(dialogConfig);
 
271
 
 
272
    if (dialog.exec() == QDialog::Accepted) {
 
273
        // Write the commit description into a temporary file, so
 
274
        // that it can be read by the command "svn commit -F". The temporary
 
275
        // file must stay alive until slotOperationCompleted() is invoked and will
 
276
        // be destroyed when the version plugin is destructed.
 
277
        if (!m_tempFile.open())  {
 
278
            emit errorMessage(i18nc("@info:status", "Commit of SVN changes failed."));
 
279
            return;
 
280
        }
 
281
 
 
282
        QTextStream out(&m_tempFile);
 
283
        const QString fileName = m_tempFile.fileName();
 
284
        out << editor->toPlainText();
 
285
        m_tempFile.close();
 
286
 
 
287
        execSvnCommand("commit -F " + KShell::quoteArg(fileName),
 
288
                       i18nc("@info:status", "Committing SVN changes..."),
 
289
                       i18nc("@info:status", "Commit of SVN changes failed."),
 
290
                       i18nc("@info:status", "Committed SVN changes."));
 
291
    }
 
292
 
 
293
    dialog.saveDialogSize(dialogConfig, KConfigBase::Persistent);
 
294
}
 
295
 
 
296
void FileViewSvnPlugin::addFiles()
 
297
{
 
298
    execSvnCommand("add",
 
299
                   i18nc("@info:status", "Adding files to SVN repository..."),
 
300
                   i18nc("@info:status", "Adding of files to SVN repository failed."),
 
301
                   i18nc("@info:status", "Added files to SVN repository."));
 
302
}
 
303
 
 
304
void FileViewSvnPlugin::removeFiles()
 
305
{
 
306
    execSvnCommand("remove",
 
307
                   i18nc("@info:status", "Removing files from SVN repository..."),
 
308
                   i18nc("@info:status", "Removing of files from SVN repository failed."),
 
309
                   i18nc("@info:status", "Removed files from SVN repository."));
 
310
}
 
311
 
 
312
void FileViewSvnPlugin::slotOperationCompleted(int exitCode, QProcess::ExitStatus exitStatus)
 
313
{
 
314
    if ((exitStatus != QProcess::NormalExit) || (exitCode != 0)) {
 
315
        emit errorMessage(m_errorMsg);
 
316
    } else if (m_contextItems.isEmpty()) {
 
317
        emit operationCompletedMessage(m_operationCompletedMsg);
 
318
        emit versionStatesChanged();
 
319
    } else {
 
320
        startSvnCommandProcess();
 
321
    }
 
322
}
 
323
 
 
324
void FileViewSvnPlugin::slotOperationError()
 
325
{
 
326
    emit errorMessage(m_errorMsg);
 
327
 
 
328
    // don't do any operation on other items anymore
 
329
    m_contextItems.clear();
 
330
}
 
331
 
 
332
void FileViewSvnPlugin::execSvnCommand(const QString& svnCommand,
 
333
                                       const QString& infoMsg,
 
334
                                       const QString& errorMsg,
 
335
                                       const QString& operationCompletedMsg)
 
336
{
 
337
    emit infoMessage(infoMsg);
 
338
 
 
339
    m_command = svnCommand;
 
340
    m_errorMsg = errorMsg;
 
341
    m_operationCompletedMsg = operationCompletedMsg;
 
342
 
 
343
    startSvnCommandProcess();
 
344
}
 
345
 
 
346
void FileViewSvnPlugin::startSvnCommandProcess()
 
347
{
 
348
    QProcess* process = new QProcess(this);
 
349
    connect(process, SIGNAL(finished(int, QProcess::ExitStatus)),
 
350
            this, SLOT(slotOperationCompleted(int, QProcess::ExitStatus)));
 
351
    connect(process, SIGNAL(error(QProcess::ProcessError)),
 
352
            this, SLOT(slotOperationError()));
 
353
 
 
354
    const QString program = "svn " + m_command + ' ';
 
355
    if (!m_contextDir.isEmpty()) {
 
356
        process->start(program + KShell::quoteArg(m_contextDir));
 
357
        m_contextDir.clear();
 
358
    } else {
 
359
        const KFileItem item = m_contextItems.takeLast();
 
360
        process->start(program + KShell::quoteArg(item.localPath()));
 
361
        // the remaining items of m_contextItems will be executed
 
362
        // after the process has finished (see slotOperationFinished())
 
363
    }
 
364
}