konq_operations.cpp 36 KB
Newer Older
1
/*  This file is part of the KDE project
2
3
4
5
6
    Copyright 2000-2007  David Faure <faure@kde.org>
    Copyright 2003       Waldo Bastian <bastian@kde.org>
    Copyright 2002       Michael Brade <brade@kde.org>
    Copyright 2001-2002  Alexander Neundorf <neundorf@kde.org>
    Copyright 2000-2001  Simon Hausmann <hausmann@kde.org>
7

8
9
10
11
    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) version 3.
12

13
    This library is distributed in the hope that it will be useful,
14
    but WITHOUT ANY WARRANTY; without even the implied warranty of
15
16
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.
17

18
19
20
21
    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.
22
23
*/

Laurent Montel's avatar
Laurent Montel committed
24
#include "konq_operations.h"
25
#include "konq_dndpopupmenuplugin.h"
26
27
#include "konqmimedata.h"

Laurent Montel's avatar
Laurent Montel committed
28
#include <ktoolinvocation.h>
29
#include <kautomount.h>
30
#include <kmountpoint.h>
31
#include <kinputdialog.h>
32
33
#include <klocale.h>
#include <kmessagebox.h>
Laurent Montel's avatar
Laurent Montel committed
34
#include <knotification.h>
35
#include <krun.h>
36
#include <kshell.h>
Christian Ehrlicher's avatar
Christian Ehrlicher committed
37
#include <kprocess.h>
38
#include <kshortcut.h>
39
#include <kprotocolmanager.h>
40
#include <kio/deletejob.h>
41
42
#include <kio/fileundomanager.h>
#include <kio/job.h>
Laurent Montel's avatar
Laurent Montel committed
43
#include <kio/jobuidelegate.h>
44
#include <kio/jobclasses.h>
45
#include <kio/copyjob.h>
46
#include <kio/paste.h>
Holger Freyther's avatar
Holger Freyther committed
47
#include <kio/renamedialog.h>
Laurent Montel's avatar
Laurent Montel committed
48
#include <kdirnotify.h>
Laurent Montel's avatar
Laurent Montel committed
49
#include <kuiserverjobtracker.h>
50
#include <kstandarddirs.h>
51
// For doDrop
Stephen Kelly's avatar
Stephen Kelly committed
52
#include <kicon.h>
53
54
#include <kauthorized.h>
#include <kglobal.h>
55
#include <kglobalsettings.h>
56
#include <kdebug.h>
57
#include <kfileitem.h>
58
#include <kdesktopfile.h>
59

60
61
62
63
64
//for _addPluginActions
#include <kfileitemlistproperties.h>
#include <kservice.h>
#include <kmimetypetrader.h>

65
//#include <konq_iconviewwidget.h>
66
#include <QMenu>
67
68
69
70
71
#include <QApplication>
#include <QClipboard>
#include <QDropEvent>
#include <QList>
#include <QDir>
72

73
74
#include <assert.h>
#include <unistd.h>
75
#include <kconfiggroup.h>
76

77
KonqOperations::KonqOperations( QWidget *parent )
Dave Rowe's avatar
Dave Rowe committed
78
    : QObject( parent ),
79
      m_method( UNKNOWN ), m_info(0), m_pasteInfo(0)
80
{
81
    setObjectName( QLatin1String( "KonqOperations" ) );
82
}
83

84
85
86
KonqOperations::~KonqOperations()
{
    delete m_info;
87
    delete m_pasteInfo;
88
89
}

Luboš Luňák's avatar
Luboš Luňák committed
90
void KonqOperations::editMimeType( const QString & mimeType, QWidget* parent )
91
{
92
    QString keditfiletype = QLatin1String("keditfiletype");
Luboš Luňák's avatar
Luboš Luňák committed
93
    KRun::runCommand( keditfiletype
94
                      + " --parent " + QString::number( (qptrdiff)parent->winId())
Laurent Montel's avatar
Laurent Montel committed
95
                      + ' ' + KShell::quoteArg(mimeType),
Luboš Luňák's avatar
Luboš Luňák committed
96
                      keditfiletype, keditfiletype /*unused*/, parent );
97
98
}

99
void KonqOperations::del( QWidget * parent, Operation method, const KUrl::List & selectedUrls )
100
{
101
    kDebug(1203) << parent->metaObject()->className();
102
103
    if ( selectedUrls.isEmpty() )
    {
104
        kWarning(1203) << "Empty URL list !" ;
105
106
107
108
        return;
    }

    KonqOperations * op = new KonqOperations( parent );
109
    ConfirmationType confirmation = DEFAULT_CONFIRMATION;
110
    op->_del( method, selectedUrls, confirmation );
111
112
}

