~ubuntu-filemanager-dev/ubuntu-filemanager-app/trunk

« back to all changes in this revision

Viewing changes to src/plugin/folderlistmodel/clipboard.cpp

  • Committer: Bileto Bot
  • Date: 2017-04-04 17:06:41 UTC
  • mfrom: (588.1.19 fix-desktop-file)
  • Revision ID: ci-train-bot@canonical.com-20170404170641-1p15lmx8wodlx2ut
* Rename binary file to ubuntu-filemanager-app
* Join plugin packages into the main package 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**************************************************************************
 
2
 *
 
3
 * Copyright 2014 Canonical Ltd.
 
4
 * Copyright 2014 Carlos J Mazieri <carlos.mazieri@gmail.com>
 
5
 *
 
6
 * You may use this file under the terms of the BSD license as follows:
 
7
 *
 
8
 * "Redistribution and use in source and binary forms, with or without
 
9
 * modification, are permitted provided that the following conditions are
 
10
 * met:
 
11
 *   * Redistributions of source code must retain the above copyright
 
12
 *     notice, this list of conditions and the following disclaimer.
 
13
 *   * Redistributions in binary form must reproduce the above copyright
 
14
 *     notice, this list of conditions and the following disclaimer in
 
15
 *     the documentation and/or other materials provided with the
 
16
 *     distribution.
 
17
 *   * Neither the name of Nemo Mobile nor the names of its contributors
 
18
 *     may be used to endorse or promote products derived from this
 
19
 *     software without specific prior written permission.
 
20
 *
 
21
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 
22
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 
23
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 
24
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 
25
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 
26
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 
27
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 
28
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 
29
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
30
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 
31
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
 
32
 *
 
33
 * File: clipboard.cpp
 
34
 * Date: 1/22/2014
 
35
 */
 
36
 
 
37
#include "clipboard.h"
 
38
#include "locationurl.h"
 
39
 
 
40
#include <QClipboard>
 
41
#include <QApplication>
 
42
#include <QDir>
 
43
#include <QFileInfo>
 
44
#include <QDebug>
 
45
 
 
46
static  QLatin1String GNOME_COPIED_MIME_TYPE  ("x-special/gnome-copied-files");
 
47
static  QLatin1String KDE_CUT_MIME_TYPE       ("application/x-kde-cutselection");
 
48
 
 
49
 
 
50
int DirModelMimeData::m_instances = 0;
 
51
DirModelMimeData*  DirModelMimeData::m_globalMimeData = 0;
 
52
 
 
53
 
 
54
bool DirModelMimeData::hasFormat ( const QString & mimeType ) const
 
55
{
 
56
   bool ret = false;
 
57
   if (  mimeType == KDE_CUT_MIME_TYPE  )
 
58
   {
 
59
      ret = true;
 
60
   }
 
61
   else
 
62
   {
 
63
      ret = m_formats.contains(mimeType);
 
64
   }
 
65
   return ret;
 
66
}
 
67
 
 
68
//===============================================================================================
 
69
/*!
 
70
 * \brief DirModelMimeData::DirModelMimeData
 
71
 */
 
72
DirModelMimeData::DirModelMimeData() :
 
73
    QMimeData()
 
74
  , m_appMime(0)
 
75
{
 
76
    m_formats.append("text/uri-list");
 
77
    m_formats.append(GNOME_COPIED_MIME_TYPE);
 
78
    m_formats.append("text/plain");
 
79
    m_formats.append("COMPOUND_TEXT");
 
80
    m_formats.append("TARGETS");
 
81
    m_formats.append("MULTIPLE");
 
82
    m_formats.append("TIMESTAMP");
 
83
    m_formats.append("SAVE_TARGETS");
 
84
 
 
85
    ++m_instances;
 
86
#if DEBUG_MESSAGES
 
87
     qDebug() << Q_FUNC_INFO << this << "instances" << m_instances;
 
88
#endif
 
89
}
 
90
 
 
91
 
 
92
 
 
93
 
 
94
DirModelMimeData::~DirModelMimeData()
 
