lighttablethumbbar.cpp 11.6 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/* ============================================================
 *
 * This file is a part of digiKam project
 * http://www.digikam.org
 *
 * Date        : 2007-04-11
 * Description : light table thumbs bar
 *
 * Copyright (C) 2007-2011 by Gilles Caulier <caulier dot gilles at gmail dot com>
 *
 * This program is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General
 * Public License as published by the Free Software Foundation;
 * either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * ============================================================ */

#include "lighttablethumbbar.moc"

// Qt includes

Gilles Caulier's avatar
polish    
Gilles Caulier committed
28
#include <QAction>
29
30
31
#include <QList>
#include <QPixmap>
#include <QPainter>
Gilles Caulier's avatar
polish    
Gilles Caulier committed
32
#include <QContextMenuEvent>
33
34
35
36

// KDE includes

#include <kmenu.h>
Gilles Caulier's avatar
polish    
Gilles Caulier committed
37
38
#include <klocale.h>
#include <kiconloader.h>
39
#include <kapplication.h>
Gilles Caulier's avatar
polish    
Gilles Caulier committed
40
#include <kdebug.h>
41
42
43
44
45

// Local includes

#include "albumdb.h"
#include "albumsettings.h"
Gilles Caulier's avatar
Gilles Caulier committed
46
#include "contextmenuhelper.h"
Gilles Caulier's avatar
port++    
Gilles Caulier committed
47
48
#include "imagefiltermodel.h"
#include "imagedragdrop.h"
49
#include "fileactionmngr.h"
Gilles Caulier's avatar
port++    
Gilles Caulier committed
50
#include "thumbnailloadthread.h"
51
52
53
54

namespace Digikam
{

55
56
57
58
template <typename T, class Container>
void removeAnyInInterval(Container& list, const T& begin, const T& end)
{
    typename Container::iterator it;
Andi Clemens's avatar
Andi Clemens committed
59
60

    for (it = list.begin(); it != list.end();)
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
    {
        if ((*it) >= begin && (*it) <= end)
        {
            it = list.erase(it);
        }
        else
        {
            ++it;
        }
    }
}

class LightTableImageListModel : public ImageListModel
{
public:

77
    explicit LightTableImageListModel(QObject* parent = 0)
Andi Clemens's avatar
Andi Clemens committed
78
        : ImageListModel(parent), m_exclusive(false)
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
    {
    }

    void clearLightTableState()
    {
        m_leftIndexes.clear();
        m_rightIndexes.clear();
    }

    void setExclusiveLightTableState(bool exclusive)
    {
        m_exclusive = exclusive;
    }

    virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const
    {
        if (role == LTLeftPanelRole)
        {
            return m_leftIndexes.contains(index.row());
        }
        else if (role == LTRightPanelRole)
        {
            return m_rightIndexes.contains(index.row());
        }
Andi Clemens's avatar
Andi Clemens committed
103

104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
        return ImageListModel::data(index, role);
    }

    virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::DisplayRole)
    {
        if (!index.isValid())
        {
            return false;
        }

        if (role == LTLeftPanelRole)
        {
            if (m_exclusive)
            {
                m_leftIndexes.clear();
            }
Andi Clemens's avatar
Andi Clemens committed
120

121
122
123
124
125
126
127
128
129
            m_leftIndexes << index.row();
            return true;
        }
        else if (role == LTRightPanelRole)
        {
            if (m_exclusive)
            {
                m_rightIndexes.clear();
            }
Andi Clemens's avatar
Andi Clemens committed
130

131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
            m_rightIndexes << index.row();
            return true;
        }

        return ImageListModel::setData(index, value, role);
    }

    virtual void imageInfosAboutToBeRemoved(int begin, int end)
    {
        removeAnyInInterval(m_leftIndexes, begin, end);
        removeAnyInInterval(m_rightIndexes, begin, end);
    }

    virtual void imageInfosCleared()
    {
        clearLightTableState();
    }

protected:

    QSet<int> m_leftIndexes;
    QSet<int> m_rightIndexes;
    bool      m_exclusive;
};

156
157
158
159
160
class LightTableThumbBar::LightTableThumbBarPriv
{

public:

Gilles Caulier's avatar
port++    
Gilles Caulier committed
161
    LightTableThumbBarPriv()
162
    {
Gilles Caulier's avatar
port++    
Gilles Caulier committed
163
164
165
166
        navigateByPair   = false;
        imageInfoModel   = 0;
        imageFilterModel = 0;
        dragDropHandler  = 0;
167
168
    }

Gilles Caulier's avatar
port++    
Gilles Caulier committed
169
170
    bool                  navigateByPair;

171
    LightTableImageListModel* imageInfoModel;
Gilles Caulier's avatar
port++    
Gilles Caulier committed
172
173
    ImageFilterModel*     imageFilterModel;
    ImageDragDropHandler* dragDropHandler;
174
175
176
177
178
179
};