113
void KonqOperations::emptyTrash( QWidget* parent )
114
{
115
    KonqOperations *op = new KonqOperations( parent );
116
    op->_del( EMPTYTRASH, KUrl("trash:/"), DEFAULT_CONFIRMATION );
117
}
118

119
void KonqOperations::restoreTrashedItems( const KUrl::List& urls, QWidget* parent )
120
{
121
122
    KonqOperations *op = new KonqOperations( parent );
    op->_restoreTrashedItems( urls );
123
124
}

125
KIO::SimpleJob* KonqOperations::mkdir( QWidget *parent, const KUrl & url )
126
{
127
128
129
    KIO::SimpleJob * job = KIO::mkdir(url);
    job->ui()->setWindow(parent);
    job->ui()->setAutoErrorHandlingEnabled(true);
130
    KIO::FileUndoManager::self()->recordJob( KIO::FileUndoManager::Mkdir, KUrl(), url, job );
131
    return job;
132
133
}

134
void KonqOperations::doPaste( QWidget * parent, const KUrl & destUrl, const QPoint &pos )
135
{
136
137
138
    QClipboard* clipboard = QApplication::clipboard();
    const QMimeData *data = clipboard->mimeData();
    const bool move = KonqMimeData::decodeIsCutSelection(data);
139

140
    KIO::Job *job = KIO::pasteClipboard( destUrl, parent, move );
141
    if (job) {
142
        KonqOperations * op = new KonqOperations( parent );
143
144
145
        KIOPasteInfo * pi = new KIOPasteInfo;
        pi->mousePos = pos;
        op->setPasteInfo( pi );
146
147
148
149
150
151
152
153
        KIO::CopyJob * copyJob = qobject_cast<KIO::CopyJob *>(job);
        if (copyJob) {
            op->setOperation( job, move ? MOVE : COPY, copyJob->destUrl() );
            KIO::FileUndoManager::self()->recordJob( move ? KIO::FileUndoManager::Move : KIO::FileUndoManager::Copy, KUrl::List(), destUrl, job );
        } else if (KIO::SimpleJob* simpleJob = qobject_cast<KIO::SimpleJob *>(job)) {
            op->setOperation(job, PUT, simpleJob->url());
            KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Put, KUrl::List(), simpleJob->url(), job);
        }
154
155
156
    }
}

157
void KonqOperations::copy( QWidget * parent, Operation method, const KUrl::List & selectedUrls, const KUrl& destUrl )
Alexander Neundorf's avatar
   
Alexander Neundorf committed
158
{
159
    kDebug(1203) << parent->metaObject()->className() << selectedUrls << destUrl;
160
161
    if ((method!=COPY) && (method!=MOVE) && (method!=LINK))
    {
162
        kWarning(1203) << "Illegal copy method !" ;
163
164
165
166
        return;
    }
    if ( selectedUrls.isEmpty() )
    {
167
        kWarning(1203) << "Empty URL list !" ;
168
169
170
171
        return;
    }

    KonqOperations * op = new KonqOperations( parent );
172
    KIO::CopyJob* job;
173
174
175
176
177
178
179
180
181
    if (method == LINK)
        job = KIO::link( selectedUrls, destUrl );
    else if (method == MOVE)
        job = KIO::move( selectedUrls, destUrl );
    else
        job = KIO::copy( selectedUrls, destUrl );

    op->setOperation( job, method, destUrl );

182
    KIO::FileUndoManager::self()->recordCopyJob(job);
Dirk Mueller's avatar
Dirk Mueller committed
183
}
Alexander Neundorf's avatar
   
Alexander Neundorf committed
184

185
void KonqOperations::_del( Operation method, const KUrl::List & _selectedUrls, ConfirmationType confirmation )
186
{
187
188
    KUrl::List selectedUrls;
    for (KUrl::List::ConstIterator it = _selectedUrls.begin(); it != _selectedUrls.end(); ++it)
Aaron J. Seigo's avatar
Aaron J. Seigo committed
189
        if (KProtocolManager::supportsDeleting(*it))
190
191
            selectedUrls.append(*it);
    if (selectedUrls.isEmpty()) {
192
        delete this; // this one is ok, _del is always called directly
193
194
195
        return;
    }

196
    if ( confirmation == SKIP_CONFIRMATION || askDeleteConfirmation( selectedUrls, method, confirmation, parentWidget() ) )
197
    {
198
199
        //m_srcUrls = selectedUrls;
        KIO::Job *job;
200
        m_method = method;
201
202
203
204
205
        switch( method )
        {
        case TRASH:
        {
            job = KIO::trash( selectedUrls );
206
            KIO::FileUndoManager::self()->recordJob( KIO::FileUndoManager::Trash, selectedUrls, KUrl("trash:/"), job );
207
208
209
210
211
212
213
214
215
            break;
        }
        case EMPTYTRASH:
        {
            // Same as in ktrash --empty
            QByteArray packedArgs;
            QDataStream stream( &packedArgs, QIODevice::WriteOnly );
            stream << (int)1;
            job = KIO::special( KUrl("trash:/"), packedArgs );
216
            KNotification::event("Trash: emptied", QString() , QPixmap() , 0l, KNotification::DefaultEvent );
217
218
219
220
221
222
            break;
        }
        case DEL:
            job = KIO::del( selectedUrls );
            break;
        default:
223
            kWarning() << "Unknown operation: " << method ;
224
            delete this; // this one is ok, _del is always called directly
225
226
227
            return;
        }
        job->ui()->setWindow(parentWidget());
Laurent Montel's avatar
Laurent Montel committed
228
229
        connect( job, SIGNAL(result(KJob*)),
                 SLOT(slotResult(KJob*)) );
230
231
232
    } else {
        delete this; // this one is ok, _del is always called directly
    }
233
234
}

