[meego-commits] 7307: Changes to Trunk:Handset/meego-handset-photos

longbu no_reply at build.meego.com
Fri Sep 3 10:23:12 UTC 2010


Hi,
I have made the following changes to meego-handset-photos in project Trunk:Handset. Please review and accept ASAP.

Thank You,
longbu

[This message was auto-generated]

---

Request #7307:

  submit:   devel:ux:handset/meego-handset-photos(r13) -> Trunk:Handset/meego-handset-photos


Message:
    0.0.22

State:   new          2010-09-03T03:23:11 longbu
Comment: None



changes files:
--------------
--- meego-handset-photos.changes
+++ meego-handset-photos.changes
@@ -0,0 +1,5 @@
+* Fri Sep 3 2010 Long Bu<long.bu at intel.com> -0.0.22
+ - Fixes: BMC6197, BMC6177
+ - lots of optimization on album related functions
+ - Add album automatically split rules
+

old:
----
  meego-handset-photos-0.0.21.tar.bz2

new:
----
  meego-handset-photos-0.0.22.tar.bz2

spec files:
-----------
--- meego-handset-photos.spec
+++ meego-handset-photos.spec
@@ -7,7 +7,7 @@
 
 Name:       meego-handset-photos
 Summary:    A Photo Viewer
-Version:    0.0.21
+Version:    0.0.22
 Release:    1
 Group:      System/Libraries
 License:    Apache License, Version 2.0

other changes:
--------------