LightTableThumbBar::LightTableThumbBar(QWidget* parent)
    : ImageThumbnailBar(parent),
      d(new LightTableThumbBarPriv)
{
180
181
182
    d->imageInfoModel   = new LightTableImageListModel(this);
    // only one is left, only one is right at a time
    d->imageInfoModel->setExclusiveLightTableState(true);
Gilles Caulier's avatar
port++    
Gilles Caulier committed
183
184
185
186
187
188
189
190
191
192

    d->imageFilterModel = new ImageFilterModel(this);
    d->imageFilterModel->setSourceImageModel(d->imageInfoModel);

    d->imageInfoModel->setWatchFlags(d->imageFilterModel->suggestedWatchFlags());
    d->imageInfoModel->setThumbnailLoadThread(ThumbnailLoadThread::defaultIconViewThread());

    d->imageFilterModel->setCategorizationMode(ImageSortSettings::NoCategories);
    d->imageFilterModel->setSortRole((ImageSortSettings::SortRole)AlbumSettings::instance()->getImageSortOrder());
    d->imageFilterModel->setSortOrder((ImageSortSettings::SortOrder)AlbumSettings::instance()->getImageSorting());
193
    d->imageFilterModel->setAllGroupsOpen(true); // disable filtering out by group, see bug #308948
194
    d->imageFilterModel->sort(0); // an initial sorting is necessary
Gilles Caulier's avatar
port++    
Gilles Caulier committed
195
196

    d->dragDropHandler = new ImageDragDropHandler(d->imageInfoModel);
Gilles Caulier's avatar
polish    
Gilles Caulier committed
197
    d->dragDropHandler->setReadOnlyDrop(true);
Gilles Caulier's avatar
port++    
Gilles Caulier committed
198
199
    d->imageInfoModel->setDragDropHandler(d->dragDropHandler);

Gilles Caulier's avatar
polish    
Gilles Caulier committed
200
    setModels(d->imageInfoModel, d->imageFilterModel);
201
    setSelectionMode(QAbstractItemView::SingleSelection);
Gilles Caulier's avatar
Gilles Caulier committed
202

Laurent Montel's avatar
Laurent Montel committed
203
204
    connect(d->dragDropHandler, SIGNAL(imageInfosDropped(QList<ImageInfo>)),
            this, SIGNAL(signalDroppedItems(QList<ImageInfo>)));
205

Laurent Montel's avatar
Laurent Montel committed
206
    connect(d->imageInfoModel, SIGNAL(imageInfosAdded(QList<ImageInfo>)),
207
            this, SIGNAL(signalContentChanged()));
208
209
210
211
212
213
214
}

LightTableThumbBar::~LightTableThumbBar()
{
    delete d;
}

Gilles Caulier's avatar
polish    
Gilles Caulier committed
215
216
void LightTableThumbBar::setItems(const ImageInfoList& list)
{
217
218
219
    foreach(ImageInfo info, list)
    {
        if (!d->imageInfoModel->hasImage(info))
Andi Clemens's avatar
Andi Clemens committed
220
        {
221
            d->imageInfoModel->addImageInfo(info);
Andi Clemens's avatar
Andi Clemens committed
222
        }
223
    }
Gilles Caulier's avatar
polish    
Gilles Caulier committed
224
225
}

Gilles Caulier's avatar
Gilles Caulier committed
226
227
void LightTableThumbBar::clear()
{
228
229
    d->imageInfoModel->clearImageInfos();
    emit signalContentChanged();
Gilles Caulier's avatar
Gilles Caulier committed
230
231
}

232
233
234
235
236
void LightTableThumbBar::setNavigateByPair(bool b)
{
    d->navigateByPair = b;
}