95
{
 
96
    --m_instances;
 
97
#if DEBUG_MESSAGES
 
98
    qDebug() << Q_FUNC_INFO << this  << "instances" << m_instances
 
99
             << "m_globalMimeData" << m_globalMimeData;
 
100
#endif
 
101
    if (m_instances == 1 && m_globalMimeData)
 
102
    {
 
103
        DirModelMimeData * tmp = m_globalMimeData;
 
104
        m_globalMimeData = 0;
 
105
        delete tmp;
 
106
    }
 
107
}
 
108
 
 
109
//===============================================================================================
 
110
/*!
 
111
 * \brief DirModelMimeData::gnomeUrls
 
112
 * \param mime
 
113
 * \param operation
 
114
 * \return
 
115
 */
 
116
QList<QUrl>
 
117
DirModelMimeData::gnomeUrls(const QMimeData * mime,
 
118
                            ClipboardOperation& operation)
 
119
{
 
120
    QList<QUrl>  urls;
 
121
    if (mime->hasFormat(GNOME_COPIED_MIME_TYPE))
 
122
    {
 
123
       QByteArray  bytes = mime->data(GNOME_COPIED_MIME_TYPE);
 
124
       QList<QString>  d = QString(bytes).split(QLatin1String("\n"),
 
125
                                                QString::SkipEmptyParts);
 
126
       operation = ClipboardCopy;
 
127
       if (d.count() > 0)
 
128
       {
 
129
           if (d.at(0).trimmed().startsWith(QLatin1String("cut")))
 
130
           {
 
131
               operation = ClipboardCut;
 
132
           }
 
133
           for (int counter= 1; counter < d.count(); counter++)
 
134
           {
 
135
               urls.append(d.at(counter).trimmed());
 
136
           }
 
137
       }
 
138
    }
 
139
    return urls;
 
140
}
 
141
 
 
142
//===============================================================================================
 
143
/*!
 
144
 * \brief DirModelMimeData::clipBoardOperation()
 
145
 * \param mime
 
146
 * \return
 
147
 */
 
148
ClipboardOperation DirModelMimeData::clipBoardOperation()
 
149
{
 
150
    ClipboardOperation op = ClipboardCopy;
 
151
    m_appMime = clipboardMimeData();
 
152
    if (m_appMime)
 
153
    {
 
154
       //first check for GNOME clipboard format, op comes with Copy/Cut
 
155
        if (gnomeUrls(m_appMime, op).count() == 0)
 
156
        { // there is no gnome format, tries KDE format
 
157
            QStringList formats = m_appMime->formats();
 
158
            int f = formats.count();
 
159
            while(f--)
 
160
            {
 
161
                const QString &mi = formats.at(f);
 
162
                if(mi.startsWith(QLatin1String("application/x-kde")) )
 
163
                {
 
164
                    if (mi.contains(QLatin1String("cut")))
 
165
                    {
 
166
                        op = ClipboardCut;
 
167
                        break;
 
168
                    }
 
169
                }
 
170
            }
 
171
        }
 
172
    }
 
173
    return op;
 
174
}
 
175
 
 
176
 
 
177
//===============================================================================================
 
178
/*!
 
179
 * \brief DirModelMimeData::setIntoClipboard
 
180
 *
 
181
 *  Try to put data in the global cliboard
 
182
 *
 
183
 *  \note:
 
184
 *       On mobile devices clipboard might not work, in this case a local Clipboard is simulated
 
185
 *
 
186
 * \param files
 
187
 * \param path
 
188
 * \param isCut
 
189
 * \return who is owner of clipboard data
 
190
 */
 
191
DirModelMimeData::ClipBoardDataOwner
 
192
DirModelMimeData::setIntoClipboard(const QStringList &files, const QString& path, ClipboardOperation operation)
 