++++++ meego-handset-photos-0.0.21.tar.bz2 -> meego-handset-photos-0.0.22.tar.bz2
--- liballphoto
+++ liballphoto
+(directory)
--- liballphoto/abstracttasklet.cpp
+++ liballphoto/abstracttasklet.cpp
+/*
+ * Meego-handset-photos is a photo viewer.
+ * Copyright (C) 2010, Intel Corporation.
+ *
+ * This program is licensed under the terms and conditions of the
+ * Apache License, version 2.0.  The full text of the Apache License is at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ */
+
+
+#include <QTimer>
+#include "abstracttasklet.h"
+
+AbstractTasklet::AbstractTasklet(QObject *parent) :
+    QObject(parent)
+{
+    Q_UNUSED(parent);
+}
+
+AbstractTasklet::~AbstractTasklet()
+{
+    jobs.clear();
+}
+
+
+void AbstractTasklet::stopJobQueue()
+{
+    jobs.clear();
+}
+
+void AbstractTasklet::processJobQueue()
+{
+    if (jobs.isEmpty())
+        return;
+
+    Job job = jobs.takeFirst();
+//    job.dump();
+    processSingleJob(job);
+
+/*    if(jobs.count() > 0)
+        QTimer::singleShot(0, this, SLOT(processJobQueue())); */
+}
+
+void Job::dump()
+{
+    qDebug() << "entering dump";
+    MPListItem *entry = static_cast<MPListItem *>(userData.value<void *>());
+    qDebug() << "entry is " << entry;
+    qDebug() << "xxxx: " << entry->thumbnailURI;
+    qDebug() << "Job at row " << row.row() << ":" << entry->thumbnailURI;
+    qDebug() << "leaving dump";
+}
+
--- liballphoto/abstracttasklet.h
+++ liballphoto/abstracttasklet.h
+/*
+ * Meego-handset-photos is a photo viewer.
+ * Copyright (C) 2010, Intel Corporation.
+ *
+ * This program is licensed under the terms and conditions of the
+ * Apache License, version 2.0.  The full text of the Apache License is at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ */
+
+
+#ifndef ABSTRACT_TASKLET_H
+#define ABSTRACT_TASKLET_H
+
+#include <QObject>
+#include <QModelIndex>
+
+#include <QDebug>
+
+#include "mplistmodel.h"
+
+struct MPListItem;
+struct Job {
+    QModelIndex row;
+    QVariant userData;
+
+    void dump();
+};
+
+class AbstractTasklet : public QObject
+{
+    Q_OBJECT
+public:
+    explicit AbstractTasklet(QObject *parent = 0);
+    virtual ~AbstractTasklet();
+
+    virtual void processSingleJob(Job& j) = 0;
+
+    void addJob(Job& j) {jobs << j;}
+signals:
+
+public slots:
+    void stopJobQueue();
+    void processJobQueue();
+
+private:
+    QList<Job> jobs;
+};
+
+#endif // TASKLET_H
--- liballphoto/dynamicmlist.cpp
+++ liballphoto/dynamicmlist.cpp
+/*
+ * Meego-handset-photos is a photo viewer.
+ * Copyright (C) 2010, Intel Corporation.
+ *
+ * This program is licensed under the terms and conditions of the
+ * Apache License, version 2.0.  The full text of the Apache License is at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ */
+
+
+#include "dynamicmlist.h"
+#include "mcontentitemcreator.h"
+
+
+DynamicMList::DynamicMList(MWidget *parent, MContentItem::ContentItemStyle itemStyle, int id) :
+    MList(parent), tasklet(NULL)
+{
+    MContentItemCreator *thumbnailItemCreator = new MContentItemCreator(itemStyle, id);
+    setCellCreator(thumbnailItemCreator);
+}
+
+DynamicMList::~DynamicMList()
+{
+   if (tasklet)
+       disconnect(this, SIGNAL(panningStarted()), tasklet, SLOT(stopJobQueue()));
+
+   if (tasklet)
+        delete tasklet;
+
+}
+
+void DynamicMList::setTasklet (AbstractTasklet *newTasklet)
+{
+    if (!newTasklet)
+        return;
+
+    if (tasklet) {
+        disconnect(this, SIGNAL(panningStarted()), tasklet, SLOT(stopJobQueue()));
+        delete tasklet;
+    }
+
+    tasklet = newTasklet;
+    connect(this, SIGNAL(panningStarted()), tasklet, SLOT(stopJobQueue()));
+}
--- liballphoto/dynamicmlist.h
+++ liballphoto/dynamicmlist.h
+/*
+ * Meego-handset-photos is a photo viewer.
+ * Copyright (C) 2010, Intel Corporation.
+ *
+ * This program is licensed under the terms and conditions of the
+ * Apache License, version 2.0.  The full text of the Apache License is at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ */
+
+
+#ifndef DYNAMICMLIST_H
+#define DYNAMICMLIST_H
+
+#include <QObject>
+#include <MAbstractCellCreator>
+#include <QAbstractItemModel>
+#include <MList>
+#include <MContentItem>
+
+#include "abstracttasklet.h"
+
+class AbstractTasklet;
+
+class DynamicMList : public MList
+{
+Q_OBJECT
+public:
+    explicit DynamicMList(MWidget *parent = 0, MContentItem::ContentItemStyle style = MContentItem::SingleIcon, int id = 0);
+    virtual ~DynamicMList();
+
+    void setCellCreator(MCellCreator * itemCreator)
+    {
+        creator = itemCreator;
+        MList::setCellCreator(itemCreator);
+    }
+
+    MCellCreator *cellCreator() const {return creator;}
+    void setTasklet (AbstractTasklet *newTasklet);
+    AbstractTasklet *getTasklet() const {return tasklet;}
+public slots:
+    virtual void doTasklet() = 0;
+
+private:
+    MCellCreator *creator;
+    AbstractTasklet *tasklet;
+};
+
+#endif // DYNAMICMLIST_H
--- liballphoto/dynamicphotolist.cpp
+++ liballphoto/dynamicphotolist.cpp
+/*
+ * Meego-handset-photos is a photo viewer.
+ * Copyright (C) 2010, Intel Corporation.
+ *
+ * This program is licensed under the terms and conditions of the
+ * Apache License, version 2.0.  The full text of the Apache License is at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ */
+
+
+#include <QDebug>
+#include <QTimer>
+#include <QPixmapCache>
+#include <MWidgetView>
+#include "dynamicphotolist.h"
+#include "dynamicmlist.h"
+#include "mcontentitemcreator.h"
+#include "mcontentitemex.h"
+
+
+
+DynamicPhotoList::DynamicPhotoList(MWidget *parent, M::Orientation orientation) : DynamicMList(parent)
+{
+    connect(this, SIGNAL(panningStopped()), this, SLOT(doTasklet()));
+    //connect(AppWindow::instance(), SIGNAL(orientationChanged(M::Orientation)), this, SLOT(onOrientationChanged(M::Orientation)));
+    photosListModel = new MPListModel(this);
+    photosTasklet = new PhotosTasklet(this);
+    setItemModel(photosListModel);
+    setTasklet(photosTasklet);
+    MContentItemCreator *photothumbnailCreator = dynamic_cast<MContentItemCreator *>(cellCreator());
+    onOrientationChanged(orientation);
+    setObjectName("thumbnailList");
+    enableItemClick();
+    QPixmapCache::setCacheLimit (4096);
+
+    QTimer::singleShot(1500, this, SLOT(doTasklet()));
+}
+
+DynamicPhotoList::~DynamicPhotoList()
+{
+   //The base class's destructor takes care of the model and the tasklet
+    disconnect(this, SIGNAL(panningStopped()), this, SLOT(doTasklet()));
+}
+
+void DynamicPhotoList::enableItemClick()
+{
+    connect(this, SIGNAL(itemClicked(const QModelIndex&)), this, SLOT(onClick(const QModelIndex&)));
+}
+
+void DynamicPhotoList::disableItemClick()
+{
+    disconnect(this, SIGNAL(itemClicked(const QModelIndex&)), this, SLOT(onClick(const QModelIndex&)));
+}
+
+void DynamicPhotoList::onOrientationChanged(M::Orientation orientation)
+{
+    MContentItemCreator *photothumbnailCreator = dynamic_cast<MContentItemCreator *>(cellCreator());
+    if (orientation == M::Portrait)
+        photothumbnailCreator->setCellObjectName("photoThumbnail_Portrait");
+    else
+        photothumbnailCreator->setCellObjectName("photoThumbnail_Landscape");
+
+    setColumns(orientation == M::Portrait ? 5 : 8);
+}
+
+void DynamicPhotoList::onClick(const QModelIndex &index)
+{
+    QVariant data = index.data(Qt::DisplayRole);
+    MPListItem *item = static_cast<MPListItem *>(data.value<void *>());
+
+    QString thumbnailPath = item->thumbnailURI;
+    if (thumbnailPath.isEmpty())
+        thumbnailPath = PhotosTasklet::getThumbnailPath(item->photoURI);
+    emit clicked(item->photoURI, thumbnailPath);
+}
+
+const int DynamicPhotoList::numItemsBeforeFirstVisibleOne = 4*8; 
+const int DynamicPhotoList::numItemsAfterLastVisibleOne = 4*8;
+
+#define Q_JOB()    do { \
+    QModelIndex index(firstVisibleIndex.sibling(i, 0)); \
+        if (!index.isValid())\
+            continue;\
+        QVariant data = index.data(Qt::DisplayRole);\
+        MPListItem *entry = static_cast<MPListItem *>(data.value<void *>());\
+        if (entry == NULL || entry->isLoaded) \
+            continue; \
+ \
+\
+        Job job; \
+        job.userData = data; \
+        job.row = index; \
+        getTasklet()->addJob(job); \
+}  while (0) 
+
+void DynamicPhotoList::doTasklet()
+{
+    QModelIndex firstVisibleIndex = firstVisibleItem();
+    QModelIndex lastVisibleIndex = lastVisibleItem();
+
+    int firstVFlatRow = firstVisibleIndex.row();
+    int lastVFlatRow = lastVisibleIndex.row();
+    if (lastVFlatRow - firstVFlatRow < 23) {
+        //when the doTasklet gets called in the timeout handler,
+        //the lastVisbieItem() has not been calculated yet by MListFastView
+        lastVFlatRow = firstVFlatRow + 23;  //FixMe: Arbitrary number.
+    }
+    MPListModel *model = dynamic_cast<MPListModel *>(itemModel());
+
+    // remove rows before firstVFlatRow - numItemsBeforeFirstVisibleOne
+    int firstRow = firstVFlatRow - numItemsBeforeFirstVisibleOne;
+    if (firstRow < 0)
+        firstRow = 0;
+
+    int lastRow = lastVFlatRow + numItemsAfterLastVisibleOne;
+    if (lastRow >= model->rowCount())
+        lastRow = model->rowCount() - 1;
+ 
+
+   //Q tasklets
+   for (int i = firstVFlatRow; i <= lastRow; i++) {
+       QModelIndex index(firstVisibleIndex.sibling(i, 0));
+       if (!index.isValid())
+           continue;
+       QVariant data = index.data(Qt::DisplayRole);
+       MPListItem *entry = static_cast<MPListItem *>(data.value<void *>());
+       if (entry == NULL || entry->isLoaded) 
+           continue; 
+       model->setSpinner(index, true);
+       break;
+   }
+    for (int i = firstVFlatRow; i <= lastVFlatRow; i++)
+        Q_JOB();
+    for (int i = lastVFlatRow + 1; i  <= lastRow; i++)
+        Q_JOB();
+    for (int i = firstRow; i < firstVFlatRow; i++)
+        Q_JOB();
+
+    for (int i = 0; i < firstRow; i++) {
+        if (i >= model->rowCount())
+            break;
+
+        //I guess when assign a new content to pixmap, it should release the orignal buffer
+        // and share the new content
+        model->setDefaultThumbnail(i);
+    }
+
+   
+    // remove rows after lastVFlatRow + numItemsAfterLastvisibleOne
+    for (int i = lastRow + 1; i < model->rowCount(); i++)
+        model->setDefaultThumbnail(i);
+
+    int i = lastRow + 1;
+    if (i < model->rowCount()) {
+       QModelIndex index(firstVisibleIndex.sibling(i, 0));
+       if (index.isValid()) {
+           QVariant data = index.data(Qt::DisplayRole);
+           MPListItem *entry = static_cast<MPListItem *>(data.value<void *>());
+           if (entry && !entry->isLoaded) 
+            model->setSpinner(index, true);
+       }
+    }
+
+    getTasklet()->processJobQueue();
+}
+
+
+void genCSSFile(const char *filename, int edge) {
+const char *l1 = "@const THUMBNAIL_CELL_SIZE:";
+const char *l2 = "#photoThumbnail2 { \n \
+  minimum-size: $THUMBNAIL_CELL_SIZE $THUMBNAIL_CELL_SIZE; \n\
+  preferred-size: $THUMBNAIL_CELL_SIZE $THUMBNAIL_CELL_SIZE; \n\
+  maximum-size: $THUMBNAIL_CELL_SIZE $THUMBNAIL_CELL_SIZE; \n \
+  image-object-name: \"PhotoThumbnailImage2\"; \n\
+}\n \
+#photoThumbnail2:pressed { \n\
+  image-object-name: \"PhotoThumbnailImage2\"; \n\
+} \n\
+#PhotoThumbnailImage2 { \n\
+  minimum-size: $THUMBNAIL_CELL_SIZE $THUMBNAIL_CELL_SIZE; \n\
+  preferred-size: $THUMBNAIL_CELL_SIZE $THUMBNAIL_CELL_SIZE; \n\
+  maximum-size: $THUMBNAIL_CELL_SIZE $THUMBNAIL_CELL_SIZE;\n\
+}";
+
+    QFile f(filename);
+    if (f.open(QFile::WriteOnly | QFile::Truncate)) {
+        QTextStream out(&f);
+        out << l1 << " " << edge << "px;" << "\n";
+        out << l2 << "\n";
+    }
+    f.close();
+}
+
--- liballphoto/dynamicphotolist.h
+++ liballphoto/dynamicphotolist.h
+/*
+ * Meego-handset-photos is a photo viewer.
+ * Copyright (C) 2010, Intel Corporation.
+ *
+ * This program is licensed under the terms and conditions of the
+ * Apache License, version 2.0.  The full text of the Apache License is at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ */
+
+
+#ifndef DYNAMICPHOTOLIST_H
+#define DYNAMICPHOTOLIST_H
+
+#include <QObject>
+#include "dynamicmlist.h"
+#include "mplistmodel.h"
+#include "photostasklet.h"
+
+class PhotosTasklet;
+class DynamicPhotoList : public DynamicMList
+{
+    Q_OBJECT
+public:
+    DynamicPhotoList(MWidget *parent = 0, M::Orientation orientation = M::Landscape);
+
+    virtual ~DynamicPhotoList();
+
+    static const int numItemsBeforeFirstVisibleOne;
+    static const int numItemsAfterLastVisibleOne;
+
+    void setAlbumUrn(QString albumUrn);
+    void enableItemClick();
+    void disableItemClick();
+
+public slots:
+    virtual void doTasklet();
+    void onClick(const QModelIndex &index);
+    void onOrientationChanged(M::Orientation orientation);
+
+Q_SIGNALS:
+    void clicked(const QString &photoURI, const QString &thumbnailPath);
+
+private:
+    MPListModel *photosListModel;
+    PhotosTasklet *photosTasklet;
+};
+
+#endif // DYNAMICPHOTOLIST_H
--- liballphoto/liballphotos.pro
+++ liballphoto/liballphotos.pro
+include (../common.pri)
+TARGET = allphotos
+TEMPLATE = lib
+target.path = /usr/lib
+
+MOC_DIR = .moc
+OBJECTS_DIR = .obj
+
+QT += opengl network dbus
+
+QMAKE_CXXFLAGS += -Wno-reorder
+
+CONFIG += link_pkgconfig \
+    qt \
+    meegotouch \
+    qdbus 
+
+LIBS += -lqttracker
+
+SOURCES += *.cpp
+
+HEADERS += *.h
+
+headers.files = $$HEADERS
+headers.path = /usr/include
+
+INSTALLS += target headers
--- liballphoto/mcontentitemcreator.cpp
+++ liballphoto/mcontentitemcreator.cpp
+/*
+ * Meego-handset-photos is a photo viewer.
+ * Copyright (C) 2010, Intel Corporation.
+ *
+ * This program is licensed under the terms and conditions of the
+ * Apache License, version 2.0.  The full text of the Apache License is at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ */
+
+
+#include <QDebug>
+#include <MLabel>
+#include "mcontentitemcreator.h"
+#include "mcontentitemex.h"
+
+void MContentItemCreator::updateCell(const QModelIndex &index, MWidget *cell) const
+{
+    MContentItemEx *contentItem = dynamic_cast<MContentItemEx *>(cell);
+    if (contentItem == NULL) // TODO This is shouldn't happen, list must know what it doing, but with multiple columns it happens sometimes
+        return;
+
+    QVariant data = index.data(Qt::DisplayRole);
+    MPListItem *item = static_cast<MPListItem *>(data.value<void *>());
+    //contentItem->setItemMode(ContentItemMode);
+    //qDebug() << "updateCell row:" << index.row() << "thumbnail" << item->thumbnailURI;
+    //qDebug() << "cellSize" << cell->size().width() << "," << cell->size().height();
+    contentItem->setPixmap(*(item->thumbnail));
+    if (item->showSpinner) {
+        contentItem->showSpinner();
+    } else  {
+        contentItem->hideSpinner();
+    }
+
+    //if (contentItem->itemStyle() ==  MContentItem::TwoIconsTwoWidgets) {
+    // with implicit data sharing
+    //all contentItem should share one copy of defaultThumbnail's data'
+    contentItem->boundingRect();
+
+    //updateContentItemMode(index, contentItem);
+}
+
--- liballphoto/mcontentitemcreator.h
+++ liballphoto/mcontentitemcreator.h
+/*
+ * Meego-handset-photos is a photo viewer.
+ * Copyright (C) 2010, Intel Corporation.
+ *
+ * This program is licensed under the terms and conditions of the
+ * Apache License, version 2.0.  The full text of the Apache License is at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ */
+
+
+#ifndef PHOTOSCONTENTITEMCREATOR_H
+#define PHOTOSCONTENTITEMCREATOR_H
+
+#include <QModelIndex>
+#include <MContentItem>
+#include <MAbstractCellCreator>
+#include <MWidget>
+#include <MAction>
+#include <QSizeF>
+
+#include "mplistmodel.h"
+#include "mcontentitemex.h"
+
+
+class MContentItemCreator : public MAbstractCellCreator<MContentItemEx>
+{
+public:
+    MContentItemCreator(MContentItemEx::ContentItemStyle style, int _id = 0) : itemStyle(MContentItemEx::TwoIconsTwoWidgets),
+        id(_id)
+    {
+        itemStyle = style;
+        size = MAbstractCellCreator<MContentItemEx>::cellSize();
+
+    }
+
+    virtual ~MContentItemCreator()
+    {
+    }
+
+    //override
+    void setCellViewType(const QString &viewType) {
+        cellViewType = viewType;
+        MAbstractCellCreator<MContentItemEx>::setCellViewType(viewType);
+        updateSize();
+    }
+
+    void setCellObjectName(const QString &objectName) {
+        cellObjectName = objectName;
+        MAbstractCellCreator<MContentItemEx>::setCellObjectName(objectName);
+        updateSize();
+    }
+/*
+    void setOddCellObjectName(const QString &objectName) {
+        oddCellObjectName = objectName;
+        //MAbstractCellCreator<MContentItemEx>::setCellObjectName(objectName);
+        updateSize();
+    }
+
+    void setEvenCellObjectName(const QString &objectName) {
+        evenCellObjectName = objectName;
+        //MAbstractCellCreator<MContentItemEx>::setCellObjectName(objectName);
+        updateSize();
+    }
+*/
+
+    //override this for specifying Style to SingleIcon
+    virtual MWidget *createCell(const QModelIndex &index, MWidgetRecycler &recycler) const {
+        recycler.setMaxItemsPerClass(0);
+        MContentItemEx *cell = dynamic_cast<MContentItemEx *>(recycler.take(MContentItem::staticMetaObject.className()));
+        if (cell == NULL) {
+            cell = new MContentItemEx(itemStyle, NULL, id);
+
+            cell->index = index;
+            if (!cellViewType.isEmpty())
+                cell->setViewType(cellViewType);
+            if (!cellObjectName.isEmpty())
+                cell->setObjectName(cellObjectName);
+
+
+        }
+
+        updateCell(index, cell);
+        return cell;
+    }
+
+    void updateCell(const QModelIndex &index, MWidget *cell) const;
+
+ //   void setColumns(int columns) {
+ //       Q_ASSERT(columns > 0);
+ //       amountOfColumns = columns;
+ //   }
+
+    virtual QSizeF cellSize() const {
+         return size;
+    }
+
+private:
+    void updateSize() {
+        MContentItemEx *cell = new MContentItemEx(itemStyle, NULL, id);
+        if (!cellViewType.isEmpty())
+            cell->setViewType(cellViewType);
+        if (!cellObjectName.isEmpty())
+            cell->setObjectName(cellObjectName);
+        size = cell->effectiveSizeHint(Qt::PreferredSize);
+        delete cell;
+        qDebug() << "update cell Size to" << size;
+    }
+
+   QSizeF size;
+   QString cellViewType;
+   QString cellObjectName;
+   MContentItemEx::ContentItemStyle itemStyle;
+
+   int id; //identify the cell is for photo or album
+};
+
+
+
+#endif // PHOTOSCONTENTITEMCREATOR_H
+
--- liballphoto/mcontentitemex.cpp
+++ liballphoto/mcontentitemex.cpp
+/*
+ * Meego-handset-photos is a photo viewer.
+ * Copyright (C) 2010, Intel Corporation.
+ *
+ * This program is licensed under the terms and conditions of the
+ * Apache License, version 2.0.  The full text of the Apache License is at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ */
+
+
+#include "mcontentitemex.h"
+
+void MContentItemEx::setPixmap(const QPixmap &pixmap)
+{
+    MContentItem::setPixmap(pixmap);
+}
+
+void MContentItemEx::showSpinner()
+{
+    MProgressIndicator *org = dynamic_cast<MProgressIndicator *>(additionalItem());
+    if (!org) {
+        MProgressIndicator *spinner = new MProgressIndicator(this, MProgressIndicator::spinnerType);
+        spinner->setUnknownDuration(true);
+        spinner->setObjectName("loadingSpinner");
+        setAdditionalItem(spinner);
+        delete org;
+    }
+}
+
+void MContentItemEx::hideSpinner()
+{
+    MProgressIndicator *org = dynamic_cast<MProgressIndicator *>(additionalItem());
+    setAdditionalItem(NULL);
+    if (org)
+        delete org;
+}
+
--- liballphoto/mcontentitemex.h
+++ liballphoto/mcontentitemex.h
+/*
+ * Meego-handset-photos is a photo viewer.
+ * Copyright (C) 2010, Intel Corporation.
+ *
+ * This program is licensed under the terms and conditions of the
+ * Apache License, version 2.0.  The full text of the Apache License is at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ */
+
+#ifndef MCONTENTITEMEX_H
+#define MCONTENTITEMEX_H
+
+#include <MContentItem>
+#include <QModelIndex>
+
+class MContentItemEx : public MContentItem
+{
+public:
+
+    MContentItemEx(MContentItem::ContentItemStyle itemStyle = MContentItem::TwoIconsTwoWidgets, QGraphicsItem *parent = 0, int _id = 0)
+        :   MContentItem(itemStyle, parent), id(_id)
+    {
+    }
+
+    virtual ~MContentItemEx(){};
+
+
+    QModelIndex index;  // the index of this item in some model
+    int id;
+
+    void showSpinner();
+    void hideSpinner();
+
+public slots:
+    void setPixmap(const QPixmap &pixmap);
+
+};
+
+#endif // MCONTENTITEMEX_H
--- liballphoto/mplistmodel.cpp
+++ liballphoto/mplistmodel.cpp
+/*
+ * Meego-handset-photos is a photo viewer.
+ * Copyright (C) 2010, Intel Corporation.
+ *
+ * This program is licensed under the terms and conditions of the
+ * Apache License, version 2.0.  The full text of the Apache License is at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ */
+
+
+#include <QDir>
+#include <QDebug>
+#include <QtDBus/QtDBus>
+#include <MTheme>
+#include "mplistmodel.h"
+#include "searchengine.h"
+#include "thumbnailer.h"
+#include "photostasklet.h"
+
+QHash<QString, QString> MPListModel::urnToPathHashMap;
+
+QString MPListModel::homePath = QDir::toNativeSeparators(QDir::homePath());
+QString MPListModel::thumbnail_folder = QDir::separator() + QString(".thumbnails") + QDir::separator() + QString("normal");
+QString MPListModel::thumbnail_suffix = ".png";
+
+MPListModel::MPListModel(DynamicMList *controller) : list(controller)
+{
+    //Create Thumbnailer
+    Thumbnailer::instance();
+    QObject::connect(Thumbnailer::instance(), SIGNAL(ready(uint,QStringList)), this, SLOT(handleReady(uint,QStringList)), Qt::QueuedConnection);
+    QObject::connect(Thumbnailer::instance(), SIGNAL(error(uint, QStringList, const int &, const QString &)), this, SLOT(handleError(uint,QStringList, const int &, const QString &)), Qt::QueuedConnection);
+    //QObject::connect(TrackerListener::instance(), SIGNAL(SubjectsAdded(const QStringList &subjects)), this, SLOT(handlePhotoAdded(const QStringList &urns)));
+    int indexValue = 0;
+
+    //Build photoItemVector
+
+    //defaultThumbnail.load(QString(MPListModel::testThumbnailDir).append("/defaultThumbnail.png"));
+    pDefaultThumbnail = new QPixmap; //(QPixmap *)MTheme::pixmap("defaultThumbnail");
+    //pDefaultThumbnail = new QPixmap(*defaultPixmap);
+    //MTheme::releasePixmap(defaultPixmap);
+     
+#if 0
+    // for debug only, pull all thumbnails int ~/.thumbnail into this model
+    QDir thumbnailDir(MPListModel::testThumbnailDir);
+    qDebug() << "dir is " << MPListModel::testThumbnailDir;
+    bool yyy = thumbnailDir.exists();
+    thumbnailDir.setNameFilters(QStringList() << "*.png");
+
+    QFileInfoList fileInfoList = thumbnailDir.entryInfoList();
+    QStringList xxx = thumbnailDir.entryList(QDir::Files);
+
+    for (int i = 0; i < fileInfoList.size(); i++) {
+        listItemsVector << new MPListItem("", fileInfoList.at(i).absoluteFilePath(), defaultThumbnail);
+    }
+#endif
+        thumbnailDir = new QDir(homePath + thumbnail_folder);
+    
+    //get all photo urls from tracker
+    QVector<QStringList> *photos = NULL;   //hold all photo urls got from tracker
+    bool deletePhotos = false;
+        //get all photo urls from tracker
+            photos = new QVector<QStringList>();
+            deletePhotos = true;
+            SearchEngine::getAllPhoto(*photos);
+
+    for (QVector<QStringList>::iterator i = photos->begin(); i != photos->end(); i++) {
+        QFile f;
+        QUrl url = QUrl::fromEncoded((*i)[0].toAscii());
+
+        f.setFileName(url.toLocalFile());
+        if (f.exists()) // only create photoItem for files that exist; Tracker may has bug
+        {
+            MPListItem *item = new MPListItem( (*i)[0] , "", pDefaultThumbnail, (*i)[1]);
+            item->urn = (*i)[2];
+            listItemsVector << item;
+
+            pathToIndexHashMap.insert(url.toLocalFile(), indexValue);
+            urnToPathHashMap.insert(item->urn, url.toLocalFile());
+            indexValue++;
+        }
+    }
+
+    if (deletePhotos)
+        delete photos;
+}
+
+void MPListModel::addOutstandingThumbnailRequest(QString requestPath)
+{
+    outstandingThumbnailRequest.insert(requestPath);
+}
+
+void MPListModel::removeOutstandingThumbnailRequest(QString requestPath)
+{
+    outstandingThumbnailRequest.remove(requestPath);
+}
+
+
+
+void MPListModel::handleReady(const unsigned int &handler, const QStringList &urls)
+{
+    for (QStringList::const_iterator i = urls.begin(); i != urls.end(); i++) {
+        QString localPath = *i;
+        QString key = localPath.remove(QRegExp("^file:\/\/"));
+        if (!outstandingThumbnailRequest.contains(key)) {
+            outstandingThumbnailRequest.remove(key);
+            continue;
+        }
+        int indexValue = pathToIndexHashMap.value(key, -1);
+        if (indexValue == -1)
+            continue;
+        MPListItem *entry = listItemsVector[indexValue];
+        if (entry->thumbnailURI.isEmpty())
+            continue;
+        entry->thumbnail = new QPixmap(PhotosTasklet::squareQPixmapFromPath(entry->thumbnailURI, list->cellCreator()->cellSize().toSize().width()));
+        entry->isLoaded = true;
+        entry->showSpinner = false;
+        QModelIndex modelIndex = index(indexValue, 0);
+        QModelIndex next = modelIndex.sibling(modelIndex.row() + 1, 0);
+        if (next.isValid()) {
+            QVariant data = next.data(Qt::DisplayRole);
+            MPListItem *next_entry = static_cast<MPListItem *>(data.value<void *>());
+            if (!next_entry->isLoaded)
+                next_entry->showSpinner = true;
+            thumbnailWasLoaded(next);   //emit date changed signal;
+        }
+ 
+        thumbnailWasLoaded(modelIndex);
+        QTimer::singleShot(0, list->getTasklet(), SLOT(processJobQueue()));
+    }
+}
+
+void MPListModel::handleError(const unsigned int &handler, const QStringList &urls, const int &errorCode, const QString &message)
+{
+    if (errorCode == 0)
+        return;
+    for (QStringList::const_iterator i = urls.begin(); i != urls.end(); i++) {
+        QString localPath = *i;
+        QString key = localPath.remove(QRegExp("^file:\/\/"));
+        if (!outstandingThumbnailRequest.contains(key)) {
+            outstandingThumbnailRequest.remove(key);
+            continue;
+        }
+        int indexValue = pathToIndexHashMap.value(key, -1);
+        if (indexValue == -1)
+            continue;
+        MPListItem *entry = listItemsVector[indexValue];
+        entry->thumbnail = new QPixmap(PhotosTasklet::squareQPixmapFromPath(entry->thumbnailURI, list->cellCreator()->cellSize().toSize().width()));
+        entry->isLoaded = true;
+        entry->showSpinner = false;
+        QModelIndex modelIndex = index(indexValue, 0);
+        QModelIndex next = modelIndex.sibling(modelIndex.row() + 1, 0);
+        if (next.isValid()) {
+            QVariant data = next.data(Qt::DisplayRole);
+            MPListItem *next_entry = static_cast<MPListItem *>(data.value<void *>());
+            if (!next_entry->isLoaded) {
+                next_entry->showSpinner = true;
+            }
+            thumbnailWasLoaded(next);   //emit date changed signal;
+        }
+ 
+        thumbnailWasLoaded(modelIndex);
+        QTimer::singleShot(0, list->getTasklet(), SLOT(processJobQueue()));
+    }
+}
+
+MPListModel::~MPListModel()
+{
+    QObject::disconnect(Thumbnailer::instance(), SIGNAL(ready(uint,QStringList)), this, SLOT(handleReady(uint,QStringList)));
+    QObject::disconnect(Thumbnailer::instance(), SIGNAL(error(uint, QStringList, const int &, const QString &)), this, SLOT(handleError(uint,QStringList, const int &, const QString &)));
+    for (int i = 0; i < listItemsVector.count(); i++) {
+        delete listItemsVector[i];
+    }
+
+    listItemsVector.resize(0);
+    delete thumbnailDir;
+    //MTheme::releasePixmap(pDefaultThumbnail);
+    delete pDefaultThumbnail;
+}
+
+QString MPListModel::testThumbnailDir = "themes/.thumbnails/normal";
+
+QVariant MPListModel::data(const QModelIndex &index, int role) const
+{
+    // This function will be called many times during fast panning, lets
+    // check boundaries and validnes only in debug mode
+    Q_ASSERT(index.isValid());
+    Q_ASSERT(index.row() < listItemsVector.size());
+
+    if (role == Qt::DisplayRole) {
+        // Let's store a pointer into QVariant, otherwise QT will make a copy but we are lazy
+        // and don't want to copy stuff around
+        return QVariant::fromValue(static_cast<void *>(listItemsVector[index.row()]));
+    }
+
+    return QVariant();
+}
+
+int MPListModel::rowCount(const QModelIndex &parent) const
+{
+    Q_UNUSED(parent);
+
+    return listItemsVector.size();
+}
+
+int MPListModel::columnCount(const QModelIndex &parent) const
+{
+    Q_UNUSED(parent);
+
+    return 1;
+}
+
+/*
+bool MPListModel::insertRows(int row, int count, const QModelIndex &parent)
+{
+    if (count <= 0)
+        return true;
+
+    emit layoutAboutToBeChanged();
+    beginInsertRows(parent, row, row + count - 1);
+    for (int i=0; i < count; i++) {
+        MPListItem *item = listItemsVector[row + i];
+        QString fileString = item->photoURI.remove(QRegExp("^file:\/\/"));
+        QStringList list = fileString.split("/");
+        QString filename = list.last();
+        
+        QString targetName = "copy_of_" + filename;
+        list[list.count()-1] = targetName;
+        QString targetString = list.join("/");
+        int n = 2;
+        while (QFile::exists(targetString)) {
+            targetName = "copy_" + QString::number(n) + "_of_" + filename;
+            list[list.count()-1] = targetName;
+            targetString = list.join("/");
+            n++;
+        }
+        QFile::copy(fileString, targetString);
+    }
+    endInsertRows();
+    emit layoutChanged();
+    return true;
+}
+*/
+
+
+/*
+bool MPListModel::removeRows(int row, int count, const QModelIndex &parent)
+{
+    if (count <= 0)
+        return true; //Successfully removed 0 rows.
+
+    emit layoutAboutToBeChanged();
+    beginRemoveRows(parent, row, row + count - 1);
+    //Todo: Huan review this for possible tracker-related clean-up
+    for (int i=0; i < count; i++) {
+        MPListItem *itemToBeDeleted = listItemsVector[row + i];
+        QFile::remove(itemToBeDeleted->photoURI.remove(QRegExp("^file:\/\/")));
+        delete itemToBeDeleted;
+        QUrl url = QUrl::fromEncoded(listItemsVector[row]->photoURI.toAscii());
+        pathToIndexHashMap.remove(url.toLocalFile());
+        listItemsVector.remove(row);
+
+    }
+    endRemoveRows();
+    emit layoutChanged();
+    return true;
+}
+*/
+
+
+void MPListModel::thumbnailWasLoaded(const QModelIndex &index)
+{
+    emit dataChanged(index, index);
+}
+
+void MPListModel::setSparql(QString qlString)
+{
+    //rebuild the model with the new qlString
+}
+
+void MPListModel::setDefaultThumbnail(int index)
+{
+    if (listItemsVector[index]->isLoaded) {
+        delete listItemsVector[index]->thumbnail;
+        listItemsVector[index]->thumbnail = pDefaultThumbnail;
+        listItemsVector[index]->isLoaded = false;
+    }
+}
+
+void MPListModel::setSpinner(const QModelIndex &index, bool showSpinner)
+{
+    int i = index.row();
+    if (listItemsVector[i]->isLoaded && !showSpinner)
+        listItemsVector[i]->showSpinner = false;
+    else if (!listItemsVector[i]->isLoaded)
+        listItemsVector[i]->showSpinner = showSpinner;
+
+    thumbnailWasLoaded(index);
+}
+
+#if 0
+void MPListModel::updateAlbumModel()
+{
+    qDebug() << "MPListModel::updateAlbumModel()";
+    if (getDataType() == MPAbstractListModelOps::Album) {
+        // create a album model
+        QVector<QStringList> *result = new QVector<QStringList>();
+        SearchEngine::getAllPhotoAlbums(*result);
+        qDebug() << "MPListModel::updateAlbumModel() getallphotoalbums:" << t.elapsed();
+        emit layoutAboutToBeChanged();
+        beginRemoveRows(QModelIndex(), 0, listItemsVector.count() - 1);
+        qDebug() << "albums:" << result->size();
+        for (int i = 0; i < listItemsVector.count(); i++) {
+            delete listItemsVector[i];
+        }
+        listItemsVector.clear();
+        pathToIndexHashMap.clear();
+        endRemoveRows();
+
+        int indexValue = 0;
+        MPSettings settings;
+        for(QVector<QStringList>::iterator i = result->begin();i != result->end(); i++) {
+            MPListItem *t = new MPListItem((*i)[1], ((*i)[2]).toInt(), (*i)[0]);
+            t->thumbnail = pDefaultThumbnail;
+            t->urn = (*i)[0];
+            qDebug() << "paul debug urn in updateAlbum:" << t->urn;
+            t->photoURI = settings.getAlbumCover(t->urn);
+            qDebug() << "12345 paul debug cover uri:" << t->photoURI;
+            listItemsVector << t;
+            qDebug() << "paul debug in update album " << t->urn << "num photos" << (*i)[2];
+
+            pathToIndexHashMap.insert(QUrl::fromEncoded(t->photoURI.toAscii()).toLocalFile(), indexValue++);
+        }
+        emit layoutChanged();
+        emit dataChanged(index(0, 0), index(result->size() -1, 0));
+        reset();
+        list->getTasklet()->stopJobQueue();
+        list->doTasklet();
+    }
+    qDebug() << "MPListModel::updateAlbumModel() end";
+}
+#else
+
+#endif
+
+void MPListModel::relayout()
+{
+    emit layoutChanged();
+}
+
--- liballphoto/mplistmodel.h
+++ liballphoto/mplistmodel.h
+/*
+ * Meego-handset-photos is a photo viewer.
+ * Copyright (C) 2010, Intel Corporation.
+ *
+ * This program is licensed under the terms and conditions of the
+ * Apache License, version 2.0.  The full text of the Apache License is at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ */
+
+
+#ifndef MPLISTMODEL_H
+#define MPLISTMODEL_H
+
+#include <QAbstractTableModel>
+#include <QPixmap>
+#include "thumbnailer.h"
+#include "dynamicmlist.h"
+
+// Structure which contain data for each item 
+// This structure is used for both photo and album
+struct MPListItem {
+public:
+    MPListItem(QString newphotoURI, QString newThumbnailURI, QPixmap *t, QString newmimetype) :
+            thumbnail(NULL), showSpinner(false), photoURI(newphotoURI), thumbnailURI(newThumbnailURI), isLoaded(false), mimetype(newmimetype), numPhotos(0)
+    {
+        thumbnail = t;   //implicit data sharing
+    }
+
+    MPListItem(QString _albumName, int _numPhotos) :
+        thumbnail(NULL), showSpinner(false), isLoaded(false), numPhotos(_numPhotos) {};
+
+    MPListItem(QString _albumName, int _numPhotos, QString _albumID) :
+        thumbnail(NULL), showSpinner(false), isLoaded(false), albumID(_albumID), numPhotos(_numPhotos) {};
+
+
+    QString urn;
+
+
+    QPixmap *thumbnail; // will be filled with empty image, after real image will be loaded it will replace old one
+    bool showSpinner;   //whether to show Spinner
+    QString photoURI;
+    QString thumbnailURI;
+    bool isLoaded;
+    QString mimetype;
+
+//    QString albumName;  //album Name. for album item only
+    QString albumID;    // used for backend to idenity album
+    int numPhotos;      // number of photos in this album. for album item only
+
+private:
+ //   explicit MPListItem();   //disable default constructor
+};
+
+class DynamicMList;
+//Data model for photo listing and album listing
+class MPListModel: public QAbstractTableModel
+{
+    Q_OBJECT
+
+public:
+    // Defining roles here which will be used for sorting and filtering in PhoneBookSortedModel
+    //enum PhoneBookRoles {
+    //    PhoneBookSortRole = Qt::UserRole + 1,
+    //    PhoneBookFilterRole
+    //};
+    MPListModel(DynamicMList *controller);
+    virtual ~MPListModel();
+
+    virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
+    virtual int columnCount(const QModelIndex &parent = QModelIndex()) const;
+    virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+    void setSparql(QString qlString);
+    void removeAlbum(const QModelIndex &index);
+
+/*    bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex());
+    bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
+*/
+    virtual void thumbnailWasLoaded(const QModelIndex &index);
+    virtual void setDefaultThumbnail(int index);
+    virtual void setSpinner(const QModelIndex &index, bool showSpinner);
+
+    virtual void addOutstandingThumbnailRequest(QString ruquestPath);
+    virtual void removeOutstandingThumbnailRequest(QString ruquestPath);
+
+    virtual void relayout();
+
+    static QString testThumbnailDir;
+    static QString homePath;
+    static QString thumbnail_folder;
+    static QString thumbnail_suffix;
+    Thumbnailer *thumbnailer;
+    QDir *thumbnailDir;
+
+private:
+    QVector<MPListItem *> listItemsVector;
+    QPixmap *pDefaultThumbnail;
+    QHash<QString, int> pathToIndexHashMap;
+    static QHash<QString, QString> urnToPathHashMap;
+    QSet<QString> outstandingThumbnailRequest;
+    DynamicMList *list;
+
+    friend class DynamicPhotoList;
+    friend class DynamicAlbumList;
+private slots:
+    void handleReady(const unsigned int &handler, const QStringList &urls);
+    void handleError(const unsigned int &handler, const QStringList &urls, const int &errorcode, const QString &message);
+};
+
+
+
+#endif // MPLISTMODEL_H
--- liballphoto/photostasklet.cpp
+++ liballphoto/photostasklet.cpp
+/*
+ * Meego-handset-photos is a photo viewer.
+ * Copyright (C) 2010, Intel Corporation.
+ *
+ * This program is licensed under the terms and conditions of the
+ * Apache License, version 2.0.  The full text of the Apache License is at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ */
+
+
+#include <QCryptographicHash>
+#include <QDir>
+#include <QDebug>
+#include "photostasklet.h"
+#include "mplistmodel.h"
+
+PhotosTasklet::PhotosTasklet(DynamicPhotoList *list) : outstandingChecking(false)
+{
+    this->list = list;
+}
+
+PhotosTasklet::~PhotosTasklet()
+{
+
+}
+	
+QPixmap PhotosTasklet::squareQPixmapFromPath(QString path, qreal edgeR)
+{
+    QImage a = QImage(path);
+    int edge = (int)edgeR;
+/*    int w = a.width();
+    int h = a.height();
+    int edge = w;
+    if (edge > h)
+        edge = h; */
+    QImage b = a.scaled(edge, edge, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
+    QImage c = b.copy(0, 0, edge, edge);
+    return QPixmap::fromImage(c);
+}
+
+#if 0
+void PhotosTasklet::checkThumbnailsGenerated()
+{
+    outstandingChecking = false;
+    if (thumbnailsToBeGenerated.isEmpty())
+        return;
+    
+    int cnt = thumbnailsToBeGenerated.count();
+    QTime now;
+    now.start();
+    qDebug() << "PhotosTasklet::checkThumbnailsGenerated() time" << now << ":" << now.msec();
+    while (cnt-- > 0) {
+        ThumbnailCheckJob j = thumbnailsToBeGenerated.takeFirst();
+        qDebug() << "PhotosTasklet::checkThumbnailsGenerated() duetime" << j.dueTime << "cnt:" << cnt;
+        now.start();
+        if (j.dueTime < now)
+            continue;
+
+        MPListItem *entry = static_cast<MPListItem *>(j.userData.value<void *>());
+        QModelIndex index = j.row;
+        MPListModel *photoModel = dynamic_cast<MPListModel *>(const_cast<QAbstractItemModel *>(index.model()));
+        QFile f;
+        f.setFileName(entry->thumbnailURI);
+        if (f.exists()) {
+            entry->thumbnail = new QPixmap(squareQPixmapFromPath(entry->thumbnailURI, AppWindow::instance()->photoList->cellCreator()->cellSize().toSize().width()));
+            //entry->thumbnail = new QPixmap(squareQPixmapFromPath(entry->thumbnailURI, 112));
+            entry->isLoaded = true;
+            photoModel->thumbnailWasLoaded(index);
+        } else {
+            thumbnailsToBeGenerated << j;
+        }
+    }
+
+    /*if(thumbnailsToBeGenerated.count() > 0)
+        QTimer::singleShot(CHECK_THUMBNAIL_FILE_INTERVAL, this, SLOT(checkThumbnailsGenerated()));*/
+}
+#endif
+
+QString PhotosTasklet::getThumbnailPath(const QString &uri)
+{
+    //use its Utf8 to perform md5
+    QByteArray md5Result = QCryptographicHash::hash(uri.toUtf8(), QCryptographicHash::Md5);
+    QString thumbnailPath = MPListModel::homePath + MPListModel::thumbnail_folder + QDir::separator() + md5Result.toHex() + MPListModel::thumbnail_suffix;
+    return thumbnailPath;
+}
+
+void PhotosTasklet::processSingleJob(Job& j)
+{
+    QModelIndex index = j.row;
+    MPListItem *entry = static_cast<MPListItem *>(j.userData.value<void *>());
+    QAbstractItemModel *model = const_cast<QAbstractItemModel *>(index.model());
+    MPListModel *photoModel = dynamic_cast<MPListModel *>(model);
+    QByteArray md5Result;
+#if 0
+	if (!entry->isLoaded) {
+        qDebug() << "processSingleJob: row " << index.row() << "thumbnail:" << QUrl::fromEncoded(entry->photoURI.toAscii()).path() << "entry@" << (void *)entry;
+		entry->thumbnail = new QPixmap(squareQPixmapFromPath(QUrl::fromEncoded(entry->photoURI.toAscii()).path()));
+        entry->isLoaded = true;
+        photoModel->thumbnailWasLoaded(index);
+	}
+#else
+    //generate thumbnailURI if it does not exist here
+    if (entry->thumbnailURI.isEmpty()) {
+#if 0
+        QUrl url = QUrl::fromEncoded(entry->photoURI.toAscii());
+        QString path = QString("file://") + url.path();
+        //use its Utf8 to perform md5
+        md5Result = QCryptographicHash::hash(path.toUtf8(), QCryptographicHash::Md5);
+        entry->thumbnailURI = photoModel->homePath + photoModel->thumbnail_folder + QDir::separator() + md5Result.toHex() + photoModel->thumbnail_suffix;
+        qDebug() << "entry " << entry << " " << path  << " " << "thumbnailURI generated is " << entry->thumbnailURI;
+#else
+        entry->thumbnailURI = getThumbnailPath(entry->photoURI);
+#endif
+    }
+
+    //Some thumbnails may have not been created yet
+    QFile f;
+    f.setFileName(entry->thumbnailURI);
+    if (f.exists()) {
+        entry->thumbnail = new QPixmap(squareQPixmapFromPath(entry->thumbnailURI, list->cellCreator()->cellSize().toSize().width()));
+        //entry->thumbnail = new QPixmap(squareQPixmapFromPath(entry->thumbnailURI, 112));
+        entry->isLoaded = true;
+        entry->showSpinner = false;
+        QModelIndex next = index.sibling(index.row() + 1, 0);
+        if (next.isValid()) {
+            QVariant data = next.data(Qt::DisplayRole);
+            MPListItem *next_entry = static_cast<MPListItem *>(data.value<void *>());
+            if (!next_entry->isLoaded)
+                next_entry->showSpinner = true;
+            photoModel->thumbnailWasLoaded(next);
+        }
+        photoModel->thumbnailWasLoaded(index);
+        QTimer::singleShot(0, this, SLOT(processJobQueue()));
+    } else {
+        QUrl url = QUrl::fromEncoded(entry->photoURI.toAscii());
+        photoModel->addOutstandingThumbnailRequest(url.path());
+        Thumbnailer::instance()->requestThumbnail(QString(("file://" + url.path())), entry->mimetype);
+    }
+
+#endif
+}
--- liballphoto/photostasklet.h
+++ liballphoto/photostasklet.h
+/*
+ * Meego-handset-photos is a photo viewer.
+ * Copyright (C) 2010, Intel Corporation.
+ *
+ * This program is licensed under the terms and conditions of the
+ * Apache License, version 2.0.  The full text of the Apache License is at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ */
+
+#ifndef PHOTOSTASKLET_H
+#define PHOTOSTASKLET_H
+
+#include "abstracttasklet.h"
+#include "dynamicphotolist.h"
+
+class DynamicPhotoList;
+struct ThumbnailCheckJob : public Job
+{
+    ThumbnailCheckJob(Job &j, int dueSec = 3) : Job(j) { dueTime = QTime::currentTime().addSecs(dueSec);};
+
+    QTime dueTime;  //check the thumbnail file before this time
+};
+
+class PhotosTasklet : public AbstractTasklet
+{
+public:
+    explicit PhotosTasklet(DynamicPhotoList *parent = 0);
+    virtual ~PhotosTasklet();
+
+    virtual void processSingleJob(Job& j);
+    static const int CHECK_THUMBNAIL_FILE_INTERVAL = 100;
+    static QPixmap squareQPixmapFromPath(QString path, qreal edgeR);
+    static QString getThumbnailPath(const QString &uri);
+
+private:
+    DynamicPhotoList *list;
+    QList<ThumbnailCheckJob> thumbnailsToBeGenerated;
+    bool outstandingChecking;
+};
+
+#endif // PHOTOSTASKLET_H
--- liballphoto/searchengine.cpp
+++ liballphoto/searchengine.cpp
+/*
+ * Meego-handset-photos is a photo viewer.
+ * Copyright (C) 2010, Intel Corporation.
+ *
+ * This program is licensed under the terms and conditions of the
+ * Apache License, version 2.0.  The full text of the Apache License is at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ */
+
+
+#include "searchengine.h"
+#include <QtTracker/Tracker>
+static const QString SqlGetAllPhoto = "SELECT nie:url(nie:isStoredAs(?photo)) nie:mimeType(?photo) ?photo nfo:fileLastModified(?photo) nie:contentCreated(?photo) WHERE{?photo a nmm:Photo}";
+
+static const QString SqlGetAllPhotoAlbums = "SELECT nie:identifier(?imagelist) nie:title(?imagelist) nfo:entryCounter(?imagelist) ?imagelist " \
+                                             " nao:identifier(?tag) nao:prefLabel(?tag) ?tag  " \
+                                             " WHERE {?imagelist a nmm:ImageList . OPTIONAL {?imagelist nao:hasTag ?tag . ?tag nao:identifier 'AlbumCover' . }}";
+
+static const QString SqlGetAlbumPhotos = "SELECT nie:url(nie:isStoredAs(?image)) nie:contentCreated(?image) ?image nie:identifier(?imagelist) nie:title(?imagelist) nfo:entryCounter(?imagelist) "\
+                                          " WHERE { ?imagelist nfo:hasMediaFileListEntry ?entry . ?entry nfo:entryContent ?image "\
+                                                    " { SELECT ?imagelist WHERE {?imagelist a nmm:ImageList ; nie:identifier '%1'} } }";
+
+static const QString SqlRemoveAlbum = " DELETE { ?photoalbum a nmm:ImageList } WHERE { ?photoalbum nie:identifier '%1' }";
+
+static const QString SqlCreateAlbumBegin = "INSERT { _:a a nmm:ImageList ; nie:identifier '%1' ; nie:title '%2' ; nfo:entryCounter %3 ";
+static const QString SqlInsertItem = " nfo:hasMediaFileListEntry [ a nfo:MediaFileListEntry; nfo:entryContent <%1>; nfo:listPosition %2 ] ";
+static const QString SqlCreateAlbumEnd = " }";
+
+static const QString SqlQueryAlbumCoverTag = "SELECT ?tag nao:prefLabel(?tag) " \
+                                         " WHERE {?imagelist a nmm:ImageList; nao:hasTag ?tag . ?tag nao:identifier 'AlbumCover' . FILTER (str(?imagelist) = '%1')}";
+static const QString SqlDeleteAlbumCoverTag = "DELETE {?tag a rdfs:Resource } WHERE {?imagelist nao:hasTag ?tag . ?tag nao:identifier 'AlbumCover' . " \
+                                           "{ SELECT ?imagelist WHERE {?imagelist a nmm:ImageList . FILTER (str(?imagelist) = '%1') } } }";
+static const QString SqlInsertAlbumCoverTag = "INSERT { _:tag a nao:Tag ; nao:prefLabel '%1' ; nao:identifier 'AlbumCover' . ?imagelist nao:hasTag _:tag } " \
+                                        " WHERE { ?imagelist a nmm:ImageList . FILTER (str(?imagelist) = '%2') }";
+static const QString SqlGetAlbumURN = "SELECT ?imagelist WHERE {?imagelist a nmm:ImageList ; nie:identifier '%1' }";
+
+static const QString SqlQueryObjectTag = "SELECT nao:prefLabel(?tag) nao:identifier(?tag) ?tag " \
+                                         " WHERE {?object a nie:InformationElement; nao:hasTag ?tag . ?tag nao:identifier '%1' . " \
+                                         " FILTER (str(?object) = '%2')}";
+static const QString SqlInsertObjectTag = "INSERT { _:tag a nao:Tag ; nao:prefLabel '%1' ; nao:identifier '%2' . ?object nao:hasTag _:tag } " \
+                                          " WHERE { ?object a nie:InformationElement . FILTER (str(?object) = '%3') }";
+static const QString SqlDeleteObjectTag = "DELETE {?tag a rdfs:Resource } WHERE {?object nao:hasTag ?tag . ?tag nao:identifier '%1' . " \
+                                          " { SELECT ?object WHERE {?object a nie:InformationElement . FILTER (str(?object) = '%2') } } }";
+
+static const QString SqlGetUrlFromURN = "SELECT ?url {<%1> nie:url ?url}";
+static const QString SqlGetMimeTypeFromURN = "SELECT ?mimetype {<%1> nie:mimeType ?mimetype}";
+
+bool SearchEngine::getAllPhoto(QVector<QStringList> &result)
+{
+    result = ::tracker()->rawSparqlQuery(SqlGetAllPhoto);
+    return TRUE;
+}
+
+bool SearchEngine::getAllPhotoAlbums(QVector<QStringList> &result)
+{
+    result = ::tracker()->rawSparqlQuery(SqlGetAllPhotoAlbums);
+    return TRUE;
+}
+
+bool SearchEngine::getAlbumPhotos(const QString &albumIdentifier, QVector<QStringList> &result)
+{
+    QString sql = QString(SqlGetAlbumPhotos).arg(albumIdentifier);
+    result = ::tracker()->rawSparqlQuery(sql);
+    return TRUE;
+}
+
+bool SearchEngine::createAlbum(const QString &albumIdentifier, const QString &albumTitle, const QStringList &photoFiles)
+{
+    if (photoFiles.size() < 1)
+        return TRUE;
+
+    QString sql = QString(SqlCreateAlbumBegin).arg(albumIdentifier).arg(albumTitle).arg(photoFiles.size());
+
+    int j = 0;
+    for (QStringList::const_iterator i = photoFiles.begin(); i != photoFiles.end(); i++) {
+        QString sqlItem = QString(SqlInsertItem).arg(*i).arg(j++);
+        sql += QString(";") + sqlItem;
+    }
+    sql += SqlCreateAlbumEnd;
+
+    ::tracker()->rawSparqlUpdateQuery(sql);
+    return TRUE;
+}
+
+bool SearchEngine::deleteAlbum(const QString &albumIdentifier)
+{
+    QString sql = QString(SqlRemoveAlbum).arg(albumIdentifier);
+    ::tracker()->rawSparqlUpdateQuery(sql);
+    return TRUE;
+}
+
+bool SearchEngine::setAlbumCover(const QString &albumURN, const QString &coverPath)
+{
+    QString sql = QString(SqlQueryAlbumCoverTag).arg(albumURN);
+    bool needDelete = FALSE;
+
+    QVector<QStringList> result = ::tracker()->rawSparqlQuery(sql);
+
+    for (QVector<QStringList>::iterator i = result.begin(); i != result.end(); i++) {
+        if (!(*i)[1].isEmpty()) {
+            needDelete = TRUE;
+            break;
+        }
+    }
+
+    if (needDelete) {
+        sql = QString(SqlDeleteAlbumCoverTag).arg(albumURN);
+        ::tracker()->rawSparqlUpdateQuery(sql);
+    }
+
+    sql = QString(SqlInsertAlbumCoverTag).arg(coverPath).arg(albumURN);
+    ::tracker()->rawSparqlUpdateQuery(sql);
+
+    return TRUE;
+}
+
+bool SearchEngine::getAlbumCover(const QString &albumURN, QString &coverPath)
+{
+    QString sql = QString(SqlQueryAlbumCoverTag).arg(albumURN);
+
+    QVector<QStringList> result = ::tracker()->rawSparqlQuery(sql);
+
+    coverPath = QString("");
+
+    for (QVector<QStringList>::iterator i = result.begin(); i != result.end(); i++) {
+        if (!(*i)[1].isEmpty()) {
+            coverPath = (*i)[1];
+            break;
+        }
+    }
+
+    return TRUE;
+}
+/*
+bool SearchEngine::getAlbumURN(const QString &albumIdentifier, QString &albumURN)
+{
+    QString sql = QString(SqlGetAlbumURN).arg(albumIdentifier);
+
+    qDebug() << "SearchEngine::getAlbumURN " << sql;
+
+    QVector<QStringList> result = ::tracker()->rawSparqlQuery(sql);
+    if (result.count() > 0)
+        albumURN = result[0][0];
+
+    return TRUE;
+}
+*/
+bool SearchEngine::setObjectTag(const QString &objectURN, const QString &tagIdentifier, const QString &tagInfo)
+{
+    QString sql = QString(SqlInsertObjectTag).arg(tagInfo).arg(tagIdentifier).arg(objectURN);
+
+
+    ::tracker()->rawSparqlUpdateQuery(sql);
+
+    return TRUE;
+}
+
+bool SearchEngine::getObjectTag(const QString &objectURN, const QString &tagIdentifier, QString &tagInfo)
+{
+    QString sql = QString(SqlQueryObjectTag).arg(tagIdentifier).arg(objectURN);
+
+
+    QVector<QStringList> result = ::tracker()->rawSparqlQuery(sql);
+
+    tagInfo = "";
+
+    for (QVector<QStringList>::iterator i = result.begin(); i != result.end(); i++) {
+        if (!(*i)[0].isEmpty()) {
+            tagInfo = (*i)[0];
+            break;
+        }
+    }
+
+    return TRUE;
+}
+
+bool SearchEngine::deleteObjectTag(const QString &objectURN, const QString &tagIdentifier)
+{
+    QString sql = QString(SqlDeleteObjectTag).arg(tagIdentifier).arg(objectURN);
+
+
+    ::tracker()->rawSparqlUpdateQuery(sql);
+
+    return TRUE;
+}
+
+QString SearchEngine::getUrl(const QString &objectURN)
+{
+    QString sql = QString(SqlGetUrlFromURN).arg(objectURN);
+    QString url;
+
+
+    QVector<QStringList> result = ::tracker()->rawSparqlQuery(sql);
+    if (result.count() != 0)
+        url = result[0][0];
+    return url;
+}
+
+QString SearchEngine::getMimeType(const QString &objectURN)
+{
+    QString sql = QString(SqlGetMimeTypeFromURN).arg(objectURN);
+    QString mimeType;
+
+
+    QVector<QStringList> result = ::tracker()->rawSparqlQuery(sql);
+    mimeType = result[0][0];
+    return mimeType;
+}
--- liballphoto/searchengine.h
+++ liballphoto/searchengine.h
+/*
+ * Meego-handset-photos is a photo viewer.
+ * Copyright (C) 2010, Intel Corporation.
+ *
+ * This program is licensed under the terms and conditions of the
+ * Apache License, version 2.0.  The full text of the Apache License is at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ */
+
+
+#ifndef SEARCHENGINE_H
+#define SEARCHENGINE_H
+#include <QtCore>
+
+class SearchEngine
+{
+public:
+    static bool getAllPhoto(QVector<QStringList> &result);
+
+    /// @brief get all photo albums from tracker, including nie:title; nie:identifier; nfo:entryCounter
+    static bool getAllPhotoAlbums(QVector<QStringList> &result);
+
+    /// @brief get all photos from specific album, including photo urls, nie:contentCreated
+    static bool getAlbumPhotos(const QString &albumIdentifier, QVector<QStringList> &result);
+
+    /// @brief create an album, photoFiles must contain urn:uuid:xxxxxx
+    static bool createAlbum(const QString &albumIdentifier, const QString &albumTitle, const QStringList &photoFiles);
+
+    /// @brief delete an album
+    static bool deleteAlbum(const QString &albumIdentifier);
+
+    /// @brief set cover for an album
+    static bool setAlbumCover(const QString &albumURN, const QString &coverPath);
+
+    /// @brief get cover for an album
+    static bool getAlbumCover(const QString &albumURN, QString &coverPath);
+
+    /// @brief get album URN from identifier
+//    static bool getAlbumURN(const QString &albumIdentifier, QString &albumURN);
+
+    /// @brief set Tag for an object
+    static bool setObjectTag(const QString &objectURN, const QString &tagIdentifier, const QString &tagInfo);
+
+    /// @brief get Tag for an object
+    static bool getObjectTag(const QString &objectURN, const QString &tagIdentifier, QString &tagInfo);
+
+    /// @brief delete Tag for an object
+    static bool deleteObjectTag(const QString &objectURN, const QString &tagIdentifier);
+
+    /// @brief get url from urn
+    static QString getUrl(const QString &objectURN);
+
+    static QString getMimeType(const QString &objectURN);
+
+    static int getAllPhotoUrlIndex;
+//    static int getAllPhoto
+};
+#endif
--- liballphoto/test
+++ liballphoto/test
+(directory)
--- liballphoto/test/clickedslot.cpp
+++ liballphoto/test/clickedslot.cpp
+#include <QDebug>
+#include "clickedslot.h"
+
+void Foo::onClicked(const QString &photoURI, const QString &thumbnailPath)
+{
+    qDebug() << "Foo photoURI : " << photoURI << "ThumbnailPath: " << thumbnailPath;
+}
--- liballphoto/test/clickedslot.h
+++ liballphoto/test/clickedslot.h
+#ifndef _CLICKED_SLOTS_H_
+#define _CLICKED_SLOTS_H_
+#include <QObject>
+
+class Foo : public QObject 
+{
+    Q_OBJECT
+public slots:
+    void onClicked(const QString &photoURI, const QString &thumbnailURI);
+};
+
+#endif
--- liballphoto/test/main.cpp
+++ liballphoto/test/main.cpp
+/***************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (directui at nokia.com)
+**
+** This file is part of libmeegotouch.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at directui at nokia.com.
+**
+** This library is free software; you can redistribute it and/or
+** modify it under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation
+** and appearing in the file LICENSE.LGPL included in the packaging
+** of this file.
+**
+****************************************************************************/
+
+/*
+ * An example of a minimalistic DirectUI application
+ */
+
+#include <MApplication>
+#include <MApplicationWindow>
+#include <MApplicationPage>
+#include <MButton>
+#include <MLayout>
+#include <MLabel>
+#include <MGridLayoutPolicy>
+#include <MTextEdit>
+#include <dynamicphotolist.h>
+#include "clickedslot.h"
+
+
+int main(int argc, char **argv)
+{
+    /* The base class of all DirectUI applications */
+    MApplication app(argc, argv);
+
+    /* The application window is a top-level window that contains
+       the Home and Back/Close framework controls, Navigation bar,
+       View menu and Toolbar components */
+    MApplicationWindow w;
+    w.show();
+
+    /* Pages represent one "view" of an application, into which you
+       can add your application's contents. An application can have
+       any number of pages with transitions between them */
+    MApplicationPage p;
+
+    MButton button("xxx");
+    DynamicPhotoList *photoList = new DynamicPhotoList(NULL, M::Landscape);
+    Foo foo;
+    QObject::connect(photoList, SIGNAL(clicked(const QString &, const QString &)), &foo, SLOT(onClicked(const QString &, const QString &)));
+    p.setCentralWidget(photoList);
+    p.appear();
+
+    /* Let's add a simple push button to our page */
+    /*
+    QGraphicsWidget *container = p.centralWidget();
+    MLayout *layout = new MLayout(p.centralWidget());
+    container->setLayout(layout);
+    MGridLayoutPolicy *policy = new MGridLayoutPolicy(layout);
+    policy->setContentsMargins(0, 0, 0, 0);
+
+    MLabel *label = new MLabel("xxx");
+    label->setMinimumWidth(225);
+    label->setMaximumWidth(225);
+
+    policy->addItem(label, 0, 0);
+
+    MTextEdit *edit = new MTextEdit();
+    edit->setObjectName("xxx");
+    policy->addItem(edit, 0, 1);
+*/
+    return app.exec();
+}
--- liballphoto/test/testallphoto.pro
+++ liballphoto/test/testallphoto.pro
+TEMPLATE = app
+TARGET = testallphoto
+CONFIG += meegotouch
+LIBS += -lallphotos
+
+# Input
+SOURCES += main.cpp clickedslot.cpp
+
+HEADERS += clickedslot.h
+
+theme.files = themes/*
+theme.path = /usr/share/themes/base/meegotouch/$$TARGET
+theme.CONFIG += no_check_exist
+
+INSTALLS += theme
--- liballphoto/test/themes
+++ liballphoto/test/themes
+(directory)
--- liballphoto/test/themes/style
+++ liballphoto/test/themes/style
+(directory)
--- liballphoto/test/themes/style/testallphoto.css
+++ liballphoto/test/themes/style/testallphoto.css
+
+#thumbnailList {
+  minimum-size: 100% 100%;
+  maximum-size: 100% 100%;
+  preferred-size: 100% 100%; /*seems not working here */
+  background-color: #dadcd9;
+
+  margin-left:  0; 
+  margin-right:  0;
+  margin-top:    0;
+  margin-bottom: 0; 
+
+  padding-left: 0;
+  padding-top: 0;
+  padding-right: 0;
+  padding-bottom: 0;
+}
+
+ at const THUMBNAIL_CELL_SIZE: 108px;
+ at const THUMBNAIL_CELL_SIZE_H: 108px;
+
+ at const THUMBNAIL_SELECTED_CELL_SIZE: 88px;
+ at const THUMBNAIL_SELECTED_CELL_SIZE_H: 88px;
+
+ at const THUMBNAIL_CELL_SIZE_PORT: 96px;
+ at const THUMBNAIL_CELL_SIZE_H_PORT: 96px;
+#photoThumbnail_Landscape {
+  background-image:;
+
+  background-image-center :; 
+
+/*  background-image-single : "photo_thumb_frame_Large";  */
+  background-opacity: 1.0;
+
+  minimum-size: $THUMBNAIL_CELL_SIZE $THUMBNAIL_CELL_SIZE_H;
+  preferred-size: $THUMBNAIL_CELL_SIZE $THUMBNAIL_CELL_SIZE_H;
+  maximum-size: $THUMBNAIL_CELL_SIZE $THUMBNAIL_CELL_SIZE_H;
+
+  image-object-name: "PhotoThumbnailImage";
+  padding-left: 0;
+  padding-top: 0;
+  padding-right: 0;
+  padding-bottom: 0px;
+  margin-left:  0; 
+  margin-right:  0;
+  margin-top:    0;
+  margin-bottom: 0; 
+
+
+
+}
+
+#photoThumbnail_Landscape:selected {
+  background-image:;
+
+  background-image-center :; 
+
+/*  background-image-single : "photo_thumb_frame_Large";  */
+  background-opacity: 1.0;
+
+  minimum-size: $THUMBNAIL_CELL_SIZE $THUMBNAIL_CELL_SIZE_H;
+  preferred-size: $THUMBNAIL_CELL_SIZE $THUMBNAIL_CELL_SIZE_H;
+  maximum-size: $THUMBNAIL_CELL_SIZE $THUMBNAIL_CELL_SIZE_H;
+
+  image-object-name: "PhotoThumbnailImageSelected";
+  padding-left: 10px;
+  padding-top: 10px;
+  padding-right: 10px;
+  padding-bottom: 10px;
+  margin-left:  0; 
+  margin-right:  0;
+  margin-top:    0;
+  margin-bottom: 0; 
+}
+
+
+#photoThumbnail_Portrait {
+  background-image:;
+
+  background-image-center :; 
+
+/*  background-image-single : "photo_thumb_frame_Large";  */
+  background-opacity: 1.0;
+
+  minimum-size: $THUMBNAIL_CELL_SIZE_PORT $THUMBNAIL_CELL_SIZE_H_PORT;
+  preferred-size: $THUMBNAIL_CELL_SIZE_PORT $THUMBNAIL_CELL_SIZE_H_PORT;
+  maximum-size: $THUMBNAIL_CELL_SIZE_PORT $THUMBNAIL_CELL_SIZE_H_PORT;
+
+  image-object-name: "PhotoThumbnailImage";
+  padding-left: 0;
+  padding-top: 0;
+  padding-right: 0;
+  padding-bottom: 0px;
+  margin-left:  0; 
+  margin-right:  0;
+  margin-top:    0;
+  margin-bottom: 0; 
+
+
+
+}
+
+#photoThumbnail_Portrait:selected {
+  background-image:;
+
+  background-image-center :; 
+
+/*  background-image-single : "photo_thumb_frame_Large";  */
+  background-opacity: 1.0;
+
+  minimum-size: $THUMBNAIL_CELL_SIZE_PORT $THUMBNAIL_CELL_SIZE_H_PORT;
+  preferred-size: $THUMBNAIL_CELL_SIZE_PORT $THUMBNAIL_CELL_SIZE_H_PORT;
+  maximum-size: $THUMBNAIL_CELL_SIZE_PORT $THUMBNAIL_CELL_SIZE_H_PORT;
+
+  image-object-name: "PhotoThumbnailImageSelected";
+  padding-left: 0;
+  padding-top: 0;
+  padding-right: 0;
+  padding-bottom: 0px;
+  margin-left:  0; 
+  margin-right:  0;
+  margin-top:    0;
+  margin-bottom: 0; 
+
+
+
+}
+
+/*
+#photoThumbnail:pressed {
+  background-image : "duilist-singlecolumn-center-background-pressed" 8px 8px 8px 8px;
+
+  background-image-top-left : "duilist-topleft-background-pressed" 8px 8px 8px 8px;
+  background-image-top : "duilist-top-background-pressed" 8px 8px 8px 8px;
+  background-image-top-right : "duilist-topright-background-pressed" 8px 8px 8px 8px;
+  background-image-left : "duilist-left-background-pressed" 8px 8px 8px 8px;
+  background-image-center : "duilist-center-background-pressed" 8px 8px 8px 8px;
+  background-image-right : "duilist-right-background-pressed" 8px 8px 8px 8px;
+  background-image-bottom-left : "duilist-bottomleft-background-pressed" 8px 8px 8px 8px;
+  background-image-bottom : "duilist-bottom-background-pressed" 8px 8px 8px 8px;
+  background-image-bottom-right : "duilist-bottomright-background-pressed" 8px 8px 8px 8px;
+
+  background-image-single : "duilist-single-background-pressed" 8px 8px 8px 8px;
+
+  background-image-singlerow-left : "duilist-singlerow-left-background-pressed" 8px 8px 8px 8px;
+  background-image-singlerow-center : "duilist-singlerow-center-background-pressed" 8px 8px 8px 8px;
+  background-image-singlerow-right : "duilist-singlerow-right-background-pressed" 8px 8px 8px 8px;
+
+  background-image-singlecolumn-top : "duilist-singlecolumn-top-background-pressed" 8px 8px 8px 8px;
+  background-image-singlecolumn-center : "duilist-singlecolumn-center-background-pressed" 8px 8px 8px 8px;
+  background-image-singlecolumn-bottom : "duilist-singlecolumn-bottom-background-pressed" 8px 8px 8px 8px;
+
+  background-color: #212121;
+  background-opacity: 1.0;
+  image-object-name: "PhotoThumbnailImage";
+}
+    */
+
+ at const THUMBNAIL_SIZE: 108px;
+ at const THUMBNAIL_SELECTED_SIZE: 88px;
+ at const THUMBNAIL_SIZE_PORT: 96px;
+ at const THUMBNAIL_SELECTED_SIZE_PORT: 76px;
+#PhotoThumbnailImage.Landscape {
+  /* inherited from DuiWidgetStyle */
+  minimum-size: $THUMBNAIL_SIZE $THUMBNAIL_SIZE;
+  preferred-size: $THUMBNAIL_SIZE $THUMBNAIL_SIZE;
+  maximum-size: $THUMBNAIL_SIZE $THUMBNAIL_SIZE;
+
+  margin-left:  0; 
+  margin-right:  0;
+  margin-top:    0;
+  margin-bottom: 0; 
+  padding-left: 0px;
+	padding-top: 0px;
+	padding-right: 0px;
+	padding-bottom: 0;
+
+
+
+}
+
+#PhotoThumbnailImageSelected.Landscape {
+  /* inherited from DuiWidgetStyle */
+  minimum-size: $THUMBNAIL_SELECTED_SIZE $THUMBNAIL_SELECTED_SIZE;
+  preferred-size: $THUMBNAIL_SELECTED_SIZE $THUMBNAIL_SELECTED_SIZE;
+  maximum-size: $THUMBNAIL_SELECTED_SIZE $THUMBNAIL_SELECTED_SIZE;
+
+  margin-left:  0; 
+  margin-right:  0;
+  margin-top:    0;
+  margin-bottom: 0; 
+  padding-left: 10px;
+	padding-top: 10px;
+	padding-right: 10px;
+	padding-bottom: 10px;
+}
+
+#PhotoThumbnailImage.Portrait {
+  /* inherited from DuiWidgetStyle */
+  minimum-size: $THUMBNAIL_SIZE_PORT $THUMBNAIL_SIZE_PORT;
+  preferred-size: $THUMBNAIL_SIZE_PORT $THUMBNAIL_SIZE_PORT;
+  maximum-size: $THUMBNAIL_SIZE_PORT $THUMBNAIL_SIZE_PORT;
+    padding-left: 0px;
+	padding-top: 0px;
+	padding-right: 0px;
+	padding-bottom: 0;
+
+
+  margin-left:  0; 
+  margin-right:  0;
+  margin-top:    0;
+  margin-bottom: 0; 
+}
+
+#PhotoThumbnailImageSelected.Portrait {
+  /* inherited from DuiWidgetStyle */
+  minimum-size: $THUMBNAIL_SELECTED_SIZE_PORT $THUMBNAIL_SELECTED_SIZE_PORT;
+  preferred-size: $THUMBNAIL_SELECTED_SIZE_PORT $THUMBNAIL_SELECTED_SIZE_PORT;
+  maximum-size: $THUMBNAIL_SELECTED_SIZE_PORT $THUMBNAIL_SELECTED_SIZE_PORT;
+    padding-left: 10px;
+	padding-top: 10px;
+	padding-right: 10px;
+	padding-bottom: 10px;
+
+
+  margin-left:  0; 
+  margin-right:  0;
+  margin-top:    0;
+  margin-bottom: 0; 
+}
+
+#loadingSpinner {
+    minimum-size: 60px 60px;
+    preferred-size: 60px 60px;
+    maximum-size: 60px 60px; 
+
+    margin-left: 24px;
+    margin-top: 24px;
+    speed: 10;
+    active-element-count: 1;
+    element-size: 10px;
+    element-distance: 0.3;
+
+}
--- liballphoto/thumbnailer.cpp
+++ liballphoto/thumbnailer.cpp
+/*
+ * Meego-handset-photos is a photo viewer.
+ * Copyright (C) 2010, Intel Corporation.
+ *
+ * This program is licensed under the terms and conditions of the
+ * Apache License, version 2.0.  The full text of the Apache License is at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ */
+
+
+#include "thumbnailer.h"
+
+const QString Thumbnailer::service = "org.freedesktop.thumbnails.Thumbnailer1";
+const QString Thumbnailer::path = "/org/freedesktop/thumbnails/Thumbnailer1";
+const char   *Thumbnailer::interface = "org.freedesktop.thumbnails.Thumbnailer1";
+
+const QString Thumbnailer::default_flavor = "normal";
+const QString Thumbnailer::default_schedular = "foreground";
+
+Thumbnailer *Thumbnailer::thumbnailerInstance = NULL;
+
+Thumbnailer *Thumbnailer::instance()
+{
+    if (thumbnailerInstance)
+        return thumbnailerInstance;
+    else {
+        thumbnailerInstance = new Thumbnailer(QDBusConnection::sessionBus(), NULL);
+        return thumbnailerInstance;
+    }
+}
+
+void Thumbnailer::emitReadySignal(const unsigned int &handle, const QStringList &urls)
+{
+    emit ready(handle, urls);
+}
+
+void Thumbnailer::emitErrorSignal(const unsigned int &handle, const QStringList &urls, const int &errorCode, const QString &message)
+{
+    qDebug() << "ERROR SIGNAL " << handle << urls << errorCode << message;
+    emit error(handle, urls, errorCode, message);
+}
+
+Thumbnailer::Thumbnailer(const QDBusConnection &connection, QObject *parent) :
+        QDBusAbstractInterface(service, path, interface, connection, parent)
+{
+    QDBusConnection::sessionBus().connect(service, path, interface,
+                                    "Ready", this, SLOT(emitReadySignal(const unsigned int, const QStringList)));
+    QDBusConnection::sessionBus().connect(service, path, interface,
+                                    "Error", this, SLOT(emitErrorSignal(const unsigned int, const QStringList, \
+                                                                        const int, const QString )));
+}
+
+Thumbnailer::~Thumbnailer()
+{
+}
--- liballphoto/thumbnailer.h
+++ liballphoto/thumbnailer.h
+/*
+ * Meego-handset-photos is a photo viewer.
+ * Copyright (C) 2010, Intel Corporation.
+ *
+ * This program is licensed under the terms and conditions of the
+ * Apache License, version 2.0.  The full text of the Apache License is at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ */
+
+#ifndef THUMBNAILER_H
+#define THUMBNAILER_H
+
+#include <QtCore/QtCore>
+#include <QtCore/QObject>
+#include <QtDBus/QtDBus>
+
+/// @class Thumbnailer
+/// @brief Generates thumbnail images.
+///
+/// The Thumbnailer class:
+/// - generates thumbnail images by using tumbler service.
+///
+/// The class is a singleton. There should only ever be one instance
+/// running per application.
+///
+/// Thumbnails are generated asynchronously. A signal ready will be emitted
+/// after thumbnail is stored on disk.
+///
+/// The thumbnail size is controlled by tumbler service.
+
+
+class Thumbnailer: public QDBusAbstractInterface
+{
+    Q_OBJECT
+public:
+    /// @brief Return the instance of the thumbnailer singleton.
+    /// @return Thumbnailer Returns the single instance.
+    static Thumbnailer *instance();
+
+    /// @brief Destructor
+    ~Thumbnailer();
+
+private:
+    // Constructor is private because this is a singleton.
+    // Construct it using the instance() method.
+    Thumbnailer(const QDBusConnection &connection, QObject *parent = 0);
+
+    /// @brief Tumbler DBUS service name
+    const static QString service;
+
+    /// @brief Tumbler DBUS service path
+    const static QString path;
+
+    /// @brief Tumbler DBUS service interface
+    const static char* interface;
+
+    /// @brief Tumbler default flavor
+    const static QString default_flavor;
+
+    /// @brief Tumbler default schedular
+    const static QString default_schedular;
+
+    static Thumbnailer *thumbnailerInstance;
+
+public Q_SLOTS:
+    /// @brief Request thumbnail using default flavor and schedular
+    /// @param filePath const QString& The path of the video file
+    /// @param mimetype const QString& The mime type of the video file
+    /// @return QDBusPendingCall
+    inline QDBusPendingCall requestThumbnail(const QString &filePath, const QString &mimetype) {
+        QStringList paths;
+        QStringList mimetypes;
+        quint32 handle = 0;
+        paths << filePath;
+        mimetypes << mimetype;
+
+        return asyncCall(QLatin1String("Queue"), paths, mimetypes,
+                         default_flavor, default_schedular,
+                         handle);
+    }
+
+    /// @brief Request thumbnail using specified flavor and schedular
+    /// @param filePath const QString& The path of the video file
+    /// @param mimetype const QString& The mime type of the video file
+    /// @param flavor const QString& Specified flavor
+    /// @param scheduler const QString& Specified scheduler
+    /// @param handle quint32 Handler to dequeue from tumbler service
+    /// @return QDBusPendingCall
+    inline QDBusPendingCall requestThumbnail(const QString &filePath, const QString &mimetype,
+                                             const QString &flavor, const QString &scheduler,
+                                             quint32 handle) {
+        QStringList paths;
+        QStringList mimetypes;
+        paths << filePath;
+        mimetypes << mimetype;
+
+        return asyncCall(QLatin1String("Queue"), paths, mimetypes,
+                         flavor, scheduler,
+                         handle);
+    }
+
+    /// @brief Request thumbnails for a bunch of video files using specified flavor and schedular
+    /// @param filePath const QStringList& The paths of the video files
+    /// @param mimetype const QStringList& The mime types of the video files
+    /// @param flavor const QString& Specified flavor
+    /// @param scheduler const QString& Specified scheduler
+    /// @param handle quint32 Handler to dequeue from tumbler service
+    /// @return QDBusPendingCall
+    inline QDBusPendingCall requestThumbnail(const QStringList &paths, const QStringList &mimetypes,
+                                             const QString &flavor, const QString &scheduler,
+                                             quint32 handle) {
+        return asyncCall(QLatin1String("Queue"), paths, mimetypes,
+                         flavor, scheduler,
+                         handle);
+    }
+
+    /// @brief Emit ready signals when DBUS signal Ready is received
+    /// @param handle const unsigned int Handler that is ready
+    /// @param urls const QStringList& Urls of video files that thumbnail is ready
+    void emitReadySignal(const unsigned int &handle, const QStringList &urls);
+
+    void emitErrorSignal(const unsigned int &handle, const QStringList &urls, const int &errorCode, const QString &message);
+
+signals:
+    /// @brief ready signals when thumbnails are ready
+    /// @param handle const unsigned int Handler that is ready
+    /// @param urls const QStringList& Urls of video files that thumbnail is ready
+    void ready(const unsigned int &handle, const QStringList &urls);
+
+    void error(const unsigned int &handle, const QStringList &urls, const int &errorCode, const QString &message);
+};
+#endif
--- makedist
+++ makedist
@@ -34,7 +34,7 @@
 # Generate the base package release tar ball
 # NOTE: Becuase I used a git attribute that ignores the meego theme dir
 #       this archive will not include it... that's intentional!