Gilles Caulier's avatar
polish    
Gilles Caulier committed
237
void LightTableThumbBar::showContextMenuOnInfo(QContextMenuEvent* e, const ImageInfo& info)
238
{
Gilles Caulier's avatar
Gilles Caulier committed
239
    // temporary actions ----------------------------------
240

Andi Clemens's avatar
Andi Clemens committed
241
    QAction* leftPanelAction = 0, *rightPanelAction = 0, *editAction = 0, *removeAction = 0, *clearAllAction = 0;
242

Gilles Caulier's avatar
Gilles Caulier committed
243
244
245
246
247
    leftPanelAction  = new QAction(SmallIcon("arrow-left"),        i18n("Show on left panel"),  this);
    rightPanelAction = new QAction(SmallIcon("arrow-right"),       i18n("Show on right panel"), this);
    editAction       = new QAction(SmallIcon("editimage"),         i18n("Edit"),                this);
    removeAction     = new QAction(SmallIcon("dialog-close"),      i18n("Remove item"),         this);
    clearAllAction   = new QAction(SmallIcon("edit-delete-shred"), i18n("Clear all"),           this);
248

Gilles Caulier's avatar
Gilles Caulier committed
249
250
251
    leftPanelAction->setEnabled(d->navigateByPair  ? false : true);
    rightPanelAction->setEnabled(d->navigateByPair ? false : true);
    clearAllAction->setEnabled(countItems()        ? true  : false);
252

Gilles Caulier's avatar
Gilles Caulier committed
253
    // ----------------------------------------------------
254

255
    KMenu popmenu(this);
Gilles Caulier's avatar
Gilles Caulier committed
256
    ContextMenuHelper cmhelper(&popmenu);
Gilles Caulier's avatar
polish    
Gilles Caulier committed
257
258
259
260
261
262
263
264
    cmhelper.addAction(leftPanelAction, true);
    cmhelper.addAction(rightPanelAction, true);
    cmhelper.addSeparator();
    cmhelper.addAction(editAction);
    cmhelper.addAction(removeAction);
    cmhelper.addSeparator();
    cmhelper.addLabelsAction();
    cmhelper.addSeparator();
Gilles Caulier's avatar
Gilles Caulier committed
265
    cmhelper.addAction(clearAllAction, true);
266

Gilles Caulier's avatar
Gilles Caulier committed
267
    // special action handling --------------------------------
268

Gilles Caulier's avatar
Gilles Caulier committed
269
270
    connect(&cmhelper, SIGNAL(signalAssignPickLabel(int)),
            this, SLOT(slotAssignPickLabel(int)));
271

Gilles Caulier's avatar
Gilles Caulier committed
272
273
    connect(&cmhelper, SIGNAL(signalAssignColorLabel(int)),
            this, SLOT(slotAssignColorLabel(int)));
274

Gilles Caulier's avatar
Gilles Caulier committed
275
276
    connect(&cmhelper, SIGNAL(signalAssignRating(int)),
            this, SLOT(slotAssignRating(int)));
277

Gilles Caulier's avatar
polish    
Gilles Caulier committed
278
    QAction* choice = cmhelper.exec(e->globalPos());
279

Gilles Caulier's avatar
Gilles Caulier committed
280
281
282
    if (choice)
    {
        if (choice == leftPanelAction)
283
        {
Gilles Caulier's avatar
Gilles Caulier committed
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
            emit signalSetItemOnLeftPanel(info);
        }
        else if (choice == rightPanelAction)
        {
            emit signalSetItemOnRightPanel(info);
        }
        else if (choice == editAction)
        {
            emit signalEditItem(info);
        }
        else if (choice == removeAction)
        {
            emit signalRemoveItem(info);
        }
        else if (choice == clearAllAction)
        {
            emit signalClearAll();
301
302
303
304
        }
    }
}

Gilles Caulier's avatar
polish    
Gilles Caulier committed
305
void LightTableThumbBar::slotAssignPickLabel(int pickId)
306
{
Gilles Caulier's avatar
polish    
Gilles Caulier committed
307
    assignPickLabel(currentInfo(), pickId);
308
309
}

Gilles Caulier's avatar
polish    
Gilles Caulier committed
310
void LightTableThumbBar::slotAssignColorLabel(int colorId)
311
{
Gilles Caulier's avatar
polish    
Gilles Caulier committed
312
    assignColorLabel(currentInfo(), colorId);
313
314
}

Gilles Caulier's avatar
polish    
Gilles Caulier committed
315
316
317
318
319
320
321
322
323
324
void LightTableThumbBar::slotRatingChanged(const KUrl& url, int rating)
{
    assignRating(ImageInfo(url), rating);
}

void LightTableThumbBar::slotAssignRating(int rating)
{
    assignRating(currentInfo(), rating);
}

325
326
void LightTableThumbBar::assignPickLabel(const ImageInfo& info, int pickId)
{
327
    FileActionMngr::instance()->assignPickLabel(info, pickId);
328
329
}

Gilles Caulier's avatar
polish    
Gilles Caulier committed
330
void LightTableThumbBar::assignRating(const ImageInfo& info, int rating)
331
{
332
    FileActionMngr::instance()->assignRating(info, rating);
333
334
}

Gilles Caulier's avatar
polish    
Gilles Caulier committed
335
void LightTableThumbBar::assignColorLabel(const ImageInfo& info, int colorId)
336
{
337
    FileActionMngr::instance()->assignColorLabel(info, colorId);
338
339
340
341
}

