Warning, file /office/calligra/libs/widgets/KoResourceItemChooser.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /* This file is part of the KDE project
0002    Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
0003    Copyright (c) 2007 Jan Hambrecht <jaham@gmx.net>
0004    Copyright (c) 2007 Sven Langkamp <sven.langkamp@gmail.com>
0005    Copyright (C) 2011 Srikanth Tiyyagura <srikanth.tulasiram@gmail.com>
0006    Copyright (c) 2011 José Luis Vergara <pentalis@gmail.com>
0007    Copyright (c) 2013 Sascha Suelzer <s.suelzer@gmail.com>
0008 
0009    This library is free software; you can redistribute it and/or
0010    modify it under the terms of the GNU Library General Public
0011    License as published by the Free Software Foundation; either
0012    version 2 of the License, or (at your option) any later version.
0013 
0014    This library is distributed in the hope that it will be useful,
0015    but WITHOUT ANY WARRANTY; without even the implied warranty of
0016    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0017    Library General Public License for more details.
0018 
0019    You should have received a copy of the GNU Library General Public License
0020    along with this library; see the file COPYING.LIB.  If not, write to
0021    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0022  * Boston, MA 02110-1301, USA.
0023 */
0024 #include "KoResourceItemChooser.h"
0025 
0026 #include <math.h>
0027 
0028 #include <QGridLayout>
0029 #include <QButtonGroup>
0030 #include <QPushButton>
0031 #include <QHeaderView>
0032 #include <QAbstractProxyModel>
0033 #include <QLabel>
0034 #include <QScrollArea>
0035 #include <QImage>
0036 #include <QPixmap>
0037 #include <QPainter>
0038 #include <QSplitter>
0039 #include <QToolButton>
0040 #include <QWheelEvent>
0041 
0042 #include <klocalizedstring.h>
0043 
0044 #ifdef GHNS
0045 #include <attica/version.h>
0046 #include <knewstuff3/downloaddialog.h>
0047 #include <knewstuff3/uploaddialog.h>
0048 #endif
0049 
0050 #include <KoIcon.h>
0051 #include <KoFileDialog.h>
0052 
0053 #include "KoResourceServerAdapter.h"
0054 #include "KoResourceItemView.h"
0055 #include "KoResourceItemDelegate.h"
0056 #include "KoResourceModel.h"
0057 #include "KoResource.h"
0058 #include "KoResourceTaggingManager.h"
0059 #include "KoTagFilterWidget.h"
0060 #include "KoTagChooserWidget.h"
0061 #include "KoResourceItemChooserSync.h"
0062 
0063 class Q_DECL_HIDDEN KoResourceItemChooser::Private
0064 {
0065 public:
0066     Private()
0067         : model(0)
0068         , view(0)
0069         , buttonGroup(0)
0070         , viewModeButton(0)
0071         , usePreview(false)
0072         , previewScroller(0)
0073         , previewLabel(0)
0074         , splitter(0)
0075         , tiledPreview(false)
0076         , grayscalePreview(false)
0077         , synced(false)
0078         , updatesBlocked(false)
0079         , savedResourceWhileReset(0)
0080     {}
0081     KoResourceModel *model;
0082     KoResourceTaggingManager *tagManager;
0083     KoResourceItemView *view;
0084     QButtonGroup *buttonGroup;
0085     QToolButton  *viewModeButton;
0086 
0087     QString knsrcFile;
0088 
0089     bool usePreview;
0090     QScrollArea *previewScroller;
0091     QLabel *previewLabel;
0092     QSplitter *splitter;
0093     QGridLayout *buttonLayout;
0094     bool tiledPreview;
0095     bool grayscalePreview;
0096     bool synced;
0097     bool updatesBlocked;
0098 
0099     KoResource *savedResourceWhileReset;
0100 
0101     QList<QAbstractButton*> customButtons;
0102 };
0103 
0104 KoResourceItemChooser::KoResourceItemChooser(QSharedPointer<KoAbstractResourceServerAdapter> resourceAdapter, QWidget *parent, bool usePreview)
0105     : QWidget(parent)
0106     , d(new Private())
0107 {
0108     Q_ASSERT(resourceAdapter);
0109 
0110     d->splitter = new QSplitter(this);
0111 
0112     d->model = new KoResourceModel(resourceAdapter, this);
0113     connect(d->model, SIGNAL(beforeResourcesLayoutReset(KoResource*)), SLOT(slotBeforeResourcesLayoutReset(KoResource*)));
0114     connect(d->model, SIGNAL(afterResourcesLayoutReset()), SLOT(slotAfterResourcesLayoutReset()));
0115 
0116     d->view = new KoResourceItemView(this);
0117     d->view->setModel(d->model);
0118     d->view->setItemDelegate(new KoResourceItemDelegate(this));
0119     d->view->setSelectionMode(QAbstractItemView::SingleSelection);
0120     d->view->viewport()->installEventFilter(this);
0121 
0122     connect(d->view, SIGNAL(currentResourceChanged(QModelIndex)), this, SLOT(activated(QModelIndex)));
0123     connect(d->view, SIGNAL(contextMenuRequested(QPoint)), this, SLOT(contextMenuRequested(QPoint)));
0124 
0125     connect(d->view, SIGNAL(sigSizeChanged()), this, SLOT(updateView()));
0126 
0127     d->splitter->addWidget(d->view);
0128     d->splitter->setStretchFactor(0, 2);
0129 
0130     d->usePreview = usePreview;
0131     if (d->usePreview) {
0132         d->previewScroller = new QScrollArea(this);
0133         d->previewScroller->setWidgetResizable(true);
0134         d->previewScroller->setBackgroundRole(QPalette::Dark);
0135         d->previewScroller->setVisible(true);
0136         d->previewScroller->setAlignment(Qt::AlignCenter);
0137         d->previewLabel = new QLabel(this);
0138         d->previewScroller->setWidget(d->previewLabel);
0139         d->splitter->addWidget(d->previewScroller);
0140 
0141         if (d->splitter->count() == 2) {
0142             d->splitter->setSizes(QList<int>() << 280 << 160);
0143         }
0144     }
0145 
0146     d->splitter->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
0147     connect(d->splitter, SIGNAL(splitterMoved(int,int)), SIGNAL(splitterMoved()));
0148 
0149     d->buttonGroup = new QButtonGroup(this);
0150     d->buttonGroup->setExclusive(false);
0151 
0152     QGridLayout *layout = new QGridLayout(this);
0153 
0154     d->buttonLayout = new QGridLayout();
0155 
0156     QPushButton *button = new QPushButton(this);
0157     button->setIcon(koIcon("document-open"));
0158     button->setToolTip(i18nc("@info:tooltip", "Import resource"));
0159     button->setEnabled(true);
0160     d->buttonGroup->addButton(button, Button_Import);
0161     d->buttonLayout->addWidget(button, 0, 0);
0162 
0163     button = new QPushButton(this);
0164     button->setIcon(koIcon("trash-empty"));
0165     button->setToolTip(i18nc("@info:tooltip", "Delete resource"));
0166     button->setEnabled(false);
0167     d->buttonGroup->addButton(button, Button_Remove);
0168     d->buttonLayout->addWidget(button, 0, 1);
0169 
0170     button = new QPushButton(this);
0171     button->setIcon(koIcon("download"));
0172     button->setToolTip(i18nc("@info:tooltip", "Download resource"));
0173     button->setEnabled(true);
0174     button->hide();
0175     d->buttonGroup->addButton(button, Button_GhnsDownload);
0176     d->buttonLayout->addWidget(button, 0, 3);
0177 
0178     button = new QPushButton(this);
0179     button->setIcon(koIcon("go-up"));
0180     button->setToolTip(i18nc("@info:tooltip", "Share Resource"));
0181     button->setEnabled(false);
0182     button->hide();
0183     d->buttonGroup->addButton(button, Button_GhnsUpload);
0184     d->buttonLayout->addWidget(button, 0, 4);
0185 
0186     connect(d->buttonGroup, SIGNAL(buttonClicked(int)), this, SLOT(slotButtonClicked(int)));
0187 
0188     d->buttonLayout->setColumnStretch(0, 1);
0189     d->buttonLayout->setColumnStretch(1, 1);
0190     d->buttonLayout->setColumnStretch(2, 2);
0191     d->buttonLayout->setSpacing(0);
0192     d->buttonLayout->setMargin(0);
0193 
0194     d->viewModeButton = new QToolButton(this);
0195     d->viewModeButton->setIcon(koIcon("view-choose"));
0196     d->viewModeButton->setPopupMode(QToolButton::InstantPopup);
0197     d->viewModeButton->setVisible(false);
0198 
0199     d->tagManager = new KoResourceTaggingManager(d->model, this);
0200 
0201     layout->addWidget(d->tagManager->tagChooserWidget(), 0, 0);
0202     layout->addWidget(d->viewModeButton, 0, 1);
0203     layout->addWidget(d->splitter, 1, 0, 1, 2);
0204     layout->addWidget(d->tagManager->tagFilterWidget(), 2, 0, 1, 2);
0205     layout->addLayout(d->buttonLayout, 3, 0, 1, 2);
0206     layout->setMargin(0);
0207     layout->setSpacing(0);
0208     updateButtonState();
0209     showTaggingBar(false);
0210     activated(d->model->index(0, 0));
0211 }
0212 
0213 KoResourceItemChooser::~KoResourceItemChooser()
0214 {
0215     disconnect();
0216     delete d;
0217 }
0218 
0219 void KoResourceItemChooser::slotButtonClicked(int button)
0220 {
0221     if (button == Button_Import) {
0222         QString extensions = d->model->extensions();
0223         QString filter = QString("%1")
0224                          .arg(extensions.replace(QString(":"), QString(" ")));
0225 
0226 
0227         KoFileDialog dialog(0, KoFileDialog::OpenFile, "OpenDocument");
0228         dialog.setNameFilter(filter);
0229         dialog.setCaption(i18nc("@title:window", "Choose File to Add"));
0230         QString filename = dialog.filename();
0231 
0232         d->model->importResourceFile(filename);
0233     } else if (button == Button_Remove) {
0234         QModelIndex index = d->view->currentIndex();
0235         int row = index.row();
0236         int column = index.column();
0237         if (index.isValid()) {
0238 
0239             KoResource *resource = resourceFromModelIndex(index);
0240             if (resource) {
0241                 d->model->removeResource(resource);
0242             }
0243         }
0244         if (column == 0) {
0245             int rowMin = --row;
0246             row = qBound(0, rowMin, row);
0247         }
0248         int columnMin = --column;
0249         column = qBound(0, columnMin, column);
0250         setCurrentItem(row, column);
0251         activated(d->model->index(row, column));
0252     }
0253 #ifdef GHNS
0254     else if (button == Button_GhnsDownload) {
0255 
0256         KNS3::DownloadDialog dialog(d->knsrcFile, this);
0257         dialog.exec();
0258 
0259         foreach (const KNS3::Entry & e, dialog.changedEntries()) {
0260 
0261             foreach (const QString & file, e.installedFiles()) {
0262                 QFileInfo fi(file);
0263                 d->model->importResourceFile(fi.absolutePath() + '/' + fi.fileName() , false);
0264             }
0265 
0266             foreach (const QString & file, e.uninstalledFiles()) {
0267                 QFileInfo fi(file);
0268                 d->model->removeResourceFile(fi.absolutePath() + '/' + fi.fileName());
0269             }
0270         }
0271     } else if (button == Button_GhnsUpload) {
0272 
0273         QModelIndex index = d->view->currentIndex();
0274         if (index.isValid()) {
0275 
0276 
0277             KoResource *resource = resourceFromModelIndex(index);
0278             if (resource) {
0279                 KNS3::UploadDialog dialog(d->knsrcFile, this);
0280                 dialog.setUploadFile(QUrl::fromLocalFile(resource->filename()));
0281                 dialog.setUploadName(resource->name());
0282                 dialog.exec();
0283             }
0284         }
0285     }
0286 #endif
0287     updateButtonState();
0288 }
0289 
0290 void KoResourceItemChooser::showButtons(bool show)
0291 {
0292     foreach (QAbstractButton * button, d->buttonGroup->buttons()) {
0293         show ? button->show() : button->hide();
0294     }
0295 
0296     foreach(QAbstractButton *button, d->customButtons) {
0297         show ? button->show() : button->hide();
0298     }
0299 }
0300 
0301 void KoResourceItemChooser::addCustomButton(QAbstractButton *button, int cell)
0302 {
0303     d->buttonLayout->addWidget(button, 0, cell);
0304     d->buttonLayout->setColumnStretch(2, 1);
0305     d->buttonLayout->setColumnStretch(3, 1);
0306 }
0307 void KoResourceItemChooser::showGetHotNewStuff(bool showDownload, bool showUpload)
0308 {
0309 #ifdef GHNS
0310 
0311     QAbstractButton *button = d->buttonGroup->button(Button_GhnsDownload);
0312     showDownload ? button->show() : button->hide();
0313 
0314     // attica < 2.9 is broken for upload, so don't show the upload button. 2.9 is released as 3.0
0315     // because of binary incompatibility with 2.x.
0316     if (LIBATTICA_VERSION_MAJOR < 3) return;
0317 
0318     button = d->buttonGroup->button(Button_GhnsUpload);
0319     showUpload ? button->show() : button->hide();
0320 #else
0321     Q_UNUSED(showDownload);
0322     Q_UNUSED(showUpload);
0323 #endif
0324 }
0325 
0326 void KoResourceItemChooser::showTaggingBar(bool show)
0327 {
0328     d->tagManager->showTaggingBar(show);
0329 
0330 }
0331 
0332 void KoResourceItemChooser::setRowCount(int rowCount)
0333 {
0334     int resourceCount = d->model->resourcesCount();
0335     d->model->setColumnCount(static_cast<qreal>(resourceCount) / rowCount);
0336     //Force an update to get the right row height (in theory)
0337     QRect geometry = d->view->geometry();
0338     d->view->setViewMode(KoResourceItemView::FIXED_ROWS);
0339     d->view->setGeometry(geometry.adjusted(0, 0, 0, 1));
0340     d->view->setGeometry(geometry);
0341 }
0342 
0343 void KoResourceItemChooser::setColumnCount(int columnCount)
0344 {
0345     d->model->setColumnCount(columnCount);
0346 }
0347 
0348 void KoResourceItemChooser::setRowHeight(int rowHeight)
0349 {
0350     d->view->verticalHeader()->setDefaultSectionSize(rowHeight);
0351 }
0352 
0353 void KoResourceItemChooser::setColumnWidth(int columnWidth)
0354 {
0355     d->view->horizontalHeader()->setDefaultSectionSize(columnWidth);
0356 }
0357 
0358 void KoResourceItemChooser::setItemDelegate(QAbstractItemDelegate *delegate)
0359 {
0360     d->view->setItemDelegate(delegate);
0361 }
0362 
0363 KoResource *KoResourceItemChooser::currentResource() const
0364 {
0365     QModelIndex index = d->view->currentIndex();
0366     if (index.isValid()) {
0367         return resourceFromModelIndex(index);
0368     }
0369     return 0;
0370 }
0371 
0372 void KoResourceItemChooser::setCurrentResource(KoResource *resource)
0373 {
0374     // don't update if the change came from the same chooser
0375     if (d->updatesBlocked) {
0376         return;
0377     }
0378 
0379     QModelIndex index = d->model->indexFromResource(resource);
0380     if (!index.isValid())
0381         return;
0382 
0383     d->view->setCurrentIndex(index);
0384     updatePreview(resource);
0385 }
0386 
0387 void KoResourceItemChooser::slotBeforeResourcesLayoutReset(KoResource *activateAfterReset)
0388 {
0389     d->savedResourceWhileReset = activateAfterReset ? activateAfterReset : currentResource();
0390 }
0391 
0392 void KoResourceItemChooser::slotAfterResourcesLayoutReset()
0393 {
0394     if (d->savedResourceWhileReset) {
0395         this->blockSignals(true);
0396         setCurrentResource(d->savedResourceWhileReset);
0397         this->blockSignals(false);
0398     }
0399 }
0400 
0401 void KoResourceItemChooser::setPreviewOrientation(Qt::Orientation orientation)
0402 {
0403     d->splitter->setOrientation(orientation);
0404 }
0405 
0406 void KoResourceItemChooser::setPreviewTiled(bool tiled)
0407 {
0408     d->tiledPreview = tiled;
0409 }
0410 
0411 void KoResourceItemChooser::setGrayscalePreview(bool grayscale)
0412 {
0413     d->grayscalePreview = grayscale;
0414 }
0415 
0416 void KoResourceItemChooser::setCurrentItem(int row, int column)
0417 {
0418     QModelIndex index = d->model->index(row, column);
0419     if (!index.isValid())
0420         return;
0421 
0422     d->view->setCurrentIndex(index);
0423     if (index.isValid()) {
0424         updatePreview(resourceFromModelIndex(index));
0425     }
0426 }
0427 
0428 void KoResourceItemChooser::setProxyModel(QAbstractProxyModel *proxyModel)
0429 {
0430     proxyModel->setSourceModel(d->model);
0431     d->view->setModel(proxyModel);
0432 }
0433 
0434 void KoResourceItemChooser::activated(const QModelIndex &/*index*/)
0435 {
0436     KoResource *resource = currentResource();
0437     if (resource) {
0438         d->updatesBlocked = true;
0439         emit resourceSelected(resource);
0440         d->updatesBlocked = false;
0441 
0442         updatePreview(resource);
0443         updateButtonState();
0444     }
0445 }
0446 
0447 void KoResourceItemChooser::updateButtonState()
0448 {
0449     QAbstractButton *removeButton = d->buttonGroup->button(Button_Remove);
0450     if (! removeButton)
0451         return;
0452 
0453     QAbstractButton *uploadButton = d->buttonGroup->button(Button_GhnsUpload);
0454     if (!uploadButton)
0455         return;
0456 
0457     KoResource *resource = currentResource();
0458     if (resource) {
0459         removeButton->setEnabled(true);
0460         uploadButton->setEnabled(resource->removable());
0461         return;
0462     }
0463 
0464     removeButton->setEnabled(false);
0465     uploadButton->setEnabled(false);
0466 }
0467 
0468 void KoResourceItemChooser::updatePreview(KoResource *resource)
0469 {
0470     if (!d->usePreview || !resource) return;
0471 
0472     QImage image = resource->image();
0473 
0474     if (image.format() != QImage::Format_RGB32 ||
0475         image.format() != QImage::Format_ARGB32 ||
0476         image.format() != QImage::Format_ARGB32_Premultiplied) {
0477 
0478         image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
0479     }
0480 
0481     if (d->tiledPreview) {
0482         int width = d->previewScroller->width() * 4;
0483         int height = d->previewScroller->height() * 4;
0484         QImage img(width, height, image.format());
0485         QPainter gc(&img);
0486         gc.fillRect(img.rect(), Qt::white);
0487         gc.setPen(Qt::NoPen);
0488         gc.setBrush(QBrush(image));
0489         gc.drawRect(img.rect());
0490         image = img;
0491     }
0492 
0493     // Only convert to grayscale if it is rgb. Otherwise, it's gray already.
0494     if (d->grayscalePreview && !image.isGrayscale()) {
0495 
0496         QRgb *pixel = reinterpret_cast<QRgb *>(image.bits());
0497         for (int row = 0; row < image.height(); ++row) {
0498             for (int col = 0; col < image.width(); ++col) {
0499                 const QRgb currentPixel = pixel[row * image.width() + col];
0500                 const int red = qRed(currentPixel);
0501                 const int green = qGreen(currentPixel);
0502                 const int blue = qBlue(currentPixel);
0503                 const int grayValue = (red * 11 + green * 16 + blue * 5) / 32;
0504                 pixel[row * image.width() + col] = qRgb(grayValue, grayValue, grayValue);
0505             }
0506         }
0507     }
0508     d->previewLabel->setPixmap(QPixmap::fromImage(image));
0509 
0510 }
0511 
0512 KoResource *KoResourceItemChooser::resourceFromModelIndex(const QModelIndex &index) const
0513 {
0514     if (!index.isValid())
0515         return 0;
0516 
0517     const QAbstractProxyModel *proxyModel = dynamic_cast<const QAbstractProxyModel *>(index.model());
0518     if (proxyModel) {
0519         //Get original model index, because proxy models destroy the internalPointer
0520         QModelIndex originalIndex = proxyModel->mapToSource(index);
0521         return static_cast<KoResource *>(originalIndex.internalPointer());
0522     }
0523 
0524     return static_cast<KoResource *>(index.internalPointer());
0525 }
0526 
0527 void KoResourceItemChooser::setKnsrcFile(const QString &knsrcFileArg)
0528 {
0529     d->knsrcFile = knsrcFileArg;
0530 }
0531 
0532 QSize KoResourceItemChooser::viewSize() const
0533 {
0534     return d->view->size();
0535 }
0536 
0537 KoResourceItemView *KoResourceItemChooser::itemView() const
0538 {
0539     return d->view;
0540 }
0541 
0542 void KoResourceItemChooser::contextMenuRequested(const QPoint &pos)
0543 {
0544     d->tagManager->contextMenuRequested(currentResource(), pos);
0545 }
0546 
0547 void KoResourceItemChooser::setViewModeButtonVisible(bool visible)
0548 {
0549     d->viewModeButton->setVisible(visible);
0550 }
0551 
0552 QToolButton *KoResourceItemChooser::viewModeButton() const
0553 {
0554     return d->viewModeButton;
0555 }
0556 
0557 void KoResourceItemChooser::setSynced(bool sync)
0558 {
0559     if (d->synced == sync)
0560         return;
0561 
0562     d->synced = sync;
0563     KoResourceItemChooserSync *chooserSync = KoResourceItemChooserSync::instance();
0564     if (sync) {
0565         connect(chooserSync, SIGNAL(baseLenghtChanged(int)), SLOT(baseLengthChanged(int)));
0566         baseLengthChanged(chooserSync->baseLength());
0567     } else {
0568         chooserSync->disconnect(this);
0569     }
0570 }
0571 
0572 void KoResourceItemChooser::baseLengthChanged(int length)
0573 {
0574     if (d->synced) {
0575         int resourceCount = d->model->resourcesCount();
0576         int width = d->view->width();
0577         int maxColums = width / length;
0578         int cols = width / (2 * length) + 1;
0579         while (cols <= maxColums) {
0580             int size = width / cols;
0581             int rows = ceil(resourceCount / (double)cols);
0582             if (rows * size < (d->view->height() - 5)) {
0583                 break;
0584             }
0585             cols++;
0586         }
0587         setColumnCount(cols);
0588     }
0589     d->view->updateView();
0590 }
0591 
0592 bool KoResourceItemChooser::eventFilter(QObject *object, QEvent *event)
0593 {
0594     if (d->synced && event->type() == QEvent::Wheel) {
0595         KoResourceItemChooserSync *chooserSync = KoResourceItemChooserSync::instance();
0596         QWheelEvent *qwheel = static_cast<QWheelEvent *>(event);
0597         if (qwheel->modifiers() & Qt::ControlModifier) {
0598 
0599             int degrees = qwheel->delta() / 8;
0600             int newBaseLength = chooserSync->baseLength() + degrees / 15 * 10;
0601             chooserSync->setBaseLength(newBaseLength);
0602             return true;
0603         }
0604     }
0605     return QObject::eventFilter(object, event);
0606 }
0607 
0608 void KoResourceItemChooser::resizeEvent(QResizeEvent *event)
0609 {
0610     QWidget::resizeEvent(event);
0611     updateView();
0612 }
0613 
0614 void KoResourceItemChooser::showEvent(QShowEvent *event)
0615 {
0616     QWidget::showEvent(event);
0617     updateView();
0618 }
0619 
0620 void KoResourceItemChooser::updateView()
0621 {
0622     if (d->synced) {
0623         KoResourceItemChooserSync *chooserSync = KoResourceItemChooserSync::instance();
0624         baseLengthChanged(chooserSync->baseLength());
0625     }
0626 }