Laurent Montel's avatar
Laurent Montel committed
235
void KonqOperations::_restoreTrashedItems( const KUrl::List& urls )
236
{
David Faure's avatar
David Faure committed
237
    m_method = RESTORE;
238
    KonqMultiRestoreJob* job = new KonqMultiRestoreJob( urls );
239
    job->ui()->setWindow(parentWidget());
240
    KIO::getJobTracker()->registerJob(job);
Laurent Montel's avatar
Laurent Montel committed
241
242
    connect( job, SIGNAL(result(KJob*)),
             SLOT(slotResult(KJob*)) );
243
244
}

245
bool KonqOperations::askDeleteConfirmation( const KUrl::List & selectedUrls, int method, ConfirmationType confirmation, QWidget* widget )
246
{
247
248
249
250
251
252
253
254
255
256
257
258
259
     KIO::JobUiDelegate::DeletionType deletionType;
     switch (method) {
     case EMPTYTRASH:
         deletionType = KIO::JobUiDelegate::EmptyTrash;
         break;
     case DEL:
         deletionType = KIO::JobUiDelegate::Delete;
         break;
     default:
         deletionType = KIO::JobUiDelegate::Trash;
         break;
     }

260
261
262
263
    KIO::JobUiDelegate::ConfirmationType confirmationType = confirmation == FORCE_CONFIRMATION ? KIO::JobUiDelegate::ForceConfirmation : KIO::JobUiDelegate::DefaultConfirmation;
    KIO::JobUiDelegate uiDelegate;
    uiDelegate.setWindow(widget);
    return uiDelegate.askDeleteConfirmation(selectedUrls, deletionType, confirmationType);
264
265
}

266
void KonqOperations::doDrop( const KFileItem & destItem, const KUrl & dest, QDropEvent * ev, QWidget * parent )
267
268
269
270
271
272
{
    (void) KonqOperations::doDrop( destItem, dest, ev, parent, QList<QAction*>() );
}

KonqOperations *KonqOperations::doDrop( const KFileItem & destItem, const KUrl & dest, QDropEvent * ev, QWidget * parent,
                                        const QList<QAction*> & userActions  )