-git archive --prefix=${BASE_PREFIX} ${TAG} | bzip2 -c -- > ${BASE_ARCHIVE} && {
+git archive --prefix=${BASE_PREFIX} ${TAG} base/ themes.pro | bzip2 -c -- > ${BASE_ARCHIVE} && {
 	echo "Created: ${BASE_ARCHIVE}"
 } || {
 	echo "Creation of release archive ${BASE_ARCHIVE} failed.  Reason unknown."
--- src/album-generator/albumsmerger.cpp
+++ src/album-generator/albumsmerger.cpp
@@ -47,27 +47,30 @@
         return;
 
     QString albumID = createDateAlbumID(dateRange, subid);
-    SearchEngine::createAlbum(albumID, albumID, photos);
+    //SearchEngine::createAlbum(albumID, albumID, photos);
+    trackerTasks << TrackerTask(albumID, TrackerTask::CreateAlbum, photos);
     QString albumUrn = albumID;
-    MPSettings settings;
 //    settings.setAlbumName(albumUrn, albumName);
     for(int i = 0; i < photos.size(); i++) {
-        settings.setPhotoAlbumUrn(photos.at(i), albumUrn);
+        AppWindow::instance()->settings.setPhotoAlbumUrn(photos.at(i), albumUrn);
     }
 
-    QString urlString = settings.getPhotoUri(photos[0]);
-    settings.setAlbumCover(albumUrn, urlString);
-    settings.setAlbumDate(albumUrn, dateRange.getStartDate().toString(), dateRange.getEndDate().toString());
-    settings.setAlbumID(albumUrn, albumID);
+    QStringList photoInfo = GetAllPhotosWorker::instance()->getPhotoInfo(photos[0]);
+    QString urlString = SearchEngine::getPhotoUri(photoInfo);
+    AppWindow::instance()->settings.setAlbumCover(albumUrn, urlString);
+    AppWindow::instance()->settings.setAlbumPhotoNum(albumUrn, photos.count());
+    AppWindow::instance()->settings.setAlbumDate(albumUrn, dateRange.getStartDate().toString(), dateRange.getEndDate().toString());
+    AppWindow::instance()->settings.setAlbumID(albumUrn, albumID);
 } 
 
 void AlbumsMerger::deleteOneAlbum(const QString &id)
 {
-    MPSettings settings;
     QString albumID = id;
     QString albumUrn = albumID;
-    SearchEngine::deleteAlbum(albumID);
-    settings.remove(albumUrn);
+    //SearchEngine::deleteAlbum(albumID);
+    trackerTasks << TrackerTask(albumID, TrackerTask::DeleteAlbum);
+    AppWindow::instance()->settings.remove(albumUrn);
+    AppWindow::instance()->settings.removeAlbum(albumUrn);
 }
 
 void AlbumsMerger::deleteAllAlbums()
@@ -110,11 +113,14 @@
     dateOnlyMerge(&dateOnlyPhotos);
     locationOnlyMerge(&locationPhotos);
     emit albumsChanged();
+    QTimer::singleShot(0, this, SLOT(doTrackerTasks()));
     return true;
 }
  
 bool AlbumsMerger::merge(QVector<QStringList> *photos)
 {
+    QTime t;
+    t.start();
     deleteAllAlbums();
     return mergeOnly(photos);
 }