193
{   
 
194
    DirModelMimeData::ClipBoardDataOwner  ret = Nobody;
 
195
    QClipboard *clipboard = QApplication::clipboard();
 
196
    if (clipboard)
 
197
    {
 
198
        ret = Application;
 
199
        DirModelMimeData *mime = m_globalMimeData ? m_globalMimeData
 
200
                                                  : new DirModelMimeData();
 
201
        if (mime->fillClipboard(files, path, operation))
 
202
        {
 
203
            static bool firstTime = true;
 
204
            clipboard->setMimeData(mime);
 
205
            //it looks like some mobile devices does not have X or Clipboard does work for other reason
 
206
            //in this case we simulate our own clipboard, the QClipboard::dataChanged() signal is also
 
207
            //checked in \ref Clipboard::storeOnClipboard()
 
208
            if (firstTime)
 
209
            {
 
210
                firstTime = false;
 
211
                if (!m_globalMimeData && !testClipboardContent(files, path))
 
212
                {
 
213
                    qWarning() << "QClipboard does not work,  using own QMimeData storage";
 
214
                    m_globalMimeData = mime;
 
215
                }
 
216
            }
 
217
#if DEBUG_MESSAGES
 
218
            qDebug() << Q_FUNC_INFO << "mime" << mime
 
219
                     << "own Clipboard Mime Data" << m_globalMimeData;
 
220
#endif
 
221
        }
 
222
        else
 
223
            if (m_globalMimeData != mime)
 
224
            {
 
225
                delete mime;
 
226
            }
 
227
        //check if it is necessary to send notification about Clipboard changed
 
228
        if (m_globalMimeData)
 
229
        {
 
230
            ret = MySelf;
 
231
        }
 
232
    }
 
233
    return ret;
 
234
}
 
235
 
 
236
 
 
237
 
 
238
bool DirModelMimeData::fillClipboard(const QStringList& files, const QString &path, ClipboardOperation operation)
 
239
{
 
240
    int index = m_formats.indexOf(KDE_CUT_MIME_TYPE);
 
241
    if (index != -1 && operation != ClipboardCut)
 
242
    {
 
243
        m_formats.removeAt(index);
 
244
    }
 
245
    else
 
246
    if (operation == ClipboardCut)
 
247
    {
 
248
        m_formats.append(KDE_CUT_MIME_TYPE);
 
249
    }
 
250
    m_urls.clear();
 
251
    m_gnomeData.clear();
 
252
    m_gnomeData += operation == ClipboardCut ?
 
253
                                    QLatin1String("cut") :
 
254
                                    QLatin1String("copy");
 
255
    QStringList fullPaths = makeFullPath(files, path);
 
256
    for(int counter = 0; counter < fullPaths.count(); counter++)
 
257
    {
 
258
        QUrl item(fullPaths.at((counter)));
 
259
        if (item.scheme().isEmpty() && !item.isLocalFile())
 
260
        {
 
261
            item = QUrl::fromLocalFile(fullPaths.at((counter)));
 
262
        }
 
263
        if (LocationUrl::isSupportedUrl(item))
 
264
        {
 
265
            m_urls.append(item);
 
266
            m_gnomeData += QLatin1Char('\n') + item.toEncoded() ;
 
267
        }
 
268
    }
 
269
    bool ret = m_urls.count() > 0;
 
270
    if (ret)
 
271
    {
 
272
        setData(GNOME_COPIED_MIME_TYPE, m_gnomeData);
 
273
        setUrls(m_urls);
 
274
    }
 
275
    return ret;
 
276
}
 
277
 
 
278
//===============================================================================================
 
279
/*!
 
280
 * \brief DirModelMimeData::clipboardMimeData
 
281
 * \return
 
282
 */
 
283
const QMimeData *DirModelMimeData::clipboardMimeData()
 
284
{
 
285
    const QMimeData *ret = 0;
 
286
    QClipboard *clipboard = QApplication::clipboard();
 
287
    if (m_globalMimeData)
 
288
    {
 
289
        ret = m_globalMimeData;
 
290
    }
 
291
    else
 
292
    if (clipboard)
 
293
    {
 
294
        ret = clipboard->mimeData();
 
295
    }
 
296
#if DEBUG_MESSAGES
 
297
    qDebug() << Q_FUNC_INFO << "clipboard" << clipboard
 
298
                << "m_ownClipboardMimeData" << m_globalMimeData
 
299
                << "clipboard->mimeData()" << ret;
 
300
#endif
 
301
    return ret;
 
302
}
 