273
{
274
    kDebug(1203) << "dest:" << dest;
275
    QMap<QString, QString> metaData;
276
277
    // Prefer local urls if possible, to avoid problems with desktop:/ urls from other users (#184403)
    const KUrl::List lst = KUrl::List::fromMimeData(ev->mimeData(), KUrl::List::PreferLocalUrls, &metaData);
278
279
280
281
282
283
    if (!lst.isEmpty()) { // Are they urls ?
        //kDebug(1203) << "metaData:" << metaData.count() << "entries.";
        //QMap<QString,QString>::ConstIterator mit;
        //for( mit = metaData.begin(); mit != metaData.end(); ++mit ) {
        //    kDebug(1203) << "metaData: key=" << mit.key() << "value=" << mit.value();
        //}
284
        // Check if we dropped something on itself
Laurent Montel's avatar
Laurent Montel committed
285
        KUrl::List::ConstIterator it = lst.begin();
286
287
288
        for (; it != lst.end() ; it++) {
            kDebug(1203) << "URL:" << (*it);
            if (dest.equals(*it, KUrl::CompareWithoutTrailingSlash)) {
289
                // The event source may be the view or an item (icon)
290
                // Note: ev->source() can be 0L! (in case of kdesktop) (Simon)
291
                if ( !ev->source() || ( ev->source() != parent && ev->source()->parent() != parent ) )
292
                    KMessageBox::sorry( parent, i18n("You cannot drop a folder on to itself") );
293
                kDebug(1203) << "Dropped on itself";
Dave Rowe's avatar
Dave Rowe committed
294
                ev->setAccepted( false );
295
                return 0; // do nothing instead of displaying kfm's annoying error box
296
            }
297
        }
298

299
        // Check the state of the modifiers key at the time of the drop
300
        Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers();
301

302
        Qt::DropAction action = ev->dropAction();
303
304
305
        // Check for the drop of a bookmark -> we want a Link action
        if ( ev->provides("application/x-xbel") )
        {
306
            modifiers |= Qt::ControlModifier | Qt::ShiftModifier;
307
            action = Qt::LinkAction;
308
            kDebug(1203) << "Bookmark -> emulating Link";
309
310
        }

311
        KonqOperations * op = new KonqOperations(parent);
312
        op->setDropInfo( new DropInfo( modifiers, lst, metaData, QCursor::pos(), action, userActions ) );
313
314

        // Ok, now we need destItem.
315
        if ( !destItem.isNull() )
316
        {
317
318
319
            // We have it already, we could just call asyncDrop.
            // But popping up a menu in the middle of a DND operation confuses and crashes Qt (#157630)
            // So let's delay it.
Peter Penz's avatar
Peter Penz committed
320
            qRegisterMetaType<KFileItem>("KFileItem");
321
            QMetaObject::invokeMethod(op, "asyncDrop", Qt::QueuedConnection, Q_ARG(KFileItem, destItem));
322
        }
323
        else
324
        {
325
            // we need to stat to get it.
Laurent Montel's avatar
Laurent Montel committed
326
            op->_statUrl( dest, op, SLOT(asyncDrop(KFileItem)) );
327
        }
328
        // In both cases asyncDrop will delete op when done
329

330
        ev->acceptProposedAction();
331
        return op;
332
333
334
    }
    else
    {
335
        //kDebug(1203) << "Pasting to " << dest.url();
336
        KonqOperations * op = new KonqOperations(parent);
337
338
339
340
341
342
        KIO::Job* job = KIO::pasteMimeData(ev->mimeData(), dest,
                                           i18n( "File name for dropped contents:" ),
                                           parent);
        if (KIO::SimpleJob* simpleJob = qobject_cast<KIO::SimpleJob *>(job)) {
            op->setOperation(job, PUT, simpleJob->url());
            KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Put, KUrl::List(), simpleJob->url(), simpleJob);
343
        }
344
        ev->acceptProposedAction();
345
        return op;
346
347
348
    }
}

349
void KonqOperations::asyncDrop( const KFileItem & destItem )
350
351
{
    assert(m_info); // setDropInfo should have been called before asyncDrop
352
353
    bool m_destIsLocal = false;
    m_destUrl = destItem.mostLocalUrl(m_destIsLocal); // #168154
354

355
    //kDebug(1203) << "destItem->mode=" << destItem->mode() << " url=" << m_destUrl;
356
    // Check what the destination is
357
    if ( destItem.isDir() )
358
    {
359
        doDropFileCopy();
360
361
        return;
    }
362
    if ( !m_destIsLocal )
363
364
365
366
    {
        // We dropped onto a remote URL that is not a directory!
        // (e.g. an HTTP link in the sidebar).
        // Can't do that, but we can't prevent it before stating the dest....
367
        kWarning(1203) << "Cannot drop onto " << m_destUrl ;
368
        deleteLater();
369
370
        return;
    }
371
    if ( destItem.isDesktopFile() )
372
    {
373
        // Local .desktop file. What type ?
374
        KDesktopFile desktopFile( m_destUrl.path() );
David Faure's avatar
David Faure committed
375
        KConfigGroup desktopGroup = desktopFile.desktopGroup();
376
        if ( desktopFile.hasApplicationType() )
377
        {
378
            QString error;
379
380
381
            const QStringList urlStrList = m_info->urls.toStringList();
            if ( KToolInvocation::startServiceByDesktopPath( m_destUrl.path(), urlStrList, &error ) > 0 )
                KMessageBox::error( parentWidget(), error );
382
        }
383
        else
384
        {
385
            // Device or Link -> adjust dest
David Faure's avatar
David Faure committed
386
387
            if ( desktopFile.hasDeviceType() && desktopGroup.hasKey("MountPoint") ) {
                QString point = desktopGroup.readEntry( "MountPoint" );
388
                m_destUrl.setPath( point );
389
                QString dev = desktopFile.readDevice();
390
                KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByDevice( dev );
391
                // Is the device already mounted ?
392
                if ( mp ) {
393
394
395
                    doDropFileCopy();
                }
#ifndef Q_WS_WIN
396
                else
397
                {
David Faure's avatar
David Faure committed
398
399
                    const bool ro = desktopGroup.readEntry( "ReadOnly", false );
                    const QByteArray fstype = desktopGroup.readEntry( "FSType" ).toLatin1();
400
                    KAutoMount* am = new KAutoMount( ro, fstype, dev, point, m_destUrl.path(), false );
Laurent Montel's avatar
Laurent Montel committed
401
                    connect( am, SIGNAL(finished()), this, SLOT(doDropFileCopy()) );
402
                }
403
#endif
404
                return;
405
            }
David Faure's avatar
David Faure committed
406
            else if ( desktopFile.hasLinkType() && desktopGroup.hasKey("URL") ) {
407
                m_destUrl = desktopGroup.readPathEntry("URL", QString());
408
                doDropFileCopy();
409
                return;
410
411
            }
            // else, well: mimetype, service, servicetype or .directory. Can't really drop anything on those.
412
        }
413
414
    }
    else
415
    {
416
        // Should be a local executable
417
        // (If this fails, there is a bug in KFileItem::acceptsDrops / KDirModel::flags)
418
        kDebug(1203) << m_destUrl.path() << "should be an executable";
419
        Q_ASSERT ( access( QFile::encodeName(m_destUrl.path()), X_OK ) == 0 );
420
        // Launch executable for each of the files
Christian Ehrlicher's avatar
Christian Ehrlicher committed
421
        QStringList args;
Albert Astals Cid's avatar
Albert Astals Cid committed
422
423
        const KUrl::List lst = m_info->urls;
        KUrl::List::ConstIterator it = lst.begin();
424
        for ( ; it != lst.end() ; it++ )
Christian Ehrlicher's avatar
Christian Ehrlicher committed
425
            args << (*it).path(); // assume local files
426
        kDebug(1203) << "starting " << m_destUrl.path() << " with " << lst.count() << " arguments";
Christian Ehrlicher's avatar
Christian Ehrlicher committed
427
        KProcess::startDetached( m_destUrl.path(), args );
428
    }
429
    deleteLater();
430
431
}

