1/*
2 This library is free software; you can redistribute it and/or
3 modify it under the terms of the GNU Library General Public
4 License version 2 as published by the Free Software Foundation.
5
6 This library is distributed in the hope that it will be useful,
7 but WITHOUT ANY WARRANTY; without even the implied warranty of
8 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 Library General Public License for more details.
10
11 You should have received a copy of the GNU Library General Public License
12 along with this library; see the file COPYING.LIB. If not, write to
13 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
14 Boston, MA 02110-1301, USA.
15
16 ---
17 Copyright (C) 2004, Anders Lund <anders@alweb.dk>
18*/
19
20#include "katemwmodonhddialog.h"
21#include "katemwmodonhddialog.moc"
22
23#include "katedocmanager.h"
24#include "katemainwindow.h"
25
26#include <KLocale>
27#include <KMessageBox>
28#include <kprocess.h>
29#include <KRun>
30#include <KTemporaryFile>
31#include <KPushButton>
32#include <KVBox>
33
34#include <QHeaderView>
35#include <QLabel>
36#include <QPushButton>
37#include <QTextStream>
38
39class KateDocItem : public QTreeWidgetItem
40{
41 public:
42 KateDocItem( KTextEditor::Document *doc, const QString &status, QTreeWidget *tw )
43 : QTreeWidgetItem( tw ),
44 document( doc )
45 {
46 setText( 0, doc->url().pathOrUrl() );
47 setText( 1, status );
48 if ( ! doc->isModified() )
49 setCheckState( 0, Qt::Checked );
50 else
51 setCheckState( 0, Qt::Unchecked );
52 }
53 ~KateDocItem()
54 {}
55
56 KTextEditor::Document *document;
57};
58
59
60KateMwModOnHdDialog::KateMwModOnHdDialog( DocVector docs, QWidget *parent, const char *name )
61 : KDialog( parent ),
62 m_proc( 0 ),
63 m_diffFile( 0 )
64{
65 setCaption( i18n("Documents Modified on Disk") );
66 setButtons( User1 | User2 | User3 );
67 setButtonGuiItem( User1, KGuiItem (i18n("&Ignore Changes"), "dialog-warning") );
68 setButtonGuiItem( User2, KStandardGuiItem::overwrite() );
69 setButtonGuiItem( User3, KGuiItem (i18n("&Reload"), "view-refresh") );
70
71 setObjectName( name );
72 setModal( true );
73 setDefaultButton( KDialog::User3 );
74
75 setButtonToolTip( User1, i18n(
76 "Remove modified flag from selected documents") );
77 setButtonToolTip( User2, i18n(
78 "Overwrite selected documents, discarding disk changes") );
79 setButtonToolTip( User3, i18n(
80 "Reload selected documents from disk") );
81
82 KVBox *w = new KVBox( this );
83 setMainWidget( w );
84 w->setSpacing( KDialog::spacingHint() );
85
86 KHBox *lo1 = new KHBox( w );
87
88 // dialog text
89 QLabel *icon = new QLabel( lo1 );
90 icon->setPixmap( DesktopIcon("dialog-warning") );
91
92 QLabel *t = new QLabel( i18n(
93 "<qt>The documents listed below have changed on disk.<p>Select one "
94 "or more at once, and press an action button until the list is empty.</p></qt>"), lo1 );
95 lo1->setStretchFactor( t, 1000 );
96
97 // document list
98 twDocuments = new QTreeWidget( w );
99 QStringList header;
100 header << i18n("Filename") << i18n("Status on Disk");
101 twDocuments->setHeaderLabels(header);
102 twDocuments->setSelectionMode( QAbstractItemView::SingleSelection );
103 twDocuments->setRootIsDecorated( false );
104
105
106 m_stateTexts << "" << i18n("Modified") << i18n("Created") << i18n("Deleted");
107 for ( int i = 0; i < docs.size(); i++ )
108 {
109 new KateDocItem( docs[i], m_stateTexts[ (uint)KateDocManager::self()->documentInfo( docs[i] )->modifiedOnDiscReason ], twDocuments );
110 }
111 twDocuments->header()->setStretchLastSection(false);
112 twDocuments->header()->setResizeMode(0, QHeaderView::Stretch);
113 twDocuments->header()->setResizeMode(1, QHeaderView::ResizeToContents);
114
115 connect( twDocuments, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(slotSelectionChanged(QTreeWidgetItem*,QTreeWidgetItem*)) );
116
117 // diff button
118 KHBox *lo2 = new KHBox ( w );
119 QWidget *d = new QWidget (lo2);
120 lo2->setStretchFactor (d, 2);
121 btnDiff = new KPushButton( KGuiItem (i18n("&View Difference"), "document-preview"), lo2 );
122
123 btnDiff->setWhatsThis(i18n(
124 "Calculates the difference between the editor contents and the disk "
125 "file for the selected document, and shows the difference with the "
126 "default application. Requires diff(1).") );
127 connect( btnDiff, SIGNAL(clicked()), this, SLOT(slotDiff()) );
128 connect( this, SIGNAL(user1Clicked()), this, SLOT(slotUser1()) );
129 connect( this, SIGNAL(user2Clicked()), this, SLOT(slotUser2()) );
130 connect( this, SIGNAL(user3Clicked()), this, SLOT(slotUser3()) );
131
132 slotSelectionChanged(NULL, NULL);
133}
134
135KateMwModOnHdDialog::~KateMwModOnHdDialog()
136{
137 KateMainWindow::unsetModifiedOnDiscDialogIfIf(this);
138 delete m_proc;
139 m_proc = 0;
140 if (m_diffFile) {
141 m_diffFile->setAutoRemove(true);
142 delete m_diffFile;
143 m_diffFile = 0;
144 }
145}
146
147void KateMwModOnHdDialog::slotUser1()
148{
149 handleSelected( Ignore );
150}
151
152void KateMwModOnHdDialog::slotUser2()
153{
154 handleSelected( Overwrite );
155}
156
157void KateMwModOnHdDialog::slotUser3()
158{
159 handleSelected( Reload );
160}
161
162void KateMwModOnHdDialog::handleSelected( int action )
163{
164 // collect all items we can remove
165 QList<QTreeWidgetItem *> itemsToDelete;
166 for ( QTreeWidgetItemIterator it ( twDocuments ); *it; ++it )
167 {
168 KateDocItem *item = (KateDocItem *) * it;
169 if ( item->checkState(0) == Qt::Checked )
170 {
171 KTextEditor::ModificationInterface::ModifiedOnDiskReason reason = KateDocManager::self()->documentInfo( item->document )->modifiedOnDiscReason;
172 bool success = true;
173
174 if (KTextEditor::ModificationInterface *iface = qobject_cast<KTextEditor::ModificationInterface *>(item->document))
175 iface->setModifiedOnDisk( KTextEditor::ModificationInterface::OnDiskUnmodified );
176
177 switch ( action )
178 {
179 case Overwrite:
180 success = item->document->save();
181 if ( ! success )
182 {
183 KMessageBox::sorry( this,
184 i18n("Could not save the document \n'%1'",
185 item->document->url().pathOrUrl() ) );
186 }
187 break;
188
189 case Reload:
190 item->document->documentReload();
191 break;
192
193 default:
194 break;
195 }
196
197 if ( success )
198 itemsToDelete.append( item );
199 else
200 {
201 if (KTextEditor::ModificationInterface *iface = qobject_cast<KTextEditor::ModificationInterface *>(item->document))
202 iface->setModifiedOnDisk( reason );
203 }
204 }
205 }
206
207 // remove the marked items
208 for (int i = 0; i < itemsToDelete.count(); ++i)
209 delete itemsToDelete[i];
210
211// any documents left unhandled?
212 if ( ! twDocuments->topLevelItemCount() )
213 done( Ok );
214}
215
216void KateMwModOnHdDialog::slotSelectionChanged(QTreeWidgetItem *current, QTreeWidgetItem *)
217{
218 // set the diff button enabled
219 btnDiff->setEnabled( current &&
220 KateDocManager::self()->documentInfo( (static_cast<KateDocItem*>(current))->document )->modifiedOnDiscReason != KTextEditor::ModificationInterface::OnDiskDeleted );
221}
222
223// ### the code below is slightly modified from kdelibs/kate/part/katedialogs,
224// class KateModOnHdPrompt.
225void KateMwModOnHdDialog::slotDiff()
226{
227 if ( !btnDiff->isEnabled()) // diff button already pressed, proc not finished yet
228 return;
229
230 if ( ! twDocuments->currentItem() )
231 return;
232
233 KTextEditor::Document *doc = (static_cast<KateDocItem*>(twDocuments->currentItem()))->document;
234
235 // don't try to diff a deleted file
236 if ( KateDocManager::self()->documentInfo( doc )->modifiedOnDiscReason == KTextEditor::ModificationInterface::OnDiskDeleted )
237 return;
238
239 if (m_diffFile)
240 return;
241
242 m_diffFile = new KTemporaryFile();
243 m_diffFile->open();
244
245 // Start a KProcess that creates a diff
246 m_proc = new KProcess( this );
247 m_proc->setOutputChannelMode( KProcess::MergedChannels );
248 *m_proc << "diff" << "-ub" << "-" << doc->url().toLocalFile();
249 connect( m_proc, SIGNAL(readyRead()), this, SLOT(slotDataAvailable()) );
250 connect( m_proc, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(slotPDone()) );
251
252 setCursor( Qt::WaitCursor );
253 btnDiff->setEnabled(false);
254
255 m_proc->start();
256
257 QTextStream ts(m_proc);
258 int lastln = doc->lines() - 1;
259 for ( int l = 0; l < lastln; ++l ) {
260 ts << doc->line( l ) << '\n';
261 }
262 ts << doc->line(lastln);
263 ts.flush();
264 m_proc->closeWriteChannel();
265}
266
267void KateMwModOnHdDialog::slotDataAvailable()
268{
269 m_diffFile->write(m_proc->readAll());
270}
271
272void KateMwModOnHdDialog::slotPDone()
273{
274 setCursor( Qt::ArrowCursor );
275 slotSelectionChanged(twDocuments->currentItem(), 0);
276
277 const QProcess::ExitStatus es = m_proc->exitStatus();
278 delete m_proc;
279 m_proc = 0;
280
281 if ( es != QProcess::NormalExit )
282 {
283 KMessageBox::sorry( this,
284 i18n("The diff command failed. Please make sure that "
285 "diff(1) is installed and in your PATH."),
286 i18n("Error Creating Diff") );
287 delete m_diffFile;
288 m_diffFile = 0;
289 return;
290 }
291
292 if ( m_diffFile->size() == 0 )
293 {
294 KMessageBox::information( this,
295 i18n("Ignoring amount of white space changed, the files are identical."),
296 i18n("Diff Output") );
297 delete m_diffFile;
298 m_diffFile = 0;
299 return;
300 }
301
302 m_diffFile->setAutoRemove(false);
303 KUrl url = KUrl::fromPath(m_diffFile->fileName());
304 delete m_diffFile;
305 m_diffFile = 0;
306
307 // KRun::runUrl should delete the file, once the client exits
308 KRun::runUrl( url, "text/x-patch", this, true );
309}
310
311void KateMwModOnHdDialog::addDocument(KTextEditor::Document *doc)
312{
313 for ( QTreeWidgetItemIterator it ( twDocuments ); *it; ++it )
314 {
315 KateDocItem *item = (KateDocItem *) * it;
316 if (item->document==doc)
317 {
318 delete item;
319 break;
320 }
321 }
322 new KateDocItem( doc, m_stateTexts[ (uint)KateDocManager::self()->documentInfo( doc )->modifiedOnDiscReason ], twDocuments );
323}
324
325void KateMwModOnHdDialog::keyPressEvent( QKeyEvent *event )
326{
327 if ( event->modifiers() == 0 )
328 {
329 if ( event->key() == Qt::Key_Escape )
330 {
331 event->accept();
332 return;
333 }
334 }
335 KDialog::keyPressEvent(event);
336}
337
338void KateMwModOnHdDialog::closeEvent( QCloseEvent *e )
339{
340 if ( ! twDocuments->topLevelItemCount() )
341 KDialog::closeEvent(e);
342 else e->ignore();
343}
344// kate: space-indent on; indent-width 2; replace-tabs on;
345