diff --git a/resources/style.qss b/resources/style.qss index 92bd8d6b..a877c11b 100644 --- a/resources/style.qss +++ b/resources/style.qss @@ -1,4 +1,5 @@ QWidget#DockPaletteWidgetBox, +QWidget#documentNavigator, QWidget#UBBoardThumbnailsView, QWidget#UBLibPathViewer, QWidget#UBLibNavigatorWidget, diff --git a/src/adaptors/UBThumbnailAdaptor.cpp b/src/adaptors/UBThumbnailAdaptor.cpp index 3d87e123..b0b6a141 100644 --- a/src/adaptors/UBThumbnailAdaptor.cpp +++ b/src/adaptors/UBThumbnailAdaptor.cpp @@ -85,7 +85,6 @@ void UBThumbnailAdaptor::generateMissingThumbnails(UBDocumentProxy* proxy) const QPixmap* UBThumbnailAdaptor::get(UBDocumentProxy* proxy, int pageIndex) { - UBApplication::showMessage(tr("loading thumbnail of page %1").arg(pageIndex + 1)); QString fileName = proxy->persistencePath() + UBFileSystemUtils::digitFileFormat("/page%1.thumbnail.jpg", pageIndex); QFile file(fileName); diff --git a/src/board/UBBoardController.cpp b/src/board/UBBoardController.cpp index 082c1b0f..e9ba86c1 100644 --- a/src/board/UBBoardController.cpp +++ b/src/board/UBBoardController.cpp @@ -1994,6 +1994,7 @@ void UBBoardController::persistCurrentScene(bool isAnAutomaticBackup, bool force && (mActiveScene->isModified())) { UBPersistenceManager::persistenceManager()->persistDocumentScene(selectedDocument(), mActiveScene, mActiveSceneIndex, isAnAutomaticBackup,forceImmediateSave); + updatePage(mActiveSceneIndex); } } diff --git a/src/core/UBApplicationController.cpp b/src/core/UBApplicationController.cpp index b08edcef..d8bdc89f 100644 --- a/src/core/UBApplicationController.cpp +++ b/src/core/UBApplicationController.cpp @@ -358,7 +358,6 @@ void UBApplicationController::showBoard() } } - UBApplication::showMessage(tr("Board drawing...")); mMainMode = Board; adaptToolBar(); diff --git a/src/core/UBPersistenceManager.cpp b/src/core/UBPersistenceManager.cpp index e579f111..4f3f9704 100644 --- a/src/core/UBPersistenceManager.cpp +++ b/src/core/UBPersistenceManager.cpp @@ -183,7 +183,7 @@ void UBPersistenceManager::onSceneLoaded(QByteArray scene, UBDocumentProxy* prox qDebug() << "scene loaded " << sceneIndex; QTime time; time.start(); - mSceneCache.insert(proxy, sceneIndex, loadDocumentScene(proxy, sceneIndex)); + mSceneCache.insert(proxy, sceneIndex, loadDocumentScene(proxy, sceneIndex, false)); qDebug() << "millisecond for sceneCache " << time.elapsed(); } diff --git a/src/document/UBDocumentContainer.cpp b/src/document/UBDocumentContainer.cpp index 2d30f607..7af9e433 100644 --- a/src/document/UBDocumentContainer.cpp +++ b/src/document/UBDocumentContainer.cpp @@ -52,7 +52,7 @@ void UBDocumentContainer::setDocument(UBDocumentProxy* document, bool forceReloa { mCurrentDocument = document; - emit initThumbnailsRequired(this); + reloadThumbnails(); emit documentSet(mCurrentDocument); } } @@ -130,6 +130,7 @@ void UBDocumentContainer::deleteThumbPage(int index) void UBDocumentContainer::updateThumbPage(int index) { mDocumentThumbs[index] = UBThumbnailAdaptor::get(mCurrentDocument, index); + emit documentPageUpdated(index); } void UBDocumentContainer::insertThumbPage(int index) diff --git a/src/document/UBDocumentController.cpp b/src/document/UBDocumentController.cpp index f86fe466..47eeee22 100644 --- a/src/document/UBDocumentController.cpp +++ b/src/document/UBDocumentController.cpp @@ -468,11 +468,11 @@ void UBDocumentController::openSelectedItem() if (selectedItems.count() > 0) { - UBThumbnailPixmap* thumb = dynamic_cast (selectedItems.last()); + UBSceneThumbnailPixmap* thumb = dynamic_cast (selectedItems.last()); if (thumb) { - UBDocumentProxy* proxy = thumb->documentProxy(); + UBDocumentProxy* proxy = thumb->proxy(); if (proxy && isOKToOpenDocument(proxy)) { @@ -505,10 +505,10 @@ void UBDocumentController::duplicateSelectedItem() QList selectedSceneIndexes; foreach (QGraphicsItem *item, selectedItems) { - UBThumbnailPixmap *thumb = dynamic_cast(item); + UBSceneThumbnailPixmap *thumb = dynamic_cast(item); if (thumb) { - UBDocumentProxy *proxy = thumb->documentProxy(); + UBDocumentProxy *proxy = thumb->proxy(); if (proxy) { @@ -1366,7 +1366,7 @@ void UBDocumentController::selectionChanged() mMainWindow->actionDuplicate->setEnabled(!trashSelected); else{ for(int i = 0; i < selection.count() && !firstSceneSelected; i += 1){ - if(dynamic_cast(selection.at(i))->sceneIndex() == 0){ + if(dynamic_cast(selection.at(i))->sceneIndex() == 0){ mMainWindow->actionDuplicate->setEnabled(!trashSelected); firstSceneSelected = true; } @@ -1516,11 +1516,11 @@ void UBDocumentController::addToDocument() foreach (QGraphicsItem* item, selectedItems) { - UBThumbnailPixmap* thumb = dynamic_cast (item); + UBSceneThumbnailPixmap* thumb = dynamic_cast (item); - if (thumb && thumb->documentProxy()) + if (thumb && thumb->proxy()) { - QPair pageInfo(thumb->documentProxy(), thumb->sceneIndex()); + QPair pageInfo(thumb->proxy(), thumb->sceneIndex()); pageInfoList << pageInfo; } } @@ -1809,11 +1809,11 @@ void UBDocumentController::deletePages(QList itemsToDelete) foreach (QGraphicsItem* item, itemsToDelete) { - UBThumbnailPixmap* thumb = dynamic_cast (item); + UBSceneThumbnailPixmap* thumb = dynamic_cast (item); if (thumb) { - proxy = thumb->documentProxy(); + proxy = thumb->proxy(); if (proxy) { sceneIndexes.append(thumb->sceneIndex()); @@ -1843,7 +1843,7 @@ int UBDocumentController::getSelectedItemIndex() if (selectedItems.count() > 0) { - UBThumbnailPixmap* thumb = dynamic_cast (selectedItems.last()); + UBSceneThumbnailPixmap* thumb = dynamic_cast (selectedItems.last()); return thumb->sceneIndex(); } else return -1; @@ -1872,7 +1872,7 @@ void UBDocumentController::refreshDocumentThumbnailsView(UBDocumentContainer*) for (int i = 0; i < selectedDocument()->pageCount(); i++) { const QPixmap* pix = pageAt(i); - QGraphicsPixmapItem *pixmapItem = new UBThumbnailPixmap(*pix, proxy, i); // deleted by the tree widget + QGraphicsPixmapItem *pixmapItem = new UBSceneThumbnailPixmap(*pix, proxy, i); // deleted by the tree widget if (proxy == mBoardController->selectedDocument() && mBoardController->activeSceneIndex() == i) { @@ -1899,7 +1899,7 @@ void UBDocumentController::refreshDocumentThumbnailsView(UBDocumentContainer*) if (selection) { disconnect(mDocumentUI->thumbnailWidget->scene(), SIGNAL(selectionChanged()), this, SLOT(pageSelectionChanged())); - UBThumbnailPixmap *currentScene = dynamic_cast(selection); + UBSceneThumbnailPixmap *currentScene = dynamic_cast(selection); if (currentScene) mDocumentUI->thumbnailWidget->hightlightItem(currentScene->sceneIndex()); connect(mDocumentUI->thumbnailWidget->scene(), SIGNAL(selectionChanged()), this, SLOT(pageSelectionChanged())); diff --git a/src/gui/UBDocumentNavigator.cpp b/src/gui/UBDocumentNavigator.cpp new file mode 100644 index 00000000..7dc1564c --- /dev/null +++ b/src/gui/UBDocumentNavigator.cpp @@ -0,0 +1,442 @@ +/* + * Copyright (C) 2015-2016 Département de l'Instruction Publique (DIP-SEM) + * + * Copyright (C) 2013 Open Education Foundation + * + * Copyright (C) 2010-2013 Groupement d'Intérêt Public pour + * l'Education Numérique en Afrique (GIP ENA) + * + * This file is part of OpenBoard. + * + * OpenBoard is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License, + * with a specific linking exception for the OpenSSL project's + * "OpenSSL" library (or with modified versions of it that use the + * same license as the "OpenSSL" library). + * + * OpenBoard is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenBoard. If not, see . + */ + + + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/UBApplication.h" +#include "UBDocumentNavigator.h" +#include "board/UBBoardController.h" +#include "adaptors/UBThumbnailAdaptor.h" +#include "adaptors/UBSvgSubsetAdaptor.h" +#include "document/UBDocumentController.h" +#include "domain/UBGraphicsScene.h" +#include "board/UBBoardPaletteManager.h" +#include "core/UBApplicationController.h" + +#include "core/memcheck.h" + +/** + * \brief Constructor + * @param parent as the parent widget + * @param name as the object name + */ +UBDocumentNavigator::UBDocumentNavigator(QWidget *parent, const char *name):QGraphicsView(parent) + , mScene(NULL) + , mNbColumns(1) + , mThumbnailWidth(0) + , mThumbnailMinWidth(100) + , mSelectedThumbnail(NULL) + , mDropSource(NULL) + , mDropTarget(NULL) + , mDropBar(new QGraphicsRectItem()) + , mLongPressInterval(350) +{ + setObjectName(name); + mScene = new QGraphicsScene(this); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setScene(mScene); + mThumbnailWidth = width() - 2*border(); + + mDropBar->setPen(QPen(Qt::darkGray)); + mDropBar->setBrush(QBrush(Qt::lightGray)); + scene()->addItem(mDropBar); + mDropBar->hide(); + + mLongPressTimer.setInterval(mLongPressInterval); + mLongPressTimer.setSingleShot(true); + + setFrameShadow(QFrame::Plain); + + connect(UBApplication::boardController, SIGNAL(documentThumbnailsUpdated(UBDocumentContainer*)), this, SLOT(generateThumbnails(UBDocumentContainer*))); + connect(UBApplication::boardController, SIGNAL(documentPageUpdated(int)), this, SLOT(updateSpecificThumbnail(int))); + connect(UBApplication::boardController, SIGNAL(pageSelectionChanged(int)), this, SLOT(onScrollToSelectedPage(int))); + + connect(&mLongPressTimer, SIGNAL(timeout()), this, SLOT(longPressTimeout()), Qt::UniqueConnection); + + connect(this, SIGNAL(mousePressAndHoldEventRequired(QPoint)), this, SLOT(mousePressAndHoldEvent(QPoint)), Qt::UniqueConnection); +} + +/** + * \brief Destructor + */ +UBDocumentNavigator::~UBDocumentNavigator() +{ + if(NULL != mScene) + { + delete mScene; + mScene = NULL; + } +} + +/** + * \brief Generate the thumbnails + */ +void UBDocumentNavigator::generateThumbnails(UBDocumentContainer* source) +{ + mThumbsWithLabels.clear(); + int selectedIndex = -1; + QList graphicsItemList = mScene->items(); + for(int i = 0; i < graphicsItemList.size(); i+=1) + { + QGraphicsItem* item = graphicsItemList.at(i); + if(item->isSelected()) + selectedIndex = i; + + if (item != mDropBar) + { + mScene->removeItem(item); + delete item; + item = NULL; + } + } + + for(int i = 0; i < source->selectedDocument()->pageCount(); i++) + { + //claudio This is a very bad hack and shows a architectural problem + // source->selectedDocument()->pageCount() != source->pageCount() + if(i>=source->pageCount() || source->pageAt(i)->isNull()) + source->insertThumbPage(i); + + const QPixmap* pix = source->pageAt(i); + Q_ASSERT(!pix->isNull()); + int pageIndex = UBDocumentContainer::pageFromSceneIndex(i); + + UBSceneThumbnailNavigPixmap* pixmapItem = new UBSceneThumbnailNavigPixmap(*pix, source->selectedDocument(), i); + + QString label = tr("Page %0").arg(pageIndex); + UBThumbnailTextItem *labelItem = new UBThumbnailTextItem(label); + + UBImgTextThumbnailElement thumbWithText(pixmapItem, labelItem); + thumbWithText.setBorder(border()); + mThumbsWithLabels.append(thumbWithText); + + mScene->addItem(pixmapItem); + mScene->addItem(labelItem); + } + + if (selectedIndex >= 0 && selectedIndex < mThumbsWithLabels.count()) + mSelectedThumbnail = mThumbsWithLabels.at(selectedIndex).getThumbnail(); + else + mSelectedThumbnail = NULL; + + // Draw the items + refreshScene(); +} + +void UBDocumentNavigator::onScrollToSelectedPage(int index) +{ + int c = 0; + foreach(UBImgTextThumbnailElement el, mThumbsWithLabels) + { + if (c==index) + { + el.getThumbnail()->setSelected(true); + mSelectedThumbnail = el.getThumbnail(); + } + else + { + el.getThumbnail()->setSelected(false); + } + c++; + } + if(NULL != mSelectedThumbnail) + centerOn(mSelectedThumbnail); +} + +/** + * \brief Refresh the given thumbnail + * @param iPage as the given page related thumbnail + */ +void UBDocumentNavigator::updateSpecificThumbnail(int iPage) +{ + const QPixmap* pix = UBApplication::boardController->pageAt(iPage); + UBSceneThumbnailNavigPixmap* newItem = new UBSceneThumbnailNavigPixmap(*pix, UBApplication::boardController->selectedDocument(), iPage); + + // Get the old thumbnail + UBSceneThumbnailNavigPixmap* oldItem = mThumbsWithLabels.at(iPage).getThumbnail(); + if(NULL != oldItem) + { + mScene->removeItem(oldItem); + mScene->addItem(newItem); + mThumbsWithLabels[iPage].setThumbnail(newItem); + delete oldItem; + oldItem = NULL; + } + +} + +/** + * \brief Put the element in the right place in the scene. + */ +void UBDocumentNavigator::refreshScene() +{ + qreal thumbnailHeight = mThumbnailWidth / UBSettings::minScreenRatio; + + for(int i = 0; i < mThumbsWithLabels.size(); i++) + { + // Get the item + UBImgTextThumbnailElement& item = mThumbsWithLabels[i]; + int columnIndex = i % mNbColumns; + int rowIndex = i / mNbColumns; + item.Place(rowIndex, columnIndex, mThumbnailWidth, thumbnailHeight); + } + scene()->setSceneRect(scene()->itemsBoundingRect()); +} + +/** + * \brief Set the number of thumbnails columns + * @param nbColumns as the number of columns + */ +void UBDocumentNavigator::setNbColumns(int nbColumns) +{ + mNbColumns = nbColumns; +} + +/** + * \brief Get the number of columns + * @return the number of thumbnails columns + */ +int UBDocumentNavigator::nbColumns() +{ + return mNbColumns; +} + +/** + * \brief Set the thumbnails minimum width + * @param width as the minimum width + */ +void UBDocumentNavigator::setThumbnailMinWidth(int width) +{ + mThumbnailMinWidth = width; +} + +/** + * \brief Get the thumbnails minimum width + * @return the minimum thumbnails width + */ +int UBDocumentNavigator::thumbnailMinWidth() +{ + return mThumbnailMinWidth; +} + +/** + * \brief Get the border size + * @return the border size in pixels + */ +int UBDocumentNavigator::border() +{ + return 20; +} + +/** + * \brief Handle the resize event + * @param event as the resize event + */ +void UBDocumentNavigator::resizeEvent(QResizeEvent *event) +{ + Q_UNUSED(event); + + // Update the thumbnails width + mThumbnailWidth = (width() > mThumbnailMinWidth) ? width() - 2*border() : mThumbnailMinWidth; + + if(mSelectedThumbnail) + centerOn(mSelectedThumbnail); + + // Refresh the scene + refreshScene(); +} + +/** + * \brief Handle the mouse press event + * @param event as the mouse event + */ +void UBDocumentNavigator::mousePressEvent(QMouseEvent *event) +{ + mLongPressTimer.start(); + mLastPressedMousePos = event->pos(); + + QGraphicsItem* pClickedItem = itemAt(event->pos()); + if(NULL != pClickedItem) + { + + // First, select the clicked item + UBSceneThumbnailNavigPixmap* pCrntItem = dynamic_cast(pClickedItem); + + if(NULL == pCrntItem) + { + // If we fall here we may have clicked on the label instead of the thumbnail + UBThumbnailTextItem* pTextItem = dynamic_cast(pClickedItem); + if(NULL != pTextItem) + { + for(int i = 0; i < mThumbsWithLabels.size(); i++) + { + const UBImgTextThumbnailElement& el = mThumbsWithLabels.at(i); + if(el.getCaption() == pTextItem) + { + pCrntItem = el.getThumbnail(); + break; + } + } + } + } + + int index = 0; + for(int i = 0; i < mThumbsWithLabels.size(); i++) + { + if (mThumbsWithLabels.at(i).getThumbnail() == pCrntItem) + { + mSelectedThumbnail = pCrntItem; + index = i; + break; + } + } + UBApplication::boardController->persistViewPositionOnCurrentScene(); + UBApplication::boardController->persistCurrentScene(); + UBApplication::boardController->setActiveDocumentScene(index); + UBApplication::boardController->centerOn(UBApplication::boardController->activeScene()->lastCenter()); + } + QGraphicsView::mousePressEvent(event); +} + +void UBDocumentNavigator::mouseReleaseEvent(QMouseEvent *event) +{ + event->accept(); +} + +void UBDocumentNavigator::longPressTimeout() +{ + if (QApplication::mouseButtons() != Qt::NoButton) + emit mousePressAndHoldEventRequired(mLastPressedMousePos); + + mLongPressTimer.stop(); +} + +void UBDocumentNavigator::mousePressAndHoldEvent(QPoint pos) +{ + UBSceneThumbnailNavigPixmap* item = dynamic_cast(itemAt(pos)); + if (item) + { + mDropSource = item; + mDropTarget = item; + + QPixmap pixmap = item->pixmap().scaledToWidth(mThumbnailWidth/2); + + QDrag *drag = new QDrag(this); + drag->setMimeData(new QMimeData()); + drag->setPixmap(pixmap); + drag->setHotSpot(QPoint(pixmap.width()/2, pixmap.height()/2)); + + drag->exec(); + } +} + +void UBDocumentNavigator::dragEnterEvent(QDragEnterEvent *event) +{ + mDropBar->show(); + + if (event->source() == this) + { + event->setDropAction(Qt::MoveAction); + event->accept(); + } + else + { + event->acceptProposedAction(); + } +} + +void UBDocumentNavigator::dragMoveEvent(QDragMoveEvent *event) +{ + QPointF position = event->pos(); + + //autoscroll during drag'n'drop + QPointF scenePos = mapToScene(position.toPoint()); + int thumbnailHeight = mThumbnailWidth / UBSettings::minScreenRatio; + QRectF thumbnailArea(0, scenePos.y() - thumbnailHeight/2, mThumbnailWidth, thumbnailHeight); + + ensureVisible(thumbnailArea); + + UBSceneThumbnailNavigPixmap* item = dynamic_cast(itemAt(position.toPoint())); + if (item) + { + if (item != mDropTarget) + mDropTarget = item; + + qreal scale = item->transform().m11(); + + QPointF itemCenter(item->pos().x() + (item->boundingRect().width()-verticalScrollBar()->width()) * scale, + item->pos().y() + item->boundingRect().height() * scale / 2); + + bool dropAbove = mapToScene(position.toPoint()).y() < itemCenter.y(); + bool movingUp = mDropSource->sceneIndex() > item->sceneIndex(); + qreal y = 0; + + if (movingUp) + { + if (dropAbove) + { + y = item->pos().y() - UBSettings::thumbnailSpacing / 2; + if (mDropBar->y() != y) + mDropBar->setRect(QRectF(item->pos().x(), y, mThumbnailWidth-verticalScrollBar()->width(), 3)); + } + } + else + { + if (!dropAbove) + { + y = item->pos().y() + item->boundingRect().height() * scale + UBSettings::thumbnailSpacing / 2; + if (mDropBar->y() != y) + mDropBar->setRect(QRectF(item->pos().x(), y, mThumbnailWidth-verticalScrollBar()->width(), 3)); + } + } + } + + event->acceptProposedAction(); +} + +void UBDocumentNavigator::dropEvent(QDropEvent *event) +{ + Q_UNUSED(event); + + if (mDropSource->sceneIndex() != mDropTarget->sceneIndex()) + UBApplication::boardController->moveSceneToIndex(mDropSource->sceneIndex(), mDropTarget->sceneIndex()); + + mDropSource = NULL; + mDropTarget = NULL; + + mDropBar->setRect(QRectF()); + mDropBar->hide(); +} diff --git a/src/gui/UBDocumentNavigator.h b/src/gui/UBDocumentNavigator.h new file mode 100644 index 00000000..3b68dbd7 --- /dev/null +++ b/src/gui/UBDocumentNavigator.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2015-2016 Département de l'Instruction Publique (DIP-SEM) + * + * Copyright (C) 2013 Open Education Foundation + * + * Copyright (C) 2010-2013 Groupement d'Intérêt Public pour + * l'Education Numérique en Afrique (GIP ENA) + * + * This file is part of OpenBoard. + * + * OpenBoard is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License, + * with a specific linking exception for the OpenSSL project's + * "OpenSSL" library (or with modified versions of it that use the + * same license as the "OpenSSL" library). + * + * OpenBoard is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenBoard. If not, see . + */ + + + + +#ifndef UBDOCUMENTNAVIGATOR_H +#define UBDOCUMENTNAVIGATOR_H + +#include +#include +#include +#include +#include + +#include "document/UBDocumentProxy.h" +#include "document/UBDocumentContainer.h" +#include "UBThumbnailWidget.h" + +#define NO_PAGESELECTED -1 + +class UBDocumentNavigator : public QGraphicsView +{ + Q_OBJECT +public: + UBDocumentNavigator(QWidget* parent=0, const char* name="documentNavigator"); + ~UBDocumentNavigator(); + + void setNbColumns(int nbColumns); + int nbColumns(); + void setThumbnailMinWidth(int width); + int thumbnailMinWidth(); + +public slots: + void onScrollToSelectedPage(int index);// { if (mCrntItem) centerOn(mCrntItem); } + void generateThumbnails(UBDocumentContainer* source); + void updateSpecificThumbnail(int iPage); + + void longPressTimeout(); + void mousePressAndHoldEvent(QPoint pos); + +protected: + virtual void resizeEvent(QResizeEvent *event); + virtual void mousePressEvent(QMouseEvent *event); + virtual void mouseReleaseEvent(QMouseEvent *event); + + virtual void dragEnterEvent(QDragEnterEvent* event); + virtual void dragMoveEvent(QDragMoveEvent* event); + virtual void dropEvent(QDropEvent* event); + +signals: + void mousePressAndHoldEventRequired(QPoint pos); + void moveThumbnailRequired(int from, int to); + +private: + + void refreshScene(); + int border(); + + /** The scene */ + QGraphicsScene* mScene; + /** The current selected item */ + //UBSceneThumbnailNavigPixmap* mCrntItem; + /** The list of current thumbnails with labels*/ + QList mThumbsWithLabels; + /** The current number of columns */ + int mNbColumns; + /** The current thumbnails width */ + int mThumbnailWidth; + /** The current thumbnails minimum width */ + int mThumbnailMinWidth; + /** The selected thumbnail */ + UBSceneThumbnailNavigPixmap* mSelectedThumbnail; + + UBSceneThumbnailNavigPixmap* mDropSource; + UBSceneThumbnailNavigPixmap* mDropTarget; + QGraphicsRectItem *mDropBar; + + int mLongPressInterval; + QTimer mLongPressTimer; + QPoint mLastPressedMousePos; +}; + +#endif // UBDOCUMENTNAVIGATOR_H + diff --git a/src/gui/UBDocumentThumbnailWidget.cpp b/src/gui/UBDocumentThumbnailWidget.cpp index b70f3926..f700530b 100644 --- a/src/gui/UBDocumentThumbnailWidget.cpp +++ b/src/gui/UBDocumentThumbnailWidget.cpp @@ -75,17 +75,17 @@ void UBDocumentThumbnailWidget::mouseMoveEvent(QMouseEvent *event) QList graphicsItems = items(mMousePressPos); - UBThumbnailPixmap* sceneItem = 0; + UBSceneThumbnailPixmap* sceneItem = 0; while (!graphicsItems.isEmpty() && !sceneItem) - sceneItem = dynamic_cast(graphicsItems.takeFirst()); + sceneItem = dynamic_cast(graphicsItems.takeFirst()); if (sceneItem) { QDrag *drag = new QDrag(this); QList mimeDataItems; foreach (QGraphicsItem *item, selectedItems()) - mimeDataItems.append(UBMimeDataItem(sceneItem->documentProxy(), mGraphicItems.indexOf(item))); + mimeDataItems.append(UBMimeDataItem(sceneItem->proxy(), mGraphicItems.indexOf(item))); UBMimeData *mime = new UBMimeData(mimeDataItems); drag->setMimeData(mime); @@ -305,7 +305,7 @@ void UBDocumentThumbnailWidget::hightlightItem(int index) } if (0 <= index && index < mGraphicItems.length()) { - UBThumbnailPixmap *thumbnail = dynamic_cast(mGraphicItems.at(index)); + UBSceneThumbnailPixmap *thumbnail = dynamic_cast(mGraphicItems.at(index)); if (thumbnail) thumbnail->highlight(); } diff --git a/src/gui/UBMessageWindow.cpp b/src/gui/UBMessageWindow.cpp index 14adbbb6..d973810a 100644 --- a/src/gui/UBMessageWindow.cpp +++ b/src/gui/UBMessageWindow.cpp @@ -88,6 +88,7 @@ void UBMessageWindow::showMessage(const QString& message, bool showSpinningWheel repaint(); // I mean it, *right now*, also on Mac qApp->flush(); + //qApp->sendPostedEvents(); } void UBMessageWindow::timerEvent(QTimerEvent *event) diff --git a/src/gui/UBPageNavigationWidget.cpp b/src/gui/UBPageNavigationWidget.cpp index eba9e401..a6564573 100644 --- a/src/gui/UBPageNavigationWidget.cpp +++ b/src/gui/UBPageNavigationWidget.cpp @@ -60,7 +60,7 @@ UBPageNavigationWidget::UBPageNavigationWidget(QWidget *parent, const char *name mLayout = new QVBoxLayout(this); setLayout(mLayout); - mNavigator = new UBBoardThumbnailsView(this); + mNavigator = new UBDocumentNavigator(this); mLayout->addWidget(mNavigator, 1); mHLayout = new QHBoxLayout(); diff --git a/src/gui/UBPageNavigationWidget.h b/src/gui/UBPageNavigationWidget.h index bba9cc86..e72b74f4 100644 --- a/src/gui/UBPageNavigationWidget.h +++ b/src/gui/UBPageNavigationWidget.h @@ -39,6 +39,7 @@ #include #include "UBBoardThumbnailsView.h" +#include "UBDocumentNavigator.h" #include "UBDockPaletteWidget.h" #include "document/UBDocumentProxy.h" @@ -70,7 +71,7 @@ private: int border(); /** The thumbnails navigator widget */ - UBBoardThumbnailsView* mNavigator; + UBDocumentNavigator* mNavigator; /** The layout */ QVBoxLayout* mLayout; QHBoxLayout* mHLayout; diff --git a/src/gui/UBThumbnailWidget.cpp b/src/gui/UBThumbnailWidget.cpp index 60755aa4..2d83213a 100644 --- a/src/gui/UBThumbnailWidget.cpp +++ b/src/gui/UBThumbnailWidget.cpp @@ -771,7 +771,117 @@ UBThumbnail::~UBThumbnail() delete mSelectionItem; } -void UBWidgetTextThumbnailElement::Place(int row, int col, qreal width, qreal height) +UBSceneThumbnailNavigPixmap::UBSceneThumbnailNavigPixmap(const QPixmap& pix, UBDocumentProxy* proxy, int pSceneIndex) + : UBSceneThumbnailPixmap(pix, proxy, pSceneIndex) + , bButtonsVisible(false) + , bCanDelete(false) + , bCanMoveUp(false) + , bCanMoveDown(false) +{ + if(0 <= UBDocumentContainer::pageFromSceneIndex(pSceneIndex)){ + setAcceptHoverEvents(true); + setFlag(QGraphicsItem::ItemIsSelectable, true); + } +} + +UBSceneThumbnailNavigPixmap::~UBSceneThumbnailNavigPixmap() +{ + +} + +void UBSceneThumbnailNavigPixmap::hoverEnterEvent(QGraphicsSceneHoverEvent *event) +{ + event->accept(); + bButtonsVisible = true; + bCanDelete = true; + bCanMoveDown = false; + bCanMoveUp = false; + if(sceneIndex() < proxy()->pageCount() - 1) + bCanMoveDown = true; + if(sceneIndex() > 0) + bCanMoveUp = true; + if(proxy()->pageCount() == 1) + bCanDelete = false; + update(); +} + +void UBSceneThumbnailNavigPixmap::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) +{ + event->accept(); + bButtonsVisible = false; + update(); +} + +void UBSceneThumbnailNavigPixmap::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) +{ + Q_UNUSED(option); + Q_UNUSED(widget); + + UBSceneThumbnailPixmap::paint(painter, option, widget); + if(bButtonsVisible) + { + if(bCanDelete) + painter->drawPixmap(0, 0, BUTTONSIZE, BUTTONSIZE, QPixmap(":images/close.svg")); + else + painter->drawPixmap(0, 0, BUTTONSIZE, BUTTONSIZE, QPixmap(":images/closeDisabled.svg")); + + painter->drawPixmap(BUTTONSIZE + BUTTONSPACING, 0, BUTTONSIZE, BUTTONSIZE, QPixmap(":images/duplicate.svg")); + + if(bCanMoveUp) + painter->drawPixmap(2*(BUTTONSIZE + BUTTONSPACING), 0, BUTTONSIZE, BUTTONSIZE, QPixmap(":images/moveUp.svg")); + else + painter->drawPixmap(2*(BUTTONSIZE + BUTTONSPACING), 0, BUTTONSIZE, BUTTONSIZE, QPixmap(":images/moveUpDisabled.svg")); + if(bCanMoveDown) + painter->drawPixmap(3*(BUTTONSIZE + BUTTONSPACING), 0, BUTTONSIZE, BUTTONSIZE, QPixmap(":images/menu.svg")); + else + painter->drawPixmap(3*(BUTTONSIZE + BUTTONSPACING), 0, BUTTONSIZE, BUTTONSIZE, QPixmap(":images/menuDisabled.svg")); + } +} + +void UBSceneThumbnailNavigPixmap::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + QPointF p = event->pos(); + + // Here we check the position of the click and verify if it has to trig an action or not. + if(bCanDelete && p.x() >= 0 && p.x() <= BUTTONSIZE && p.y() >= 0 && p.y() <= BUTTONSIZE) + deletePage(); + if(p.x() >= BUTTONSIZE + BUTTONSPACING && p.x() <= 2*BUTTONSIZE + BUTTONSPACING && p.y() >= 0 && p.y() <= BUTTONSIZE) + duplicatePage(); + + if(bCanMoveUp && p.x() >= 2*(BUTTONSIZE + BUTTONSPACING) && p.x() <= 3*BUTTONSIZE + 2*BUTTONSPACING && p.y() >= 0 && p.y() <= BUTTONSIZE) + moveUpPage(); + if(bCanMoveDown && p.x() >= 3*(BUTTONSIZE + BUTTONSPACING) && p.x() <= 4*BUTTONSIZE + 3*BUTTONSPACING && p.y() >= 0 && p.y() <= BUTTONSIZE) + moveDownPage(); + + event->accept(); +} + +void UBSceneThumbnailNavigPixmap::deletePage() +{ + if(UBApplication::mainWindow->yesNoQuestion(QObject::tr("Remove Page"), QObject::tr("Are you sure you want to remove 1 page from the selected document '%0'?").arg(UBApplication::documentController->selectedDocument()->metaData(UBSettings::documentName).toString()))){ + UBApplication::boardController->deleteScene(sceneIndex()); + } +} + +void UBSceneThumbnailNavigPixmap::duplicatePage() +{ + UBApplication::boardController->duplicateScene(sceneIndex()); +} + +void UBSceneThumbnailNavigPixmap::moveUpPage() +{ + if (sceneIndex()!=0) + UBApplication::boardController->moveSceneToIndex(sceneIndex(), sceneIndex() - 1); +} + +void UBSceneThumbnailNavigPixmap::moveDownPage() +{ + if (sceneIndex() < UBApplication::boardController->selectedDocument()->pageCount()-1) + UBApplication::boardController->moveSceneToIndex(sceneIndex(), sceneIndex() + 1); +} + + +void UBImgTextThumbnailElement::Place(int row, int col, qreal width, qreal height) { int labelSpacing = 0; if(this->caption) @@ -802,8 +912,11 @@ void UBWidgetTextThumbnailElement::Place(int row, int col, qreal width, qreal he pix->setRow(row); } - QPointF pos(border + (width - w * scaleFactor) / 2 + col * (width + border), - border + row * (height + border + labelSpacing) + (height - h * scaleFactor) / 2); + QPointF pos((width - w * scaleFactor) / 2, + row * (height + labelSpacing) + (height - h * scaleFactor) / 2); + + /*QPointF pos(border + (width - w * scaleFactor) / 2 + col * (width + border), + border + row * (height + border + labelSpacing) + (height - h * scaleFactor) / 2);*/ this->thumbnail->setPos(pos); @@ -816,7 +929,7 @@ void UBWidgetTextThumbnailElement::Place(int row, int col, qreal width, qreal he this->caption->setWidth(fm.width(elidedText) + 2 * this->caption->document()->documentMargin()); pos.setY(pos.y() + (height + h * scaleFactor) / 2 + 5); // What is this 5 ?? qreal labelWidth = fm.width(elidedText); - pos.setX(border + (width - labelWidth) / 2 + col * (width + border)); + pos.setX((width - labelWidth) / 2 + col * (width + border)); this->caption->setPos(pos); } } @@ -929,7 +1042,7 @@ void UBDraggableThumbnail::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) } -void UBDraggableThumbnailView::updatePos(qreal width, qreal height) +void UBDraggableThumbnail::updatePos(qreal width, qreal height) { QFontMetrics fm(mPageNumber->font()); int labelSpacing = UBSettings::thumbnailSpacing + fm.height(); @@ -959,6 +1072,37 @@ void UBDraggableThumbnailView::updatePos(qreal width, qreal height) mPageNumber->setPos(position); } +void UBDraggableThumbnailPixmap::updatePos(qreal width, qreal height) +{ + QFontMetrics fm(mPageNumber->font()); + int labelSpacing = UBSettings::thumbnailSpacing + fm.height(); + + int w = thumbnailPixmap()->boundingRect().width(); + int h = thumbnailPixmap()->boundingRect().height(); + + qreal scaledWidth = width / w; + qreal scaledHeight = height / h; + qreal scaledFactor = qMin(scaledWidth, scaledHeight); + + QTransform transform; + transform.scale(scaledFactor, scaledFactor); + + // Apply the scaling + thumbnailPixmap()->setTransform(transform); + thumbnailPixmap()->setFlag(QGraphicsItem::ItemIsSelectable, true); + + QPointF position((width - w * scaledFactor) / 2, + sceneIndex() * (height + labelSpacing) + (height - h * scaledFactor) / 2); + + thumbnailPixmap()->setPos(position); + + position.setY(position.y() + (height + h * scaledFactor) / 2); + position.setX(position.x() + (w * scaledFactor - fm.width(mPageNumber->toPlainText())) / 2); + + mPageNumber->setPos(position); +} + + UBThumbnailUI::UBThumbnailUIIcon* UBThumbnailUI::addIcon(const QString& thumbnailIcon, int pos) { QString thumbnailIconPath = ":images/" + thumbnailIcon + ".svg"; diff --git a/src/gui/UBThumbnailWidget.h b/src/gui/UBThumbnailWidget.h index 887edc7e..d47f0f6b 100644 --- a/src/gui/UBThumbnailWidget.h +++ b/src/gui/UBThumbnailWidget.h @@ -48,6 +48,8 @@ #define STARTDRAGTIME 1000000 +#define BUTTONSIZE 48 +#define BUTTONSPACING 5 class UBDocumentProxy; class UBThumbnailTextItem; @@ -274,44 +276,9 @@ class UBThumbnailTextItem : public QGraphicsTextItem bool mIsHighlighted; }; -class UBWidgetTextThumbnailElement -{ -protected: - QGraphicsItem* thumbnail; - UBThumbnailTextItem* caption; - int border; - -public: - UBWidgetTextThumbnailElement(QGraphicsProxyWidget* proxyWidget, UBThumbnailTextItem* text) - : thumbnail(proxyWidget) - , caption(text) - , border(0) - { - } - - QGraphicsItem* getThumbnail() const { return this->thumbnail; } - void setThumbnail(QGraphicsItem* newGItem) { this->thumbnail = newGItem; } - - UBThumbnailTextItem* getCaption() const { return this->caption; } - void setCaption(UBThumbnailTextItem* newcaption) { this->caption = newcaption; } - - void Place(int row, int col, qreal width, qreal height); - - int getBorder() const { return this->border; } - void setBorder(int newBorder) { this->border = newBorder; } -}; - class UBThumbnailPixmap : public QGraphicsPixmapItem, public UBThumbnail { public: - UBThumbnailPixmap(const QPixmap& pix, UBDocumentProxy* proxy, int pSceneIndex) - : QGraphicsPixmapItem(pix) - , mDocumentProxy(proxy) - , mSceneIndex(pSceneIndex) - { - - } - UBThumbnailPixmap(const QPixmap& pix) : QGraphicsPixmapItem(pix) { @@ -320,6 +287,11 @@ class UBThumbnailPixmap : public QGraphicsPixmapItem, public UBThumbnail setFlag(QGraphicsItem::ItemSendsGeometryChanges, true); } + virtual ~UBThumbnailPixmap() + { + // NOOP + } + virtual QPainterPath shape () const { QPainterPath path; @@ -327,6 +299,7 @@ class UBThumbnailPixmap : public QGraphicsPixmapItem, public UBThumbnail return path; } + virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { QStyleOptionGraphicsItem styleOption = UBThumbnail::muteStyleOption(option); @@ -338,26 +311,94 @@ class UBThumbnailPixmap : public QGraphicsPixmapItem, public UBThumbnail UBThumbnail::itemChange(this, change, value); return QGraphicsPixmapItem::itemChange(change, value); } +}; - void highlight() +class UBSceneThumbnailPixmap : public UBThumbnailPixmap +{ + public: + UBSceneThumbnailPixmap(const QPixmap& pix, UBDocumentProxy* proxy, int pSceneIndex) + : UBThumbnailPixmap(pix) + , mProxy(proxy) + , mSceneIndex(pSceneIndex) { + // NOOP + } + virtual ~UBSceneThumbnailPixmap() + { + // NOOP } - UBDocumentProxy* documentProxy() + UBDocumentProxy* proxy() { - return mDocumentProxy; + return mProxy; } int sceneIndex() { return mSceneIndex; } - protected: - UBDocumentProxy* mDocumentProxy; + + void highlight() + { + //NOOP + } + + private: + UBDocumentProxy* mProxy; int mSceneIndex; }; +class UBSceneThumbnailNavigPixmap : public UBSceneThumbnailPixmap +{ + public: + UBSceneThumbnailNavigPixmap(const QPixmap& pix, UBDocumentProxy* proxy, int pSceneIndex); + ~UBSceneThumbnailNavigPixmap(); + + protected: + void hoverEnterEvent(QGraphicsSceneHoverEvent *event); + void hoverLeaveEvent(QGraphicsSceneHoverEvent *event); + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); + void mousePressEvent(QGraphicsSceneMouseEvent *event); + + private: + void deletePage(); + void duplicatePage(); + void moveUpPage(); + void moveDownPage(); + + bool bButtonsVisible; + bool bCanDelete; + bool bCanMoveUp; + bool bCanMoveDown; +}; + +class UBImgTextThumbnailElement +{ +private: + UBSceneThumbnailNavigPixmap* thumbnail; + UBThumbnailTextItem* caption; + int border; + +public: + UBImgTextThumbnailElement(UBSceneThumbnailNavigPixmap* thumb, UBThumbnailTextItem* text): border(0) + { + this->thumbnail = thumb; + this->caption = text; + } + + UBSceneThumbnailNavigPixmap* getThumbnail() const { return this->thumbnail; } + void setThumbnail(UBSceneThumbnailNavigPixmap* newGItem) { this->thumbnail = newGItem; } + + UBThumbnailTextItem* getCaption() const { return this->caption; } + void setCaption(UBThumbnailTextItem* newcaption) { this->caption = newcaption; } + + void Place(int row, int col, qreal width, qreal height); + + int getBorder() const { return this->border; } + void setBorder(int newBorder) { this->border = newBorder; } +}; + class UBThumbnailProxyWidget : public QGraphicsProxyWidget { public: @@ -393,11 +434,17 @@ class UBDraggableThumbnail : public UBThumbnailProxyWidget public: UBDraggableThumbnail(UBDocumentProxy* documentProxy, int index) : UBThumbnailProxyWidget(documentProxy, index) + , mPageNumber(new UBThumbnailTextItem(index)) , mEditable(false) { } + ~UBDraggableThumbnail() + { + delete mPageNumber; // not a child of "this" QObject so it has to be deleted manually + } + bool editable() { return mEditable; @@ -433,6 +480,23 @@ class UBDraggableThumbnail : public UBThumbnailProxyWidget mEditable = editable; } + UBThumbnailTextItem* pageNumber() + { + return mPageNumber; + } + + void setPageNumber(int i) + { + mPageNumber->setPlainText(tr("Page %0").arg(i+1)); + + if (UBApplication::boardController->activeSceneIndex() == i) + mPageNumber->setHtml("" + tr("Page %0").arg(i+1) + ""); + else + mPageNumber->setHtml("" + tr("Page %0").arg(i+1) + ""); + } + + virtual void updatePos(qreal w, qreal h); + protected: void hoverEnterEvent(QGraphicsSceneHoverEvent *event); void hoverLeaveEvent(QGraphicsSceneHoverEvent *event); @@ -440,6 +504,7 @@ class UBDraggableThumbnail : public UBThumbnailProxyWidget void mousePressEvent(QGraphicsSceneMouseEvent *event); void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); + UBThumbnailTextItem* mPageNumber; private: void deletePage(); void duplicatePage(); @@ -451,9 +516,30 @@ class UBDraggableThumbnail : public UBThumbnailProxyWidget class UBDraggableThumbnailPixmap : public UBDraggableThumbnail { + Q_OBJECT public: - UBDraggableThumbnailPixmap(const QPixmap& pix, UBDocumentProxy* proxy, int pSceneIndex); - ~UBDraggableThumbnailPixmap(); + UBDraggableThumbnailPixmap(UBThumbnailPixmap* thumbnailPixmap, UBDocumentProxy* documentProxy, int index) + : UBDraggableThumbnail(documentProxy, index) + , mThumbnailPixmap(thumbnailPixmap) + { + setFlag(QGraphicsItem::ItemIsSelectable, true); + setAcceptDrops(true); + } + + ~UBDraggableThumbnailPixmap() + { + + } + + UBThumbnailPixmap* thumbnailPixmap() + { + return mThumbnailPixmap; + } + + void updatePos(qreal w, qreal h); + + private: + UBThumbnailPixmap* mThumbnailPixmap; }; class UBDraggableThumbnailView : public UBDraggableThumbnail @@ -463,7 +549,6 @@ class UBDraggableThumbnailView : public UBDraggableThumbnail UBDraggableThumbnailView(UBThumbnailView* thumbnailView, UBDocumentProxy* documentProxy, int index) : UBDraggableThumbnail(documentProxy, index) , mThumbnailView(thumbnailView) - , mPageNumber(new UBThumbnailTextItem(index)) { setFlag(QGraphicsItem::ItemIsSelectable, true); setWidget(mThumbnailView); @@ -475,8 +560,6 @@ class UBDraggableThumbnailView : public UBDraggableThumbnail delete mPageNumber; // not a child of "this" QObject so it has to be deleted manually } - void updatePos(qreal w, qreal h); - UBThumbnailView* thumbnailView() { return mThumbnailView; @@ -498,8 +581,7 @@ class UBDraggableThumbnailView : public UBDraggableThumbnail } private: - UBThumbnailView* mThumbnailView; - UBThumbnailTextItem* mPageNumber; + UBThumbnailView* mThumbnailView; }; namespace UBThumbnailUI diff --git a/src/gui/gui.pri b/src/gui/gui.pri index 8f9dea58..fe59b318 100644 --- a/src/gui/gui.pri +++ b/src/gui/gui.pri @@ -41,7 +41,8 @@ HEADERS += src/gui/UBThumbnailView.h \ src/gui/UBOpenSankoreImporterWidget.h \ src/gui/UBStartupHintsPalette.h \ src/gui/UBBackgroundPalette.h \ - src/gui/UBBoardThumbnailsView.h + src/gui/UBBoardThumbnailsView.h \ + src/gui/UBDocumentNavigator.h SOURCES += src/gui/UBThumbnailView.cpp \ src/gui/UBFloatingPalette.cpp \ src/gui/UBToolbarButtonGroup.cpp \ @@ -85,7 +86,8 @@ SOURCES += src/gui/UBThumbnailView.cpp \ src/gui/UBOpenSankoreImporterWidget.cpp \ src/gui/UBStartupHintsPalette.cpp \ src/gui/UBBackgroundPalette.cpp \ - src/gui/UBBoardThumbnailsView.cpp + src/gui/UBBoardThumbnailsView.cpp \ + src/gui/UBDocumentNavigator.cpp win32:SOURCES += src/gui/UBKeyboardPalette_win.cpp macx:OBJECTIVE_SOURCES += src/gui/UBKeyboardPalette_mac.mm linux-g++:SOURCES += src/gui/UBKeyboardPalette_linux.cpp