432
433
434
435
436
437
438
439
440
441
KUrl::List KonqOperations::droppedUrls() const
{
    return m_info->urls;
}

QPoint KonqOperations::dropPosition() const
{
   return m_info->mousePos;
}

442
void KonqOperations::doDropFileCopy()
443
444
{
    assert(m_info); // setDropInfo - and asyncDrop - should have been called before asyncDrop
445
    const KUrl::List lst = m_info->urls;
446
    Qt::DropAction action = m_info->action;
447
    bool isDesktopFile = false;
448
    bool itemIsOnDesktop = false;
449
    bool allItemsAreFromTrash = true;
Laurent Montel's avatar
Laurent Montel committed
450
451
    KUrl::List mlst; // list of items that can be moved
    for (KUrl::List::ConstIterator it = lst.begin(); it != lst.end(); ++it)
452
    {
453
        bool local = (*it).isLocalFile();
Aaron J. Seigo's avatar
Aaron J. Seigo committed
454
        if ( KProtocolManager::supportsDeleting( *it ) && (!local || QFileInfo((*it).directory()).isWritable() ))
455
            mlst.append(*it);
456
        if ( local && KDesktopFile::isDesktopFile((*it).toLocalFile()))
457
            isDesktopFile = true;
458
459
        if ( local && (*it).path().startsWith(KGlobalSettings::desktopPath()))
            itemIsOnDesktop = true;
460
461
        if ( local || (*it).protocol() != "trash" )
            allItemsAreFromTrash = false;
462
463
464
    }

    bool linkOnly = false;
Laurent Montel's avatar
Laurent Montel committed
465
    if (isDesktopFile && !KAuthorized::authorizeKAction("run_desktop_files") &&
466
        (m_destUrl.path( KUrl::AddTrailingSlash ) == KGlobalSettings::desktopPath()) )
467
468
    {
       linkOnly = true;
469
    }
470

471
    if ( !mlst.isEmpty() && m_destUrl.protocol() == "trash" )
472
    {
Laurent Montel's avatar
Laurent Montel committed
473
        if ( itemIsOnDesktop && !KAuthorized::authorizeKAction("editable_desktop_icons") )
474
        {
475
            deleteLater();
476
477
            return;
        }
478

David Faure's avatar
David Faure committed
479
        m_method = TRASH;
480
        if ( askDeleteConfirmation( mlst, TRASH, DEFAULT_CONFIRMATION, parentWidget() ) )
481
            action = Qt::MoveAction;
482
        else
483
        {
484
            deleteLater();
485
486
487
            return;
        }
    }
488
    else if ( allItemsAreFromTrash || m_destUrl.protocol() == "trash" ) {
489
        // No point in asking copy/move/link when using dnd from or to the trash.
490
        action = Qt::MoveAction;
491
    }
492
    else if ( (
493
        ((m_info->keyboardModifiers & Qt::ControlModifier) == 0) &&
494
495
        ((m_info->keyboardModifiers & Qt::ShiftModifier) == 0) &&
        ((m_info->keyboardModifiers & Qt::AltModifier) == 0) ) || linkOnly )
496
    {
497
        // Neither control, shift or alt are pressed => show popup menu
498
499

        // TODO move this code out somehow. Allow user of KonqOperations to add his own actions...
500
#if 0
501
502
        KonqIconViewWidget *iconView = dynamic_cast<KonqIconViewWidget*>(parent());
        bool bSetWallpaper = false;
503
504
        if ( iconView && iconView->maySetWallpaper() && lst.count() == 1 )
	{
Laurent Montel's avatar
Laurent Montel committed
505
            KUrl url = lst.first();
506
            KMimeType::Ptr mime = KMimeType::findByUrl( url );
Szombathelyi György's avatar
Szombathelyi György committed
507
508
            if ( mime && ( ( KImageIO::isSupported(mime->name(), KImageIO::Reading) ) ||
                 mime->is( "image/svg+xml" ) ) )
509
510
511
            {
                bSetWallpaper = true;
            }
512
        }
513
#endif
514
515

        // Check what the source can do
516
517
518
519
        // we'll assume it's the same for all URLs (hack)
        // TODO: if we had a KFileItemList instead of a KUrl::List,
        // we could use KFileItemsCapabilities
        const KUrl url = lst.first();
Aaron J. Seigo's avatar
Aaron J. Seigo committed
520
521
522
        bool sReading = KProtocolManager::supportsReading( url );
        bool sDeleting = KProtocolManager::supportsDeleting( url );
        bool sMoving = KProtocolManager::supportsMoving( url );
523
        // Check what the destination can do
524
        bool dWriting = KProtocolManager::supportsWriting( m_destUrl );
525
526
        if ( !dWriting )
        {
527
            deleteLater();
528
529
            return;
        }
530

531
532
        bool enableLinking = true;			// for now, but see below

533
534
535
536
537
        // We don't want to offer "move" for temp files. They might come from
        // kmail using a tempfile for attachments, or ark using a tempdir for
        // extracting an archive -- in all cases, we can't implement a real move,
        // it's just a copy of the tempfile [and the source app will delete it later].
        // https://www.intevation.de/roundup/kolab/issue2026
538
539
        //
        // Similarly, linking to a temp file is pointless.
540
        if (url.isLocalFile() && url.toLocalFile().startsWith(KStandardDirs::locateLocal("tmp", QString()))) {
541
542
            sMoving = false;
            sDeleting = false;
543
            enableLinking = false;
544
545
        }

546
        QMenu popup;
Pascal Létourneau's avatar
Pascal Létourneau committed
547
548
        QString seq = QKeySequence( Qt::ShiftModifier ).toString();
        seq.chop(1); // chop superfluous '+'
Thorsten Roeder's avatar
Thorsten Roeder committed
549
        QAction* popupMoveAction = new QAction(i18n( "&Move Here" ) + '\t' + seq, this);
550
        popupMoveAction->setIcon(KIcon("go-jump"));
Pascal Létourneau's avatar
Pascal Létourneau committed
551
552
        seq = QKeySequence( Qt::ControlModifier ).toString();
        seq.chop(1);
Thorsten Roeder's avatar
Thorsten Roeder committed
553
        QAction* popupCopyAction = new QAction(i18n( "&Copy Here" ) + '\t' + seq, this);
554
        popupCopyAction->setIcon(KIcon("edit-copy"));
Pascal Létourneau's avatar
Pascal Létourneau committed
555
556
        seq = QKeySequence( Qt::ControlModifier + Qt::ShiftModifier ).toString();
        seq.chop(1);
Thorsten Roeder's avatar
Thorsten Roeder committed
557
        QAction* popupLinkAction = new QAction(i18n( "&Link Here" ) + '\t' + seq, this);
558
        popupLinkAction->setIcon(KIcon("edit-link"));
559
        QAction* popupWallAction = new QAction( i18n( "Set as &Wallpaper" ), this );
560
        popupWallAction->setIcon(KIcon("preferences-desktop-wallpaper"));
Thorsten Roeder's avatar
Thorsten Roeder committed
561
        QAction* popupCancelAction = new QAction(i18n( "C&ancel" ) + '\t' + QKeySequence( Qt::Key_Escape ).toString(), this);
562
        popupCancelAction->setIcon(KIcon("process-stop"));
563
564

        if (!mlst.isEmpty() && (sMoving || (sReading && sDeleting)) && !linkOnly )
565
566
567
568
        {
            bool equalDestination = true;
            foreach ( const KUrl & src, lst )
            {
569
570
                const bool equalProtocol = ( m_destUrl.protocol() == src.protocol() );
                if ( !equalProtocol || m_destUrl.path(KUrl::RemoveTrailingSlash) != src.directory() )
571
572
573
574
575
                {
                    equalDestination = false;
                    break;
                }
            }
576

577
578
579
            if ( !equalDestination )
                popup.addAction(popupMoveAction);
        }
580

581
582
583
        if ( sReading && !linkOnly)
            popup.addAction(popupCopyAction);

584
585
        if ( enableLinking )
            popup.addAction(popupLinkAction);
586

587
#if 0
588
589
        if (bSetWallpaper)
            popup.addAction(popupWallAction);
590
#endif
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606

        //now initialize the drop plugins
        KFileItemList fileItems;
        foreach(const KUrl& url, lst) {
            fileItems.append(KFileItem(
                        KFileItem::Unknown,
                        KFileItem::Unknown,
                        url));

        }

        QList<QAction*> pluginActions;
        KFileItemListProperties info(fileItems);
        _addPluginActions(pluginActions, m_destUrl, info);

        if (!m_info->userActions.isEmpty() || !pluginActions.isEmpty()) {
607
608
            popup.addSeparator();
            popup.addActions(m_info->userActions);
609
            popup.addActions(pluginActions);
610
        }
611

612
613
614
615
        popup.addSeparator();
        popup.addAction(popupCancelAction);

        QAction* result = popup.exec( m_info->mousePos );
Laurent Montel's avatar
Laurent Montel committed
616

617
618
619
620
621
622
        if(result == popupCopyAction)
            action = Qt::CopyAction;
        else if(result == popupMoveAction)
            action = Qt::MoveAction;
        else if(result == popupLinkAction)
            action = Qt::LinkAction;
623
        else {
624
            deleteLater();
625
            return;
626
627
628
        }
    }

629
    KIO::CopyJob * job = 0;
630
    switch ( action ) {
631
    case Qt::MoveAction :
632
        job = KIO::move( lst, m_destUrl );
633
        job->setMetaData( m_info->metaData );
634
        setOperation( job, m_method == TRASH ? TRASH : MOVE, m_destUrl );
635
636
        KIO::FileUndoManager::self()->recordJob(
            m_method == TRASH ? KIO::FileUndoManager::Trash : KIO::FileUndoManager::Move,
637
            lst, m_destUrl, job );
638
        return; // we still have stuff to do -> don't delete ourselves
639
    case Qt::CopyAction :
640
        job = KIO::copy( lst, m_destUrl );
641
        job->setMetaData( m_info->metaData );
642
        setOperation( job, COPY, m_destUrl );
643
        KIO::FileUndoManager::self()->recordCopyJob(job);
644
        return;
645
    case Qt::LinkAction :
646
        kDebug(1203) << "lst.count=" << lst.count();
647
        job = KIO::link( lst, m_destUrl );
648
        job->setMetaData( m_info->metaData );
649
        setOperation( job, LINK, m_destUrl );
650
        KIO::FileUndoManager::self()->recordCopyJob(job);
651
        return;
Laurent Montel's avatar
Laurent Montel committed
652
    default : kError(1203) << "Unknown action " << (int)action << endl;
653
    }
654
    deleteLater();
655
656
}

