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 | |
39 | class 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 | |
60 | KateMwModOnHdDialog::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 ; |
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 | |
135 | KateMwModOnHdDialog::~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 | |
147 | void KateMwModOnHdDialog::slotUser1() |
148 | { |
149 | handleSelected( Ignore ); |
150 | } |
151 | |
152 | void KateMwModOnHdDialog::slotUser2() |
153 | { |
154 | handleSelected( Overwrite ); |
155 | } |
156 | |
157 | void KateMwModOnHdDialog::slotUser3() |
158 | { |
159 | handleSelected( Reload ); |
160 | } |
161 | |
162 | void 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 | |
216 | void 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. |
225 | void 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 | |
267 | void KateMwModOnHdDialog::slotDataAvailable() |
268 | { |
269 | m_diffFile->write(m_proc->readAll()); |
270 | } |
271 | |
272 | void 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 | |
311 | void 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 | |
325 | void 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 | |
338 | void 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 | |