~ubuntu-branches/ubuntu/jaunty/fqterm/jaunty

« back to all changes in this revision

Viewing changes to src/ui/imageviewer.cpp

  • Committer: Bazaar Package Importer
  • Author(s): LI Daobing
  • Date: 2009-02-14 09:32:53 UTC
  • Revision ID: james.westby@ubuntu.com-20090214093253-s2e6544ox2aj79rj
Tags: upstream-0.9.3+svn632
ImportĀ upstreamĀ versionĀ 0.9.3+svn632

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***************************************************************************
 
2
 *   fqterm, a terminal emulator for both BBS and *nix.                    *
 
3
 *   Copyright (C) 2008 fqterm development group.                          *
 
4
 *                                                                         *
 
5
 *   This program is free software; you can redistribute it and/or modify  *
 
6
 *   it under the terms of the GNU General Public License as published by  *
 
7
 *   the Free Software Foundation; either version 2 of the License, or     *
 
8
 *   (at your option) any later version.                                   *
 
9
 *                                                                         *
 
10
 *   This program is distributed in the hope that it will be useful,       *
 
11
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 
12
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 
13
 *   GNU General Public License for more details.                          *
 
14
 *                                                                         *
 
15
 *   You should have received a copy of the GNU General Public License     *
 
16
 *   along with this program; if not, write to the                         *
 
17
 *   Free Software Foundation, Inc.,                                       *
 
18
 *   51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.               *
 
19
 ***************************************************************************/
 
20
 
 
21
#include <QAction>
 
22
#include <QBuffer>
 
23
#include <QCursor>
 
24
#include <QComboBox>
 
25
#include <QDateTime>
 
26
#include <QDataStream>
 
27
#include <QDir>
 
28
#include <QFile>
 
29
#include <QFileInfo>
 
30
#include <QIcon>
 
31
#include <QKeyEvent>
 
32
#include <QLineEdit>
 
33
#include <QMessageBox>
 
34
#include <QMouseEvent>
 
35
#include <QMenu>
 
36
#include <QMenuBar>
 
37
#include <QPainter>
 
38
#include <QPixmap>
 
39
#include <QPushButton>
 
40
#include <QSizeGrip>
 
41
#include <QString>
 
42
#include <QTextCodec>
 
43
#include <QTimer>
 
44
#include <QToolTip>
 
45
#include <QTransform>
 
46
#include <QTreeView>
 
47
#include <QTextEdit>
 
48
 
 
49
#include "fqterm_canvas.h"
 
50
#include "fqterm_config.h"
 
51
#include "fqterm_exif_extractor.h"
 
52
#include "fqterm_filedialog.h"
 
53
#include "fqterm_path.h"
 
54
#include "fqterm_trace.h"
 
55
#include "imageviewer.h"
 