void LightTableThumbBar::toggleTag(int tagID)
{
Gilles Caulier's avatar
polish    
Gilles Caulier committed
342
    ImageInfo info = currentInfo();
343
344
345

    if (!info.isNull())
    {
346
        if (!info.tagIds().contains(tagID))
Andi Clemens's avatar
Andi Clemens committed
347
        {
348
            FileActionMngr::instance()->assignTag(info, tagID);
Andi Clemens's avatar
Andi Clemens committed
349
        }
350
        else
Andi Clemens's avatar
Andi Clemens committed
351
        {
352
            FileActionMngr::instance()->removeTag(info, tagID);
Andi Clemens's avatar
Andi Clemens committed
353
        }
354
355
356
    }
}

357
void LightTableThumbBar::setOnLeftPanel(const ImageInfo& info)
358
{
359
360
361
    QModelIndex index = d->imageInfoModel->indexForImageInfo(info);
    // model has exclusiveLightTableState, so any previous index will be reset
    d->imageInfoModel->setData(index, true, ImageModel::LTLeftPanelRole);
362
    viewport()->update();
363
364
}

365
void LightTableThumbBar::setOnRightPanel(const ImageInfo& info)
366
{
367
368
369
    QModelIndex index = d->imageInfoModel->indexForImageInfo(info);
    // model has exclusiveLightTableState, so any previous index will be reset
    d->imageInfoModel->setData(index, true, ImageModel::LTRightPanelRole);
370
    viewport()->update();
371
372
}

373
374
bool LightTableThumbBar::isOnLeftPanel(const ImageInfo& info) const
{
375
    return d->imageInfoModel->indexForImageInfo(info).data(ImageModel::LTLeftPanelRole).toBool();
376
377
378
379
}

bool LightTableThumbBar::isOnRightPanel(const ImageInfo& info) const
{
380
    return d->imageInfoModel->indexForImageInfo(info).data(ImageModel::LTRightPanelRole).toBool();
381
}
Gilles Caulier's avatar
port ++    
Gilles Caulier committed
382
383
384
385

QModelIndex LightTableThumbBar::findItemByInfo(const ImageInfo& info) const
{
    if (!info.isNull())
Andi Clemens's avatar
Andi Clemens committed
386
    {
Gilles Caulier's avatar
port ++    
Gilles Caulier committed
387
        return d->imageInfoModel->indexForImageInfo(info);
Andi Clemens's avatar
Andi Clemens committed
388
    }
Gilles Caulier's avatar
port ++    
Gilles Caulier committed
389
390
391
392

    return QModelIndex();
}

Gilles Caulier's avatar
polish    
Gilles Caulier committed
393
394
395
ImageInfo LightTableThumbBar::findItemByIndex(const QModelIndex& index) const
{
    if (index.isValid())
Andi Clemens's avatar
Andi Clemens committed
396
    {
Gilles Caulier's avatar
polish    
Gilles Caulier committed
397
        return d->imageInfoModel->imageInfo(index);
Andi Clemens's avatar
Andi Clemens committed
398
    }
Gilles Caulier's avatar
polish    
Gilles Caulier committed
399
400
401
402

    return ImageInfo();
}

Gilles Caulier's avatar
polish    
Gilles Caulier committed
403
void LightTableThumbBar::removeItemByInfo(const ImageInfo& info)
404
405
{
    if (info.isNull())
Andi Clemens's avatar
Andi Clemens committed
406
    {
407
        return;
Andi Clemens's avatar
Andi Clemens committed
408
    }
409

Gilles Caulier's avatar
polish    
Gilles Caulier committed
410
    d->imageInfoModel->removeImageInfo(info);
411
    emit signalContentChanged();
412
413
}

Gilles Caulier's avatar
port ++    
Gilles Caulier committed
414
415
int LightTableThumbBar::countItems() const
{
416
    return d->imageInfoModel->rowCount();
Gilles Caulier's avatar
polish    
Gilles Caulier committed
417
418
}

419
void LightTableThumbBar::paintEvent(QPaintEvent* e)
Gilles Caulier's avatar
polish    
Gilles Caulier committed
420
{
421
422
423
424
425
    if (!countItems())
    {
        QPainter p(viewport());
        p.setPen(QPen(kapp->palette().color(QPalette::Text)));
        p.drawText(0, 0, width(), height(),
Andi Clemens's avatar
Andi Clemens committed
426
427
                   Qt::AlignCenter | Qt::TextWordWrap,
                   i18n("Drag and drop images here"));
428
429
430
431
432
        p.end();
        return;
    }

    ImageThumbnailBar::paintEvent(e);
Gilles Caulier's avatar
polish    
Gilles Caulier committed
433
434
}

435
}  // namespace Digikam