303
 
 
304
//===============================================================================================
 
305
/*!
 
306
 * \brief DirModelMimeData::storedUrls
 
307
 * \return the list of Urls stored in the Clipboard
 
308
 */
 
309
QStringList
 
310
DirModelMimeData::storedUrls(ClipboardOperation& operation)
 
311
{
 
312
     m_appMime = clipboardMimeData();
 
313
     QStringList paths;
 
314
     //it may have external urls
 
315
     if (m_appMime)
 
316
     {
 
317
         QList<QUrl> urls;
 
318
         if (m_appMime->hasUrls())
 
319
         {
 
320
             urls =  m_appMime->urls();
 
321
             operation = clipBoardOperation();
 
322
         }
 
323
         else
 
324
         {
 
325
             urls = gnomeUrls(m_appMime, operation);
 
326
         }
 
327
         for (int counter=0; counter < urls.count(); counter++)
 
328
         {
 
329
             if (LocationUrl::isSupportedUrl(urls.at(counter)))
 
330
             {
 
331
                 if (urls.at(counter).isLocalFile())
 
332
                 {
 
333
                     paths.append(urls.at(counter).toLocalFile());
 
334
                 }
 
335
                 else
 
336
                 {
 
337
                     paths.append(urls.at(counter).toString());
 
338
                 }
 
339
             }
 
340
         }
 
341
     }
 
342
#if DEBUG_MESSAGES
 
343
        qDebug() << Q_FUNC_INFO << paths;
 
344
#endif
 
345
     return paths;
 
346
}
 
347
 
 
348
 
 
349
//===============================================================================================
 
350
/*!
 
351
 * \brief DirModelMimeData::testClipboardContent() Gets the clipboard content and compare with data previously stored
 
352
 * \param files
 
353
 * \param path
 
354
 * \return true if clipboard has content and it matches data previously stored
 
355
 */
 
356
bool  DirModelMimeData::testClipboardContent(const QStringList &files, const QString &path)
 
357
{
 
358
    bool ret = false;
 
359
    ClipboardOperation tmpOperation;
 
360
    QStringList expectedList = makeFullPath(files,path);
 
361
    QStringList realList     = storedUrls(tmpOperation);
 
362
    if (realList == expectedList)
 
363
    {
 
364
        ret = true;
 
365
    }
 
366
    else
 
367
    {
 
368
        qWarning() << Q_FUNC_INFO << "FAILED, Clipboard does not work";
 
369
    }
 
370
    return ret;
 
371
}
 
372
 
 
373
//===============================================================================================
 
374
/*!
 
375
 * \brief DirModelMimeData::makeFullPath() Just creates a fulpath file list if files are relative
 
376
 * \param files
 
377
 * \param path
 
378
 * \return the list itself
 
379
 */
 
380
QStringList DirModelMimeData::makeFullPath(const QStringList& files, const QString &path)
 
381
{
 
382
    QStringList fullPathnameList;
 
383
    if (files.count() > 0)
 
384
    {
 
385
        if (path.length() > 0 && !files.at(0).startsWith(path))
 
386
        {
 
387
            for(int counter = 0; counter < files.count(); counter++)
 
388
            {
 
389
                fullPathnameList.append(path + QDir::separator() + files.at(counter));
 
390
            }
 
391
        }
 
392
        else
 
393
        {
 
394
            //they already have a full path
 
395
            fullPathnameList = files;
 
396
        }
 
397
    }
 
398
    return fullPathnameList;
 
399
}
 
400
 
 
401
 
 
402
//===========================================================================
 
403
//
 
404
//===========================================================================
 
405
Clipboard::Clipboard(QObject *parent):
 
406
    QObject(parent)
 
407
  , m_mimeData ( new DirModelMimeData() )
 
408
  , m_clipboardModifiedByOther(false)
 
409
{
 
410
    QClipboard *clipboard = QApplication::clipboard();
 
411
 
 
412
    connect(clipboard, SIGNAL(dataChanged()), this,    SIGNAL(clipboardChanged()));
 
413
    connect(clipboard, SIGNAL(dataChanged()), this,    SLOT(onClipboardChanged()));
 
414
}
 
