1 /***************************************************************************
2 * Copyright (C) 2009 by Peter Penz <peter.penz@gmx.at> *
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. *
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. *
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 ***************************************************************************/
20 #include "revisioncontrolplugin.h"
22 RevisionControlPlugin::RevisionControlPlugin()
26 RevisionControlPlugin::~RevisionControlPlugin()
30 #include "revisioncontrolplugin.moc"
32 // ----------------------------------------------------------------------------
40 #include <kfileitem.h>
46 #include <QTextStream>
48 SubversionPlugin::SubversionPlugin() :
52 m_showLocalChangesAction(0),
59 m_updateAction
= new KAction(this);
60 m_updateAction
->setIcon(KIcon("view-refresh"));
61 m_updateAction
->setText(i18nc("@item:inmenu", "SVN Update"));
62 connect(m_updateAction
, SIGNAL(triggered()),
63 this, SLOT(updateFiles()));
65 m_showLocalChangesAction
= new KAction(this);
66 m_showLocalChangesAction
->setIcon(KIcon("view-split-left-right"));
67 m_showLocalChangesAction
->setText(i18nc("@item:inmenu", "Show Local SVN Changes"));
68 connect(m_showLocalChangesAction
, SIGNAL(triggered()),
69 this, SLOT(showLocalChanges()));
71 m_commitAction
= new KAction(this);
72 m_commitAction
->setText(i18nc("@item:inmenu", "SVN Commit..."));
73 connect(m_commitAction
, SIGNAL(triggered()),
74 this, SLOT(commitFiles()));
76 m_addAction
= new KAction(this);
77 m_addAction
->setIcon(KIcon("list-add"));
78 m_addAction
->setText(i18nc("@item:inmenu", "SVN Add"));
79 connect(m_addAction
, SIGNAL(triggered()),
80 this, SLOT(addFiles()));
82 m_removeAction
= new KAction(this);
83 m_removeAction
->setIcon(KIcon("list-remove"));
84 m_removeAction
->setText(i18nc("@item:inmenu", "SVN Delete"));
85 connect(m_removeAction
, SIGNAL(triggered()),
86 this, SLOT(removeFiles()));
89 SubversionPlugin::~SubversionPlugin()
93 QString
SubversionPlugin::fileName() const
98 bool SubversionPlugin::beginRetrieval(const QString
& directory
)
100 Q_ASSERT(directory
.endsWith('/'));
101 m_retrievalDir
= directory
;
102 const QString path
= directory
+ ".svn/text-base/";
105 const QFileInfoList fileInfoList
= dir
.entryInfoList();
106 const int size
= fileInfoList
.size();
108 for (int i
= 0; i
< size
; ++i
) {
109 const QFileInfo fileInfo
= fileInfoList
.at(i
);
110 fileName
= fileInfo
.fileName();
111 // Remove the ".svn-base" postfix to be able to compare the filenames
112 // in a fast way in SubversionPlugin::revisionState().
113 fileName
.chop(sizeof(".svn-base") / sizeof(char) - 1);
114 if (!fileName
.isEmpty()) {
116 info
.size
= fileInfo
.size();
117 info
.timeStamp
= fileInfo
.lastModified();
118 m_revisionInfoHash
.insert(directory
+ fileName
, info
);
124 void SubversionPlugin::endRetrieval()
128 RevisionControlPlugin::RevisionState
SubversionPlugin::revisionState(const KFileItem
& item
)
130 const QString itemUrl
= item
.localPath();
132 QFile
file(itemUrl
+ "/.svn");
133 if (file
.open(QIODevice::ReadOnly
)) {
135 return RevisionControlPlugin::NormalRevision
;
137 } else if (m_revisionInfoHash
.contains(itemUrl
)) {
138 const RevisionInfo info
= m_revisionInfoHash
.value(itemUrl
);
139 const QDateTime localTimeStamp
= item
.time(KFileItem::ModificationTime
).dateTime();
140 const QDateTime versionedTimeStamp
= info
.timeStamp
;
142 if (localTimeStamp
> versionedTimeStamp
) {
143 if ((info
.size
!= item
.size()) || !equalRevisionContent(item
.name())) {
144 return RevisionControlPlugin::LocallyModifiedRevision
;
146 } else if (localTimeStamp
< versionedTimeStamp
) {
147 if ((info
.size
!= item
.size()) || !equalRevisionContent(item
.name())) {
148 return RevisionControlPlugin::UpdateRequiredRevision
;
151 return RevisionControlPlugin::NormalRevision
;
154 return RevisionControlPlugin::UnversionedRevision
;
157 QList
<QAction
*> SubversionPlugin::contextMenuActions(const KFileItemList
& items
)
159 Q_ASSERT(!items
.isEmpty());
161 m_contextItems
= items
;
162 m_contextDir
.clear();
164 // iterate all items and check the revision state to know which
165 // actions can be enabled
166 const int itemsCount
= items
.count();
167 int revisionedCount
= 0;
168 int editingCount
= 0;
169 foreach (const KFileItem
& item
, items
) {
170 const RevisionState state
= revisionState(item
);
171 if (state
!= UnversionedRevision
) {
176 case LocallyModifiedRevision
:
177 case ConflictingRevision
:
184 m_commitAction
->setEnabled(editingCount
> 0);
185 m_addAction
->setEnabled(revisionedCount
== 0);
186 m_removeAction
->setEnabled(revisionedCount
== itemsCount
);
188 QList
<QAction
*> actions
;
189 actions
.append(m_updateAction
);
190 actions
.append(m_commitAction
);
191 actions
.append(m_addAction
);
192 actions
.append(m_removeAction
);
196 QList
<QAction
*> SubversionPlugin::contextMenuActions(const QString
& directory
)
198 m_contextDir
= directory
;
199 m_contextItems
.clear();
201 QList
<QAction
*> actions
;
202 actions
.append(m_updateAction
);
203 actions
.append(m_showLocalChangesAction
);
204 actions
.append(m_commitAction
);
208 void SubversionPlugin::updateFiles()
210 execSvnCommand("update");
213 void SubversionPlugin::showLocalChanges()
215 Q_ASSERT(!m_contextDir
.isEmpty());
216 Q_ASSERT(m_contextItems
.isEmpty());
218 const QString command
= "mkfifo /tmp/fifo; svn diff " +
219 KShell::quoteArg(m_contextDir
) +
220 " > /tmp/fifo & kompare /tmp/fifo; rm /tmp/fifo";
221 KRun::runCommand(command
, 0);
224 void SubversionPlugin::commitFiles()
226 KDialog
dialog(0, Qt::Dialog
);
228 KVBox
* box
= new KVBox(&dialog
);
229 new QLabel(i18nc("@label", "Description:"), box
);
230 QTextEdit
* editor
= new QTextEdit(box
);
232 dialog
.setMainWidget(box
);
233 dialog
.setCaption(i18nc("@title:window", "SVN Commit"));
234 dialog
.setButtons(KDialog::Ok
| KDialog::Cancel
);
235 dialog
.setDefaultButton(KDialog::Ok
);
236 dialog
.setButtonText(KDialog::Ok
, i18nc("@action:button", "Commit"));
238 KConfigGroup
dialogConfig(KSharedConfig::openConfig("dolphinrc"),
240 dialog
.restoreDialogSize(dialogConfig
);
242 if (dialog
.exec() == QDialog::Accepted
) {
243 const QString description
= editor
->toPlainText();
244 execSvnCommand("commit -m " + KShell::quoteArg(description
));
247 dialog
.saveDialogSize(dialogConfig
, KConfigBase::Persistent
);
250 void SubversionPlugin::addFiles()
252 execSvnCommand("add");
255 void SubversionPlugin::removeFiles()
257 execSvnCommand("remove");
260 void SubversionPlugin::execSvnCommand(const QString
& svnCommand
)
262 const QString command
= "svn " + svnCommand
+ ' ';
263 if (!m_contextDir
.isEmpty()) {
264 KRun::runCommand(command
+ KShell::quoteArg(m_contextDir
), 0);
266 foreach (const KFileItem
& item
, m_contextItems
) {
267 KRun::runCommand(command
+ KShell::quoteArg(item
.localPath()), 0);
272 bool SubversionPlugin::equalRevisionContent(const QString
& name
) const
274 QFile
localFile(m_retrievalDir
+ '/' + name
);
275 if (!localFile
.open(QIODevice::ReadOnly
| QIODevice::Text
)) {
279 QFile
revisionedFile(m_retrievalDir
+ "/.svn/text-base/" + name
+ ".svn-base");
280 if (!revisionedFile
.open(QIODevice::ReadOnly
| QIODevice::Text
)) {
284 QTextStream
localText(&localFile
);
285 QTextStream
revisionedText(&revisionedFile
);
286 while (!localText
.atEnd() && !revisionedText
.atEnd()) {
287 if (localText
.readLine() != revisionedText
.readLine()) {
292 return localText
.atEnd() && revisionedText
.atEnd();