~nskaggs/ubuntu-filemanager-app/plugin-cmake-build

« back to all changes in this revision

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

  • Committer: nskaggs
  • Date: 2014-03-26 14:25:39 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20140326142539-tym4ip9b0z83ox2r
revert to r143 to merge

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
 
 
39
 
#include <QClipboard>
40
 
#include <QApplication>
41
 
#include <QDir>
42
 
#include <QFileInfo>
43
 
#include <QDebug>
44
 
 
45
 
static  QLatin1String GNOME_COPIED_MIME_TYPE  ("x-special/gnome-copied-files");
46
 
static  QLatin1String KDE_CUT_MIME_TYPE       ("application/x-kde-cutselection");
47
 
 
48
 
 
49
 
int DirModelMimeData::m_instances = 0;
50
 
DirModelMimeData*  DirModelMimeData::m_globalMimeData = 0;
51
 
 
52
 
 
53
 
bool DirModelMimeData::hasFormat ( const QString & mimeType ) const
54
 
{
55
 
   bool ret = false;
56
 
   if (  mimeType == KDE_CUT_MIME_TYPE  )
57
 
   {
58
 
      ret = true;
59
 
   }
60
 
   else
61
 
   {
62
 
      ret = m_formats.contains(mimeType);
63
 
   }
64
 
   return ret;
65
 
}
66
 
 
67
 
//===============================================================================================
68
 
/*!
69
 
 * \brief DirModelMimeData::DirModelMimeData
70
 
 */
71
 
DirModelMimeData::DirModelMimeData() :
72
 
    QMimeData()
73
 
  , m_appMime(0)
74
 
{
75
 
    m_formats.append("text/uri-list");
76
 
    m_formats.append(GNOME_COPIED_MIME_TYPE);
77
 
    m_formats.append("text/plain");
78
 
    m_formats.append("COMPOUND_TEXT");
79
 
    m_formats.append("TARGETS");
80
 
    m_formats.append("MULTIPLE");
81
 
    m_formats.append("TIMESTAMP");
82
 
    m_formats.append("SAVE_TARGETS");
83
 
 
84
 
    ++m_instances;
85
 
#if DEBUG_MESSAGES
86
 
     qDebug() << Q_FUNC_INFO << this << "instances" << m_instances;
87
 
#endif
88
 
}
89
 
 
90
 
 
91
 
 
92
 
 
93
 
DirModelMimeData::~DirModelMimeData()
94
 
{
95
 
    --m_instances;
96
 
#if DEBUG_MESSAGES
97
 
    qDebug() << Q_FUNC_INFO << this  << "instances" << m_instances
98
 
             << "m_globalMimeData" << m_globalMimeData;
99
 
#endif
100
 
    if (m_instances == 1 && m_globalMimeData)
101
 
    {
102
 
        DirModelMimeData * tmp = m_globalMimeData;
103
 
        m_globalMimeData = 0;
104
 
        delete tmp;
105
 
    }
106
 
}
107
 
 
108
 
//===============================================================================================
109
 
/*!
110
 
 * \brief DirModelMimeData::gnomeUrls
111
 
 * \param mime
112
 
 * \param operation
113
 
 * \return
114
 
 */
115
 
QList<QUrl>
116
 
DirModelMimeData::gnomeUrls(const QMimeData * mime,
117
 
                            ClipboardOperation& operation)
118
 
{
119
 
    QList<QUrl>  urls;
120
 
    if (mime->hasFormat(GNOME_COPIED_MIME_TYPE))
121
 
    {
122
 
       QByteArray  bytes = mime->data(GNOME_COPIED_MIME_TYPE);
123
 
       QList<QString>  d = QString(bytes).split(QLatin1String("\n"),
124
 
                                                QString::SkipEmptyParts);
125
 
       operation = ClipboardCopy;
126
 
       if (d.count() > 0)
127
 
       {
128
 
           if (d.at(0).trimmed().startsWith(QLatin1String("cut")))
129
 
           {
130
 
               operation = ClipboardCut;
131
 
           }
132
 
           for (int counter= 1; counter < d.count(); counter++)
133
 
           {
134
 
               urls.append(d.at(counter).trimmed());
135
 
           }
136
 
       }
137
 
    }
138
 
    return urls;
139
 
}
140
 
 
141
 