415
 
 
416
 
 
417
Clipboard::~Clipboard()
 
418
{
 
419
    delete m_mimeData;
 
420
}
 
421
 
 
422
//================================================================================
 
423
/*!
 
424
 * \brief Clipboard::clipboardHasChanged() used to identify if the clipboard changed during a Cut operation
 
425
 *
 
426
 *  \sa \ref endCurrentAction()
 
427
 */
 
428
void Clipboard::onClipboardChanged()
 
429
{
 
430
    m_clipboardModifiedByOther = true;
 
431
}
 
432
 
 
433
 
 
434
//==================================================================
 
435
/*!
 
436
 * \brief Clipboard::storeOnClipboard() store data on Clipboard
 
437
 * \param pathnames files list
 
438
 * \param op \ref ClipboardOperation as  \ref ClipboardCopy or  \ref ClipboardCut
 
439
 *
 
440
 *  Stores data on clipboard by calling \ref DirModelMimeData::setIntoClipboard() which uses Qt class QClipboard
 
441
 *  It is expected that QClipboard class emits the dataChanged() signal when a new content is set into it,
 
442
 *  if it does we caught that signal in \ref clipboardHasChanged() which sets \ref m_clipboardModifiedByOther to true.
 
443
 */
 
444
void  Clipboard::storeOnClipboard(const QStringList &names, ClipboardOperation op, const QString& curPath)
 
445
{
 
446
#if DEBUG_MESSAGES
 
447
    qDebug() << Q_FUNC_INFO << names << "ClipboardOperation" << op;
 
448
#endif
 
449
     DirModelMimeData::ClipBoardDataOwner owner =
 
450
         m_mimeData->setIntoClipboard(names, curPath, op);
 
451
     if (owner == DirModelMimeData::MySelf || !m_clipboardModifiedByOther)
 
452
     {
 
453
         emit clipboardChanged();
 
454
     }
 
455
     m_clipboardModifiedByOther = false;
 
456
}
 
457
 
 
458
//===============================================================================================
 
459
/*!
 
460
 * \brief Clipboard::copy
 
461
 * \param pathnames
 
462
 */
 
463
void Clipboard::copy(const QStringList &names, const QString& path)
 
464
{
 
465
    storeOnClipboard(names, ClipboardCopy, path);
 
466
}
 
467
 
 
468
//===============================================================================================
 
469
/*!
 
470
 * \brief Clipboard::cut
 
471
 * \param pathnames
 
472
 */
 
473
void Clipboard::cut(const QStringList &names, const QString &path)
 
474
{
 
475
    storeOnClipboard(names, ClipboardCut, path);
 
476
}
 
477
 
 
478
 
 
479
//=======================================================
 
480
/*!
 
481
 * \brief Clipboard::storedUrlsCounter
 
482
 * \return
 
483
 */
 
484
int Clipboard::storedUrlsCounter()
 
485
{
 
486
    ClipboardOperation operation;
 
487
    return m_mimeData->storedUrls(operation).count();
 
488
}
 
489
 
 
490
 
 
491
//=======================================================
 
492
/*!
 
493
 * \brief Clipboard::paste
 
494
 * \param operation
 
495
 * \return
 
496
 */
 
497
QStringList Clipboard::paste(ClipboardOperation &operation)
 
498
{
 
499
    QStringList items = m_mimeData->storedUrls(operation);
 
500
    if (operation == ClipboardCut)
 
501
    {
 
502
        //this must still be false when cut finishes to change the clipboard to the target
 
503
        m_clipboardModifiedByOther = false;
 
504
    }
 
505
    return items;
 
506
}
 
507
 
 
508
/*!
 
509
 * \brief Clears clipboard entries
 
510
 */
 
511
void Clipboard::clear()
 
512
{
 
513
    qDebug() << Q_FUNC_INFO << "Clearing clipboard";
 
514
    storeOnClipboard(QStringList(), ClipboardCopy, "");
 
515
}