657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
void KonqOperations::_addPluginActions(QList<QAction*>& pluginActions,const KUrl& destination, const KFileItemListProperties& info)
{
    kDebug(1203);
    const QString commonMimeType = info.mimeType();
    kDebug() << commonMimeType;
    const KService::List plugin_offers = KMimeTypeTrader::self()->query(commonMimeType.isEmpty() ? QLatin1String("application/octet-stream") : commonMimeType, "KonqDndPopupMenu/Plugin", "exist Library");

    KService::List::ConstIterator iterator = plugin_offers.begin();
    const KService::List::ConstIterator end = plugin_offers.end();
    for(; iterator != end; ++iterator) {
        //kDebug() << (*iterator)->name() << (*iterator)->library();
        KonqDndPopupMenuPlugin *plugin = (*iterator)->createInstance<KonqDndPopupMenuPlugin>(this);
        if (!plugin)
            continue;
        plugin->setup(info, destination, pluginActions);
    }
}

675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
// these two are from /apps/konqueror/settings/konq/globalpaths.cpp
static bool cleanHomeDirPath( QString &path, const QString &homeDir )
{
#ifdef Q_WS_WIN //safer
    if (!QDir::convertSeparators(path).startsWith(QDir::convertSeparators(homeDir)))
        return false;
#else
    if (!path.startsWith(homeDir))
        return false;
#endif

    int len = homeDir.length();
    // replace by "$HOME" if possible
    if (len && (path.length() == len || path[len] == '/')) {
        path.replace(0, len, QString::fromLatin1("$HOME"));
        return true;
    } else
        return false;
}
static QString translatePath( QString path ) // krazy:exclude=passbyvalue
{
    // keep only one single '/' at the beginning - needed for cleanHomeDirPath()
    while (path[0] == '/' && path[1] == '/')
        path.remove(0,1);

    // we probably should escape any $ ` and \ characters that may occur in the path, but the Qt code that reads back
    // the file doesn't unescape them so not much point in doing so

    // All of the 3 following functions to return the user's home directory
    // can return different paths. We have to test all them.
    const QString homeDir0 = QFile::decodeName(qgetenv("HOME"));
    const QString homeDir1 = QDir::homePath();
    const QString homeDir2 = QDir(homeDir1).canonicalPath();
    if (cleanHomeDirPath(path, homeDir0) ||
        cleanHomeDirPath(path, homeDir1) ||
        cleanHomeDirPath(path, homeDir2) ) {
        // kDebug() << "Path was replaced\n";
    }

    return path;
}
716