56
 
 
57
 
 
58
namespace FQTerm {
 
59
 
 
60
  typedef enum {
 
61
        V_UNKNOWN = -1,
 
62
        V_ORIGINAL = 0,
 
63
        V_SHADOW,
 
64
        V_BESTFIT,
 
65
        V_ZOOMIN,
 
66
        V_ZOOMOUT,
 
67
        V_ROTLEFT,
 
68
        V_ROTRIGHT,
 
69
        V_FULLSCREEN
 
70
  } slideViewModes;
 
71
 
 
72
  typedef enum {
 
73
    SORT_UNSORTED = -1,
 
74
    SORT_LIKE,
 
75
    SORT_NEW,
 
76
    SORT_TRASH,
 
77
    SORT_RECOVER,
 
78
    SORT_TITLE
 
79
  } slideSortFlags;
 
80
 
 
81
 
 
82
  typedef enum {
 
83
    STAT_UNKNOWN = -1,
 
84
    STAT_LIKE,
 
85
    STAT_NEW,
 
86
    STAT_TRASH,
 
87
    STAT_RECOVER,
 
88
    STAT_TITLE,
 
89
    STAT_ALL
 
90
  } slideStatus;
 
91
 
 
92
  typedef enum {
 
93
    GEN_UNKNOWN = -1,
 
94
    GEN_CANCEL,
 
95
    GEN_NOCHANGE,
 
96
    GEN_RESHUFFLE,
 
97
    GEN_NOTAG,
 
98
    GEN_INTRASH,
 
99
    GEN_INTRASHES,
 
100
    GEN_TOSAVE,
 
101
    GEN_TOSAVES,
 
102
    GEN_TRASH,
 
103
    GEN_TRASHES,
 
104
    GEN_NOTRASH,
 
105
    GEN_RECOVER,
 
106
    GEN_RECOVERS,
 
107
    GEN_NORECOVER,
 
108
    GEN_SAVE,
 
109
    GEN_SAVES,
 
110
    GEN_DELETE,
 
111
    GEN_DELETES,
 
112
    GEN_SEARCHWRAP,
 
113
    GEN_SEARCHNOMATCH
 
114
  } generalStatus;
 
115
 
 
116
  static const char *fqloraTagStatus[] = {
 
117
    "Like",
 
118
    "New",
 
119
    "Trash",
 
120
    "Recover",
 
121
    "Title"
 
122
  };
 
123
 
 
124
  static const char *fqloraGeneralStatus[] = {
 
125
    "Canceled.",
 
126
    "No changes for ",
 
127
    "Reshuffled by ",
 
128
    "No tags by ",
 
129
    "item in the trash.",
 
130
    "items in the trash.",
 
131
    "item to save.",
 
132
    "items to save.",
 
133
    "image trashed.",
 
134
    "images trashed.",
 
135
    "Nothing to trash.",
 
136
    "image recovered.",
 
137
    "images recovered.",
 
138
    "Nothing to recover.",
 
139
    "image saved in ",
 
140
    "images saved in ",
 
141
    "image deleted.",
 
142
    "images deleted.",
 
143
    "Search wrapped.",
 
144
    "No matches."
 
145
  };
 
146
 
 
147
  static const int toolBarFixedHeight = 40;
 
148
  static const int statusBarFixedHeight = 18;
 
149
  static const QSize sortBoxFixedSize(110, 25);
 
150
 
 
151
  static const QString &iconPath(const QString &fileName) {
 
152
 
 
153
    static QString p = "";
 
154
 
 
155
    if (!fileName.isEmpty()) {
 
156
      p = getPath(RESOURCE) + ICON_SOURCE + fileName;
 
157
    }
 
158
 
 
159
    return p;
 
160
  }
 
161
 
 
162
  const QString &isPlural(const int num, const int status) {
 
163
 
 
164
    static QString m = "";
 
165
 
 
166
    if (num == 1) {
 
167
      return (m = fqloraGeneralStatus[status]);
 
168
    } else if (num > 1 && status > GEN_UNKNOWN && status < GEN_DELETES) {
 
169
      return (m = fqloraGeneralStatus[status + 1]);
 
170
    }
 
171
 
 
172
    return (m = fqloraGeneralStatus[GEN_UNKNOWN]);
 
173
  }
 
174
 
 
175
  // get user's desktop rectangle
 
176
  static const QRect &desktopSize() {
 
177
 
 
178
        static QRect size;
 
179
    QDesktopWidget myDesktop;
 
180
        return ((size = myDesktop.screenGeometry(myDesktop.primaryScreen())));
 
181
  }
 
182
 
 
183
  // FQTermImageFlow
 
184
  FQTermImageFlow::~FQTermImageFlow() {
 
185
 
 
186
        delete statusBar_;
 
187
    delete imageMenu_;
 
188
        delete imageFlow_;
 
189
 
 
190
    statusBar_ = NULL;
 
191
    imageMenu_ = NULL;
 
192
    imageFlow_ = NULL;
 
193
  }
 
194
  
 
195
  FQTermImageFlow::FQTermImageFlow(FQTermConfig * config, QWidget *parent,
 
196
                                                                   Qt::WindowFlags wflag) :
 
197
        FQTermImage(parent, wflag),
 
198
        imageFlow_(new ImageFlow(this)),
 
199
    imageMenu_(new ImageMenu(this)),
 
200
    statusBar_(new QStatusBar(this)),
 
201
    config_(config) {
 
202
 
 
203
    setWindowTitle(IMAGE_BROWSER_NAME);
 
204
    setAutoFillBackground(true);
 
205
    setBackgroundRole(QPalette::Midlight);
 
206
        setFixedSize(desktopSize().width() / 1.3, desktopSize().height() / 1.9);
 
207
 
 
208
    FQ_VERIFY(connect(this, SIGNAL(isTrashEmpty()), this, SLOT(checkTrashState())));
 
209
 
 
210
    // emblem state
 
211
    FQ_VERIFY(connect(imageFlow_, SIGNAL(emblemStatus(const int)), imageMenu_, SLOT(updateEmblems(const int))));
 
212
    FQ_VERIFY(connect(imageMenu_, SIGNAL(toggleFlowStatus(const int)), imageFlow_, SLOT(toggleStatus(const int))));
 
213
 
 
214
    // clear state
 
215
    FQ_VERIFY(connect(imageFlow_, SIGNAL(clearStatus(const bool)), imageMenu_, SLOT(updateClear(const bool))));
 
216
    FQ_VERIFY(connect(imageMenu_, SIGNAL(clearImages()), this, SLOT(clearImages())));
 
217
 
 
218
    // save state
 
219
    FQ_VERIFY(connect(imageFlow_, SIGNAL(saveStatus(const bool)), imageMenu_, SLOT(updateSave(const bool))));
 
220
    FQ_VERIFY(connect(imageMenu_, SIGNAL(saveImages()), this, SLOT(saveImages())));
 
221
 
 
222
    // trash state
 
223
    FQ_VERIFY(connect(this, SIGNAL(trashStatus(const bool)), imageMenu_, SLOT(updateDustbin(const bool))));
 
224
    FQ_VERIFY(connect(imageMenu_, SIGNAL(recoverImages()), this, SLOT(recoverImages())));
 
225
 
 
226
        // constructs a tool bar and its tool buttons
 
227
        QAction *closeBrowserAct = new QAction(QIcon(iconPath("window-close.png")), tr("Close"), this);
 
228
        closeBrowserAct->setShortcut(QKeySequence(Qt::Key_Q));
 
229
        FQ_VERIFY(connect(closeBrowserAct, SIGNAL(triggered()), this, SLOT(close())));
 
230
 
 
231
        QAction *trashAllAct = new QAction(QIcon(iconPath("trash-empty.png")), tr("Trash All"), this);
 
232
        trashAllAct->setShortcut(QKeySequence(Qt::Key_Delete));
 
233
        FQ_VERIFY(connect(trashAllAct, SIGNAL(triggered()), this, SLOT(trashAllImages())));
 
234
 
 
235
    QLabel *sortLabel = new QLabel(this);
 
236
    sortLabel->setMargin(5);
 
237
    sortLabel->setToolTip("Reshuffle images by tags");
 
238
    sortLabel->setPixmap(QPixmap(iconPath("edit-shuffle.png")));
 
239
 
 
240
    QComboBox *sortBox = new QComboBox(this);
 
241
    sortBox->setFocusPolicy(Qt::ClickFocus);
 
242
    sortBox->setFont(QFont("Serif", 12));
 
243
    sortBox->setFrame(false);
 
244
    sortBox->setFixedSize(sortBoxFixedSize);
 
245
    sortBox->insertItem(0, QIcon(iconPath("emblem-like-16x16.png")), tr("Like "));
 
246
    sortBox->insertItem(1, QIcon(iconPath("emblem-new-16x16.png")), tr("New "));
 
247
    sortBox->insertItem(2, QIcon(iconPath("emblem-trash-16x16.png")), tr("Trash "));
 
248
    sortBox->insertItem(3, QIcon(iconPath("emblem-recover-16x16.png")), tr("Recover "));
 
249
    sortBox->insertItem(4, QIcon(iconPath("emblem-title-16x16.png")), tr("Title "));
 
250
    FQ_VERIFY(connect(sortBox, SIGNAL(currentIndexChanged(int)), this, SLOT(reshuffleImages(int))));
 
251
 
 
252
        QToolBar *toolBar = new QToolBar(this);
 
253
        toolBar->setFixedHeight(toolBarFixedHeight);
 
254
        toolBar->setIconSize(QSize(32, 32));
 
255
        toolBar->addAction(closeBrowserAct);
 
256
    toolBar->addSeparator();
 
257
    toolBar->addWidget(sortLabel);
 
258
    toolBar->addWidget(sortBox);
 
259
    toolBar->addSeparator();
 
260
        toolBar->addAction(trashAllAct);
 
261
 
 
262
        // constructs a status bar to show instant messages
 
263
        statusBar_->setSizeGripEnabled(false);
 
264
        statusBar_->setFixedHeight(statusBarFixedHeight);
 
265
        statusBar_->setFont(QFont("Serif", 10, QFont::Bold));
 
266
    FQ_VERIFY(connect(imageFlow_, SIGNAL(statusMessage(const QString &)), this, SLOT(showStatusMessage(const QString &))));
 
267
    FQ_VERIFY(connect(this, SIGNAL(statusMessage(const QString &)), this, SLOT(showStatusMessage(const QString &))));
 
268
        // constructs a horizontal layout
 
269
        QVBoxLayout *vBox = new QVBoxLayout(this);
 
270
        vBox->setMargin(0);
 
271
    vBox->setSpacing(0);
 
272
    vBox->addWidget(toolBar);
 
273
        vBox->addWidget(imageFlow_);
 
274
    vBox->addWidget(imageMenu_);
 
275
        vBox->addWidget(statusBar_);
 
276
        vBox->setEnabled(true);
 
277
        setLayout(vBox);
 
278
  }
 
279
 
 
280
  const QString &FQTermImageFlow::poolSource(void) const {
 
281
 
 
282
    static QString p = "";
 
283
 
 
284
    p = config_->getItemValue("preference", "pool");
 
285
 
 
286
    if (p.isEmpty()) {
 
287
      p = getPath(USER_CONFIG) + POOL_SOURCE;
 
288
    }
 
289
 
 
290
    return p;
 
291
  }
 
292
 
 
293
  const QString &FQTermImageFlow::trashSource(void) const {
 
294
 
 
295
    static QString p = "";
 
296
 
 
297
    p = poolSource() + TRASH_SOURCE;
 
298
    return p;
 
299
  }
 
300
 
 
301
  // these three functions below are kept for
 
302
  // API compatibility.
 
303
  void FQTermImageFlow::adjustItemSize() {
 
304
 
 
305
  }
 
306
 
 
307
  void FQTermImageFlow::scrollTo(const QString &filePath) {
 
308
 
 
309
  }
 
310
 
 
311
  void FQTermImageFlow::updateImage(const QString &filePath) {
 
312
 
 
313
  }
 
314
 
 
315
  // slots and functions
 
316
  void FQTermImageFlow::loadImages(const int status) {
 
317
 
 
318
    int i;
 
319
    int itemStatus = (status == STAT_RECOVER) ? status : STAT_UNKNOWN;
 
320
    QString comment;
 
321
    QFileInfoList sourceList = sortedList(poolSource());
 
322
    QFileInfoList targetList = imageFlow_->digest(STAT_ALL);
 
323
 
 
324
    for (i = 0; i < sourceList.count(); i++) {
 
325
 
 
326
      if (!targetList.contains(sourceList[i])) {
 
327
 
 
328
        QImage image;
 
329
 
 
330
        if (!image.load(sourceList[i].absoluteFilePath())) {
 
331
          QFileIconProvider iconProvider;
 
332
          image = iconProvider.icon(sourceList[i]).pixmap(desktopSize().height() / 6.0).toImage();
 
333
        } else {
 
334
          image = image.scaled(desktopSize().width() / 6.0, desktopSize().height() / 6.0,
 
335
            Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
 
336
        }
 
337
 
 
338
        /* TODO */
 
339
        /* there should be a more flexible */
 
340
        /* method to deal with items' status */
 
341
        if (status == STAT_ALL) {
 
342
          itemStatus = STAT_NEW;
 
343
        }
 
344
 
 
345
        ImageFlowItem newItem(image, sourceList[i], comment,itemStatus);
 
346
        imageFlow_->add(newItem);
 
347
      }
 
348
    }
 
349
  }
 
350
 
 
351
  void FQTermImageFlow::reshuffleImages(const int status) {
 
352
 
 
353
    // there are images in the pool directory
 
354
    QList<qint64> itemKeys = imageFlow_->sort(status);
 
355
 
 
356
    if (imageFlow_->reorder(itemKeys)) {
 
357
      imageFlow_->setCurrentSlide(0);
 
358
      emit statusMessage(tr(fqloraGeneralStatus[GEN_RESHUFFLE]) + tr(fqloraTagStatus[status]));
 
359
    } else {
 
360
      emit statusMessage(tr(fqloraGeneralStatus[GEN_NOCHANGE]) + tr(fqloraTagStatus[status]));
 
361
    }
 
362
  }
 
363
 
 
364
  void FQTermImageFlow::saveImages() {
 
365
 
 
366
    QFileInfoList toSave = imageFlow_->digest(STAT_LIKE);
 
367
 
 
368
    if (!toSave.isEmpty()) {
 
369
 
 
370
      QMessageBox box;
 
371
      box.setWindowModality(Qt::NonModal);
 
372
      box.setWindowTitle("Information");
 
373
      box.setIconPixmap(QPixmap(iconPath("emblem-info.png")));
 
374
      box.setTextFormat(Qt::RichText);
 
375
      box.setText(QString("<b>%1</b> ").arg(toSave.count()) + isPlural(toSave.count(), GEN_TOSAVE));
 
376
      QPushButton *cancelButton = box.addButton(QMessageBox::Cancel);
 
377
      QPushButton *saveAllButton = box.addButton(QMessageBox::SaveAll);
 
378
      box.setDefaultButton(cancelButton);
 
379
      box.exec();
 
380
 
 
381
      if (box.clickedButton() == saveAllButton) {
 
382
 
 
383
        FQTermFileDialog fileDialog(config_);
 
384
        QString savePath =
 
385
          fileDialog.getExistingDirectory(tr("Save your likes under"), "*");
 
386
 
 
387
        if (savePath.isEmpty()) {
 
388
          emit statusMessage(tr(fqloraGeneralStatus[GEN_CANCEL]));
 
389
          return;
 
390
        }
 
391
 
 
392
        int i;
 
393
        for (i = 0; i < toSave.count(); i++) {
 
394
          QFile::rename(toSave[i].absoluteFilePath(), savePath + toSave[i].fileName());
 
395
        }
 
396
 
 
397
        imageFlow_->strip(STAT_LIKE);
 
398
        emit statusMessage(QString("%1 ").arg(i) + isPlural(i, GEN_SAVE) + savePath);
 
399
      } else {
 
400
        emit statusMessage(tr(fqloraGeneralStatus[GEN_CANCEL]));
 
401
      }
 
402
    } else {
 
403
      emit statusMessage(tr(fqloraGeneralStatus[GEN_NOTAG]) + tr(fqloraTagStatus[STAT_LIKE]));
 
404
    }
 
405
  }
 
406
 
 
407
  void FQTermImageFlow::checkTrashState() {
 
408
 
 
409
    QDir trashPath(trashSource());
 
410
 
 
411
    if (trashPath.count() <= 2) {
 
412
      emit trashStatus(false);
 
413
    } else {
 
414
      emit trashStatus(true);
 
415
    }
 
416
  }
 
417
 
 
418
  void FQTermImageFlow::clearImages() {
 
419
 
 
420
    int i;
 
421
    QString trashPath = trashSource();
 
422
    QDir trashDir(trashPath);
 
423
    QFileInfoList toRemove = imageFlow_->digest(STAT_TRASH);
 
424
 
 
425
    if (!trashDir.exists()) {
 
426
      trashDir.mkdir(trashPath);
 
427
    }
 
428
 
 
429
    if (!toRemove.isEmpty()) {
 
430
 
 
431
      for (i = 0; i < toRemove.count(); i++) {
 
432
        if (QFile::exists(toRemove[i].absoluteFilePath())) {
 
433
          QFile::rename(toRemove[i].absoluteFilePath(), trashPath + toRemove[i].fileName());
 
434
        }
 
435
      }
 
436
 
 
437
      imageFlow_->strip(STAT_TRASH);
 
438
      emit statusMessage(QString("%1 ").arg(i) + isPlural(i, GEN_TRASH));
 
439
      emit isTrashEmpty();
 
440
    } else {
 
441
      emit statusMessage(tr(fqloraGeneralStatus[GEN_NOTAG]) + tr(fqloraTagStatus[STAT_TRASH]));
 
442
    }
 
443
  }
 
444
 
 
445
  void FQTermImageFlow::trashAllImages() {
 
446
 
 
447
        int i;
 
448
        QString trashPath = trashSource();
 
449
        QDir trashDir(trashPath);
 
450
        QFileInfoList toRemove = imageFlow_->digest(STAT_ALL);
 
451
 
 
452
        if (!trashDir.exists()) {
 
453
          trashDir.mkdir(trashPath);
 
454
        }
 
455
 
 
456
        if (imageFlow_->count() > 0) {
 
457
          for (i = 0; i < imageFlow_->count(); i++) {
 
458
                if (QFile::exists(toRemove[i].absoluteFilePath())) {
 
459
                  QFile::rename(toRemove[i].absoluteFilePath(), trashPath + toRemove[i].fileName());
 
460
                }
 
461
          }
 
462
 
 
463
          imageFlow_->clear();
 
464
          emit statusMessage(QString("%1 ").arg(i) + isPlural(i, GEN_TRASH));
 
465
          emit isTrashEmpty();
 
466
        } else {
 
467
          emit statusMessage(tr(fqloraGeneralStatus[GEN_NOTAG]) + tr(fqloraTagStatus[STAT_TRASH]));
 
468
        }
 
469
  }
 
470
 
 
471
  void FQTermImageFlow::recoverImages() {
 
472
 
 
473
    QString trashPath = trashSource();
 
474
    QDir trashDir(trashPath);
 
475
 
 
476
    if (!trashDir.exists()) {
 
477
      trashDir.mkdir(trashPath);
 
478
    } else if (trashDir.count() > 2) {
 
479
 
 
480
      int action = -1;;
 
481
      QFileInfoList toRecover = sortedList(trashPath);
 
482
 
 
483
      if (!toRecover.isEmpty()) {
 
484
 
 
485
        QMessageBox box;
 
486
        box.setWindowModality(Qt::NonModal);
 
487
        box.setWindowTitle("Take action");
 
488
        box.setIconPixmap(QPixmap(iconPath("emblem-info.png")));
 
489
        box.setTextFormat(Qt::RichText);
 
490
        box.setText(QString("<b>%1</b> ").arg(toRecover.count()) + isPlural(toRecover.count(), GEN_INTRASH));
 
491
        QPushButton *cancelButton = box.addButton(QMessageBox::Cancel);
 
492
        QPushButton *recoverButton = box.addButton(tr("Recover"), QMessageBox::ApplyRole);
 
493
        recoverButton->setIcon(QIcon(iconPath("button-recover.png")));
 
494
        QPushButton *deleteButton = box.addButton(tr("Delete"), QMessageBox::ApplyRole);
 
495
        deleteButton->setIcon(QIcon(iconPath("button-delete.png")));
 
496
        box.setDefaultButton(cancelButton);
 
497
        box.exec();
 
498
 
 
499
        if (box.clickedButton() == cancelButton) {
 
500
          emit statusMessage(tr(fqloraGeneralStatus[GEN_CANCEL]));
 
501
          return;
 
502
        } else if (box.clickedButton() == recoverButton) {
 
503
          action = 1;
 
504
        } else if (box.clickedButton() == deleteButton) {
 
505
          action = 0;
 
506
        }
 
507
 
 
508
        int i;
 
509
        QFile imageFile;
 
510
        QString poolPath = poolSource();
 
511
 
 
512
        for (i = 0; i < toRecover.count(); i++) {
 
513
          if (action == 1) {
 
514
            QFile::rename(toRecover[i].absoluteFilePath(), poolPath + toRecover[i].fileName());
 
515
          } else if (action == 0) {
 
516
            imageFile.setFileName(toRecover[i].absoluteFilePath());
 
517
            imageFile.remove();
 
518
          }
 
519
        }
 
520
 
 
521
        if (action == 1) {
 
522
          loadImages(STAT_RECOVER);
 
523
          emit statusMessage(QString("%1 ").arg(i) + isPlural(i, GEN_RECOVER));
 
524
        } else if (action == 0) {
 
525
          emit statusMessage(QString("%1 ").arg(i) + isPlural(i, GEN_DELETE));
 
526
        }
 
527
      }
 
528
    } else {
 
529
 
 
530
      emit statusMessage(tr(fqloraGeneralStatus[GEN_NORECOVER]));
 
531
    }
 
532
 
 
533
    emit isTrashEmpty();
 
534
  }
 
535
 
 
536
 
 
537
  void FQTermImageFlow::showStatusMessage(const QString &message) {
 
538
 
 
539
    if (statusBar_) {
 
540
      statusBar_->showMessage(message, 3000);
 
541
    }
 
542
  }
 
543
 
 
544
  QFileInfoList FQTermImageFlow::sortedList(const QString &path) {
 
545
 
 
546
    QDir sortPath(path);
 
547
 
 
548
    if (!sortPath.exists()) {
 
549
      sortPath.mkdir(path);
 
550
    }
 
551
 
 
552
    sortPath.setNameFilters((QStringList() << "*.jpg" << "*.jpeg" << "*.png" << "*.gif"
 
553
        << "*.bmp" << "*.tiff" << "*.mng"));
 
554
 
 
555
    return (sortPath.entryInfoList(QDir::Files | QDir::Readable | QDir::NoSymLinks, QDir::Time));
 
556
  }
 
557
 
 
558
  // FQTermImageFlow events.
 
559
  void FQTermImageFlow::showEvent(QShowEvent *event) {
 
560
 
 
561
    emit isTrashEmpty();
 
562
 
 
563
    if (imageFlow_->count() <= 0) {
 
564
      loadImages(STAT_UNKNOWN);
 
565
      move((desktopSize().width() - width()) / 2.0, (desktopSize().height() - height()) / 2.0);
 
566
    } else {
 
567
    // TODO: insert new images
 
568
        loadImages(STAT_ALL);
 
569
        }
 
570
  }
 
571
 
 
572
  void FQTermImageFlow::closeEvent(QCloseEvent *event) {
 
573
 
 
574
        hide();
 
575
  }
 
576
 
 
577
  class ImageMenu::Private {
 
578
  public:
 
579
    int emblemStatus;
 
580
    bool dustbinStatus;
 
581
    bool saveStatus;
 
582
    bool clearStatus;
 
583
 
 
584
    QString tipMessage;
 
585
 
 
586
    QRect rectLike;
 
587
    QRect rectTrash;
 
588
    QRect rectNew;
 
589
    QRect rectRecover;
 
590
    QRect rectEmblems;
 
591
 
 
592
    QRect rectClear;
 
593
    QRect rectSave;
 
594
    QRect rectDustbin;
 
595
 
 
596
    QPixmap pixmapLike, pixmapLikeGray;
 
597
    QPixmap pixmapTrash, pixmapTrashGray;
 
598
    QPixmap pixmapNew, pixmapNewGray;
 
599
    QPixmap pixmapRecover, pixmapRecoverGray;
 
600
 
 
601
    QPixmap pixmapClear, pixmapClearGray;
 
602
    QPixmap pixmapSave, pixmapSaveGray;
 
603
    QPixmap pixmapDustbin, pixmapDustbinGray;
 
604
  };
 
605
 
 
606
  //ImageMenu
 
607
  ImageMenu::ImageMenu(QWidget *parent)
 
608
    : fh(new ImageMenu::Private) {
 
609
 
 
610
    setAutoFillBackground(true);
 
611
    setBackgroundRole(QPalette::Shadow);
 
612
    setFocusPolicy(Qt::NoFocus);
 
613
    setMouseTracking(true);
 
614
    setFixedHeight(60);
 
615
 
 
616
    fh->emblemStatus = -2;
 
617
    fh->dustbinStatus = false;
 
618
    fh->saveStatus = false;
 
619
    fh->clearStatus = false;
 
620
 
 
621
    fh->tipMessage = "";
 
622
 
 
623
    fh->rectLike = QRect(50, 14, 32, 32);
 
624
    fh->rectTrash = QRect(100, 14, 32, 32);
 
625
    fh->rectNew = QRect(150, 14, 32, 32);
 
626
    fh->rectRecover = QRect(200, 14, 32, 32);
 
627
    fh->rectEmblems = QRect(50, 14, 232, 32);
 
628
 
 
629
    fh->rectClear = QRect(300, 14, 32, 32);
 
630
    fh->rectSave = QRect(350, 14, 32, 32);
 
631
    fh->rectDustbin = QRect(400, 14, 32, 32);
 
632
 
 
633
    fh->pixmapLike = QPixmap(iconPath("emblem-like.png"));
 
634
    fh->pixmapLikeGray = QPixmap(iconPath("emblem-like-gray.png"));
 
635
 
 
636
    fh->pixmapTrash = QPixmap(iconPath("emblem-trash.png"));
 
637
    fh->pixmapTrashGray = QPixmap(iconPath("emblem-trash-gray.png"));
 
638
 
 
639
    fh->pixmapNew = QPixmap(iconPath("emblem-new.png"));
 
640
    fh->pixmapNewGray = QPixmap(iconPath("emblem-new-gray.png"));
 
641
 
 
642
    fh->pixmapRecover = QPixmap(iconPath("emblem-recover.png"));
 
643
    fh->pixmapRecoverGray = QPixmap(iconPath("emblem-recover-gray.png"));
 
644
 
 
645
    fh->pixmapClear = QPixmap(iconPath("clear-state.png"));
 
646
    fh->pixmapClearGray = QPixmap(iconPath("clear-state-gray.png"));
 
647
 
 
648
    fh->pixmapSave = QPixmap(iconPath("save-state.png"));
 
649
    fh->pixmapSaveGray = QPixmap(iconPath("save-state-gray.png"));
 
650
 
 
651
    fh->pixmapDustbin = QPixmap(iconPath("trash-state.png"));
 
652
    fh->pixmapDustbinGray = QPixmap(iconPath("trash-state-gray.png"));
 
653
  }
 
654
 
 
655
  ImageMenu::~ImageMenu() {
 
656
 
 
657
    delete fh;
 
658
    fh = NULL;
 
659
  }
 
660
 
 
661
  void ImageMenu::updateEmblems(const int status) {
 
662
 
 
663
    if (fh->emblemStatus != status) {
 
664
      update(fh->rectEmblems);
 
665
    }
 
666
 
 
667
    fh->emblemStatus = status;
 
668
  }
 
669
 
 
670
  void ImageMenu::updateClear(const bool hasOrNot) {
 
671
 
 
672
    if (fh->clearStatus != hasOrNot) {
 
673
      update(fh->rectClear);
 
674
    }
 
675
 
 
676
    fh->clearStatus = hasOrNot;
 
677
  }
 
678
 
 
679
  void ImageMenu::updateSave(const bool hasOrNot) {
 
680
 
 
681
    if (fh->saveStatus != hasOrNot) {
 
682
      update(fh->rectSave);
 
683
    }
 
684
 
 
685
    fh->saveStatus = hasOrNot;
 
686
  }
 
687
 
 
688
  void ImageMenu::updateDustbin(const bool fullOrNot) {
 
689
 
 
690
    if (fh->dustbinStatus != fullOrNot) {
 
691
      update(fh->rectDustbin);
 
692
    }
 
693
 
 
694
    fh->dustbinStatus = fullOrNot;
 
695
  }
 
696
 
 
697
  void ImageMenu::paintEvent(QPaintEvent *event) {
 
698
 
 
699
    QPainter p(this);
 
700
 
 
701
    p.setRenderHints(QPainter::SmoothPixmapTransform | QPainter::Antialiasing);
 
702
 
 
703
    if (fh->emblemStatus == STAT_LIKE) {
 
704
      p.drawPixmap(fh->rectLike, fh->pixmapLike);
 
705
    } else {
 
706
      p.drawPixmap(fh->rectLike, fh->pixmapLikeGray);
 
707
    }
 
708
 
 
709
    if (fh->emblemStatus == STAT_TRASH) {
 
710
      p.drawPixmap(fh->rectTrash, fh->pixmapTrash);
 
711
    } else {
 
712
      p.drawPixmap(fh->rectTrash, fh->pixmapTrashGray);
 
713
    }
 
714
 
 
715
    if (fh->emblemStatus == STAT_NEW) {
 
716
      p.drawPixmap(fh->rectNew, fh->pixmapNew);
 
717
    } else {
 
718
      p.drawPixmap(fh->rectNew, fh->pixmapNewGray);
 
719
    }
 
720
 
 
721
    if (fh->emblemStatus == STAT_RECOVER) {
 
722
      p.drawPixmap(fh->rectRecover, fh->pixmapRecover);
 
723
    } else {
 
724
      p.drawPixmap(fh->rectRecover, fh->pixmapRecoverGray);
 
725
    }
 
726
 
 
727
    if (fh->clearStatus == true) {
 
728
      p.drawPixmap(fh->rectClear, fh->pixmapClear);
 
729
    } else {
 
730
      p.drawPixmap(fh->rectClear, fh->pixmapClearGray);
 
731
    }
 
732
 
 
733
    if (fh->saveStatus == true) {
 
734
      p.drawPixmap(fh->rectSave, fh->pixmapSave);
 
735
    } else {
 
736
      p.drawPixmap(fh->rectSave, fh->pixmapSaveGray);
 
737
    }
 
738
 
 
739
    if (fh->dustbinStatus == true) {
 
740
      p.drawPixmap(fh->rectDustbin, fh->pixmapDustbin);
 
741
    } else {
 
742
      p.drawPixmap(fh->rectDustbin, fh->pixmapDustbinGray);
 
743
    }
 
744
 
 
745
    p.end();
 
746
  }
 
747
 
 
748
  void ImageMenu::mouseReleaseEvent(QMouseEvent *event) {
 
749
 
 
750
    if (event->button() == Qt::LeftButton
 
751
      || event->button() == Qt::RightButton) {
 
752
 
 
753
      if (fh->rectLike.contains(event->pos())) {
 
754
        emit toggleFlowStatus(STAT_LIKE);
 
755
      }
 
756
 
 
757
      if (fh->rectTrash.contains(event->pos())) {
 
758
        emit toggleFlowStatus(STAT_TRASH);
 
759
      }
 
760
 
 
761
      if (fh->rectClear.contains(event->pos())) {
 
762
        emit clearImages();
 
763
      }
 
764
 
 
765
      if (fh->rectSave.contains(event->pos())) {
 
766
        emit saveImages();
 
767
      }
 
768
 
 
769
      if (fh->rectDustbin.contains(event->pos())) {
 
770
        emit recoverImages();
 
771
      }
 
772
    }
 
773
  }
 
774
 
 
775
  // ImageFlow: this subclasses PictureFlow
 
776
  class ImageFlow::Private {
 
777
  public:
 
778
    int emblemStatus;
 
779
    int likeCount;
 
780
    int clearCount;
 
781
 
 
782
    QList<QByteArray> itemsTitles;
 
783
    QHash<QByteArray, qint64> itemsTitleKeyPairs;
 
784
        QList<ImageFlowItem> items;
 
785
  };
 
786
 
 
787
  ImageFlow::~ImageFlow() {
 
788
 
 
789
        delete m;
 
790
        m = NULL;
 
791
  }
 
792
 
 
793
  // construct an emptry pictureflow.
 
794
  ImageFlow::ImageFlow(QWidget *parent)
 
795
        : PictureFlow(parent), m(new ImageFlow::Private) {
 
796
 
 
797
    // initial setup
 
798
    setFocusPolicy(Qt::StrongFocus);
 
799
    setZoomFactor(120);
 
800
        setSlideCount(0);
 
801
    m->emblemStatus = -1;
 
802
    m->likeCount = 0;
 
803
    m->clearCount = 0;
 
804
  }
 
805
 
 
806
  const ImageFlowItem& ImageFlow::operator[](int index) const {
 
807
 
 
808
    return (m->items[index]);
 
809
  }
 
810
 
 
811
  ImageFlowItem& ImageFlow::operator[](int index) {
 
812
 
 
813
    return (m->items[index]);
 
814
  }
 
815
 
 
816
  const ImageFlowItem& ImageFlow::at(int index) const {
 
817
 
 
818
    return (m->items[index]);
 
819
  }
 
820
 
 
821
  void ImageFlow::add(const ImageFlowItem &item) {
 
822
 
 
823
    QByteArray title = item.info().fileName().toLocal8Bit();
 
824
 
 
825
    m->items.append(item);
 
826
    m->itemsTitles.append(title);
 
827
    m->itemsTitleKeyPairs.insert(title, item.key());
 
828
 
 
829
    // set the status pixmap
 
830
    int index = m->items.indexOf(item);
 
831
    m->items[index].setStatus(item.status());
 
832
 
 
833
    int count = m->items.count();
 
834
    setSlideCount(count);
 
835
    setSlide(count - 1, item.image());
 
836
  }
 
837
 
 
838
  QList<QByteArray>& ImageFlow::itemsTitles() const {
 
839
 
 
840
    return (m->itemsTitles);
 
841
  }
 
842
 
 
843
  void ImageFlow::clear() {
 
844
 
 
845
    m->items.clear();
 
846
    setSlideCount(0);
 
847
    setCurrentSlide(0);
 
848
    PictureFlow::clear();
 
849
  }
 
850
 
 
851
  int ImageFlow::count() const {
 
852
 
 
853
    return (m->items.count());
 
854
  }
 
855
 
 
856
  int ImageFlow::size() const {
 
857
 
 
858
    return (m->items.size());
 
859
  }
 
860
 
 
861
  QFileInfoList& ImageFlow::digest(const int status) {
 
862
 
 
863
    int i;
 
864
    int count = m->items.size();
 
865
    static QFileInfoList result;
 
866
 
 
867
    result.clear();
 
868
 
 
869
    for (i = 0; i < count; i++) {
 
870
      switch (status) {
 
871
        case STAT_ALL:
 
872
          result.append(m->items[i].info());
 
873
          break;
 
874
        default:
 
875
          if (m->items[i].status() == status) {
 
876
            result.append(m->items[i].info());
 
877
          }
 
878
          break;
 
879
      }
 
880
    }
 
881
 
 
882
    return result;
 
883
  }
 
884
 
 
885
  void ImageFlow::strip(const int status) {
 
886
 
 
887
    int i;
 
888
    int count = m->items.count();
 
889
    QList<int> dirtyIndexes;
 
890
 
 
891
    // We cannot delete items in a loop because
 
892
    // items count will change while deleting.
 
893
    // A better method is to use another list
 
894
    // to record 'dirty' items, and by looping this
 
895
    // dirty list, we can correctly delete those
 
896
    // dirty items successfully.
 
897
    for (i = 0; i < count; i++) {
 
898
      if (m->items[i].status() == status) {
 
899
        dirtyIndexes.append(i);
 
900
      }
 
901
    }
 
902
 
 
903
    if (!dirtyIndexes.isEmpty()) {
 
904
 
 
905
      for (i = 0; i < dirtyIndexes.count(); i++) {
 
906
        // Note: dirtyIndexes[i] - i: the actual
 
907
        // position of the dirty item.
 
908
        m->items.removeAt(dirtyIndexes[i] - i);
 
909
      }
 
910
 
 
911
      dirtyIndexes.clear();
 
912
      reorder(sort(STAT_ALL));
 
913
 
 
914
      for (i = 0; i < m->items.count(); i++) {
 
915
        setSlide(i, m->items[i].image());
 
916
      }
 
917
 
 
918
      setSlideCount(i);
 
919
      setCurrentSlide(0);
 
920
 
 
921
      switch(status) {
 
922
        case STAT_LIKE:
 
923
          m->likeCount = 0;
 
924
          emit saveStatus(false);
 
925
          emit emblemStatus(STAT_UNKNOWN);
 
926
          break;
 
927
        case STAT_TRASH:
 
928
          m->clearCount = 0;
 
929
          emit clearStatus(false);
 
930
          emit emblemStatus(STAT_UNKNOWN);
 
931
          break;
 
932
        default:
 
933
          break;
 
934
      }
 
935
    }
 
936
  }
 
937
 
 
938
  QList<qint64> ImageFlow::sort(const int status) {
 
939
 
 
940
    int i;
 
941
    int count = m->items.count();
 
942
    QList<qint64> itemKeys;
 
943
 
 
944
    switch (status) {
 
945
      case STAT_TITLE:
 
946
        qStableSort(m->itemsTitles.begin(), m->itemsTitles.end(), qLess<QByteArray>());
 
947
        for (i = 0; i < m->itemsTitles.count(); i++) {
 
948
          itemKeys.append(m->itemsTitleKeyPairs.value(m->itemsTitles[i]));
 
949
        }
 
950
        break;
 
951
      default:
 
952
        for (i = 0; i < count; i++) {
 
953
          if (m->items[i].status() == status) {
 
954
            itemKeys.prepend(m->items[i].key());
 
955
          } else {
 
956
            itemKeys.append(m->items[i].key());
 
957
          }
 
958
        }
 
959
        break;
 
960
    }
 
961
 
 
962
    // this might be unnecessary here.
 
963
    return itemKeys;
 
964
  }
 
965
 
 
966
  bool ImageFlow::reorder(const QList<qint64>& itemsKey) {
 
967
 
 
968
        int items_count = m->items.size();
 
969
 
 
970
        if (itemsKey.size() != items_count) {
 
971
 
 
972
      return (false);
 
973
    }
 
974
 
 
975
        // Collect Items Key
 
976
        QList<qint64> currentItemsKey;
 
977
        for (int i = 0; i < items_count; i++) {
 
978
 
 
979
      currentItemsKey.append(m->items.at(i).key());
 
980
    }
 
981
 
 
982
    if (currentItemsKey == itemsKey) {
 
983
      // if identical, we don't sort.
 
984
      return (false);
 
985
    }
 
986
 
 
987
        // Swap Items
 
988
        for (int i=0; i < items_count; i++) {
 
989
 
 
990
      int index = currentItemsKey.indexOf(itemsKey.at(i));
 
991
      if (i == index) {
 
992
        continue;
 
993
      }
 
994
 
 
995
      QImage imgA = m->items[i].image();
 
996
      QImage imgB = m->items[index].image();
 
997
 
 
998
      setSlide(index, imgA);
 
999
      setSlide(i, imgB);
 
1000
 
 
1001
      m->items.swap(i, index);
 
1002
      currentItemsKey.swap(i, index);   
 
1003
        }
 
1004
 
 
1005
        update();
 
1006
    rehash();
 
1007
        return(true);
 
1008
  }
 
1009
 
 
1010
  void ImageFlow::rehash(void) {
 
1011
 
 
1012
    int count = m->items.count();
 
1013
 
 
1014
    if (count <= 0) {
 
1015
      return;
 
1016
    }
 
1017
 
 
1018
    int i;
 
1019
 
 
1020
    m->itemsTitles.clear();
 
1021
    m->itemsTitleKeyPairs.clear();
 
1022
 
 
1023
    qint64 k;
 
1024
    QByteArray b;
 
1025
 
 
1026
    for (i = 0; i < count; i++) {
 
1027
      k = m->items[i].key();
 
1028
      b = m->items[i].info().fileName().toLocal8Bit();
 
1029
      m->itemsTitles.append(b);
 
1030
      m->itemsTitleKeyPairs.insert(b, k);
 
1031
    }
 
1032
  }
 
1033
 
 
1034
  void ImageFlow::toggleStatus(const int status) {
 
1035
 
 
1036
    int index = currentSlide();
 
1037
    int count = m->items.count();
 
1038
 
 
1039
    if (index >= 0 && index < count) {
 
1040
 
 
1041
      setUpdatesEnabled(false);
 
1042
      if (m->items[index].status() != status) {
 
1043
 
 
1044
        switch (status) {
 
1045
          case STAT_LIKE:
 
1046
            m->likeCount++;
 
1047
            if (m->items[index].status() == STAT_TRASH) {
 
1048
              m->clearCount--;
 
1049
            }
 
1050
            emit saveStatus(true);
 
1051
            break;
 
1052
          case STAT_TRASH:
 
1053
            m->clearCount++;
 
1054
            if (m->items[index].status() == STAT_LIKE) {
 
1055
              m->likeCount--;
 
1056
            }
 
1057
            emit clearStatus(true);
 
1058
            break;
 
1059
          default:
 
1060
            break;
 
1061
        }
 
1062
 
 
1063
        m->items[index].setStatus(status);
 
1064
        emit statusMessage(tr("Tagged as ") + tr(fqloraTagStatus[status]));
 
1065
 
 
1066
      } else {
 
1067
 
 
1068
        switch (status) {
 
1069
          case STAT_LIKE:
 
1070
            m->likeCount--;
 
1071
            break;
 
1072
          case STAT_TRASH:
 
1073
            m->clearCount--;
 
1074
            break;
 
1075
          default:
 
1076
            break;
 
1077
        }
 
1078
 
 
1079
        m->items[index].setStatus(STAT_UNKNOWN);
 
1080
        emit statusMessage("Tag cleared");
 
1081
      }
 
1082
 
 
1083
      if (m->likeCount <= 0) {
 
1084
        emit saveStatus(false);
 
1085
      } else {
 
1086
        emit saveStatus(true);
 
1087
      }
 
1088
 
 
1089
      if (m->clearCount <= 0) {
 
1090
        emit clearStatus(false);
 
1091
      } else {
 
1092
        emit clearStatus(true);
 
1093
      }
 
1094
 
 
1095
      setUpdatesEnabled(true);
 
1096
    }
 
1097
  }
 
1098
 
 
1099
  void ImageFlow::setCurrentImage(const int index) {
 
1100
 
 
1101
    int count = size();
 
1102
 
 
1103
    if (count > 0 && index > 0 && index < count) {
 
1104
 
 
1105
      setCurrentSlide(index);
 
1106
    }
 
1107
  }
 
1108
 
 
1109
// events
 
1110
  void ImageFlow::paintEvent(QPaintEvent *event) {
 
1111
 
 
1112
        PictureFlow::paintEvent(event);
 
1113
 
 
1114
        if (slideCount() < 1) {
 
1115
      return;
 
1116
    }
 
1117
 
 
1118
        QPainter p(this);
 
1119
 
 
1120
        // White Pen for File Info
 
1121
        p.setPen(Qt::gray);
 
1122
 
 
1123
        int cw = width() / 2;
 
1124
        int wh = height();
 
1125
 
 
1126
        ImageFlowItem& item = m->items[currentSlide()];
 
1127
 
 
1128
        // Draw File Name if it's not empty
 
1129
    QString title = item.info().fileName();
 
1130
    if (!title.isEmpty()) {
 
1131
 
 
1132
      p.setFont(QFont(p.font().family(), p.font().pointSize() + 1, QFont::Bold));
 
1133
      p.drawText(cw - (QFontMetrics(p.font()).width(title) / 2), wh - 20, title);
 
1134
    }
 
1135
 
 
1136
    p.end();
 
1137
 
 
1138
    if (m->emblemStatus != item.status()) {
 
1139
      emit emblemStatus(item.status());
 
1140
    }
 
1141
 
 
1142
    m->emblemStatus = item.status();
 
1143
  }
 
1144
 
 
1145
  // ImageFlowItem
 
1146
  class ImageFlowItem::Private {
 
1147
  public:
 
1148
        QString filePath;
 
1149
    QFileInfo info;
 
1150
        QString comment;
 
1151
        QImage image;
 
1152
    int status;
 
1153
        qint64 key;
 
1154
 
 
1155
  };
 
1156
 
 
1157
  ImageFlowItem::~ImageFlowItem() {
 
1158
 
 
1159
        delete m;
 
1160
        m = NULL;
 
1161
  }
 
1162
 
 
1163
  // constructs an empty item
 
1164
  ImageFlowItem::ImageFlowItem(QObject *parent)
 
1165
        : QObject(parent), m(new ImageFlowItem::Private) {
 
1166
 
 
1167
        m->key = qHash(QString::number(m->image.cacheKey()) + m->info.fileName() + m->comment);
 
1168
  }
 
1169
 
 
1170
  ImageFlowItem::ImageFlowItem(const ImageFlowItem &item)
 
1171
    : QObject(item.parent()), m(new ImageFlowItem::Private) {
 
1172
 
 
1173
    operator=(item);
 
1174
  }
 
1175
 
 
1176
  // constructs an item with a given image
 
1177
  ImageFlowItem::ImageFlowItem(const QImage &image, QObject *parent)
 
1178
        : QObject(parent), m(new ImageFlowItem::Private) {
 
1179
 
 
1180
        m->image = image;
 
1181
        m->key = qHash(QString::number(m->image.cacheKey()) + m->info.fileName() + m->comment);
 
1182
  }
 
1183
 
 
1184
  // constructs an image with given image and title
 
1185
  ImageFlowItem::ImageFlowItem(const QImage &image, const QFileInfo &info, QObject *parent)
 
1186
        : QObject(parent), m(new ImageFlowItem::Private) {
 
1187
 
 
1188
        m->image = image;
 
1189
        m->info = info;
 
1190
        m->key = qHash(QString::number(m->image.cacheKey()) + m->info.fileName() + m->comment);
 
1191
  }
 
1192
 
 
1193
  // constructs an image with given image, title and comment
 
1194
  ImageFlowItem::ImageFlowItem(const QImage &image, const QFileInfo &info, const QString &comment, QObject *parent)
 
1195
        : QObject(parent), m(new ImageFlowItem::Private) {
 
1196
 
 
1197
        m->image = image;
 
1198
        m->info = info;
 
1199
        m->comment = comment;
 
1200
        m->key = qHash(QString::number(m->image.cacheKey()) + m->info.fileName() + m->comment);
 
1201
  }
 
1202
 
 
1203
  // constructs an image with given image, title, type, size, time and state
 
1204
  ImageFlowItem::ImageFlowItem(const QImage &image, const QFileInfo &info, const QString& comment, const int status, QObject *parent)
 
1205
    : QObject(parent), m(new ImageFlowItem::Private) {
 
1206
 
 
1207
    m->image = image;
 
1208
    m->info = info;
 
1209
    m->comment = comment;
 
1210
    m->status = status;
 
1211
 
 
1212
    m->key = qHash(QString::number(m->image.cacheKey()) + m->info.fileName() + m->comment);
 
1213
  }
 
1214
 
 
1215
  // funcs and slots
 
1216
  ImageFlowItem& ImageFlowItem::operator=(const ImageFlowItem &item) {
 
1217
 
 
1218
    m->info = item.m->info;
 
1219
    m->comment = item.m->comment;
 
1220
    m->key = item.m->key;
 
1221
 
 
1222
    if (item.m->filePath.isEmpty()) {
 
1223
      m->image = item.m->image;
 
1224
    } else {
 
1225
      setImage(item.m->filePath, m->key);
 
1226
    }
 
1227
 
 
1228
    return (*this);
 
1229
  }
 
1230
 
 
1231
  bool ImageFlowItem::operator==(const ImageFlowItem& item) const {
 
1232
 
 
1233
        return (item.m->key == m->key);
 
1234
  }
 
1235
 
 
1236
  bool ImageFlowItem::operator!=(const ImageFlowItem& item) const {
 
1237
 
 
1238
        return (item.m->key != m->key);
 
1239
  }
 
1240
 
 
1241
  const QString& ImageFlowItem::comment() const {
 
1242
 
 
1243
        return (m->comment);
 
1244
  }
 
1245
 
 
1246
  const QImage& ImageFlowItem::image() const {
 
1247
 
 
1248
        return (m->image);
 
1249
  }
 
1250
 
 
1251
  int ImageFlowItem::status(void) const {
 
1252
 
 
1253
    return (m->status);
 
1254
  }
 
1255
 
 
1256
  const QFileInfo& ImageFlowItem::info(void) const {
 
1257
 
 
1258
    return (m->info);
 
1259
  }
 
1260
 
 
1261
  qint64 ImageFlowItem::key() const {
 
1262
 
 
1263
        return (m->key);
 
1264
  }
 
1265
 
 
1266
  void ImageFlowItem::setInfo(const QFileInfo& info) {
 
1267
 
 
1268
        m->info = info;
 
1269
        m->key = qHash(QString::number(m->image.cacheKey()) + m->info.fileName() + m->comment);
 
1270
  }
 
1271
 
 
1272
  void ImageFlowItem::setComment(const QString &comment) {
 
1273
 
 
1274
        m->comment = comment;
 
1275
        m->key = qHash(QString::number(m->image.cacheKey()) + m->info.fileName() + m->comment);
 
1276
  }
 
1277
 
 
1278
  void ImageFlowItem::setImage(const QImage &image) {
 
1279
 
 
1280
        m->image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
 
1281
        m->key = qHash(QString::number(m->image.cacheKey()) + m->info.fileName() + m->comment);
 
1282
  }
 
1283
 
 
1284
  void ImageFlowItem::setImage(const QString &filePath, int size) {
 
1285
 
 
1286
        m->filePath = filePath;
 
1287
        m->key = size;
 
1288
 
 
1289
        QTimer::singleShot(1000, this, SLOT(loadImage()));
 
1290
  }
 
1291
 
 
1292
  void ImageFlowItem::setStatus(const int status) {
 
1293
 
 
1294
    m->status = status;
 
1295
  }
 
1296
 
 
1297
  void ImageFlowItem::loadImage() {
 
1298
 
 
1299
        int imageHeight = m->key;
 
1300
 
 
1301
        if (!m->image.load(m->filePath)) {
 
1302
          QFileIconProvider iconProvider;
 
1303
          m->image = iconProvider.icon(QFileInfo(m->filePath)).pixmap(imageHeight).toImage();
 
1304
        } else {
 
1305
          m->image = resizeImage(m->image, imageHeight);
 
1306
        }
 
1307
 
 
1308
        m->filePath.clear();
 
1309
        m->key = qHash(QString::number(m->image.cacheKey()) + m->info.fileName() + m->comment);
 
1310
  }
 
1311
 
 
1312
  QImage ImageFlowItem::resizeImage(const QImage &image, int size) {
 
1313
 
 
1314
    if (size == image.width() && size == image.height()) {
 
1315
      return image;
 
1316
    }
 
1317
 
 
1318
    double scaleWidth = size / (double)image.width();
 
1319
    double scaleHeight = size / (double)image.height();
 
1320
    double smaller = qMin(scaleWidth, scaleHeight);
 
1321
 
 
1322
    int w = (int) qRound(smaller * image.width());
 
1323
    int h = (int) qRound(smaller * image.height());
 
1324
 
 
1325
    return (image.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
 
1326
  }
 
1327
 
 
1328
  // original viewer
 
1329
  void FQTermImageOrigin::onChange(const QModelIndex & index) {
 
1330
 
 
1331
    if (!model_->isDir(index)) {
 
1332
 
 
1333
      if (!isHidden())
 
1334
        canvas_->hide();
 
1335
 
 
1336
      QString exifInfo = QString::fromStdString(exifExtractor_->extractExifInfo(model_->filePath(tree_->currentIndex()).toLocal8Bit().data()));
 
1337
      bool resized = false;
 
1338
 
 
1339
      if (exifInfo != "") {
 
1340
 
 
1341
        if (!isExifTableShown_) {
 
1342
          adjustLayout(true);
 
1343
          isExifTableShown_ = true;
 
1344
          resized = true;
 
1345
        }
 
1346
 
 
1347
        updateExifInfo();
 
1348
      } else {
 
1349
 
 
1350
        if (isExifTableShown_) {
 
1351
          adjustLayout(false);
 
1352
          isExifTableShown_ = false;
 
1353
          resized = true;
 
1354
        }
 
1355
      }
 
1356
 
 
1357
      QString path  = QDir::toNativeSeparators(model_->filePath(index));
 
1358
 
 
1359
      if (path.endsWith(QDir::separator()))
 
1360
        path.chop(1);
 
1361
 
 
1362
      canvas_->loadImage(path, !resized);
 
1363
 
 
1364
      //    canvas_->autoAdjust();
 
1365
      if (!isHidden())
 
1366
        canvas_->show();
 
1367
    }
 
1368
  }
 
1369
 
 
1370
  FQTermImageOrigin::~FQTermImageOrigin() {
 
1371
    delete menuBar_;
 
1372
    delete canvas_;
 
1373
    delete tree_;
 
1374
    delete model_;
 
1375
  }
 
1376
 
 
1377
  FQTermImageOrigin::FQTermImageOrigin(FQTermConfig * config, QWidget *parent,
 
1378
    Qt::WindowFlags wflag) :
 
1379
    FQTermImage(parent, wflag),
 
1380
    config_(config),
 
1381
    isExifTableShown_(false) {
 
1382
 
 
1383
    setWindowTitle(tr("FQTerm Image Viewer"));
 
1384
    ItemDelegate* itemDelegate = new ItemDelegate;
 
1385
    exifExtractor_ = new ExifExtractor;
 
1386
    exifTable_ = new ExifTable(this);
 
1387
    exifTable_->setTextFormat(Qt::RichText);
 
1388
    exifTable_->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
 
1389
    canvas_ = new FQTermCanvas(config, this, 0);
 
1390
    canvas_->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
 
1391
    model_ = new ImageViewerDirModel;
 
1392
    tree_ = new QTreeView;
 
1393
    tree_->setModel(model_);
 
1394
    tree_->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
 
1395
    adjustItemSize();
 
1396
    tree_->setItemDelegate(itemDelegate);
 
1397
    tree_->setColumnWidth(0, 150);
 
1398
    //  tree_->hideColumn(0);
 
1399
 
 
1400
    tree_->setUniformRowHeights(true);
 
1401
    tree_->setWordWrap(true);
 
1402
 
 
1403
    comboBox_ = new QComboBox(this);
 
1404
    comboBox_->addItem(tr("Sort by name"), QDir::Name);
 
1405
    comboBox_->addItem(tr("Sort by time"), QDir::Time);
 
1406
    comboBox_->addItem(tr("Sort by size"), QDir::Size);
 
1407
    comboBox_->addItem(tr("Sort by type"), QDir::Type);
 
1408
 
 
1409
    FQ_VERIFY(connect(comboBox_, SIGNAL(currentIndexChanged(int)), this, SLOT(sortFileList(int))));
 
1410
 
 
1411
    comboBox_->setCurrentIndex(1);
 
1412
 
 
1413
    layout_ = new QGridLayout;
 
1414
    menuBar_ = new QMenuBar(this);
 
1415
    menuBar_->addMenu(canvas_->menu());
 
1416
    menuBar_->resize(1,1);
 
1417
 
 
1418
    canvas_->ToolBar()->addAction(
 
1419
      QIcon(getPath(RESOURCE) + ICON_SOURCE + "prev.png"), tr("Previous"),
 
1420
      this, SLOT(previous()));
 
1421
    canvas_->ToolBar()->addAction(
 
1422
      QIcon(getPath(RESOURCE) + ICON_SOURCE + "next.png"), tr("Next"),
 
1423
      this, SLOT(next()));
 
1424
 
 
1425
    layout_->addWidget(tree_, 0, 0, 12, 1);
 
1426
    layout_->addWidget(comboBox_, 12, 0, 1, 1);
 
1427
    layout_->addWidget(canvas_, 0, 1, 12, 10);
 
1428
    //  layout_->addWidget(exifTable_, 10, 1, 2, 10);
 
1429
    layout_->addWidget(canvas_->ToolBar(), 12, 1, 1, 10, Qt::AlignHCenter);
 
1430
    layout_->setColumnMinimumWidth(0, tree_->columnWidth(0) + 150);
 
1431
    setLayout(layout_);
 
1432
 
 
1433
    /*
 
1434
      FQ_VERIFY(connect(tree_, SIGNAL(clicked(const QModelIndex &)),
 
1435
      this, SLOT(onChange(const QModelIndex &))));
 
1436
    */
 
1437
    FQ_VERIFY(connect(tree_, SIGNAL(activated(const QModelIndex &)),
 
1438
        this, SLOT(onChange(const QModelIndex &))));
 
1439
    FQ_VERIFY(connect(tree_->selectionModel(),
 
1440
        SIGNAL(selectionChanged(const QItemSelection&,
 
1441
            const QItemSelection&)),
 
1442
        this, SLOT(selectionChanged(const QItemSelection&,
 
1443
            const QItemSelection&))));
 
1444
    FQ_VERIFY(connect(exifTable_, SIGNAL(showExifDetails()),
 
1445
        this, SLOT(showFullExifInfo())));
 
1446
  }
 
1447
 
 
1448
  void FQTermImageOrigin::scrollTo(const QString& filename) {
 
1449
 
 
1450
    QString path = QFileInfo(filename).absolutePath();
 
1451
    model_->refresh();
 
1452
    tree_->setRootIndex(model_->index(path));
 
1453
    canvas_->loadImage(filename);
 
1454
 
 
1455
    if (canvas_->isHidden() && !isHidden()) {
 
1456
      canvas_->show();
 
1457
    }
 
1458
 
 
1459
    const QModelIndex& index = model_->index(filename);
 
1460
    tree_->scrollTo(index);
 
1461
    tree_->setCurrentIndex(index);
 
1462
  }
 
1463
 
 
1464
  void FQTermImageOrigin::updateImage(const QString& filename) {
 
1465
 
 
1466
    static int i = 0;
 
1467
    if (++i == 10) {
 
1468
      model_->refresh(model_->index(filename));
 
1469
      i = 0;
 
1470
    }
 
1471
    canvas_->updateImage(filename);
 
1472
  }
 
1473
 
 
1474
  void FQTermImageOrigin::previous() {
 
1475
 
 
1476
    const QModelIndex& index = tree_->indexAbove(tree_->currentIndex());
 
1477
    if (index.isValid()) {
 
1478
      tree_->setCurrentIndex(index);
 
1479
      canvas_->loadImage(QDir::toNativeSeparators(model_->filePath(index)));
 
1480
    }
 
1481
  }
 
1482
 
 
1483
  void FQTermImageOrigin::next() {
 
1484
 
 
1485
    const QModelIndex& index = tree_->indexBelow(tree_->currentIndex());
 
1486
    if (index.isValid()) {
 
1487
      tree_->setCurrentIndex(index);
 
1488
      canvas_->loadImage(QDir::toNativeSeparators(model_->filePath(index)));
 
1489
    }
 
1490
  }
 
1491
 
 
1492
  void FQTermImageOrigin::adjustItemSize() {
 
1493
 
 
1494
    QFontMetrics fm(font());
 
1495
    ItemDelegate::size_.setWidth(qMax(128, fm.width("WWWWWWWW.WWW")));
 
1496
    ItemDelegate::size_.setHeight(fm.height() + 150);
 
1497
  }
 
1498
 
 
1499
  void FQTermImageOrigin::selectionChanged(const QItemSelection & selected,
 
1500
    const QItemSelection & deselected) {
 
1501
 
 
1502
    onChange(tree_->selectionModel()->currentIndex());
 
1503
  }
 
1504
 
 
1505
  void FQTermImageOrigin::sortFileList(int index) {
 
1506
 
 
1507
    model_->setSorting(QDir::SortFlag(comboBox_->itemData(index).toInt()));
 
1508
    QString poolPath = config_->getItemValue("preference", "pool");
 
1509
 
 
1510
    if (poolPath.isEmpty()) {
 
1511
      poolPath = getPath(USER_CONFIG) + "pool/";
 
1512
    }
 
1513
 
 
1514
    tree_->setRootIndex(model_->index(poolPath));
 
1515
  }
 
1516
 
 
1517
  void FQTermImageOrigin::showFullExifInfo() {
 
1518
 
 
1519
    QString exifInfo = QString::fromStdString(exifExtractor_->extractExifInfo(model_->filePath(tree_->currentIndex()).toLocal8Bit().data()));
 
1520
    QString comment;
 
1521
 
 
1522
    if ((*exifExtractor_)["UserComment"].length() > 8) {
 
1523
 
 
1524
      QString commentEncoding = QString::fromStdString((*exifExtractor_)["UserComment"].substr(0, 8));
 
1525
 
 
1526
      if (commentEncoding.startsWith("UNICODE")) {
 
1527
        //UTF-16
 
1528
        QTextCodec* c = QTextCodec::codecForName("UTF-16");
 
1529
        comment = c->toUnicode((*exifExtractor_)["UserComment"].substr(8).c_str());
 
1530
      } else if (commentEncoding.startsWith("JIS")) {
 
1531
        //JIS X 0208
 
1532
        QTextCodec* c = QTextCodec::codecForName("JIS X 0208");
 
1533
        comment = c->toUnicode((*exifExtractor_)["UserComment"].substr(8).c_str());
 
1534
      } else {
 
1535
        comment = QString::fromStdString((*exifExtractor_)["UserComment"].substr(8));
 
1536
      }
 
1537
    }
 
1538
 
 
1539
 
 
1540
    QTextEdit* info = new QTextEdit;
 
1541
    info->setText(exifInfo + tr("Comment : ") + comment + "\n");
 
1542
    info->setWindowFlags(Qt::Dialog);
 
1543
    info->setAttribute(Qt::WA_DeleteOnClose);
 
1544
    info->setAttribute(Qt::WA_ShowModal);
 
1545
    //  info->setLineWrapMode(QTextEdit::NoWrap);
 
1546
    info->setReadOnly(true);
 
1547
    QFontMetrics fm(font());
 
1548
    info->resize(fm.width("Orientation : 1st row - 1st col : top - left side    "), fm.height() * 20);
 
1549
    info->show();
 
1550
  }
 
1551
 
 
1552
  void FQTermImageOrigin::adjustLayout(bool withExifTable) {
 
1553
 
 
1554
    if (withExifTable) {
 
1555
 
 
1556
      layout_->addWidget(canvas_, 0, 1, 11, 10);
 
1557
      layout_->addWidget(exifTable_, 11, 1, 1, 10, Qt::AlignHCenter);
 
1558
      if (!isHidden() && exifTable_->isHidden()) {
 
1559
        exifTable_->show();
 
1560
      }
 
1561
 
 
1562
      layout_->addWidget(canvas_->ToolBar(), 12, 1, 1, 10, Qt::AlignHCenter);
 
1563
    } else {
 
1564
      layout_->addWidget(canvas_, 0, 1, 12, 10);
 
1565
      layout_->removeWidget(exifTable_);
 
1566
      exifTable_->hide();
 
1567
      layout_->addWidget(canvas_->ToolBar(), 12, 1, 1, 10, Qt::AlignHCenter);
 
1568
    }
 
1569
  }
 
1570
 
 
1571
  void FQTermImageOrigin::updateExifInfo() {
 
1572
 
 
1573
    exifTable_->clear();
 
1574
 
 
1575
    QString exifInfoToShow = "<table border=\"1\"><tr><td>"
 
1576
      + tr("Model") + " : " + QString::fromStdString((*exifExtractor_)["Model"]) + "</td><td>"
 
1577
      + QString::fromStdString((*exifExtractor_)["DateTime"]) + "</td><td>"
 
1578
      + QString::fromStdString((*exifExtractor_)["Flash"]) + "</td>"
 
1579
      + "</tr><tr><td>"
 
1580
      + tr("ExposureTime") + " : " + QString::fromStdString((*exifExtractor_)["ExposureTime"]) + "</td><td>"
 
1581
      + tr("FNumber") + " : " + QString::fromStdString((*exifExtractor_)["FNumber"]) + "</td><td>"
 
1582
      + tr("ISO") + " : " + QString::fromStdString((*exifExtractor_)["ISOSpeedRatings"]) + "</td>"
 
1583
      + "</tr><tr><td>"
 
1584
      + tr("FocalLength") + " : " + QString::fromStdString((*exifExtractor_)["FocalLength"]) + "</td><td>"
 
1585
      + tr("MeteringMode") + " : " + QString::fromStdString((*exifExtractor_)["MeteringMode"]) + "</td><td>" 
 
1586
      + tr("ExposureBias") + " : " + QString::fromStdString((*exifExtractor_)["ExposureBiasValue"]) + "</td></tr></tabel>";
 
1587
 
 
1588
    exifTable_->setText(exifInfoToShow);
 
1589
    if (!isHidden() && exifTable_->isHidden()) {
 
1590
      exifTable_->show();
 
1591
    }
 
1592
  }
 
1593
 
 
1594
  void FQTermImageOrigin::closeEvent( QCloseEvent *clse ) {
 
1595
 
 
1596
    hide();
 
1597
    clse->ignore();
 
1598
    return ;
 
1599
  }
 
1600
 
 
1601
  ImageViewerDirModel::ImageViewerDirModel(QObject *parent /*= 0*/)
 
1602
  : QDirModel(parent) {
 
1603
 
 
1604
    insertColumn(1);
 
1605
    QStringList nameFilterList;
 
1606
    nameFilterList << "*.jpg" <<  "*.jpeg" << "*.png"
 
1607
                   << "*.mng" << "*.bmp" << "*.gif";
 
1608
    setNameFilters(nameFilterList);
 
1609
    setFilter(QDir::Files);
 
1610
  }
 
1611
 
 
1612
  int ImageViewerDirModel::columnCount(const QModelIndex &/*parent*/) const {
 
1613
 
 
1614
    return 1;
 
1615
  }
 
1616
 
 
1617
  QVariant ImageViewerDirModel::headerData(
 
1618
    int section, Qt::Orientation orientation, int role) const {
 
1619
 
 
1620
    if (role == Qt::DisplayRole) {
 
1621
      if (section == 0) {
 
1622
        return QString(tr("Image Preview"));
 
1623
      }
 
1624
    }
 
1625
    return QDirModel::headerData(section, orientation, role);
 
1626
  }
 
1627
 
 
1628
  QVariant ImageViewerDirModel::data(const QModelIndex &index, int role) const {
 
1629
 
 
1630
    if (role == Qt::DecorationRole) {
 
1631
      if (isDir(index)) {
 
1632
        return QVariant();
 
1633
      }
 
1634
 
 
1635
      QString path  = QDir::toNativeSeparators(filePath(index));
 
1636
      if (path.endsWith(QDir::separator()))
 
1637
        path.chop(1);
 
1638
 
 
1639
      QPixmap pixmap(path);
 
1640
      if (pixmap.height() > 128 || pixmap.width() > 128) {
 
1641
        return pixmap.scaled(128, 128,
 
1642
          Qt::KeepAspectRatio, Qt::SmoothTransformation);
 
1643
      }
 
1644
 
 
1645
      return pixmap;
 
1646
    } else if (role == Qt::DisplayRole) {
 
1647
      return fileName(index);
 
1648
    }/*
 
1649
       else if (role == Qt::TextAlignmentRole) {
 
1650
       return Qt::Qt::AlignBottom;
 
1651
       }*/
 
1652
    return QVariant();
 
1653
  }
 
1654
 
 
1655
  QSize ItemDelegate::size_;
 
1656
 
 
1657
  void ItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem & option,
 
1658
    const QModelIndex & index ) const {
 
1659
    QStyleOptionViewItemV3 opt = setOptions(index, option);
 
1660
 
 
1661
    // prepare
 
1662
    painter->save();
 
1663
 
 
1664
    // get the data and the rectangles
 
1665
    const QPixmap& pixmap
 
1666
      = qvariant_cast<QPixmap>(index.data(Qt::DecorationRole));
 
1667
    QRect decorationRect = QRect(opt.rect.topLeft(), pixmap.size());
 
1668
    decorationRect.moveTo(decorationRect.left(), decorationRect.top() + 10);
 
1669
    const QString& text = index.data(Qt::DisplayRole).toString();
 
1670
    QFontMetrics fm(painter->font());
 
1671
    QRect displayRect = QRect(decorationRect.bottomLeft(),
 
1672
      QSize(fm.width(text),fm.height()));
 
1673
 
 
1674
    QRect checkRect;
 
1675
    Qt::CheckState checkState = Qt::Unchecked;
 
1676
    QVariant value = index.data(Qt::CheckStateRole);
 
1677
 
 
1678
    if (value.isValid()) {
 
1679
      checkState = static_cast<Qt::CheckState>(value.toInt());
 
1680
      checkRect = check(opt, opt.rect, value);
 
1681
    }
 
1682
 
 
1683
    // do the layout
 
1684
 
 
1685
    //  doLayout(opt, &checkRect, &decorationRect, &displayRect, false);
 
1686
 
 
1687
    // draw the item
 
1688
 
 
1689
    drawBackground(painter, opt, index);
 
1690
    painter->drawPixmap(decorationRect, pixmap);
 
1691
    painter->drawText(displayRect, text);
 
1692
 
 
1693
    drawFocus(painter, opt, displayRect);
 
1694
 
 
1695
    // done
 
1696
    painter->restore();
 
1697
  }
 
1698
 
 
1699
  void ExifTable::mouseReleaseEvent(QMouseEvent *pEvent) {
 
1700
 
 
1701
    if (pEvent->button() == Qt::LeftButton) {
 
1702
      emit(showExifDetails());
 
1703
    }
 
1704
  }
 
1705
 
 
1706
  ExifTable::ExifTable(QWidget *parent) : QLabel(parent) {
 
1707
 
 
1708
  }
 
1709
 
 
1710
  FQTermImage::FQTermImage( QWidget * parent, Qt::WindowFlags f ) : QWidget(parent, f) {
 
1711
  }
 
1712
 
 
1713
}  // namespace FQTerm
 
1714
 
 
1715
#include "imageviewer.moc"