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 }