//===============================================================================================
142
 
/*!
143
 
 * \brief DirModelMimeData::clipBoardOperation()
144
 
 * \param mime
145
 
 * \return
146
 
 */
147
 
ClipboardOperation DirModelMimeData::clipBoardOperation()
148
 
{
149
 
    ClipboardOperation op = ClipboardCopy;
150
 
    m_appMime = clipboardMimeData();
151
 
    if (m_appMime)
152
 
    {
153
 
       //first check for GNOME clipboard format, op comes with Copy/Cut
154
 
        if (gnomeUrls(m_appMime, op).count() == 0)
155
 
        { // there is no gnome format, tries KDE format
156
 
            QStringList formats = m_appMime->formats();
157
 
            int f = formats.count();
158
 
            while(f--)
159
 
            {
160
 
                const QString &mi = formats.at(f);
161
 
                if(mi.startsWith(QLatin1String("application/x-kde")) )
162
 
                {
163
 
                    if (mi.contains(QLatin1String("cut")))
164
 
                    {
165
 
                        op = ClipboardCut;
166
 
                        break;
167
 
                    }
168
 
                }
169
 
            }
170
 
        }
171
 
    }
172
 
    return op;
173
 
}
174
 
 
175
 
 
176
 
//===============================================================================================
177
 
/*!
178
 
 * \brief DirModelMimeData::setIntoClipboard
179
 
 *
180
 
 *  Try to put data in the global cliboard
181
 
 *
182
 
 *  \note:
183
 
 *       On mobile devices clipboard might not work, in this case a local Clipboard is simulated
184
 
 *
185
 
 * \param files
186
 
 * \param path
187
 
 * \param isCut
188
 
 * \return who is owner of clipboard data
189
 
 */
190
 
DirModelMimeData::ClipBoardDataOwner
191
 
DirModelMimeData::setIntoClipboard(const QStringList &files, const QString& path, ClipboardOperation operation)
192
 
{
193
 
    static bool firstTime = true;
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
 
            clipboard->setMimeData(mime);
204
 
            //it looks like some mobile devices does not have X or Clipboard does work for other reason
205
 
            //in this case we simulate our own clipboard, the QClipboard::dataChanged() signal is also
206
 
            //checked in \ref Clipboard::storeOnClipboard()
207
 
            if (firstTime)
208
 
            {
209
 
                firstTime = false;
210
 
                if (!m_globalMimeData && !testClipboardContent(files, path))
211
 
                {
212
 
                    qWarning() << "QClipboard does not work,  using own QMimeData storage";
213
 
                    m_globalMimeData = mime;
214
 
                }
215
 
            }
216
 
#if DEBUG_MESSAGES
217
 
            qDebug() << Q_FUNC_INFO << "mime" << mime
218
 
                     << "own Clipboard Mime Data" << m_globalMimeData;
219
 
#endif
220
 
        }
221
 
        else
222
 
            if (m_globalMimeData != mime)
223
 
            {
224
 
                delete mime;
225
 
            }
226
 
        //check if it is necessary to send notification about Clipboard changed
227
 
        if (m_globalMimeData)
228
 
        {
229
 
            ret = MySelf;
230
 
        }
231
 
    }
232
 
    return ret;
233
 
}
234
 
 
235
 
 
236
 
 
237
 
bool DirModelMimeData::fillClipboard(const QStringList& files, const QString &path, ClipboardOperation operation)
238
 
{
239
 
    bool ret = false;
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 = QUrl::fromLocalFile(fullPaths.at((counter)));
259
 
        m_urls.append(item);
260
 
        m_gnomeData += QLatin1Char('\n') + item.toEncoded() ;
261
 
    }
262
 
    if (m_urls.count() > 0)
263
 
    {
264
 
        setData(GNOME_COPIED_MIME_TYPE, m_gnomeData);
265
 
        setUrls(m_urls);
266
 
        ret = true;
267
 
    }
268
 
    else
269
 
    {
270
 
     // emit error( QObject::tr("Item does not exist"), item);
271
 
    }
272
 
    return ret;
273
 
}
274
 
 
275
 
//===============================================================================================
276
 