Laurent Montel's avatar
Laurent Montel committed
717
void KonqOperations::rename( QWidget * parent, const KUrl & oldurl, const KUrl& newurl )
718
{
719
    kDebug(1203) << "oldurl=" << oldurl << " newurl=" << newurl;
720
721
722
    if ( oldurl == newurl )
        return;

Laurent Montel's avatar
Laurent Montel committed
723
    KUrl::List lst;
724
    lst.append(oldurl);
David Faure's avatar
David Faure committed
725
    KIO::Job * job = KIO::moveAs( oldurl, newurl, oldurl.isLocalFile() ? KIO::HideProgressInfo : KIO::DefaultFlags );
726
    KonqOperations * op = new KonqOperations( parent );
727
    op->setOperation( job, MOVE, newurl );
728
    KIO::FileUndoManager::self()->recordJob( KIO::FileUndoManager::Rename, lst, newurl, job );
729
    // if moving the desktop then update config file and emit
730
    if ( oldurl.isLocalFile() && oldurl.toLocalFile( KUrl::AddTrailingSlash ) == KGlobalSettings::desktopPath() )
Laurent Montel's avatar
Laurent Montel committed
731
    {
732
        kDebug(1203) << "That rename was the Desktop path, updating config files";
733
734
735
736
        //save in XDG path
        const QString userDirsFile(KGlobal::dirs()->localxdgconfdir() + QLatin1String("user-dirs.dirs"));
        KConfig xdgUserConf( userDirsFile, KConfig::SimpleConfig );
        KConfigGroup g( &xdgUserConf, "" );
737
        g.writeEntry( "XDG_DESKTOP_DIR", QString("\"" + translatePath( newurl.path() ) + "\"") );
738
        KGlobalSettings::self()->emitChange(KGlobalSettings::SettingsChanged, KGlobalSettings::SETTINGS_PATHS);
739
740
741
    }
}