@@ -124,14 +130,14 @@
     if (photos->count() <= 0)
         return false;
 
-    MPSettings settings;
-    qDebug() << "12345 dateOnlyMerge photos count:" << photos->count();
-
     for (QVector<QStringList>::iterator i = photos->begin(); i != photos->end(); i++) {
         QString urn = (*i)[2];
+        QTime t;
+        t.start();
         DateRange &dateRange = getDateRange(urn);
 
         if (!dateAlbumsHash[dateRange].contains(urn)) {
+            t.start();
             dateAlbumsHash[dateRange] << urn;
             QStringList &photoInAlbums = dateAlbumsHash[dateRange];
     
@@ -191,21 +197,24 @@
         for (int j = 0; j < numSplitAlbums; j++) {
             QString albumID = createLocationAlbumID(locationVec[j]);
             deleteOneAlbum(albumID);
-            SearchEngine::createAlbum(albumID, albumID, photosVec[j]);
+            //SearchEngine::createAlbum(albumID, albumID, photosVec[j]);
+            trackerTasks << TrackerTask(albumID, TrackerTask::CreateAlbum, photosVec[j]);
             QString albumUrn = albumID;
-            MPSettings settings;
             for(int i = 0; i < photosVec[j].size(); i++) {
-                settings.setPhotoAlbumUrn(photosVec[j].at(i), albumUrn);
+                AppWindow::instance()->settings.setPhotoAlbumUrn(photosVec[j].at(i), albumUrn);
             }
 
-            QString urlString = settings.getPhotoUri(photosVec[j][0]);
+            QStringList photoInfo = GetAllPhotosWorker::instance()->getPhotoInfo(photosVec[j][0]);
+            QString urlString = SearchEngine::getPhotoUri(photoInfo);
+
             //        QUrl coverUrl(urlString);
 
-            settings.setAlbumCover(albumUrn, urlString);
-            settings.setAlbumDate(albumUrn, GetAllPhotosWorker::getPhotoDate(photosVec[j][0]).toString(),
+            AppWindow::instance()->settings.setAlbumCover(albumUrn, urlString);
+            AppWindow::instance()->settings.setAlbumDate(albumUrn, GetAllPhotosWorker::getPhotoDate(photosVec[j][0]).toString(),
                     GetAllPhotosWorker::getPhotoDate(photosVec[j].last()).toString());
-            settings.setAlbumID(albumUrn, albumID);
-            settings.setAlbumLocation(albumUrn, locationVec[j]);
+            AppWindow::instance()->settings.setAlbumID(albumUrn, albumID);
+            AppWindow::instance()->settings.setAlbumPhotoNum(albumUrn, photosVec[j].count());
+            AppWindow::instance()->settings.setAlbumLocation(albumUrn, locationVec[j]);
         }
         emit albumsChanged();
     }
@@ -213,16 +222,19 @@
 
 bool AlbumsMerger::locationOnlyMerge(QVector<QStringList> *photos)
 {
-    MPSettings settings;
-
     for (QVector<QStringList>::iterator i = photos->begin(); i != photos->end(); i++) {
-        QString urn = (*i)[2];
-        QString location = settings.getPhotoLocation(urn);
+        QString urn = SearchEngine::getPhotoUrn(*i);
+            QTime t;
+            t.start();
+        QString location = AppWindow::instance()->settings.getPhotoLocation(urn);
 
         if (!location.isEmpty()) {
+            QTime t;
+            t.start();
             addOneLocationPhotoIntoAlbum(urn, location);
         } else  {
-            QString uri = settings.getPhotoUri(urn);
+            QStringList photoInfo = GetAllPhotosWorker::instance()->getPhotoInfo(urn);
+            QString uri = SearchEngine::getPhotoUri(photoInfo);
             QString localPath = QUrl(uri).toLocalFile();
             ExifUtils exif(localPath);
             float lat, lon;
@@ -237,11 +249,11 @@
 
 bool AlbumsMerger::isLocationPhoto(const QString &photoUrn)
 {
-    MPSettings settings;
-    QString location = settings.getPhotoLocation(photoUrn);
+    QString location = AppWindow::instance()->settings.getPhotoLocation(photoUrn);
     bool res = false;
     if (location.isEmpty()) {
-        QString uri = settings.getPhotoUri(photoUrn);
+        QStringList photoInfo = GetAllPhotosWorker::instance()->getPhotoInfo(photoUrn);
+        QString uri = SearchEngine::getPhotoUri(photoInfo);
         QString localPath = QUrl(uri).toLocalFile();
         ExifUtils exif(localPath);
         float dummy;
@@ -267,32 +279,31 @@
 
 void AlbumsMerger::onTranslated(const QVariant &userdata, const QString &location)
 {
-    MPSettings settings;
     QString urn = userdata.toString();
-    settings.setPhotoLocation(urn, location);
+    AppWindow::instance()->settings.setPhotoLocation(urn, location);
 
     addOneLocationPhotoIntoAlbum(urn,location);
+    QTimer::singleShot(0, this, SLOT(doTrackerTasks()));
 }
 
 void AlbumsMerger::handlePhotoAdded(const QStringList &urns)
 {
     QVector<QStringList> x(urns.count());
-    MPSettings settings;
+/*    MPSettings settings;
     for (int i = 0; i < urns.count(); i++) {
         x[i] << "" << "" << urns[i];
         settings.setPhotoUri(urns[i], SearchEngine::getUrl(urns[i]));
-    }
+    } */
     mergeOnly(&x);
 }
 
 void AlbumsMerger::handlePhotoRemoved(const QStringList &urns)
 {
-    MPSettings settings;
     QString location;
     bool shouldUpdate = false;
     for (int i = 0; i < urns.count(); i++) {
         if (!AppWindow::instance()->photoRemoveBlacklist.contains(urns[i])) {
-            location = settings.getPhotoLocation(urns[i]);
+            location = AppWindow::instance()->settings.getPhotoLocation(urns[i]);
             if (!location.isEmpty()) {
                 locationAlbumsHash[location].removeAll(urns[i]);
                 if (locationAlbumsHash[location].count() == 0) {
@@ -302,7 +313,7 @@
                 }
             } else {
                 // the photo belongs to a date only location
-                QDate date = QDate::fromString(settings.getPhotoDate(urns[i]));
+                QDate date = GetAllPhotosWorker::instance()->getPhotoDate(urns[i]);
                 DateRange range(date, date);
                 if (dateRanges.contains(range)) {
                     dateAlbumsHash[range].removeAll(urns[i]);
@@ -321,8 +332,34 @@
             shouldUpdate = true;
         } else {
         }
-        settings.remove(urns[i]);
+        AppWindow::instance()->settings.remove(urns[i]);
     }
     if (shouldUpdate)
         emit albumsChanged();
+    QTimer::singleShot(0, this, SLOT(doTrackerTasks()));
+}
+
+void AlbumsMerger::doTrackerTasks()
+{
+    while (!trackerTasks.isEmpty()) {
+        TrackerTask task = trackerTasks.takeFirst();
+        task.run();
+    }
+}
+
+
+TrackerTask::TrackerTask(const QString &albumID, TrackerTask::Command cmd, const QStringList &photos) : albumID(albumID), command(cmd), photos(photos)
+{
+}
+
+void TrackerTask::run()
+{
+    switch (command) {
+        case TrackerTask::DeleteAlbum:
+            SearchEngine::deleteAlbum(albumID);
+            break;
+        case TrackerTask::CreateAlbum:
+            SearchEngine::createAlbum(albumID, albumID, photos); 
+            break;
+    }
 }
--- src/album-generator/albumsmerger.h
+++ src/album-generator/albumsmerger.h
@@ -15,6 +15,7 @@
 #include <QDate>
 #include <QHash>
 #include <QVector>
+#include <QStringList>
 
 #include "reversegeocoder.h"
 #define MaxAlbums 20
@@ -56,6 +57,22 @@
     QDate end;
 };
 
+class TrackerTask
+{
+public:
+    enum Command {
+        DeleteAlbum,
+        CreateAlbum
+    };
+    TrackerTask(const QString &albumID, TrackerTask::Command cmd, const QStringList &photos = QStringList());
+
+    void run();
+private:
+    Command command;
+    QString albumID;
+    QStringList photos;
+};
+
 class AlbumsMerger : public QObject, public ReverseGeocoderAction
 {
     Q_OBJECT
@@ -98,10 +115,13 @@
 
     ReverseGeocoder *geocoder;
 
+    QList<TrackerTask> trackerTasks;
+
 
 private slots:
     void handlePhotoAdded(const QStringList &);
     void handlePhotoRemoved(const QStringList &);
+    void doTrackerTasks();
 
 };
 
--- src/album-generator/albumsmergethread.cpp
+++ src/album-generator/albumsmergethread.cpp
@@ -20,7 +20,6 @@
 
 void AlbumsMergeThread::run()
 {
-    qDebug() << "12345 AlbumsMergeThread starts\n";
     QEventLoop loop;
     QVector<QStringList> *photos = NULL;
     GetAllPhotosWorker *worker = GetAllPhotosWorker::instance();
@@ -32,7 +31,10 @@
     AlbumsMerger merger;
     if (photos) {
         QObject::connect(&merger, SIGNAL(albumsChanged()), this, SLOT(onAlbumsChanged()));
+        QTime t;
+        t.start();
         merger.merge(photos);
+        qDebug() << "12345 merge elaped:" << t.elapsed();
     }
     loop.exec();
 }
--- src/appwindow.cpp
+++ src/appwindow.cpp
@@ -568,7 +568,6 @@
         topToolbarLayout->insertItem(1, albumInfoFreestyleContainer);
         albumInfoFreestyleContainer->show();
 
-        MPSettings settings;
         currentAlbumName = settings.getPhotoAlbumName(currentPhotoUrn);
         currentAlbumUrn = settings.getPhotoAlbumUrn(currentPhotoUrn);
 
@@ -735,7 +734,6 @@
 
 void AppWindow::showAlbum(QString &urn)
 {
-    MPSettings settings;
     QString displayString = settings.getAlbumName(urn);
     albumInfoButtonInInfoContainer->setText(displayString); 
     photoInfoButtonInInfoContainer->hide();
@@ -783,7 +781,8 @@
     //viewport->setObjectName("viewportAlbum");
     currentPhotoList = photosInOneAlbumList;
 
-    currentPage()->disappear();
+    if (currentPage())
+        currentPage()->disappear();
     allPhotosPage->appear();
     allPhotosPage->setEscapeMode(MApplicationPageModel::EscapeManualBack);
     autoHideTimer.stop();
@@ -1025,10 +1024,12 @@
         MPListModel *orgModel = dynamic_cast<MPListModel *>(photoList->itemModel());
         searchPhotoProxyModel = new MPProxyListModel(MPAbstractListModelOps::Photo);
         searchPhotoProxyModel->setSourceModel(orgModel);
+        searchPhotoProxyModel->setDynamicSortFilter(true);
 
         MPListModel *orgAlbumModel = dynamic_cast<MPListModel *>(albumList->itemModel());
         searchAlbumProxyModel = new MPProxyListModel(MPAbstractListModelOps::Album);
         searchAlbumProxyModel->setSourceModel(orgAlbumModel);
+        searchAlbumProxyModel->setDynamicSortFilter(true);
 
         SearchResults *searchResults = new SearchResults(searchPhotoProxyModel, searchAlbumProxyModel);
         searchResultsPage->setCentralWidget(searchResults);
--- src/appwindow.h
+++ src/appwindow.h
@@ -73,6 +73,7 @@
     DynamicPhotoList *photosInOneAlbumList;
     DynamicMList* currentPhotoList;
     QStringList photoRemoveBlacklist;   //don't handle if a urn is removed in this list
+    MPSettings settings;
     
 
     void showOverlay(void);
--- src/dynamicalbumlist.cpp
+++ src/dynamicalbumlist.cpp
@@ -84,8 +84,7 @@
     MContentItemEx *itemEx = dynamic_cast<MContentItemEx *>(sender()->parent());
     QVariant data = (itemEx->index).data(Qt::DisplayRole);
     MPListItem *item = static_cast<MPListItem *>(data.value<void *>());
-    MPSettings settings;
-    QString albumID = settings.getAlbumID(item->urn);
+    QString albumID = AppWindow::instance()->settings.getAlbumID(item->urn);
     
     QVector<QStringList> result;
     SearchEngine::getAlbumPhotos(albumID, result);
--- src/getallphotosworker.cpp
+++ src/getallphotosworker.cpp
@@ -15,59 +15,108 @@
 #include "exifutils.h"
 
 GetAllPhotosWorker *GetAllPhotosWorker::worker_instance = NULL;
+QVector<QStringList> GetAllPhotosWorker::allPhotos;
+QHash<QString, QStringList> GetAllPhotosWorker::allPhotosMetaHash;
 
 void GetAllPhotosWorker::storePhotoMetaInfo(QVector<QStringList> &allPhotos) 
 {
     for (int i = 0; i < allPhotos.count(); i++) {
-        QString photoUrn = allPhotos[i][2];
-        QString photoUri = allPhotos[i][0]; 
-        MPSettings settings;
-        settings.setPhotoUri(photoUrn, photoUri);
-        QString photoLastModifiedDate = allPhotos[i][3];
-        QString dateOnly = QDate::fromString(((photoLastModifiedDate.split("T"))[0]), "yyyy-MM-dd").toString();
-        settings.setPhotoLastModifiedDate(photoUrn, dateOnly);
+        QString photoUrn = SearchEngine::getPhotoUrn(allPhotos[i]);
+        allPhotosMetaHash.insert(photoUrn, allPhotos[i]);
     }
 }
 
+void GetAllPhotosWorker::addOnePhoto(const QStringList &photo)
+{
+    mutexForAllPhotos.lock();
+    allPhotos << photo;
+    allPhotosMetaHash.insert(SearchEngine::getPhotoUrn(photo), photo);
+    sortByDate(allPhotos);
+    mutexForAllPhotos.unlock();
+}
+
+QStringList GetAllPhotosWorker::getPhotoInfo(const QString &photoUrn)
+{
+    mutexForAllPhotos.lock();
+    QStringList ret = allPhotosMetaHash.value(photoUrn);
+    mutexForAllPhotos.unlock();
+    return ret;
+}
+
+void GetAllPhotosWorker::removeOnePhoto(const QString photoUrn)
+{
+    mutexForAllPhotos.lock();
+    QStringList photoInfo = allPhotosMetaHash.value(photoUrn);
+    if (photoInfo.count() > 0) {
+        allPhotos.remove(allPhotos.indexOf(photoInfo));
+        allPhotosMetaHash.remove(photoUrn);
+    }
+    mutexForAllPhotos.unlock();
+}
+
 void GetAllPhotosWorker::run()
 {
+    QTime t2;
+    t2.start();
     SearchEngine::getAllPhoto(allPhotos);
+    qDebug() << "12345: getAllPhotos elapsed:" << t2.elapsed();
+    QTime t;
+    t.start();
     storePhotoMetaInfo(allPhotos);
+    qDebug() << "12345: storePhotoMetaInfo elapsed:" << t.elapsed();
+    t.start();
     sortByDate(allPhotos);
+    qDebug() << "12345: sortByDate all photos t:" << t.elapsed();
     mutex.lock();
     isDone = true;
     mutex.unlock();
     cond.wakeAll();
-    qDebug() << "12345 GetAllPhotosWorker thread done";
 }
 
 QDate GetAllPhotosWorker::getPhotoDate(const QString &photoUrn)
 {
-    MPSettings settings;
-    QString dateString = settings.getPhotoDate(photoUrn);
-    if (dateString.isEmpty()) {
-        QString uri = settings.getPhotoUri(photoUrn);
-        QString path = QUrl(uri).toLocalFile();
-        QDate date;
-        bool result = ExifUtils(path).getDate(date);
-        if (!result) {
-            date = QFileInfo(path).lastModified().date();
-        }
-        dateString = date.toString();
+    QStringList photoMeta = allPhotosMetaHash.value(photoUrn);
+    return getPhotoDate(photoMeta);
+}
+
+QDate GetAllPhotosWorker::extractLastModifiedDate(const QStringList &a)
+{
+    return QDate::fromString(((a[3].split("T"))[0]), "yyyy-MM-dd");
+}
 
-        settings.setPhotoDate(photoUrn, dateString);
+QDate GetAllPhotosWorker::getPhotoDate(const QStringList &a)
+{
+    QString dateString = SearchEngine::getPhotoContentCreated(a);
+    if (dateString.isEmpty())
+        dateString = SearchEngine::getPhotoLastModified(a);
+
+    return QDate::fromString(((dateString.split("T"))[0]), "yyyy-MM-dd");
+    /*(
+    QDate date;
+    QString uri = a[0];
+    QString path = QUrl(uri).toLocalFile();
+    bool result = ExifUtils(path).getDate(date);
+    if (!result) {
+        date = extractLastModifiedDate(a);
     }
-    return QDate::fromString(dateString);
+    return date;
+    */
 }
 
 bool GetAllPhotosWorker::compare(const QStringList &a, const QStringList &b)
 {
-    return compare2(a[2], b[2]);
+    QDate dateOnlyA = getPhotoDate(a);
+    QDate dateOnlyB = getPhotoDate(b);
+    return dateOnlyA > dateOnlyB;
 }
 
 bool GetAllPhotosWorker::compare2(const QString &a, const QString &b)
 {
+    QTime t;
+    static int i = 0;
+    t.start();
     bool res = getPhotoDate(a) > getPhotoDate(b);
+    qDebug() << "12345 compare2 no: " << i++ << " time; " << t.elapsed();
     return res;
 }
 
--- src/getallphotosworker.h
+++ src/getallphotosworker.h
@@ -16,7 +16,8 @@
 
 class GetAllPhotosWorker : public QThread {
 public:
-    QVector<QStringList> allPhotos;
+    static QVector<QStringList> allPhotos;
+    static QHash<QString, QStringList> allPhotosMetaHash;
     inline void waitTillDone() {
        mutex.lock();
        while (!isDone) {
@@ -34,19 +35,25 @@
 //    static QString photoUrnToPath(const QString &photoUrn);
 
     static QDate getPhotoDate(const QString &photoUrn);
+    static QDate getPhotoDate(const QStringList &a);
 
     static GetAllPhotosWorker *instance();
+    void addOnePhoto(const QStringList &photo);
+    void removeOnePhoto(const QString photoUrn);
+    QStringList getPhotoInfo(const QString &photoUrn);
 
 private:
     static bool compare(const QStringList &a, const QStringList &b);
     static bool compare2(const QString &a, const QString &b);
     static GetAllPhotosWorker *worker_instance;
+    static QDate extractLastModifiedDate(const QStringList &a);
     
     void storePhotoMetaInfo(QVector<QStringList> &allPhotos);
 
     bool isDone;
     QWaitCondition cond;
     QMutex mutex;
+    QMutex mutexForAllPhotos;
 };
 
 #endif // UTILS_H
--- src/mplistmodel.cpp
+++ src/mplistmodel.cpp
@@ -21,6 +21,7 @@
 #include "trackerlistener.h"
 #include "mpsettings.h"
 #include "getallphotosworker.h"
+#include "appwindow.h"
 
 QHash<QString, QString> MPListModel::urnToPathHashMap;
 
@@ -65,7 +66,6 @@
     
     if (getDataType() == MPAbstractListModelOps::Album) {
         // create a album model
-        MPSettings settings;
         if (albumUrn == "") {
             QVector<QStringList> *result = new QVector<QStringList>();
             SearchEngine::getAllPhotoAlbums(*result);
@@ -74,7 +74,7 @@
                 MPListItem *t = new MPListItem((*i)[1], ((*i)[2]).toInt(), (*i)[0]);
                 t->thumbnail = pDefaultThumbnail;
                 t->urn = (*i)[0];   //intended, use ID as urn. albumID == albumURN
-                t->photoURI = settings.getAlbumCover(t->urn);
+                t->photoURI = AppWindow::instance()->settings.getAlbumCover(t->urn);
                 listItemsVector << t;
             
                 pathToIndexHashMap.insert(QUrl::fromEncoded(t->photoURI.toAscii()).toLocalFile(), indexValue++);
@@ -101,8 +101,7 @@
         }
 
     } else {
-        MPSettings settings;
-        QString albumID = settings.getAlbumID(albumUrn);
+        QString albumID = AppWindow::instance()->settings.getAlbumID(albumUrn);
         photos = new QVector<QStringList>();
         deletePhotos = true;
         SearchEngine::getAlbumPhotos(albumID, *photos);
@@ -226,6 +225,7 @@
         //We don't remove this urn from the hashmap since it may be used later by 
         //other model
         QString urn = urns[i];
+        GetAllPhotosWorker::instance()->removeOnePhoto(urn);
         QString path = urnToPathHashMap.value(urn, QString());
         if (!path.isEmpty()) {
             int index = pathToIndexHashMap.value(path, -1);
@@ -251,17 +251,18 @@
     emit layoutAboutToBeChanged();
     int cnt = listItemsVector.count();
     beginInsertRows(QModelIndex(), cnt - 1, cnt - 1);
-    MPSettings settings;
     for (int i = 0; i < urns.count(); i++) {
         QString urn = urns[i];
-        QString path = SearchEngine::getUrl(urn);
-        settings.setPhotoUri(urn, path);
+        QStringList photoResult = SearchEngine::getOnePhoto(urn);
+        GetAllPhotosWorker::instance()->addOnePhoto(photoResult);
+
+        QString path = SearchEngine::getPhotoUri(photoResult);
         QString key = path.remove(QRegExp("^file:\/\/"));
         int indexValue = pathToIndexHashMap.value(key, -1);
         if (indexValue != -1)
             continue;   /* we already have it */
 
-        QString mime = SearchEngine::getMimeType(urn);
+        QString mime = SearchEngine::getPhotoMimeType(photoResult);
         MPListItem *item = new MPListItem(path, "", pDefaultThumbnail, mime);
         item->urn = urn;
         listItemsVector << item;
@@ -300,6 +301,8 @@
     // This function will be called many times during fast panning, lets
     // check boundaries and validnes only in debug mode
     Q_ASSERT(index.isValid());
+    if (index.row() >= listItemsVector.size())
+        qDebug() << "12345 data index:" << index.row() << "size:" << listItemsVector.size();
     Q_ASSERT(index.row() < listItemsVector.size());
 
     if (role == Qt::DisplayRole) {
@@ -476,13 +479,17 @@
     qDebug() << "MPListModel::updateAlbumModel() end";
 }
 #else
+
 void MPListModel::updateAlbumModel()
 {
-    qDebug() << "MPListModel::updateAlbumModel()";
+    QTime t;
+    t.start();
     if (getDataType() == MPAbstractListModelOps::Album) {
         // create a album model
         QVector<QStringList> *result = new QVector<QStringList>();
-        SearchEngine::getAllPhotoAlbums(*result);
+        qDebug() << "12345: ready";
+        AppWindow::instance()->settings.getAllAlbums(*result);
+        qDebug() << "12345: done: result cnt:" << result->count() << "listitemsvector " << listItemsVector.count();
         bool isRemove = false;
         int delta = 0;
         if (result->count() < listItemsVector.count()) {
@@ -490,8 +497,9 @@
             delta = listItemsVector.count() - result->count();
             qDebug() << "12345: delta is " << delta;
             emit layoutAboutToBeChanged();
-            beginRemoveRows(QModelIndex(), listItemsVector.count() - delta, listItemsVector.count() - 1);
-            for (int i = listItemsVector.count() - delta; i < listItemsVector.count() - 1; i++) {
+            int cnt = listItemsVector.count();
+            beginRemoveRows(QModelIndex(), cnt - delta, cnt - 1);
+            for (int i = cnt - delta; i < cnt - 1; i++) {
                 delete listItemsVector[i];
             }
             listItemsVector.remove(listItemsVector.count() - delta, delta);
@@ -499,7 +507,6 @@
             endRemoveRows();
             emit layoutChanged();
         } else if (result->count() > listItemsVector.count()) {
-            MPSettings settings;
             delta = result->count() - listItemsVector.count();
             qDebug() << "12345: delta for add is " << delta << "result is " << result->count();
             emit layoutAboutToBeChanged();
@@ -509,7 +516,7 @@
                 MPListItem *t = new MPListItem((result->at(i))[1], ((result->at(i))[2]).toInt(), (result->at(i))[0]);
                 t->thumbnail = pDefaultThumbnail;
                 t->urn = result->at(i)[0];
-                t->photoURI = settings.getAlbumCover(t->urn);
+                t->photoURI = AppWindow::instance()->settings.getAlbumCover(t->urn);
                 
                 listItemsVector << t;
             }
@@ -521,7 +528,6 @@
         }
 
         int indexValue = 0;
-        MPSettings settings;
         QVector<MPListItem *> itemsNeedUpdated;
         QVector<MPListItem *> itemsNewAdded;
 
@@ -530,7 +536,7 @@
             MPListItem *t = new MPListItem((result->at(i))[1], ((result->at(i))[2]).toInt(), (result->at(i))[0]);
             t->thumbnail = pDefaultThumbnail;
             t->urn = result->at(i)[0];
-            t->photoURI = settings.getAlbumCover(t->urn);
+            t->photoURI = AppWindow::instance()->settings.getAlbumCover(t->urn);
 
             if (i < listItemsVector.count()) {
                 if (t->photoURI == listItemsVector[i]->photoURI) {
@@ -551,6 +557,7 @@
         }
         list->getTasklet()->stopJobQueue();
         list->doTasklet();
+        qDebug() << "12345 updatealbum : " << t.elapsed();
     }
 }
 
@@ -562,19 +569,7 @@
 }
 
 QString MPListItem::albumName() {
-    QString start, end;
-    QString location;
-    QString name;
-    MPSettings settings;
-    location = settings.getAlbumLocation(urn);
-    if (!location.isEmpty()) {
-        name = location + " "; 
-    } 
-    settings.getAlbumDate(urn, start, end);
-    qDebug() << "paul debug albumName: urn:" << urn << "date" << start;
-    name.append(start);
-    settings.setAlbumName(urn, name);
-    return name;
+    return AppWindow::instance()->settings.getAlbumName(urn);
 }
 
 
--- src/mpproxylistmodel.cpp
+++ src/mpproxylistmodel.cpp
@@ -14,6 +14,8 @@
 #include "mpabstractlistmodelops.h"
 #include "mpsettings.h"
 #include "searchengine.h"
+#include "getallphotosworker.h"
+#include "appwindow.h"
 
 void MPProxyListModel::parseDate(QString in, int &year, int &month, int &day, int &dayOfAWeek) const
 {
@@ -107,6 +109,7 @@
     qDebug() << "photoDate: " << dateString1;
     qDebug() << "user input Date year:" << year << "month:" << month << "day:" << day << "dayOfAWeek:" << dayOfAWeek;
 
+
     if (day != -1) {
         if (photoDate.day() == day)
             result = true;
@@ -144,15 +147,14 @@
 
 bool MPProxyListModel::matchPhoto(const QString &photoUrn, const QString &userInput) const
 {
-    MPSettings settings;
-    QString dateString = settings.getPhotoDate(photoUrn);
+    QString dateString = GetAllPhotosWorker::instance()->getPhotoDate(photoUrn).toString();
     if (compareDate(dateString, userInput)) {
         qDebug() << "MPProxyListModel::filterAcceptsRow compare date returns ture";
         return true;
     }
 
     //Now location
-    QString location = settings.getPhotoLocation(photoUrn);
+    QString location = AppWindow::instance()->settings.getPhotoLocation(photoUrn);
     if (compareLocation(location, userInput)) {
         qDebug() << "MPProxyListModel::filterAcceptsRow compare location returns ture";
         return true;
@@ -174,11 +176,10 @@
     MPAbstractListModelOps::DataType  dataType;
     dataType = getDataType();
     
-   MPSettings settings;
 
     if (dataType == MPAbstractListModelOps::Album) {
         QString albumUrn = item->urn;
-        QString albumID = settings.getAlbumID(albumUrn);
+        QString albumID = AppWindow::instance()->settings.getAlbumID(albumUrn);
         QVector<QStringList> photosInTheAlbum;
         SearchEngine::getAlbumPhotos(albumID, photosInTheAlbum);
         bool isMatched = false;
@@ -201,7 +202,7 @@
 void MPProxyListModel::setSearchString(QString _searchString)
 {
     searchString = _searchString;
-    reset();
+    invalidateFilter();
 }
 
 void MPProxyListModel::setSpinner(const QModelIndex &index, bool showSpinner)
--- src/mpsettings.cpp
+++ src/mpsettings.cpp
@@ -9,6 +9,8 @@
  */
 
 #include <QString>
+#include <QStringList>
+#include <QDate>
 #include <QDebug>
 
 #include "mpsettings.h"
@@ -27,6 +29,7 @@
 const QString MPSettings::ALBUM_NAME_KEY = "MP_ALBUM_NAME_KEY";
 const QString MPSettings::ALBUM_ID_KEY = "MP_ALBUM_ID_KEY";
 const QString MPSettings::ALBUM_COVER_KEY = "MP_ALBUM_COVER_KEY";
+const QString MPSettings::ALBUM_PHOTONUM_KEY = "MP_ALBUM_PHOTONUM_KEY";
 
 
 MPSettings::MPSettings(const QString & organization, const QString & application, QObject * parent) : 
@@ -49,7 +52,6 @@
         
     photoMeta.insert(key, value);
     settings.setValue(photoUrn, photoMeta);
-//    settings.sync();
 }
 
 QVariant MPSettings::value(const QString &photoUrn, const QString &key) const
@@ -76,7 +78,8 @@
 {
     return value(photoUrn, PHOTO_LOCATION_KEY).toString();
 }
- 
+
+#if 0
 void MPSettings::setPhotoUri(const QString &photoUrn, const QString &uri)
 {
     setValue(photoUrn, PHOTO_URI_KEY, uri);
@@ -86,6 +89,7 @@
 {
     return value(photoUrn, PHOTO_URI_KEY).toString();
 }
+#endif
        
 void MPSettings::setPhotoAlbumUrn(const QString &photoUrn, const QString &albumUrn)
 {
@@ -121,6 +125,7 @@
     return getAlbumName(albumUrn);
 }
 
+#if 0
 void MPSettings::setPhotoDate(const QString &photoUrn, const QString &dateString)
 {
     setValue(photoUrn, PHOTO_DATE_KEY, dateString);
@@ -140,10 +145,15 @@
 {
     return value(photoUrn, PHOTO_LAST_MODIFIED_DATE_KEY).toString();
 }
+#endif
 
 void MPSettings::setAlbumDate(const QString &albumUrn, const QString &fromDateString, const QString &toDateString)
 {
-    qDebug() << "paul debug album date set album " << albumUrn << "date " << fromDateString;
+    MetaData &albumInfo = albumMeta[albumUrn];
+        
+    albumInfo.insert(ALBUM_FROM_DATE_KEY, fromDateString);
+    albumInfo.insert(ALBUM_TO_DATE_KEY, toDateString);
+
     setValue(albumUrn, ALBUM_FROM_DATE_KEY, fromDateString);
     setValue(albumUrn, ALBUM_TO_DATE_KEY, toDateString);
 }
@@ -162,17 +172,43 @@
 
 void MPSettings::setAlbumLocation(const QString &albumUrn, const QString &location)
 {
+    MetaData &albumInfo = albumMeta[albumUrn];
+        
+    albumInfo.insert(ALBUM_LOCATION_KEY, location);
+
     setValue(albumUrn, ALBUM_LOCATION_KEY, location);
 }
 
 void MPSettings::setAlbumName(const QString &albumUrn, const QString &name)
-{
-    setValue(albumUrn, ALBUM_NAME_KEY, name);
+{/*
+    MetaData &albumInfo = albumMeta[albumUrn];
+        
+    albumInfo.insert(ALBUM_NAME_KEY, name);
+    setValue(albumUrn, ALBUM_NAME_KEY, name); */
 }
 
 QString MPSettings::getAlbumName(const QString &albumUrn)
 {
-    return value(albumUrn, ALBUM_NAME_KEY).toString();
+    QString start, end;
+    QString location;
+    QString name;
+    location = getAlbumLocation(albumUrn);
+    if (!location.isEmpty()) {
+        name = location + " "; 
+    } 
+    getAlbumDate(albumUrn, start, end);
+    QDate startDate = QDate::fromString(start);
+    QDate today = QDate::currentDate();
+    int delta = startDate.daysTo(today);
+    if (delta == 0)
+        start = "Today";
+    else if (delta == 1)
+        start = "Yesterday";
+    else if (delta == -1)
+        start = "Tommorrow"; //possible????
+
+    name.append(start);
+    return name;
 }
 
 void MPSettings::setAlbumID(const QString &albumUrn, const QString &name)
@@ -186,6 +222,9 @@
 
 void MPSettings::setAlbumCover(const QString &albumUrn, const QString &coverPhotoURI)
 {
+    MetaData &albumInfo = albumMeta[albumUrn];
+        
+    albumInfo.insert(ALBUM_COVER_KEY, coverPhotoURI);
     setValue(albumUrn, ALBUM_COVER_KEY, coverPhotoURI);
 }
 
@@ -194,3 +233,36 @@
     return value(albumUrn, ALBUM_COVER_KEY).toString();
 }
 
+void MPSettings::getAllAlbums(QVector<QStringList> &result)
+{
+    QList<QString> keys = albumMeta.keys();
+    result.resize(keys.count());
+    for (int i = 0; i < keys.count(); i++) {
+        QStringList t;
+        t << keys[i];   //t[0] is urn
+        t << "";    //t[1] is albumName which is not used anymore
+        MetaData &albumInfo =  albumMeta[keys[i]];
+        t << QString("%1").arg(albumInfo[ALBUM_PHOTONUM_KEY].toInt());
+        result[i] = t;
+    }
+}
+    
+void MPSettings::setAlbumPhotoNum(const QString &albumID, const int num)
+{
+    MetaData &albumInfo = albumMeta[albumID];
+        
+    albumInfo.insert(ALBUM_PHOTONUM_KEY, num);
+}
+
+int MPSettings::getAlbumPhotoNum(const QString &albumID)
+{
+    MetaData &albumInfo = albumMeta[albumID];
+        
+    return albumInfo[ALBUM_PHOTONUM_KEY].toInt();
+}
+
+void MPSettings::removeAlbum(const QString &albumID)
+{
+    albumMeta.remove(albumID);
+}
+
--- src/mpsettings.h
+++ src/mpsettings.h
@@ -24,23 +24,25 @@
      
     void setPhotoLocation(const QString &photoUrn, const QString &location);
     QString getPhotoLocation(const QString &photoUrn);
-    
+
+#if 0    
     void setPhotoUri(const QString &photoUrn, const QString &uri);
     QString getPhotoUri(const QString &photoUrn);
+#endif
 
     void setPhotoAlbumUrn(const QString &photoUrn, const QString &albumUrn);
     QString getPhotoAlbumUrn(const QString &photoUrn);
     
     QString getPhotoAlbumName(const QString &photoUrn);
-
+#if 0
     void setPhotoDate(const QString &photoUrn, const QString &dateString);
     QString getPhotoDate(const QString &photoUrn);
-
     //void setPhotoLocalPath(const QString &photoUrn, const QString &localPath);
     //QString getPhotoLocalPath(const QString &photoUrn);
     
     void setPhotoLastModifiedDate(const QString &photoUrn, const QString &dateString);
     QString getPhotoLastModifiedDate(const QString &photoUrn);
+#endif
     
     void setAlbumDate(const QString &albumUrn, const QString &fromDateString, const QString &toDateString);
     void getAlbumDate(const QString &albumUrn, QString &fromDateString, QString &toDateString);
@@ -57,6 +59,12 @@
     void setAlbumCover(const QString &albumID, const QString &photoURI);
     QString getAlbumCover(const QString &albumID);
 
+    void setAlbumPhotoNum(const QString &albumID, const int num);
+    int getAlbumPhotoNum(const QString &albumID);
+
+    void getAllAlbums(QVector<QStringList> &result);
+    void removeAlbum(const QString &albumID);
+
 protected:
     static const QString PHOTO_ALBUM_URN_KEY;    /* ID of the album that the photo belongs to */
     static const QString PHOTO_LOCATION_KEY;
@@ -71,6 +79,7 @@
     static const QString ALBUM_NAME_KEY;  /* name of the album that the photo  */
     static const QString ALBUM_ID_KEY;  
     static const QString ALBUM_COVER_KEY; 
+    static const QString ALBUM_PHOTONUM_KEY;
 
     void setValue(const QString &photoUrn, const QString &key, const QVariant &value);
     QVariant value(const QString &photoUrn, const QString &key) const;
@@ -78,6 +87,7 @@
 private:
     QSettings settings;
     typedef QHash<QString, QVariant> MetaData;
+    QHash<QString, MetaData> albumMeta;
 };
 
 #endif
--- src/photostasklet.cpp
+++ src/photostasklet.cpp
@@ -90,7 +90,7 @@
     QModelIndex index = j.row;
     MPListItem *entry = static_cast<MPListItem *>(j.userData.value<void *>());
     QAbstractItemModel *model = const_cast<QAbstractItemModel *>(index.model());
-    MPListModel *photoModel = dynamic_cast<MPListModel *>(model);
+    MPAbstractListModelOps *photoModel = const_cast<MPAbstractListModelOps *>(dynamic_cast<const MPAbstractListModelOps *>(index.model()));
     QByteArray md5Result;
 #if 0
 	if (!entry->isLoaded) {
--- src/searchengine.cpp
+++ src/searchengine.cpp
@@ -11,14 +11,13 @@
 
 #include "searchengine.h"
 #include <QtTracker/Tracker>
-
-static const QString SqlGetAllPhoto = "SELECT nie:url(nie:isStoredAs(?photo)) nie:mimeType(?photo) ?photo nfo:fileLastModified(?photo) WHERE{?photo a nmm:Photo}";
+static const QString SqlGetAllPhoto = "SELECT nie:url(nie:isStoredAs(?photo)) nie:mimeType(?photo) ?photo nfo:fileLastModified(?photo) nie:contentCreated(?photo) WHERE{?photo a nmm:Photo}";
 
 static const QString SqlGetAllPhotoAlbums = "SELECT nie:identifier(?imagelist) nie:title(?imagelist) nfo:entryCounter(?imagelist) ?imagelist " \
                                              " nao:identifier(?tag) nao:prefLabel(?tag) ?tag  " \
                                              " WHERE {?imagelist a nmm:ImageList . OPTIONAL {?imagelist nao:hasTag ?tag . ?tag nao:identifier 'AlbumCover' . }}";
 
-static const QString SqlGetAlbumPhotos = "SELECT nie:url(nie:isStoredAs(?image)) nie:contentCreated(?image) ?image nie:identifier(?imagelist) nie:title(?imagelist) nfo:entryCounter(?imagelist) "\
+static const QString SqlGetAlbumPhotos = "SELECT nie:url(nie:isStoredAs(?image)) nie:mimeType(?image) ?image nfo:fileLastModified(?image) nie:contentCreated(?image) nie:identifier(?imagelist) nie:title(?imagelist) nfo:entryCounter(?imagelist) "\
                                           " WHERE { ?imagelist nfo:hasMediaFileListEntry ?entry . ?entry nfo:entryContent ?image "\
                                                     " { SELECT ?imagelist WHERE {?imagelist a nmm:ImageList ; nie:identifier '%1'} } }";
 
@@ -44,19 +43,24 @@
 static const QString SqlDeleteObjectTag = "DELETE {?tag a rdfs:Resource } WHERE {?object nao:hasTag ?tag . ?tag nao:identifier '%1' . " \
                                           " { SELECT ?object WHERE {?object a nie:InformationElement . FILTER (str(?object) = '%2') } } }";
 
+static const QString SqlGetPhotoInfoFromURN = "SELECT ?url ?mimetype ?placeholder ?lastmodified ?contentcreated " \
+                                         " {   OPTIONAL { <%1> nie:url ?url . } " \ 
+                                             " OPTIONAL { <%1> nie:mimeType ?mimetype . } " \
+                                             " OPTIONAL { <%1> nie:mimeType ?mimetype . } " \
+                                             " OPTIONAL { <%1> nfo:fileLastModified ?lastmodified . } " \
+                                             " OPTIONAL { <%1> nie:contentCreated ?contentcreated .} }";
+
 static const QString SqlGetUrlFromURN = "SELECT ?url {<%1> nie:url ?url}";
 static const QString SqlGetMimeTypeFromURN = "SELECT ?mimetype {<%1> nie:mimeType ?mimetype}";
 
 bool SearchEngine::getAllPhoto(QVector<QStringList> &result)
 {
-    qDebug() << "SearchEngine::getAllPhoto " << SqlGetAllPhoto;
     result = ::tracker()->rawSparqlQuery(SqlGetAllPhoto);
     return TRUE;
 }
 
 bool SearchEngine::getAllPhotoAlbums(QVector<QStringList> &result)
 {
-    qDebug() << "SearchEngine::getAllPhotoAlbums " << SqlGetAllPhotoAlbums;
     result = ::tracker()->rawSparqlQuery(SqlGetAllPhotoAlbums);
     return TRUE;
 }
@@ -64,7 +68,6 @@
 bool SearchEngine::getAlbumPhotos(const QString &albumIdentifier, QVector<QStringList> &result)
 {
     QString sql = QString(SqlGetAlbumPhotos).arg(albumIdentifier);
-    qDebug() << "SearchEngine::getAlbumPhotos " << sql;
     result = ::tracker()->rawSparqlQuery(sql);
     return TRUE;
 }
@@ -83,7 +86,6 @@
     }
     sql += SqlCreateAlbumEnd;
 
-    qDebug() << "SearchEngine::createAlbum " << sql;
     ::tracker()->rawSparqlUpdateQuery(sql);
     return TRUE;
 }
@@ -91,7 +93,6 @@
 bool SearchEngine::deleteAlbum(const QString &albumIdentifier)
 {
     QString sql = QString(SqlRemoveAlbum).arg(albumIdentifier);
-    qDebug() << "SearchEngine::deleteAlbum " << sql;
     ::tracker()->rawSparqlUpdateQuery(sql);
     return TRUE;
 }
@@ -101,7 +102,6 @@
     QString sql = QString(SqlQueryAlbumCoverTag).arg(albumURN);
     bool needDelete = FALSE;
 
-    qDebug() << "SearchEngine::setAlbumCover query album tag " << sql;
     QVector<QStringList> result = ::tracker()->rawSparqlQuery(sql);
 
     for (QVector<QStringList>::iterator i = result.begin(); i != result.end(); i++) {
@@ -113,12 +113,10 @@
 
     if (needDelete) {
         sql = QString(SqlDeleteAlbumCoverTag).arg(albumURN);
-        qDebug() << "SearchEngine::setAlbumCover delete album tag " << sql;
         ::tracker()->rawSparqlUpdateQuery(sql);
     }
 
     sql = QString(SqlInsertAlbumCoverTag).arg(coverPath).arg(albumURN);
-    qDebug() << "SearchEngine::setAlbumCover insert album tag " << sql;
     ::tracker()->rawSparqlUpdateQuery(sql);
 
     return TRUE;
@@ -127,7 +125,6 @@
 bool SearchEngine::getAlbumCover(const QString &albumURN, QString &coverPath)
 {
     QString sql = QString(SqlQueryAlbumCoverTag).arg(albumURN);
-    qDebug() << "SearchEngine::getAlbumCover " << sql;
 
     QVector<QStringList> result = ::tracker()->rawSparqlQuery(sql);
 
@@ -160,7 +157,6 @@
 {
     QString sql = QString(SqlInsertObjectTag).arg(tagInfo).arg(tagIdentifier).arg(objectURN);
 
-    qDebug() << "SearchEngine::setObjectTag " << sql;
 
     ::tracker()->rawSparqlUpdateQuery(sql);
 
@@ -171,7 +167,6 @@
 {
     QString sql = QString(SqlQueryObjectTag).arg(tagIdentifier).arg(objectURN);
 
-    qDebug() << "SearchEngine::getObjectTag " << sql;
 
     QVector<QStringList> result = ::tracker()->rawSparqlQuery(sql);
 
@@ -191,7 +186,6 @@
 {
     QString sql = QString(SqlDeleteObjectTag).arg(tagIdentifier).arg(objectURN);
 
-    qDebug() << "SearchEngine::deleteObjectTag " << sql;
 
     ::tracker()->rawSparqlUpdateQuery(sql);
 
@@ -203,8 +197,6 @@
     QString sql = QString(SqlGetUrlFromURN).arg(objectURN);
     QString url;
 
-    qDebug() << "SearchEngine::getUrl " << sql;
-
     QVector<QStringList> result = ::tracker()->rawSparqlQuery(sql);
     if (result.count() != 0)
         url = result[0][0];
@@ -216,9 +208,18 @@
     QString sql = QString(SqlGetMimeTypeFromURN).arg(objectURN);
     QString mimeType;
 
-    qDebug() << "SearchEngine::getMimeType " << sql;
-
     QVector<QStringList> result = ::tracker()->rawSparqlQuery(sql);
     mimeType = result[0][0];
     return mimeType;
 }
+
+QStringList SearchEngine::getOnePhoto(const QString &urn)
+{
+    QString sql = QString(SqlGetPhotoInfoFromURN).arg(urn);
+
+    QVector<QStringList> result = ::tracker()->rawSparqlQuery(sql);
+    QStringList ret = result[0];
+    ret[2] = urn;
+    return ret;
+}
+
--- src/searchengine.h
+++ src/searchengine.h
@@ -17,6 +17,30 @@
 {
 public:
     static bool getAllPhoto(QVector<QStringList> &result);
+    static inline QString getPhotoUrn(const QStringList &photoResult)
+    {
+        return photoResult[2];
+    }
+
+    static inline QString getPhotoUri(const QStringList &photoResult)
+    {
+        return photoResult[0];
+    }
+
+    static inline QString getPhotoLastModified(const QStringList &photoResult)
+    {
+        return photoResult[3];
+    }
+
+    static inline QString getPhotoMimeType(const QStringList &photoResult)
+    {
+        return photoResult[1];
+    }
+
+    static inline QString getPhotoContentCreated(const QStringList &photoResult)
+    {
+        return photoResult[4];
+    }
 
     /// @brief get all photo albums from tracker, including nie:title; nie:identifier; nfo:entryCounter
     static bool getAllPhotoAlbums(QVector<QStringList> &result);
@@ -52,5 +76,8 @@
     static QString getUrl(const QString &objectURN);
 
     static QString getMimeType(const QString &objectURN);
+
+    static QStringList getOnePhoto(const QString &photoUrn);
+
 };
 #endif
--- themes/.gitattributes
+++ themes/.gitattributes
-meego export-ignore

++++++ meego-handset-photos.yaml
--- meego-handset-photos.yaml
+++ meego-handset-photos.yaml
@@ -1,6 +1,6 @@
 Name: meego-handset-photos
 Summary: A Photo Viewer
-Version: 0.0.21
+Version: 0.0.22
 Release: 1
 Group: System/Libraries
 License:  Apache License, Version 2.0




More information about the MeeGo-commits mailing list