/*!
277
 
 * \brief DirModelMimeData::clipboardMimeData
278
 
 * \return
279
 
 */
280
 
const QMimeData *DirModelMimeData::clipboardMimeData()
281
 
{
282
 
    const QMimeData *ret = 0;
283
 
    QClipboard *clipboard = QApplication::clipboard();
284
 
    if (m_globalMimeData)
285
 
    {
286
 
        ret = m_globalMimeData;
287
 
    }
288
 
    else
289
 
    if (clipboard)
290
 
    {
291
 
        ret = clipboard->mimeData();
292
 
    }
293
 
#if DEBUG_MESSAGES
294
 
    qDebug() << Q_FUNC_INFO << "clipboard" << clipboard
295
 
                << "m_ownClipboardMimeData" << m_globalMimeData
296
 
                << "clipboard->mimeData()" << ret;
297
 
#endif
298
 
    return ret;
299
 
}
300
 
 
301
 
//===============================================================================================
302
 
/*!
303
 
 * \brief DirModelMimeData::localUrls
304
 
 * \return
305
 
 */
306
 
QStringList
307
 
DirModelMimeData::localUrls(ClipboardOperation& operation)
308
 
{
309
 
     m_appMime = clipboardMimeData();
310
 
     QStringList paths;
311
 
     //it may have external urls
312
 
     if (m_appMime)
313
 
     {
314
 
         QList<QUrl> urls;
315
 
         if (m_appMime->hasUrls())
316
 
         {
317
 
             urls =  m_appMime->urls();
318
 
             operation = clipBoardOperation();
319
 
         }
320
 
         else
321
 
         {
322
 
             urls = gnomeUrls(m_appMime, operation);
323
 
         }
324
 
         for (int counter=0; counter < urls.count(); counter++)
325
 
         {
326
 
             if (urls.at(counter).toString().startsWith(QLatin1String("file://")))
327
 
             {
328
 
                 paths.append(urls.at(counter).toLocalFile());
329
 
             }
330
 
         }
331
 
     }
332
 
#if DEBUG_MESSAGES
333
 
        qDebug() << Q_FUNC_INFO << paths;
334
 
#endif
335
 
     return paths;
336
 
}
337
 
 
338
 
 
339
 
//===============================================================================================
340
 
/*!
341
 
 * \brief DirModelMimeData::testClipboardContent() Gets the clipboard content and compare with data previously stored
342
 
 * \param files
343
 
 * \param path
344
 
 * \return true if clipboard has content and it matches data previously stored
345
 
 */
346
 
bool  DirModelMimeData::testClipboardContent(const QStringList &files, const QString &path)
347
 
{
348
 
    bool ret = false;
349
 
    ClipboardOperation tmpOperation;
350
 
    QStringList expectedList = makeFullPath(files,path);
351
 
    QStringList realList     = localUrls(tmpOperation);
352
 
    if (realList == expectedList)
353
 
    {
354
 
        ret = true;
355
 
    }
356
 
    else
357
 
    {
358
 
        qWarning() << Q_FUNC_INFO << "FAILED, Clipboard does not work";
359
 
    }
360
 
    return ret;
361
 
}
362
 
 
363
 
//===============================================================================================
364
 
/*!
365
 
 * \brief DirModelMimeData::makeFullPath() Just creates a fulpath file list when they do exist
366
 
 * \param files
367
 
 * \param path
368
 
 * \return the list itself
369
 
 */
370
 
QStringList DirModelMimeData::makeFullPath(const QStringList& files, const QString &path)
371
 
{
372
 
    QStringList fullPathnameList;
373
 
    QFileInfo fi;
374
 
    for(int counter = 0; counter < files.count(); counter++)
375
 
    {
376
 
        const QString& item = files.at(counter);
377
 
        fi.setFile(item);
378
 
        if (!fi.isAbsolute())
379
 
        {
380
 
            fi.setFile(path + QDir::separator() + item);
381
 
        }
382
 
        if (fi.exists())
383
 
        {
384
 
            fullPathnameList.append(fi.absoluteFilePath());
385
 
        }
386
 
    }
387
 
    return fullPathnameList;
388
 
}
389
 
 
390
 
 
391
 
//===========================================================================
392
 
//
393
 