742
void KonqOperations::setOperation( KIO::Job * job, Operation method, const KUrl & dest )
743
{
744
    m_method = method;
745
    m_destUrl = dest;
746
747
    if ( job )
    {
748
        job->ui()->setWindow(parentWidget());
Laurent Montel's avatar
Laurent Montel committed
749
750
        connect( job, SIGNAL(result(KJob*)),
                 SLOT(slotResult(KJob*)) );
751
#if 0
752
753
754
755
        KIO::CopyJob *copyJob = dynamic_cast<KIO::CopyJob*>(job);
        KonqIconViewWidget *iconView = dynamic_cast<KonqIconViewWidget*>(parent());
        if (copyJob && iconView)
        {
Laurent Montel's avatar
Laurent Montel committed
756
757
            connect(copyJob, SIGNAL(aboutToCreate(KIO::Job*,QList<KIO::CopyInfo>)),
                 this, SLOT(slotAboutToCreate(KIO::Job*,QList<KIO::CopyInfo>)));
758
            // TODO move this connect into the iconview!
Laurent Montel's avatar
Laurent Montel committed
759
760
            connect(this, SIGNAL(aboutToCreate(QPoint,QList<KIO::CopyInfo>)),
                 iconView, SLOT(slotAboutToCreate(QPoint,QList<KIO::CopyInfo>)));
761
        }
762
#endif
763
764
765
766
767
    }
    else // for link
        slotResult( 0L );
}

Laurent Montel's avatar
Laurent Montel committed
768
void KonqOperations::slotAboutToCreate(KIO::Job *, const QList<KIO::CopyInfo> &files)
769
770
{
    emit aboutToCreate( m_info ? m_info->mousePos : m_pasteInfo ? m_pasteInfo->mousePos : QPoint(), files);
771
772
}

773
void KonqOperations::statUrl( const KUrl & url, const QObject *receiver, const char *member, QWidget* parent )
774
{