//===========================================================================
394
 
Clipboard::Clipboard(QObject *parent):
395
 
    QObject(parent)
396
 
  , m_mimeData ( new DirModelMimeData() )
397
 
  , m_clipboardModifiedByOther(false)
398
 
{
399
 
    QClipboard *clipboard = QApplication::clipboard();
400
 
 
401
 
    connect(clipboard, SIGNAL(dataChanged()), this,    SIGNAL(clipboardChanged()));
402
 
    connect(clipboard, SIGNAL(dataChanged()), this,    SLOT(onClipboardChanged()));
403
 
}
404
 
 
405
 
 
406
 
Clipboard::~Clipboard()
407
 
{
408
 
    delete m_mimeData;
409
 
}
410
 
 
411
 
//================================================================================
412
 
/*!
413
 
 * \brief Clipboard::clipboardHasChanged() used to identify if the clipboard changed during a Cut operation
414
 
 *
415
 
 *  \sa \ref endCurrentAction()
416
 
 */
417
 
void Clipboard::onClipboardChanged()
418
 
{
419
 
    m_clipboardModifiedByOther = true;
420
 
}
421
 
 
422
 
 
423
 
//==================================================================
424
 
/*!
425
 
 * \brief Clipboard::storeOnClipboard() store data on Clipboard
426
 
 * \param pathnames files list
427
 
 * \param op \ref ClipboardOperation as  \ref ClipboardCopy or  \ref ClipboardCut
428
 
 *
429
 
 *  Stores data on clipboard by calling \ref DirModelMimeData::setIntoClipboard() which uses Qt class QClipboard
430
 
 *  It is expected that QClipboard class emits the dataChanged() signal when a new content is set into it,
431
 
 *  if it does we caught that signal in \ref clipboardHasChanged() which sets \ref m_clipboardModifiedByOther to true.
432
 
 */
433
 
void  Clipboard::storeOnClipboard(const QStringList &names, ClipboardOperation op, const QString& curPath)
434
 
{
435
 
#if DEBUG_MESSAGES
436
 
    qDebug() << Q_FUNC_INFO << names << "ClipboardOperation" << op;
437
 
#endif
438
 
     DirModelMimeData::ClipBoardDataOwner owner =
439
 
         m_mimeData->setIntoClipboard(names, curPath, op);
440
 
     if (owner == DirModelMimeData::MySelf || !m_clipboardModifiedByOther)
441
 
     {
442
 
         emit clipboardChanged();
443
 
     }
444
 
     m_clipboardModifiedByOther = false;
445
 
}
446
 
 
447
 
//===============================================================================================
448
 
/*!
449
 
 * \brief Clipboard::copy
450
 
 * \param pathnames
451
 
 */
452
 
void Clipboard::copy(const QStringList &names, const QString& path)
453
 
{
454
 
    storeOnClipboard(names, ClipboardCopy, path);
455
 
}
456
 
 
457
 
//===============================================================================================
458
 
/*!
459
 
 * \brief Clipboard::cut
460
 
 * \param pathnames
461
 
 */
462
 
void Clipboard::cut(const QStringList &names, const QString &path)
463
 
{
464
 
    storeOnClipboard(names, ClipboardCut, path);
465
 
}
466
 
 
467
 
 
468
 
//=======================================================
469
 
/*!
470
 
 * \brief Clipboard::clipboardLocalUrlsCounter
471
 
 * \return
472
 
 */
473
 
int Clipboard::clipboardLocalUrlsCounter()
474
 
{
475
 
    ClipboardOperation operation;
476
 
    return m_mimeData->localUrls(operation).count();
477
 
}
478
 
 
479
 
 
480
 
//=======================================================
481
 
/*!
482
 
 * \brief Clipboard::paste
483
 
 * \param operation
484
 
 * \return
485
 
 */
486
 
QStringList Clipboard::paste(ClipboardOperation &operation)
487
 
{
488
 
    QStringList items = m_mimeData->localUrls(operation);
489
 
    if (operation == ClipboardCut)
490
 
    {
491
 
        //this must still be false when cut finishes to change the clipboard to the target
492
 
        m_clipboardModifiedByOther = false;
493
 
    }
494
 
    return items;
495
 
}