1 /*
2 * Cantata
3 *
4 * Copyright (c) 2011-2020 Craig Drummond <craig.p.drummond@gmail.com>
5 *
6 * ----
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; see the file COPYING. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24 #include "itemview.h"
25 #include "groupedview.h"
26 #include "tableview.h"
27 #ifdef ENABLE_CATEGORIZED_VIEW
28 #include "categorizedview.h"
29 #endif
30 #include "messageoverlay.h"
31 #include "models/roles.h"
32 #include "gui/covers.h"
33 #include "gui/stdactions.h"
34 #include "models/proxymodel.h"
35 #include "actionitemdelegate.h"
36 #include "basicitemdelegate.h"
37 #include "models/actionmodel.h"
38 #include "support/icon.h"
39 #include "config.h"
40 #include "support/gtkstyle.h"
41 #include "support/proxystyle.h"
42 #include "support/spinner.h"
43 #include "support/action.h"
44 #include "support/actioncollection.h"
45 #include "support/configuration.h"
46 #include "support/flattoolbutton.h"
47 #include "support/monoicon.h"
48 #include <QStyleOption>
49 #include <QStyle>
50 #include <QPainter>
51 #include <QAction>
52 #include <QTimer>
53 #include <QKeyEvent>
54 #include <QProxyStyle>
55 #include <QScrollBar>
56 #include <QDebug>
57
58 #define COVERS_DBUG if (Covers::verboseDebugEnabled()) qWarning() << metaObject()->className() << QThread::currentThread()->objectName() << __FUNCTION__
59
60 #define RESPONSIVE_LAYOUT
61 static int detailedViewDecorationSize=22;
62 static int simpleViewDecorationSize=16;
63 static int listCoverSize=22;
64 static int gridCoverSize=22;
65
adjust(int v)66 static inline int adjust(int v)
67 {
68 if (v>48) {
69 static const int constStep=4;
70 return (((int)(v/constStep))*constStep)+((v%constStep) ? constStep : 0);
71 } else {
72 return Icon::stdSize(v);
73 }
74 }
75
setup()76 void ItemView::setup()
77 {
78 int height=QApplication::fontMetrics().height();
79
80 if (height>22) {
81 detailedViewDecorationSize=Icon::stdSize(height*1.4);
82 simpleViewDecorationSize=Icon::stdSize(height);
83 } else {
84 detailedViewDecorationSize=22;
85 simpleViewDecorationSize=16;
86 }
87 listCoverSize=qMax(32, adjust(2*height));
88 gridCoverSize=qMax(128, adjust(8*height));
89 }
90
KeyEventHandler(QAbstractItemView * v,QAction * a)91 KeyEventHandler::KeyEventHandler(QAbstractItemView *v, QAction *a)
92 : QObject(v)
93 , view(v)
94 , deleteAct(a)
95 , interceptBackspace(qobject_cast<ListView *>(view))
96 {
97 }
98
eventFilter(QObject * obj,QEvent * event)99 bool KeyEventHandler::eventFilter(QObject *obj, QEvent *event)
100 {
101 if (view->hasFocus()) {
102 if (QEvent::KeyRelease==event->type()) {
103 QKeyEvent *keyEvent=static_cast<QKeyEvent *>(event);
104 if (deleteAct && Qt::Key_Delete==keyEvent->key() && Qt::NoModifier==keyEvent->modifiers()) {
105 deleteAct->trigger();
106 return true;
107 }
108 } else if (QEvent::KeyPress==event->type()) {
109 QKeyEvent *keyEvent=static_cast<QKeyEvent *>(event);
110 if (interceptBackspace && Qt::Key_Backspace==keyEvent->key() && Qt::NoModifier==keyEvent->modifiers()) {
111 emit backspacePressed();
112 }
113 }
114 }
115 return QObject::eventFilter(obj, event);
116 }
117
ViewEventHandler(ActionItemDelegate * d,QAbstractItemView * v)118 ViewEventHandler::ViewEventHandler(ActionItemDelegate *d, QAbstractItemView *v)
119 : KeyEventHandler(v, nullptr)
120 , delegate(d)
121 {
122 }
123
124 // HACK time. For some reason, IconView is not always re-drawn when mouse leaves the view.
125 // We sometimes get an item that is left in the mouse-over state. So, work-around this by
126 // keeping track of when mouse is over listview.
eventFilter(QObject * obj,QEvent * event)127 bool ViewEventHandler::eventFilter(QObject *obj, QEvent *event)
128 {
129 if (delegate) {
130 if (QEvent::Enter==event->type()) {
131 delegate->setUnderMouse(true);
132 view->viewport()->update();
133 } else if (QEvent::Leave==event->type()) {
134 delegate->setUnderMouse(false);
135 view->viewport()->update();
136 }
137 }
138
139 return KeyEventHandler::eventFilter(obj, event);
140 }
141
142 static const int constDevImageSize=32;
143
subTextAlpha(bool selected)144 static inline double subTextAlpha(bool selected)
145 {
146 return selected ? 0.7 : 0.5;
147 }
148
zoomedSize(QListView * view,int size)149 static int zoomedSize(QListView *view, int size) {
150 return view
151 ? qobject_cast<ListView *>(view)
152 ? static_cast<ListView *>(view)->zoom()*size
153 #ifdef ENABLE_CATEGORIZED_VIEW
154 : qobject_cast<CategorizedView *>(view)
155 ? static_cast<CategorizedView *>(view)->zoom()*size
156 #endif
157 : size
158 : size;
159 }
160
zoomedSize(QListView * view,QSize size)161 static QSize zoomedSize(QListView *view, QSize size) {
162 return view
163 ? qobject_cast<ListView *>(view)
164 ? static_cast<ListView *>(view)->zoom()*size
165 #ifdef ENABLE_CATEGORIZED_VIEW
166 : qobject_cast<CategorizedView *>(view)
167 ? static_cast<CategorizedView *>(view)->zoom()*size
168 #endif
169 : size
170 : size;
171 }
172
173 class ListDelegate : public ActionItemDelegate
174 {
175 public:
ListDelegate(QListView * v,QAbstractItemView * p)176 ListDelegate(QListView *v, QAbstractItemView *p)
177 : ActionItemDelegate(p)
178 , view(v)
179 #ifdef RESPONSIVE_LAYOUT
180 , viewGap(Utils::scaleForDpi(8))
181 #endif
182 #ifdef ENABLE_CATEGORIZED_VIEW
183 , isCategorizedView(v && qobject_cast<CategorizedView *>(v))
184 #else
185 , isCategorizedView(false)
186 #endif
187 {
188 }
189
~ListDelegate()190 ~ListDelegate() override
191 {
192 }
193
194 #ifdef RESPONSIVE_LAYOUT
195 // Calculate width for each item in IconMode. The idea is to have the icons evenly spaced out.
calcItemWidth() const196 int calcItemWidth() const
197 {
198 int viewWidth = view->viewport()->width();
199
200 // KVantum returns a -ve number for spacing if using overlay scrollbars. I /think/ this
201 // is what messes the layout code. The subtracion below seems to work-around this - but
202 // give a larger right-hand margin!
203 int sbarSpacing = view->style()->pixelMetric(QStyle::PM_ScrollView_ScrollBarSpacing);
204 if (sbarSpacing<0) {
205 viewWidth-=3*viewGap;
206 } else {
207 QScrollBar *sb=view->verticalScrollBar();
208 int sbarWidth=sb && sb->isVisible() ? 0 : view->style()->pixelMetric(QStyle::PM_ScrollBarExtent);
209 viewWidth-=sbarSpacing+sbarWidth;
210 }
211
212 int standardWidth = zoomedSize(view, gridCoverSize)+viewGap;
213 int iconWidth = standardWidth;
214 int viewCount = view->model() ? view->model()->rowCount(view->rootIndex()) : -1;
215 int numItems = viewWidth/iconWidth;
216 if (numItems>1 && viewCount>1 && (viewCount+1)>numItems) {
217 iconWidth = qMax(iconWidth-1, (int)(viewWidth/numItems)-1);
218 } else if (numItems==1) {
219 iconWidth = viewWidth;
220 }
221 return iconWidth;
222 }
223 #endif
sizeHint(const QStyleOptionViewItem & option,const QModelIndex & index) const224 QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
225 {
226 Q_UNUSED(option)
227 if (view && QListView::IconMode==view->viewMode()) {
228 double textSpace = !isCategorizedView || view->model()->data(QModelIndex(), Cantata::Role_CatergizedHasSubText).toBool() ? 2.5 : 1.75;
229 #ifdef RESPONSIVE_LAYOUT
230 if (!isCategorizedView) {
231 return QSize(calcItemWidth(), zoomedSize(view, gridCoverSize)+(QApplication::fontMetrics().height()*textSpace));
232 }
233 #endif
234 return QSize(zoomedSize(view, gridCoverSize)+8, zoomedSize(view, gridCoverSize)+(QApplication::fontMetrics().height()*textSpace));
235 } else {
236 int imageSize = index.data(Cantata::Role_ListImage).toBool() ? listCoverSize : 0;
237 // TODO: Any point to checking one-line here? All models return sub-text...
238 // Things will be quicker if we dont call SubText here...
239 bool oneLine = false ; // index.data(Cantata::Role_SubText).toString().isEmpty();
240 bool showCapacity = !index.data(Cantata::Role_CapacityText).toString().isEmpty();
241 int textHeight = QApplication::fontMetrics().height()*(oneLine ? 1 : 2);
242
243 if (showCapacity) {
244 imageSize=constDevImageSize;
245 }
246 return QSize(qMax(64, imageSize) + (constBorder * 2),
247 qMax(textHeight, imageSize) + (constBorder*2) + (int)((showCapacity ? textHeight*Utils::smallFontFactor(QApplication::font()) : 0)+0.5));
248 }
249 }
250
paint(QPainter * painter,const QStyleOptionViewItem & option,const QModelIndex & index) const251 void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
252 {
253 if (!index.isValid()) {
254 return;
255 }
256 bool mouseOver=option.state&QStyle::State_MouseOver;
257 bool gtk=mouseOver && GtkStyle::isActive();
258 bool selected=option.state&QStyle::State_Selected;
259 bool active=option.state&QStyle::State_Active;
260 bool drawBgnd=true;
261 bool iconMode = view && QListView::IconMode==view->viewMode();
262 bool iconSubText = iconMode && (!isCategorizedView || view->model()->data(QModelIndex(), Cantata::Role_CatergizedHasSubText).toBool());
263
264 QStyleOptionViewItem opt(option);
265 opt.showDecorationSelected=true;
266
267 if (!isCategorizedView && !underMouse) {
268 if (mouseOver && !selected) {
269 drawBgnd=false;
270 }
271 mouseOver=false;
272 }
273
274 if (drawBgnd) {
275 if (mouseOver && gtk) {
276 GtkStyle::drawSelection(opt, painter, selected ? 0.75 : 0.25);
277 } else {
278 QApplication::style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, itemView());
279 }
280 }
281
282 QString capacityText=index.data(Cantata::Role_CapacityText).toString();
283 bool showCapacity = !capacityText.isEmpty();
284 QString text = iconMode ? index.data(Cantata::Role_BriefMainText).toString() : QString();
285 if (text.isEmpty()) {
286 text=index.data(Cantata::Role_MainText).toString();
287 }
288 if (text.isEmpty()) {
289 text=index.data(Qt::DisplayRole).toString();
290 }
291 #ifdef Q_OS_WIN
292 QColor textColor(option.palette.color(active ? QPalette::Active : QPalette::Inactive, QPalette::Text));
293 #else
294 QColor textColor(option.palette.color(active ? QPalette::Active : QPalette::Inactive, selected ? QPalette::HighlightedText : QPalette::Text));
295 #endif
296 QRect r(option.rect);
297 QRect r2(r);
298 QString childText = index.data(Cantata::Role_SubText).toString();
299
300 QPixmap pix;
301 if (showCapacity) {
302 const_cast<QAbstractItemModel *>(index.model())->setData(index, iconMode ? zoomedSize(view, gridCoverSize) : listCoverSize, Cantata::Role_Image);
303 pix=index.data(Cantata::Role_Image).value<QPixmap>();
304 }
305 if (pix.isNull() && (iconMode || index.data(Cantata::Role_ListImage).toBool())) {
306 Song cSong=index.data(iconMode ? Cantata::Role_GridCoverSong : Cantata::Role_CoverSong).value<Song>();
307 COVERS_DBUG << "Cover song" << cSong.albumArtist() << cSong.album << cSong.file << index.data().toString() << iconMode;
308 if (!cSong.isEmpty()) {
309 QPixmap *cp=Covers::self()->get(cSong, iconMode ? zoomedSize(view, gridCoverSize) : listCoverSize, getCoverInUiThread(index));
310 if (cp) {
311 pix=*cp;
312 }
313 }
314 }
315 if (pix.isNull()) {
316 COVERS_DBUG << "No cover image, use decoration";
317 int size=iconMode ? zoomedSize(view, gridCoverSize) : detailedViewDecorationSize;
318 pix=index.data(Qt::DecorationRole).value<QIcon>().pixmap(size, size, selected &&
319 textColor==qApp->palette().color(QPalette::HighlightedText)
320 ? QIcon::Selected : QIcon::Normal);
321 }
322
323 bool oneLine = (iconMode && !iconSubText) || childText.isEmpty();
324 ActionPos actionPos = iconMode ? AP_VTop : AP_HMiddle;
325 bool rtl = QApplication::isRightToLeft();
326
327 if (childText==QLatin1String("-")) {
328 childText.clear();
329 }
330
331 painter->save();
332 //painter->setClipRect(r);
333
334 QFont textFont(index.data(Qt::FontRole).value<QFont>());
335 QFontMetrics textMetrics(textFont);
336 int textHeight=textMetrics.height();
337
338 if (showCapacity) {
339 r.adjust(2, 0, 0, -(textHeight+4));
340 }
341
342 if (AP_VTop==actionPos) {
343 r.adjust(constBorder, constBorder*4, -constBorder, -constBorder);
344 r2=r;
345 } else {
346 r.adjust(constBorder, 0, -constBorder, 0);
347 }
348 if (!pix.isNull()) {
349 QSize layoutSize = pix.size() / pix.DEVICE_PIXEL_RATIO();
350 int adjust=qMax(layoutSize.width(), layoutSize.height());
351 if (AP_VTop==actionPos) {
352 int xpos=r.x()+((r.width()-layoutSize.width())/2);
353 painter->drawPixmap(xpos, r.y(), layoutSize.width(), layoutSize.height(), pix);
354 QColor color(option.palette.color(active ? QPalette::Active : QPalette::Inactive, QPalette::Text));
355 double alphas[]={0.25, 0.125, 0.061};
356 QRect border(xpos, r.y(), layoutSize.width(), layoutSize.height());
357 QRect shadow(border);
358 for (int i=0; i<3; ++i) {
359 shadow.adjust(1, 1, 1, 1);
360 color.setAlphaF(alphas[i]);
361 painter->setPen(color);
362 painter->drawLine(shadow.bottomLeft()+QPoint(i+1, 0),
363 shadow.bottomRight()+QPoint(-((i*2)+2), 0));
364 painter->drawLine(shadow.bottomRight()+QPoint(0, -((i*2)+2)),
365 shadow.topRight()+QPoint(0, i+1));
366 if (1==i) {
367 painter->drawPoint(shadow.bottomRight()-QPoint(2, 1));
368 painter->drawPoint(shadow.bottomRight()-QPoint(1, 2));
369 painter->drawPoint(shadow.bottomLeft()-QPoint(1, 1));
370 painter->drawPoint(shadow.topRight()-QPoint(1, 1));
371 } else if (2==i) {
372 painter->drawPoint(shadow.bottomRight()-QPoint(4, 1));
373 painter->drawPoint(shadow.bottomRight()-QPoint(1, 4));
374 painter->drawPoint(shadow.bottomLeft()-QPoint(0, 1));
375 painter->drawPoint(shadow.topRight()-QPoint(1, 0));
376 painter->drawPoint(shadow.bottomRight()-QPoint(2, 2));
377 }
378 }
379 color.setAlphaF(0.4);
380 painter->setPen(color);
381 painter->drawRect(border.adjusted(0, 0, -1, -1));
382 r.adjust(0, adjust+3, 0, -3);
383 } else {
384 if (rtl) {
385 painter->drawPixmap(r.x()+r.width()-layoutSize.width(), r.y()+((r.height()-layoutSize.height())/2), layoutSize.width(), layoutSize.height(), pix);
386 r.adjust(3, 0, -(3+adjust), 0);
387 } else {
388 painter->drawPixmap(r.x()+2, r.y()+((r.height()-layoutSize.height())/2), layoutSize.width(), layoutSize.height(), pix);
389 r.adjust(adjust+5, 0, -3, 0);
390 }
391 }
392 }
393
394 QRect textRect;
395 QTextOption textOpt(AP_VTop==actionPos ? Qt::AlignHCenter|Qt::AlignVCenter : Qt::AlignVCenter);
396 QVariant col = index.data(Cantata::Role_TextColor);
397 if (col.isValid()) {
398 textColor = col.value<QColor>();
399 }
400 textOpt.setWrapMode(QTextOption::NoWrap);
401 if (oneLine) {
402 textRect=QRect(r.x(), r.y()+((r.height()-textHeight)/2), r.width(), textHeight);
403 text = textMetrics.elidedText(text, Qt::ElideRight, textRect.width(), QPalette::WindowText);
404 painter->setPen(textColor);
405 painter->setFont(textFont);
406 painter->drawText(textRect, text, textOpt);
407 } else {
408 QFont childFont(Utils::smallFont(textFont));
409 QFontMetrics childMetrics(childFont);
410 QRect childRect;
411
412 int childHeight=childMetrics.height();
413 int totalHeight=textHeight+childHeight;
414 textRect=QRect(r.x(), r.y()+((r.height()-totalHeight)/2), r.width(), textHeight);
415 childRect=QRect(r.x(), r.y()+textHeight+((r.height()-totalHeight)/2), r.width(),
416 (iconMode ? childHeight-(2*constBorder) : childHeight));
417
418 text = textMetrics.elidedText(text, Qt::ElideRight, textRect.width(), QPalette::WindowText);
419 painter->setPen(textColor);
420 painter->setFont(textFont);
421 painter->drawText(textRect, text, textOpt);
422
423 if (!childText.isEmpty()) {
424 childText = childMetrics.elidedText(childText, Qt::ElideRight, childRect.width(), QPalette::WindowText);
425 textColor.setAlphaF(subTextAlpha(selected));
426 painter->setPen(textColor);
427 painter->setFont(childFont);
428 painter->drawText(childRect, childText, textOpt);
429 }
430 }
431
432 if (showCapacity) {
433 QColor col(Qt::white);
434 col.setAlphaF(0.25);
435 painter->fillRect(QRect(r2.x(), r2.bottom()-(textHeight+8), r2.width(), textHeight+8), col);
436
437 QStyleOptionProgressBar opt;
438 double capacity=index.data(Cantata::Role_Capacity).toDouble();
439
440 if (capacity<0.0) {
441 capacity=0.0;
442 } else if (capacity>1.0) {
443 capacity=1.0;
444 }
445 opt.minimum=0;
446 opt.maximum=1000;
447 opt.progress=capacity*1000;
448 opt.textVisible=true;
449 opt.text=capacityText;
450 opt.rect=QRect(r2.x()+4, r2.bottom()-(textHeight+4), r2.width()-8, textHeight);
451 opt.state=QStyle::State_Enabled;
452 opt.palette=option.palette;
453 opt.direction=QApplication::layoutDirection();
454 opt.fontMetrics=textMetrics;
455
456 QApplication::style()->drawControl(QStyle::CE_ProgressBar, &opt, painter, nullptr);
457 }
458
459 if (drawBgnd && mouseOver) {
460 QRect rect(AP_VTop==actionPos ? option.rect : r);
461 #ifdef RESPONSIVE_LAYOUT
462 if (!isCategorizedView && AP_VTop==actionPos) {
463 rect.adjust(0, 0, actionPosAdjust(), 0);
464 }
465 #endif
466 drawIcons(painter, rect, mouseOver, rtl, actionPos, index);
467 }
468 if (!iconMode) {
469 BasicItemDelegate::drawLine(painter, option.rect, textColor);
470 }
471 painter->restore();
472 }
473
getCoverInUiThread(const QModelIndex &) const474 virtual bool getCoverInUiThread(const QModelIndex &) const { return false; }
itemView() const475 virtual QWidget * itemView() const { return view; }
476 #ifdef RESPONSIVE_LAYOUT
actionPosAdjust() const477 int actionPosAdjust() const {
478 return !isCategorizedView && view && QListView::IconMode==view->viewMode() ? -(((calcItemWidth()-(zoomedSize(view, gridCoverSize)+viewGap))/2.0)+0.5) : 0;
479 }
480 #endif
481
482 protected:
483 QListView *view;
484 #ifdef RESPONSIVE_LAYOUT
485 int viewGap;
486 #endif
487 bool isCategorizedView;
488 };
489
490 class TreeDelegate : public ListDelegate
491 {
492 public:
TreeDelegate(QAbstractItemView * p)493 TreeDelegate(QAbstractItemView *p)
494 : ListDelegate(nullptr, p)
495 , simpleStyle(true)
496 , treeView(p)
497 {
498 }
499
~TreeDelegate()500 ~TreeDelegate() override
501 {
502 }
503
sizeHint(const QStyleOptionViewItem & option,const QModelIndex & index) const504 QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
505 {
506 if (noIcons) {
507 return QStyledItemDelegate::sizeHint(option, index);
508 }
509 if (!simpleStyle || !index.data(Cantata::Role_CapacityText).toString().isEmpty()) {
510 return ListDelegate::sizeHint(option, index);
511 }
512
513 QSize sz(QStyledItemDelegate::sizeHint(option, index));
514
515 if (index.data(Cantata::Role_ListImage).toBool()) {
516 sz.setHeight(qMax(sz.height(), listCoverSize));
517 }
518 int textHeight = QApplication::fontMetrics().height()*1.25;
519 sz.setHeight(qMax(sz.height(), textHeight)+(constBorder*2));
520 return sz;
521 }
522
paint(QPainter * painter,const QStyleOptionViewItem & option,const QModelIndex & index) const523 void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
524 {
525 if (!index.isValid()) {
526 return;
527 }
528
529 if (!simpleStyle || !index.data(Cantata::Role_CapacityText).toString().isEmpty()) {
530 ListDelegate::paint(painter, option, index);
531 return;
532 }
533
534 QStringList text=index.data(Qt::DisplayRole).toString().split(Song::constFieldSep);
535 bool gtk=GtkStyle::isActive();
536 bool rtl = QApplication::isRightToLeft();
537 bool selected=option.state&QStyle::State_Selected;
538 bool active=option.state&QStyle::State_Active;
539 bool mouseOver=underMouse && option.state&QStyle::State_MouseOver;
540 #ifdef Q_OS_WIN
541 QColor textColor(option.palette.color(active ? QPalette::Active : QPalette::Inactive, QPalette::Text));
542 #else
543 QColor textColor(option.palette.color(active ? QPalette::Active : QPalette::Inactive, selected ? QPalette::HighlightedText : QPalette::Text));
544 #endif
545 if (mouseOver && gtk) {
546 GtkStyle::drawSelection(option, painter, selected ? 0.75 : 0.25);
547 } else {
548 QApplication::style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, itemView());
549 }
550 QRect r(option.rect);
551 r.adjust(4, 0, -4, 0);
552
553 if (!noIcons) {
554 QPixmap pix;
555 if (index.data(Cantata::Role_ListImage).toBool()) {
556 Song cSong=index.data(Cantata::Role_CoverSong).value<Song>();
557 if (!cSong.isEmpty()) {
558 QPixmap *cp=Covers::self()->get(cSong, listCoverSize);
559 if (cp) {
560 pix=*cp;
561 }
562 }
563 }
564
565 if (pix.isNull()) {
566 pix=index.data(Qt::DecorationRole).value<QIcon>().pixmap(simpleViewDecorationSize, simpleViewDecorationSize,
567 selected &&
568 textColor==qApp->palette().color(QPalette::HighlightedText)
569 ? QIcon::Selected : QIcon::Normal);
570 }
571
572 if (!pix.isNull()) {
573 QSize layoutSize = pix.size() / pix.DEVICE_PIXEL_RATIO();
574 int adjust=qMax(layoutSize.width(), layoutSize.height());
575 if (rtl) {
576 painter->drawPixmap(r.x()+r.width()-layoutSize.width(), r.y()+((r.height()-layoutSize.height())/2), layoutSize.width(), layoutSize.height(), pix);
577 r.adjust(3, 0, -(3+adjust), 0);
578 } else {
579 painter->drawPixmap(r.x(), r.y()+((r.height()-layoutSize.height())/2), layoutSize.width(), layoutSize.height(), pix);
580 r.adjust(adjust+3, 0, -3, 0);
581 }
582 }
583 }
584
585 QVariant col = index.data(Cantata::Role_TextColor);
586 if (col.isValid()) {
587 textColor = col.value<QColor>();
588 }
589 if (text.count()>0) {
590 QFont textFont(QApplication::font());
591 QFontMetrics textMetrics(textFont);
592 int textHeight=textMetrics.height();
593 QTextOption textOpt(Qt::AlignVCenter);
594 QRect textRect(r.x(), r.y()+((r.height()-textHeight)/2), r.width(), textHeight);
595 QString str=textMetrics.elidedText(text.at(0), Qt::ElideRight, textRect.width(), QPalette::WindowText);
596
597 painter->save();
598 painter->setPen(textColor);
599 painter->setFont(textFont);
600 painter->drawText(textRect, str, textOpt);
601
602 if (text.count()>1) {
603 int mainWidth=textMetrics.horizontalAdvance(str);
604 text.takeFirst();
605 str=text.join(Song::constSep);
606 textRect=QRect(r.x()+(mainWidth+8), r.y()+((r.height()-textHeight)/2), r.width()-(mainWidth+8), textHeight);
607 if (textRect.width()>4) {
608 str = textMetrics.elidedText(str, Qt::ElideRight, textRect.width(), QPalette::WindowText);
609 textColor.setAlphaF(subTextAlpha(selected));
610 painter->setPen(textColor);
611 painter->drawText(textRect, str, textOpt/*QTextOption(Qt::AlignVCenter|Qt::AlignRight)*/);
612 }
613 }
614 painter->restore();
615 }
616
617 if (mouseOver) {
618 drawIcons(painter, option.rect, mouseOver, rtl, AP_HMiddle, index);
619 }
620 #ifdef Q_OS_WIN
621 BasicItemDelegate::drawLine(painter, option.rect, option.palette.color(active ? QPalette::Active : QPalette::Inactive,
622 QPalette::Text));
623 #else
624 BasicItemDelegate::drawLine(painter, option.rect, option.palette.color(active ? QPalette::Active : QPalette::Inactive,
625 selected ? QPalette::HighlightedText : QPalette::Text));
626 #endif
627 }
628
setSimple(bool s)629 void setSimple(bool s) { simpleStyle=s; }
setNoIcons(bool n)630 void setNoIcons(bool n) { noIcons=n; }
631
getCoverInUiThread(const QModelIndex & idx) const632 bool getCoverInUiThread(const QModelIndex &idx) const override
633 {
634 // Want album covers in artists view to load quickly...
635 return idx.isValid() && idx.data(Cantata::Role_LoadCoverInUIThread).toBool();
636 }
637
itemView() const638 QWidget * itemView() const override { return treeView; }
639
640 bool simpleStyle;
641 bool noIcons;
642 QAbstractItemView *treeView;
643 };
644
toMode(const QString & str)645 ItemView::Mode ItemView::toMode(const QString &str)
646 {
647 for (int i=0; i<Mode_Count; ++i) {
648 if (modeStr((Mode)i)==str) {
649 return (Mode)i;
650 }
651 }
652
653 // Older versions of Cantata saved mode as an integer!!!
654 int i=str.toInt();
655 switch (i) {
656 default:
657 case 0: return Mode_SimpleTree;
658 case 1: return Mode_List;
659 case 2: return Mode_IconTop;
660 case 3: return Mode_GroupedTree;
661 }
662 }
663
modeStr(Mode m)664 QString ItemView::modeStr(Mode m)
665 {
666 switch (m) {
667 default:
668 case Mode_SimpleTree: return QLatin1String("simpletree");
669 case Mode_BasicTree: return QLatin1String("basictree");
670 case Mode_DetailedTree: return QLatin1String("detailedtree");
671 case Mode_List: return QLatin1String("list");
672 case Mode_IconTop: return QLatin1String("icontop");
673 case Mode_GroupedTree: return QLatin1String("grouped");
674 case Mode_Table: return QLatin1String("table");
675 #ifdef ENABLE_CATEGORIZED_VIEW
676 case Mode_Categorized: return QLatin1String("categorized");
677 #endif
678 }
679 }
680
681 static const char *constPageProp="page";
682 static const char *constAlwaysShowProp="always";
683 static Action *backAction=nullptr;
684
685 static const QLatin1String constZoomKey("gridZoom");
686 const QLatin1String ItemView::constSearchActiveKey("searchActive");
687 const QLatin1String ItemView::constViewModeKey("viewMode");
688 const QLatin1String ItemView::constStartClosedKey("startClosed");
689 const QLatin1String ItemView::constSearchCategoryKey("searchCategory");
690
691 static const double constMinZoom = 1.0;
692 static const double constMaxZoom = 4.0;
693 static const double constZoomStep = 0.25;
694
ItemView(QWidget * p)695 ItemView::ItemView(QWidget *p)
696 : QWidget(p)
697 , searchTimer(nullptr)
698 , itemModel(nullptr)
699 , currentLevel(0)
700 , mode(Mode_SimpleTree)
701 , groupedView(nullptr)
702 , tableView(nullptr)
703 #ifdef ENABLE_CATEGORIZED_VIEW
704 , categorizedView(nullptr)
705 #endif
706 , spinner(nullptr)
707 , msgOverlay(nullptr)
708 , performedSearch(false)
709 , searchResetLevel(0)
710 , openFirstLevelAfterSearch(false)
711 , initialised(false)
712 , minSearchDebounce(250)
713 {
714 setupUi(this);
715 if (!backAction) {
716 backAction=ActionCollection::get()->createAction("itemview-goback", tr("Go Back"));
717 backAction->setShortcut(Qt::AltModifier+(Qt::LeftToRight==layoutDirection() ? Qt::Key_Left : Qt::Key_Right));
718 }
719 title->addAction(backAction);
720 title->setVisible(false);
721 Action::updateToolTip(backAction);
722 QAction *sep=new QAction(this);
723 sep->setSeparator(true);
724 listView->addAction(sep);
725 treeView->setPageDefaults();
726 // Some styles, eg Cleanlooks/Plastique require that we explicitly set mouse tracking on the treeview.
727 treeView->setAttribute(Qt::WA_MouseTracking);
728 iconGridSize=listGridSize=listView->gridSize();
729 ListDelegate *ld=new ListDelegate(listView, listView);
730 TreeDelegate *td=new TreeDelegate(treeView);
731 listView->setItemDelegate(ld);
732 treeView->setItemDelegate(td);
733 listView->setProperty(ProxyStyle::constModifyFrameProp, ProxyStyle::VF_Side|ProxyStyle::VF_Top);
734 treeView->setProperty(ProxyStyle::constModifyFrameProp, ProxyStyle::VF_Side|ProxyStyle::VF_Top);
735 ViewEventHandler *listViewEventHandler=new ViewEventHandler(ld, listView);
736 ViewEventHandler *treeViewEventHandler=new ViewEventHandler(td, treeView);
737 listView->installFilter(listViewEventHandler);
738 treeView->installFilter(treeViewEventHandler);
739 connect(searchWidget, SIGNAL(returnPressed()), this, SLOT(doSearch()));
740 connect(searchWidget, SIGNAL(textChanged(const QString)), this, SLOT(delaySearchItems()));
741 connect(searchWidget, SIGNAL(active(bool)), this, SLOT(searchActive(bool)));
742 connect(treeView, SIGNAL(itemsSelected(bool)), this, SIGNAL(itemsSelected(bool)));
743 connect(treeView, SIGNAL(itemActivated(const QModelIndex &)), this, SLOT(itemActivated(const QModelIndex &)));
744 connect(treeView, SIGNAL(doubleClicked(const QModelIndex &)), this, SIGNAL(doubleClicked(const QModelIndex &)));
745 connect(treeView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(itemClicked(const QModelIndex &)));
746 connect(listView, SIGNAL(itemsSelected(bool)), this, SIGNAL(itemsSelected(bool)));
747 connect(listView, SIGNAL(activated(const QModelIndex &)), this, SLOT(activateItem(const QModelIndex &)));
748 connect(listView, SIGNAL(itemDoubleClicked(const QModelIndex &)), this, SIGNAL(doubleClicked(const QModelIndex &)));
749 connect(listView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(itemClicked(const QModelIndex &)));
750 connect(backAction, SIGNAL(triggered()), this, SLOT(backActivated()));
751 connect(listViewEventHandler, SIGNAL(backspacePressed()), this, SLOT(backActivated()));
752 connect(title, SIGNAL(addToPlayQueue()), this, SLOT(addTitleButtonClicked()));
753 connect(title, SIGNAL(replacePlayQueue()), this, SLOT(replaceTitleButtonClicked()));
754 connect(Covers::self(), SIGNAL(loaded(Song,int)), this, SLOT(coverLoaded(Song,int)));
755 searchWidget->setVisible(false);
756 #ifdef Q_OS_MAC
757 treeView->setAttribute(Qt::WA_MacShowFocusRect, 0);
758 listView->setAttribute(Qt::WA_MacShowFocusRect, 0);
759 #endif
760
761 QWidget::addAction(StdActions::self()->zoomInAction);
762 QWidget::addAction(StdActions::self()->zoomOutAction);
763 connect(StdActions::self()->zoomInAction, SIGNAL(triggered()), SLOT(zoomIn()));
764 connect(StdActions::self()->zoomOutAction, SIGNAL(triggered()), SLOT(zoomOut()));
765 }
766
~ItemView()767 ItemView::~ItemView()
768 {
769 }
770
alwaysShowHeader()771 void ItemView::alwaysShowHeader()
772 {
773 title->setVisible(true);
774 title->setProperty(constAlwaysShowProp, true);
775 setTitle();
776 controlViewFrame();
777 }
778
load(Configuration & config)779 void ItemView::load(Configuration &config)
780 {
781 if (config.get(constSearchActiveKey, false)) {
782 focusSearch();
783 }
784 setMode(toMode(config.get(constViewModeKey, modeStr(mode))));
785 setStartClosed(config.get(constStartClosedKey, isStartClosed()));
786 setSearchCategory(config.get(constSearchCategoryKey, searchCategory()));
787 int zoom=config.get(constZoomKey, 100);
788 if (zoom>100) {
789 listView->setZoom(zoom/100.0);
790 }
791 }
792
save(Configuration & config)793 void ItemView::save(Configuration &config)
794 {
795 config.set(constSearchActiveKey, searchWidget->isActive());
796 config.set(constViewModeKey, modeStr(mode));
797 config.set(constZoomKey, (int)(listView->zoom()*100));
798 if (groupedView) {
799 config.set(constStartClosedKey, isStartClosed());
800 }
801 TableView *tv=qobject_cast<TableView *>(view());
802 if (tv) {
803 tv->saveHeader();
804 }
805 QString cat=searchCategory();
806 if (!cat.isEmpty()) {
807 config.set(constSearchCategoryKey, cat);
808 }
809 }
810
allowGroupedView()811 void ItemView::allowGroupedView()
812 {
813 if (!groupedView) {
814 groupedView=new GroupedView(stackedWidget);
815 stackedWidget->addWidget(groupedView);
816 groupedView->setProperty(constPageProp, stackedWidget->count()-1);
817 // Some styles, eg Cleanlooks/Plastique require that we explicitly set mouse tracking on the treeview.
818 groupedView->setAttribute(Qt::WA_MouseTracking, true);
819 ViewEventHandler *viewHandler=new ViewEventHandler(qobject_cast<ActionItemDelegate *>(groupedView->itemDelegate()), groupedView);
820 groupedView->installFilter(viewHandler);
821 groupedView->setAutoExpand(false);
822 groupedView->setMultiLevel(true);
823 connect(groupedView, SIGNAL(itemsSelected(bool)), this, SIGNAL(itemsSelected(bool)));
824 connect(groupedView, SIGNAL(itemActivated(const QModelIndex &)), this, SLOT(itemActivated(const QModelIndex &)));
825 connect(groupedView, SIGNAL(doubleClicked(const QModelIndex &)), this, SIGNAL(doubleClicked(const QModelIndex &)));
826 connect(groupedView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(itemClicked(const QModelIndex &)));
827 groupedView->setProperty(ProxyStyle::constModifyFrameProp, ProxyStyle::VF_Side|ProxyStyle::VF_Top);
828 #ifdef Q_OS_MAC
829 groupedView->setAttribute(Qt::WA_MacShowFocusRect, 0);
830 #endif
831 }
832 }
833
allowTableView(TableView * v)834 void ItemView::allowTableView(TableView *v)
835 {
836 if (!tableView) {
837 tableView=v;
838 tableView->setParent(stackedWidget);
839 stackedWidget->addWidget(tableView);
840 tableView->setProperty(constPageProp, stackedWidget->count()-1);
841 ViewEventHandler *viewHandler=new ViewEventHandler(nullptr, tableView);
842 tableView->installFilter(viewHandler);
843 connect(tableView, SIGNAL(itemsSelected(bool)), this, SIGNAL(itemsSelected(bool)));
844 connect(tableView, SIGNAL(itemActivated(const QModelIndex &)), this, SLOT(itemActivated(const QModelIndex &)));
845 connect(tableView, SIGNAL(doubleClicked(const QModelIndex &)), this, SIGNAL(doubleClicked(const QModelIndex &)));
846 connect(tableView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(itemClicked(const QModelIndex &)));
847 tableView->setProperty(ProxyStyle::constModifyFrameProp, ProxyStyle::VF_Side|ProxyStyle::VF_Top);
848 #ifdef Q_OS_MAC
849 tableView->setAttribute(Qt::WA_MacShowFocusRect, 0);
850 #endif
851 }
852 }
853
allowCategorized()854 void ItemView::allowCategorized()
855 {
856 #ifdef ENABLE_CATEGORIZED_VIEW
857 if (!categorizedView) {
858 categorizedView=new CategorizedView(stackedWidget);
859 categorizedView->setParent(stackedWidget);
860 stackedWidget->addWidget(categorizedView);
861 categorizedView->setProperty(constPageProp, stackedWidget->count()-1);
862 //KCategorizedView seems to handle mouse-over events better
863 //ViewEventHandler *viewHandler=new ViewEventHandler(nullptr, categorizedView);
864 //categorizedView->installFilter(viewHandler);
865 connect(categorizedView, SIGNAL(itemsSelected(bool)), this, SIGNAL(itemsSelected(bool)));
866 connect(categorizedView, SIGNAL(itemActivated(const QModelIndex &)), this, SLOT(activateItem(const QModelIndex &)));
867 connect(categorizedView, SIGNAL(itemDoubleClicked(const QModelIndex &)), this, SIGNAL(doubleClicked(const QModelIndex &)));
868 connect(categorizedView, SIGNAL(itemClicked(const QModelIndex &)), this, SLOT(itemClicked(const QModelIndex &)));
869 categorizedView->setProperty(ProxyStyle::constModifyFrameProp, ProxyStyle::VF_Side|ProxyStyle::VF_Top);
870 #ifdef Q_OS_MAC
871 categorizedView->setAttribute(Qt::WA_MacShowFocusRect, 0);
872 #endif
873 categorizedView->setItemDelegate(new ListDelegate(categorizedView, categorizedView));
874 }
875 #endif
876 }
877
addAction(QAction * act)878 void ItemView::addAction(QAction *act)
879 {
880 treeView->addAction(act);
881 listView->addAction(act);
882 if (groupedView) {
883 groupedView->addAction(act);
884 }
885 if (tableView) {
886 tableView->addAction(act);
887 }
888 #ifdef ENABLE_CATEGORIZED_VIEW
889 if (categorizedView) {
890 categorizedView->addAction(act);
891 }
892 #endif
893 }
894
addSeparator()895 void ItemView::addSeparator()
896 {
897 QAction *act=new QAction(this);
898 act->setSeparator(true);
899 addAction(act);
900 }
901
setMode(Mode m)902 void ItemView::setMode(Mode m)
903 {
904 initialised=true;
905 if (m<0 || m>=Mode_Count || (Mode_GroupedTree==m && !groupedView) || (Mode_Table==m && !tableView)
906 #ifdef ENABLE_CATEGORIZED_VIEW
907 || (Mode_Categorized==m && !categorizedView)
908 #endif
909 ) {
910 m=Mode_SimpleTree;
911 }
912
913 if (m==mode) {
914 return;
915 }
916
917 prevTopIndex.clear();
918 searchWidget->setText(QString());
919 if (!title->property(constAlwaysShowProp).toBool()) {
920 title->setVisible(false);
921 controlViewFrame();
922 }
923 QIcon oldBgndIcon=bgndIcon;
924 if (!bgndIcon.isNull()) {
925 setBackgroundImage(QIcon());
926 }
927
928 bool wasListView = usingListView();
929 mode=m;
930 int stackIndex=0;
931 if (usingTreeView()) {
932 listView->setModel(nullptr);
933 if (groupedView) {
934 groupedView->setModel(nullptr);
935 }
936 if (tableView) {
937 tableView->saveHeader();
938 tableView->setModel(nullptr);
939 }
940 #ifdef ENABLE_CATEGORIZED_VIEW
941 if (categorizedView) {
942 categorizedView->setModel(nullptr);
943 }
944 #endif
945 treeView->setModel(itemModel);
946 treeView->setHidden(false);
947 static_cast<TreeDelegate *>(treeView->itemDelegate())->setSimple(Mode_SimpleTree==mode || Mode_BasicTree==mode);
948 static_cast<TreeDelegate *>(treeView->itemDelegate())->setNoIcons(Mode_BasicTree==mode);
949 if (dynamic_cast<ProxyModel *>(itemModel)) {
950 static_cast<ProxyModel *>(itemModel)->setRootIndex(QModelIndex());
951 }
952 treeView->reset();
953 } else if (Mode_GroupedTree==mode) {
954 treeView->setModel(nullptr);
955 listView->setModel(nullptr);
956 if (tableView) {
957 tableView->saveHeader();
958 tableView->setModel(nullptr);
959 }
960 #ifdef ENABLE_CATEGORIZED_VIEW
961 if (categorizedView) {
962 categorizedView->setModel(nullptr);
963 }
964 #endif
965 groupedView->setHidden(false);
966 treeView->setHidden(true);
967 groupedView->setModel(itemModel);
968 if (dynamic_cast<ProxyModel *>(itemModel)) {
969 static_cast<ProxyModel *>(itemModel)->setRootIndex(QModelIndex());
970 }
971 stackIndex=groupedView->property(constPageProp).toInt();
972 } else if (Mode_Table==mode) {
973 int w=view()->width();
974 treeView->setModel(nullptr);
975 listView->setModel(nullptr);
976 if (groupedView) {
977 groupedView->setModel(nullptr);
978 }
979 #ifdef ENABLE_CATEGORIZED_VIEW
980 if (categorizedView) {
981 categorizedView->setModel(nullptr);
982 }
983 #endif
984 tableView->setHidden(false);
985 treeView->setHidden(true);
986 tableView->setModel(itemModel);
987 tableView->initHeader();
988 if (dynamic_cast<ProxyModel *>(itemModel)) {
989 static_cast<ProxyModel *>(itemModel)->setRootIndex(QModelIndex());
990 }
991 tableView->resize(w, tableView->height());
992 stackIndex=tableView->property(constPageProp).toInt();
993 #ifdef ENABLE_CATEGORIZED_VIEW
994 } else if (Mode_Categorized==mode) {
995 //int w=view()->width();
996 treeView->setModel(nullptr);
997 listView->setModel(nullptr);
998 if (groupedView) {
999 groupedView->setModel(nullptr);
1000 }
1001 if (tableView) {
1002 tableView->setModel(nullptr);
1003 }
1004 categorizedView->setHidden(false);
1005 treeView->setHidden(true);
1006 categorizedView->setModel(itemModel);
1007 stackIndex=categorizedView->property(constPageProp).toInt();
1008 categorizedView->setGridSize(zoomedSize(listView, iconGridSize));
1009 #endif
1010 } else {
1011 stackIndex=1;
1012 treeView->setModel(nullptr);
1013 if (groupedView) {
1014 groupedView->setModel(nullptr);
1015 }
1016 if (tableView) {
1017 tableView->saveHeader();
1018 tableView->setModel(nullptr);
1019 }
1020 #ifdef ENABLE_CATEGORIZED_VIEW
1021 if (categorizedView) {
1022 categorizedView->setModel(nullptr);
1023 }
1024 #endif
1025 listView->setModel(itemModel);
1026 goToTop();
1027 if (Mode_IconTop!=mode) {
1028 listView->setGridSize(listGridSize);
1029 listView->setViewMode(QListView::ListMode);
1030 listView->setResizeMode(QListView::Fixed);
1031 // listView->setAlternatingRowColors(true);
1032 listView->setWordWrap(false);
1033 }
1034 }
1035
1036 stackedWidget->setCurrentIndex(stackIndex);
1037 if (spinner) {
1038 spinner->setWidget(view());
1039 if (spinner->isActive()) {
1040 spinner->start();
1041 }
1042 }
1043 if (msgOverlay) {
1044 msgOverlay->setWidget(view());
1045 }
1046
1047 if (!oldBgndIcon.isNull()) {
1048 setBackgroundImage(oldBgndIcon);
1049 }
1050 controlViewFrame();
1051 if (wasListView && !usingListView()) {
1052 goToTop();
1053 }
1054 }
1055
selectedIndexes(bool sorted) const1056 QModelIndexList ItemView::selectedIndexes(bool sorted) const
1057 {
1058 if (usingTreeView()) {
1059 return treeView->selectedIndexes(sorted);
1060 } else if (Mode_GroupedTree==mode) {
1061 return groupedView->selectedIndexes(sorted);
1062 } else if (Mode_Table==mode) {
1063 return tableView->selectedIndexes(sorted);
1064 }
1065 #ifdef ENABLE_CATEGORIZED_VIEW
1066 else if (Mode_Categorized==mode) {
1067 return categorizedView->selectedIndexes(sorted);
1068 }
1069 #endif
1070 return listView->selectedIndexes(sorted);
1071 }
1072
goToTop()1073 void ItemView::goToTop()
1074 {
1075 setLevel(0);
1076 prevTopIndex.clear();
1077 if (usingListView()) {
1078 if (dynamic_cast<ProxyModel *>(itemModel)) {
1079 static_cast<ProxyModel *>(itemModel)->setRootIndex(QModelIndex());
1080 }
1081 listView->setRootIndex(QModelIndex());
1082 }
1083 #ifdef ENABLE_CATEGORIZED_VIEW
1084 else if (Mode_Categorized==mode) {
1085 categorizedView->setRootIndex(QModelIndex());
1086 categorizedView->setPlain(false);
1087 // Setting grid size causes categorizedview to re-lyout items. If we don't do this
1088 // then items are all messed up!
1089 categorizedView->setGridSizeOwn(categorizedView->gridSize());
1090 }
1091 #endif
1092 setTitle();
1093 emit rootIndexSet(QModelIndex());
1094 }
1095
setEnabled(bool en)1096 void ItemView::setEnabled(bool en)
1097 {
1098 if (treeView) {
1099 treeView->setEnabled(en);
1100 }
1101 if (groupedView) {
1102 groupedView->setEnabled(en);
1103 }
1104 if (tableView) {
1105 tableView->setEnabled(en);
1106 }
1107 if (listView) {
1108 listView->setEnabled(en);
1109 }
1110 #ifdef ENABLE_CATEGORIZED_VIEW
1111 if (categorizedView) {
1112 categorizedView->setEnabled(en);
1113 }
1114 #endif
1115 }
1116
setInfoText(const QString & info)1117 void ItemView::setInfoText(const QString &info)
1118 {
1119 listView->setInfoText(info);
1120 treeView->setInfoText(info);
1121 if (groupedView) {
1122 groupedView->setInfoText(info);
1123 }
1124 if (tableView) {
1125 tableView->setInfoText(info);
1126 }
1127 #ifdef ENABLE_CATEGORIZED_VIEW
1128 if (categorizedView) {
1129 categorizedView->setInfoText(info);
1130 }
1131 #endif
1132 }
1133
setLevel(int l,bool haveChildren)1134 void ItemView::setLevel(int l, bool haveChildren)
1135 {
1136 currentLevel=l;
1137
1138 if (Mode_IconTop==mode) {
1139 if (0==currentLevel || haveChildren) {
1140 if (QListView::IconMode!=listView->viewMode()) {
1141 listView->setGridSize(zoomedSize(listView, iconGridSize));
1142 listView->setViewMode(QListView::IconMode);
1143 listView->setResizeMode(QListView::Adjust);
1144 // listView->setAlternatingRowColors(false);
1145 listView->setWordWrap(true);
1146 listView->setDragDropMode(QAbstractItemView::DragOnly);
1147 static_cast<ActionItemDelegate *>(listView->itemDelegate())->setLargeIcons(true);
1148 }
1149 } else if(QListView::ListMode!=listView->viewMode()) {
1150 listView->setGridSize(listGridSize);
1151 listView->setViewMode(QListView::ListMode);
1152 listView->setResizeMode(QListView::Fixed);
1153 // listView->setAlternatingRowColors(true);
1154 listView->setWordWrap(false);
1155 listView->setDragDropMode(QAbstractItemView::DragOnly);
1156 static_cast<ActionItemDelegate *>(listView->itemDelegate())->setLargeIcons(false);
1157 }
1158 }
1159
1160 if (view()->selectionModel()) {
1161 view()->selectionModel()->clearSelection();
1162 }
1163
1164 if (!title->property(constAlwaysShowProp).toBool()) {
1165 title->setVisible(currentLevel>0);
1166 controlViewFrame();
1167 }
1168 setTitle();
1169 }
1170
view() const1171 QAbstractItemView * ItemView::view() const
1172 {
1173 if (usingTreeView()) {
1174 return treeView;
1175 } else if(Mode_GroupedTree==mode) {
1176 return groupedView;
1177 } else if(Mode_Table==mode) {
1178 return tableView;
1179 }
1180 #ifdef ENABLE_CATEGORIZED_VIEW
1181 else if(Mode_Categorized==mode) {
1182 return categorizedView;
1183 }
1184 #endif
1185 else {
1186 return listView;
1187 }
1188 }
1189
setModel(QAbstractItemModel * m)1190 void ItemView::setModel(QAbstractItemModel *m)
1191 {
1192 if (itemModel) {
1193 disconnect(itemModel, SIGNAL(modelReset()), this, SLOT(modelReset()));
1194 if (qobject_cast<QAbstractProxyModel *>(itemModel)) {
1195 disconnect(static_cast<QAbstractProxyModel *>(itemModel)->sourceModel(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(dataChanged(QModelIndex,QModelIndex)));
1196 } else {
1197 disconnect(itemModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(dataChanged(QModelIndex,QModelIndex)));
1198 }
1199 }
1200 itemModel=m;
1201 if (!initialised) {
1202 mode=Mode_List;
1203 setMode(Mode_SimpleTree);
1204 }
1205 if (m) {
1206 connect(m, SIGNAL(modelReset()), this, SLOT(modelReset()));
1207 if (qobject_cast<QAbstractProxyModel *>(m)) {
1208 connect(static_cast<QAbstractProxyModel *>(m)->sourceModel(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(dataChanged(QModelIndex,QModelIndex)));
1209 } else {
1210 connect(m, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(dataChanged(QModelIndex,QModelIndex)));
1211 }
1212 }
1213 view()->setModel(m);
1214 }
1215
searchVisible() const1216 bool ItemView::searchVisible() const
1217 {
1218 return searchWidget->isVisible();
1219 }
1220
searchText() const1221 QString ItemView::searchText() const
1222 {
1223 return searchWidget->isVisible() ? searchWidget->text() : QString();
1224 }
1225
searchCategory() const1226 QString ItemView::searchCategory() const
1227 {
1228 return searchWidget->category();
1229 }
1230
clearSearchText()1231 void ItemView::clearSearchText()
1232 {
1233 return searchWidget->setText(QString());
1234 }
1235
setUniformRowHeights(bool v)1236 void ItemView::setUniformRowHeights(bool v)
1237 {
1238 treeView->setUniformRowHeights(v);
1239 }
1240
setAcceptDrops(bool v)1241 void ItemView::setAcceptDrops(bool v)
1242 {
1243 listView->setAcceptDrops(v);
1244 treeView->setAcceptDrops(v);
1245 if (groupedView) {
1246 groupedView->setAcceptDrops(v);
1247 }
1248 if (tableView) {
1249 tableView->setAcceptDrops(v);
1250 }
1251 #ifdef ENABLE_CATEGORIZED_VIEW
1252 if (categorizedView) {
1253 categorizedView->setAcceptDrops(v);
1254 }
1255 #endif
1256 }
1257
setDragDropOverwriteMode(bool v)1258 void ItemView::setDragDropOverwriteMode(bool v)
1259 {
1260 listView->setDragDropOverwriteMode(v);
1261 treeView->setDragDropOverwriteMode(v);
1262 if (groupedView) {
1263 groupedView->setDragDropOverwriteMode(v);
1264 }
1265 if (tableView) {
1266 tableView->setDragDropOverwriteMode(v);
1267 }
1268 #ifdef ENABLE_CATEGORIZED_VIEW
1269 if (categorizedView) {
1270 categorizedView->setDragDropOverwriteMode(v);
1271 }
1272 #endif
1273 }
1274
setDragDropMode(QAbstractItemView::DragDropMode v)1275 void ItemView::setDragDropMode(QAbstractItemView::DragDropMode v)
1276 {
1277 listView->setDragDropMode(v);
1278 treeView->setDragDropMode(v);
1279 if (groupedView) {
1280 groupedView->setDragDropMode(v);
1281 }
1282 if (tableView) {
1283 tableView->setDragDropMode(v);
1284 }
1285 #ifdef ENABLE_CATEGORIZED_VIEW
1286 if (categorizedView) {
1287 categorizedView->setDragDropMode(v);
1288 }
1289 #endif
1290 }
1291
update()1292 void ItemView::update()
1293 {
1294 view()->update();
1295 }
1296
setDeleteAction(QAction * act)1297 void ItemView::setDeleteAction(QAction *act)
1298 {
1299 if (!listView->filter() || !qobject_cast<KeyEventHandler *>(listView->filter())) {
1300 listView->installFilter(new KeyEventHandler(listView, act));
1301 } else {
1302 static_cast<KeyEventHandler *>(listView->filter())->setDeleteAction(act);
1303 }
1304 if (!treeView->filter() || !qobject_cast<KeyEventHandler *>(treeView->filter())) {
1305 treeView->installEventFilter(new KeyEventHandler(treeView, act));
1306 } else {
1307 static_cast<KeyEventHandler *>(treeView->filter())->setDeleteAction(act);
1308 }
1309 if (groupedView) {
1310 if (!groupedView->filter() || !qobject_cast<KeyEventHandler *>(groupedView->filter())) {
1311 groupedView->installEventFilter(new KeyEventHandler(groupedView, act));
1312 } else {
1313 static_cast<KeyEventHandler *>(groupedView->filter())->setDeleteAction(act);
1314 }
1315 }
1316 if (tableView) {
1317 if (!tableView->filter() || !qobject_cast<KeyEventHandler *>(tableView->filter())) {
1318 tableView->installEventFilter(new KeyEventHandler(tableView, act));
1319 } else {
1320 static_cast<KeyEventHandler *>(tableView->filter())->setDeleteAction(act);
1321 }
1322 }
1323 #ifdef ENABLE_CATEGORIZED_VIEW
1324 if (categorizedView) {
1325 if (!categorizedView->filter() || !qobject_cast<KeyEventHandler *>(categorizedView->filter())) {
1326 categorizedView->installEventFilter(new KeyEventHandler(categorizedView, act));
1327 } else {
1328 static_cast<KeyEventHandler *>(categorizedView->filter())->setDeleteAction(act);
1329 }
1330 }
1331 #endif
1332 }
1333
showIndex(const QModelIndex & idx,bool scrollTo)1334 void ItemView::showIndex(const QModelIndex &idx, bool scrollTo)
1335 {
1336 if (usingTreeView() || Mode_GroupedTree==mode || Mode_Table==mode) {
1337 TreeView *v=static_cast<TreeView *>(view());
1338 QModelIndex i=idx;
1339 while (i.isValid()) {
1340 v->setExpanded(i, true);
1341 i=i.parent();
1342 }
1343 if (scrollTo) {
1344 v->scrollTo(idx, QAbstractItemView::PositionAtTop);
1345 }
1346 }
1347 #ifdef ENABLE_CATEGORIZED_VIEW
1348 else if (Mode_Categorized==mode) {
1349 // TODO
1350 }
1351 #endif
1352 else {
1353 if (idx.parent().isValid()) {
1354 QList<QModelIndex> indexes;
1355 QModelIndex i=idx.parent();
1356 QModelIndex p=idx;
1357 while (i.isValid()) {
1358 indexes.prepend(i);
1359 i=i.parent();
1360 }
1361 setLevel(0);
1362 listView->setRootIndex(QModelIndex());
1363 if (dynamic_cast<ProxyModel *>(itemModel)) {
1364 static_cast<ProxyModel *>(itemModel)->setRootIndex(QModelIndex());
1365 }
1366 for (const QModelIndex &i: indexes) {
1367 activateItem(i, false);
1368 }
1369 if (p.isValid()) {
1370 emit rootIndexSet(p);
1371 }
1372 if (scrollTo) {
1373 listView->scrollTo(idx, QAbstractItemView::PositionAtTop);
1374 }
1375 setTitle();
1376 } else if (idx.isValid() && scrollTo) {
1377 listView->scrollTo(idx, QAbstractItemView::PositionAtTop);
1378 }
1379 }
1380
1381 if (view()->selectionModel()) {
1382 view()->selectionModel()->select(idx, QItemSelectionModel::Select|QItemSelectionModel::Rows);
1383 }
1384 }
1385
focusSearch(const QString & text)1386 void ItemView::focusSearch(const QString &text)
1387 {
1388 if (isEnabled()) {
1389 performedSearch=false;
1390 searchWidget->activate(searchWidget->text().isEmpty() ? text : QString());
1391 }
1392 }
1393
focusView()1394 void ItemView::focusView()
1395 {
1396 view()->setFocus();
1397 }
1398
setSearchVisible(bool v)1399 void ItemView::setSearchVisible(bool v)
1400 {
1401 searchWidget->setVisible(v);
1402 }
1403
isSearchActive() const1404 bool ItemView::isSearchActive() const
1405 {
1406 return searchWidget->isActive();
1407 }
1408
setSearchToolTip(const QString & str)1409 void ItemView::setSearchToolTip(const QString &str)
1410 {
1411 searchWidget->setToolTip(str);
1412 }
1413
closeSearch()1414 void ItemView::closeSearch()
1415 {
1416 if (searchWidget->isActive()) {
1417 searchWidget->close();
1418 }
1419 }
1420
setStartClosed(bool sc)1421 void ItemView::setStartClosed(bool sc)
1422 {
1423 if (groupedView) {
1424 groupedView->setStartClosed(sc);
1425 }
1426 }
1427
isStartClosed()1428 bool ItemView::isStartClosed()
1429 {
1430 return groupedView ? groupedView->isStartClosed() : false;
1431 }
1432
updateRows()1433 void ItemView::updateRows()
1434 {
1435 if (groupedView) {
1436 groupedView->updateCollectionRows();
1437 }
1438 }
1439
updateRows(const QModelIndex & idx)1440 void ItemView::updateRows(const QModelIndex &idx)
1441 {
1442 if (groupedView) {
1443 groupedView->updateRows(idx);
1444 }
1445 }
1446
expandAll(const QModelIndex & index)1447 void ItemView::expandAll(const QModelIndex &index)
1448 {
1449 if (usingTreeView()) {
1450 treeView->expandAll(index);
1451 } else if (Mode_GroupedTree==mode && groupedView) {
1452 groupedView->expandAll(index);
1453 } else if (Mode_Table==mode && tableView) {
1454 tableView->expandAll(index);
1455 }
1456 }
1457
expand(const QModelIndex & index,bool singleOnly)1458 void ItemView::expand(const QModelIndex &index, bool singleOnly)
1459 {
1460 if (usingTreeView()) {
1461 treeView->expand(index, singleOnly);
1462 } else if (Mode_GroupedTree==mode && groupedView) {
1463 groupedView->expand(index, singleOnly);
1464 } else if (Mode_Table==mode && tableView) {
1465 tableView->expand(index, singleOnly);
1466 }
1467 }
1468
showMessage(const QString & message,int timeout)1469 void ItemView::showMessage(const QString &message, int timeout)
1470 {
1471 if (!msgOverlay) {
1472 msgOverlay=new MessageOverlay(this);
1473 msgOverlay->setWidget(view());
1474 }
1475 msgOverlay->setText(message, timeout, false);
1476 }
1477
setBackgroundImage(const QIcon & icon)1478 void ItemView::setBackgroundImage(const QIcon &icon)
1479 {
1480 bgndIcon=icon;
1481 if (usingTreeView()) {
1482 treeView->setBackgroundImage(bgndIcon);
1483 } else if (Mode_GroupedTree==mode && groupedView) {
1484 groupedView->setBackgroundImage(bgndIcon);
1485 } else if (Mode_Table==mode && tableView) {
1486 tableView->setBackgroundImage(bgndIcon);
1487 }
1488 #ifdef ENABLE_CATEGORIZED_VIEW
1489 else if (Mode_Categorized==mode && categorizedView) {
1490 categorizedView->setBackgroundImage(bgndIcon);
1491 }
1492 #endif
1493 else if (Mode_List==mode || Mode_IconTop==mode) {
1494 listView->setBackgroundImage(bgndIcon);
1495 }
1496 }
1497
isAnimated() const1498 bool ItemView::isAnimated() const
1499 {
1500 if (usingTreeView()) {
1501 return treeView->isAnimated();
1502 }
1503 if (Mode_GroupedTree==mode && groupedView) {
1504 return groupedView->isAnimated();
1505 }
1506 if (Mode_Table==mode && tableView) {
1507 return tableView->isAnimated();
1508 }
1509 return false;
1510 }
1511
setAnimated(bool a)1512 void ItemView::setAnimated(bool a)
1513 {
1514 if (usingTreeView()) {
1515 treeView->setAnimated(a);
1516 } else if (Mode_GroupedTree==mode && groupedView) {
1517 groupedView->setAnimated(a);
1518 } else if (Mode_Table==mode && tableView) {
1519 tableView->setAnimated(a);
1520 }
1521 }
1522
setPermanentSearch()1523 void ItemView::setPermanentSearch()
1524 {
1525 searchWidget->setPermanent();
1526 }
1527
hideSearch()1528 void ItemView::hideSearch()
1529 {
1530 if (searchVisible()) {
1531 searchWidget->close();
1532 }
1533 }
1534
setSearchCategories(const QList<SearchWidget::Category> & categories)1535 void ItemView::setSearchCategories(const QList<SearchWidget::Category> &categories)
1536 {
1537 searchWidget->setCategories(categories);
1538 }
1539
setSearchCategory(const QString & id)1540 void ItemView::setSearchCategory(const QString &id)
1541 {
1542 searchWidget->setCategory(id);
1543 }
1544
showSpinner(bool v)1545 void ItemView::showSpinner(bool v)
1546 {
1547 if (v) {
1548 if (!spinner) {
1549 spinner=new Spinner(this);
1550 }
1551 spinner->setWidget(view());
1552 spinner->start();
1553 } else {
1554 hideSpinner();
1555 }
1556 }
1557
hideSpinner()1558 void ItemView::hideSpinner()
1559 {
1560 if (spinner) {
1561 spinner->stop();
1562 }
1563 }
1564
updating()1565 void ItemView::updating()
1566 {
1567 showSpinner();
1568 showMessage(tr("Updating..."), -1);
1569 }
1570
updated()1571 void ItemView::updated()
1572 {
1573 hideSpinner();
1574 showMessage(QString(), 0);
1575 }
1576
collectionRemoved(quint32 key)1577 void ItemView::collectionRemoved(quint32 key)
1578 {
1579 if (groupedView) {
1580 groupedView->collectionRemoved(key);
1581 }
1582 }
1583
backActivated()1584 void ItemView::backActivated()
1585 {
1586 if (!isVisible()) {
1587 return;
1588 }
1589
1590 emit headerClicked(currentLevel);
1591
1592 if (!(usingListView() || Mode_Categorized==mode) || 0==currentLevel) {
1593 return;
1594 }
1595 setLevel(currentLevel-1);
1596
1597 if (usingListView()) {
1598 if (dynamic_cast<ProxyModel *>(itemModel)) {
1599 static_cast<ProxyModel *>(itemModel)->setRootIndex(listView->rootIndex().parent());
1600 }
1601 listView->setRootIndex(listView->rootIndex().parent());
1602 emit rootIndexSet(listView->rootIndex().parent());
1603 }
1604 #ifdef ENABLE_CATEGORIZED_VIEW
1605 else {
1606 categorizedView->setRootIndex(categorizedView->rootIndex().parent());
1607 categorizedView->setPlain(false);
1608 // Setting grid size causes categorizedview to re-lyout items. If we don't do this
1609 // then items are all messed up!
1610 categorizedView->setGridSizeOwn(categorizedView->gridSize());
1611 emit rootIndexSet(categorizedView->rootIndex().parent());
1612 }
1613 #endif
1614 setTitle();
1615
1616 if (prevTopIndex.isEmpty()) {
1617 return;
1618 }
1619
1620 QModelIndex prevTop = prevTopIndex.takeLast();
1621 if (usingListView()) {
1622 if (qobject_cast<QSortFilterProxyModel *>(listView->model())) {
1623 QModelIndex idx=static_cast<QSortFilterProxyModel *>(listView->model())->mapFromSource(prevTop);
1624 if (idx.isValid()) {
1625 listView->scrollTo(idx, QAbstractItemView::PositionAtTop);
1626 }
1627 } else {
1628 listView->scrollTo(prevTop, QAbstractItemView::PositionAtTop);
1629 }
1630 }
1631 #ifdef ENABLE_CATEGORIZED_VIEW
1632 else {
1633 categorizedView->scrollTo(prevTop, QAbstractItemView::PositionAtTop);
1634 }
1635 #endif
1636 }
1637
setExpanded(const QModelIndex & idx,bool exp)1638 void ItemView::setExpanded(const QModelIndex &idx, bool exp)
1639 {
1640 if (usingTreeView()) {
1641 treeView->setExpanded(idx, exp);
1642 }
1643 }
1644
getAction(const QModelIndex & index)1645 QAction * ItemView::getAction(const QModelIndex &index)
1646 {
1647 QModelIndex idx =
1648 #ifdef ENABLE_CATEGORIZED_VIEW
1649 Mode_Categorized==mode ? categorizedView->mapFromSource(index) :
1650 #endif
1651 index;
1652 QAbstractItemDelegate *abs=view()->itemDelegate();
1653 ActionItemDelegate *d=abs ? qobject_cast<ActionItemDelegate *>(abs) : nullptr;
1654 #ifdef RESPONSIVE_LAYOUT
1655 ListDelegate *l=abs ? dynamic_cast<ListDelegate *>(abs) : nullptr;
1656 return d ? d->getAction(idx, l ? l->actionPosAdjust() : 0) : nullptr;
1657 #else
1658 return d ? d->getAction(idx) : nullptr;
1659 #endif
1660 }
1661
itemClicked(const QModelIndex & index)1662 void ItemView::itemClicked(const QModelIndex &index)
1663 {
1664 QAction *act=getAction(index);
1665 if (act) {
1666 act->trigger();
1667 return;
1668 }
1669
1670 if (TreeView::getForceSingleClick()) {
1671 activateItem(index);
1672 }
1673 }
1674
itemActivated(const QModelIndex & index)1675 void ItemView::itemActivated(const QModelIndex &index)
1676 {
1677 if (!TreeView::getForceSingleClick()) {
1678 activateItem(index);
1679 }
1680 }
1681
activateItem(const QModelIndex & index,bool emitRootSet)1682 void ItemView::activateItem(const QModelIndex &index, bool emitRootSet)
1683 {
1684 if (getAction(index)) {
1685 return;
1686 }
1687
1688 if (usingTreeView()) {
1689 treeView->setExpanded(index, !treeView->isExpanded(index));
1690 } else if (Mode_GroupedTree==mode) {
1691 if (!index.parent().isValid()) {
1692 groupedView->setExpanded(index, !groupedView->TreeView::isExpanded(index));
1693 }
1694 } else if (Mode_Table==mode) {
1695 if (!index.parent().isValid()) {
1696 tableView->setExpanded(index, !tableView->TreeView::isExpanded(index));
1697 }
1698 #ifdef ENABLE_CATEGORIZED_VIEW
1699 } else if (Mode_Categorized==mode) {
1700 if (index == categorizedView->rootIndex()) {
1701 return;
1702 }
1703 if (itemModel->canFetchMore(index)) {
1704 itemModel->fetchMore(index);
1705 }
1706 QModelIndex fistChild=itemModel->index(0, 0, index);
1707 if (!fistChild.isValid()) {
1708 return;
1709 }
1710
1711 QModelIndex curTop=categorizedView->indexAt(QPoint(8, 24), true);
1712 //if (qobject_cast<QSortFilterProxyModel *>(categorizedView->model())) {
1713 // curTop=static_cast<QSortFilterProxyModel *>(categorizedView->model())->mapToSource(curTop);
1714 //}
1715 prevTopIndex.append(curTop);
1716 bool haveChildren=itemModel->canFetchMore(fistChild);
1717 setLevel(currentLevel+1, haveChildren || itemModel->index(0, 0, fistChild).isValid());
1718 categorizedView->setPlain(!haveChildren);
1719 categorizedView->setRootIndex(index);
1720 setTitle();
1721
1722 // if (dynamic_cast<ProxyModel *>(itemModel)) {
1723 // static_cast<ProxyModel *>(itemModel)->setRootIndex(index);
1724 // }
1725 if (emitRootSet) {
1726 emit rootIndexSet(index);
1727 }
1728 categorizedView->scrollToTop();
1729 #endif
1730 } else if (usingListView() && (index.isValid() && (itemModel->index(0, 0, index).isValid() || itemModel->canFetchMore(index)) && index!=listView->rootIndex())) {
1731 if (itemModel->canFetchMore(index)) {
1732 itemModel->fetchMore(index);
1733 }
1734 QModelIndex fistChild=itemModel->index(0, 0, index);
1735 if (!fistChild.isValid()) {
1736 return;
1737 }
1738
1739 QModelIndex curTop=listView->indexAt(QPoint(8, 8));
1740 if (qobject_cast<QSortFilterProxyModel *>(listView->model())) {
1741 curTop=static_cast<QSortFilterProxyModel *>(listView->model())->mapToSource(curTop);
1742 }
1743 prevTopIndex.append(curTop);
1744 setLevel(currentLevel+1, itemModel->canFetchMore(fistChild) || itemModel->index(0, 0, fistChild).isValid());
1745 listView->setRootIndex(index);
1746 setTitle();
1747
1748 if (dynamic_cast<ProxyModel *>(itemModel)) {
1749 static_cast<ProxyModel *>(itemModel)->setRootIndex(index);
1750 }
1751 if (emitRootSet) {
1752 emit rootIndexSet(index);
1753 }
1754 listView->scrollToTop();
1755 }
1756 }
1757
modelReset()1758 void ItemView::modelReset()
1759 {
1760 if (Mode_List==mode || Mode_IconTop==mode) {
1761 goToTop();
1762 } else if (usingTreeView() && !searchText().isEmpty()) {
1763 for (int r=0; r<itemModel->rowCount(); ++r) {
1764 treeView->expand(itemModel->index(r, 0, QModelIndex()));
1765 }
1766 }
1767 }
1768
dataChanged(const QModelIndex & tl,const QModelIndex & br)1769 void ItemView::dataChanged(const QModelIndex &tl, const QModelIndex &br)
1770 {
1771 if (!tl.isValid() && !br.isValid()) {
1772 setTitle();
1773 }
1774 }
1775
addTitleButtonClicked()1776 void ItemView::addTitleButtonClicked()
1777 {
1778 if ((Mode_List==mode || Mode_IconTop==mode) && view()->rootIndex().isValid()) {
1779 emit updateToPlayQueue(view()->rootIndex(), false);
1780 }
1781 #ifdef ENABLE_CATEGORIZED_VIEW
1782 else if (Mode_Categorized==mode && categorizedView->rootIndex().isValid()) {
1783 emit updateToPlayQueue(categorizedView->rootIndex(), false);
1784 }
1785 #endif
1786 }
1787
replaceTitleButtonClicked()1788 void ItemView::replaceTitleButtonClicked()
1789 {
1790 if ((Mode_List==mode || Mode_IconTop==mode) && view()->rootIndex().isValid()) {
1791 emit updateToPlayQueue(view()->rootIndex(), true);
1792 }
1793 #ifdef ENABLE_CATEGORIZED_VIEW
1794 else if (Mode_Categorized==mode && categorizedView->rootIndex().isValid()) {
1795 emit updateToPlayQueue(categorizedView->rootIndex(), true);
1796 }
1797 #endif
1798 }
1799
coverLoaded(const Song & song,int size)1800 void ItemView::coverLoaded(const Song &song, int size)
1801 {
1802 Q_UNUSED(song)
1803
1804 if (Mode_BasicTree==mode || Mode_GroupedTree==mode || !isVisible() || (Mode_IconTop==mode && size!=zoomedSize(listView, gridCoverSize)) ||
1805 (Mode_IconTop!=mode && Mode_Categorized!=mode && size!=listCoverSize)
1806 #ifdef ENABLE_CATEGORIZED_VIEW
1807 || (Mode_Categorized==mode && size!=zoomedSize(categorizedView, gridCoverSize))
1808 #endif
1809 ) {
1810 return;
1811 }
1812 view()->viewport()->update();
1813 }
1814
zoomIn()1815 void ItemView::zoomIn()
1816 {
1817 if (listView->isVisible() && Mode_IconTop==mode) {
1818 if (listView->zoom()+constZoomStep<=constMaxZoom) {
1819 listView->setZoom(listView->zoom()+constZoomStep);
1820 listView->setGridSize(zoomedSize(listView, iconGridSize));
1821 }
1822 }
1823 #ifdef ENABLE_CATEGORIZED_VIEW
1824 else if (categorizedView && categorizedView->isVisible()) {
1825 if (categorizedView->zoom()+constZoomStep<=constMaxZoom) {
1826 categorizedView->setZoom(categorizedView->zoom()+constZoomStep);
1827 categorizedView->setGridSize(zoomedSize(categorizedView, iconGridSize));
1828 }
1829 }
1830 #endif
1831 }
1832
zoomOut()1833 void ItemView::zoomOut()
1834 {
1835 if (listView->isVisible() && Mode_IconTop==mode) {
1836 if (listView->zoom()-constZoomStep>=constMinZoom) {
1837 listView->setZoom(listView->zoom()-constZoomStep);
1838 listView->setGridSize(zoomedSize(listView, iconGridSize));
1839 }
1840 }
1841 #ifdef ENABLE_CATEGORIZED_VIEW
1842 else if (categorizedView && categorizedView->isVisible()) {
1843 if (categorizedView->zoom()-constZoomStep>=constMinZoom) {
1844 categorizedView->setZoom(categorizedView->zoom()-constZoomStep);
1845 categorizedView->setGridSize(zoomedSize(categorizedView, iconGridSize));
1846 }
1847 }
1848 #endif
1849 }
1850
delaySearchItems()1851 void ItemView::delaySearchItems()
1852 {
1853 if (searchWidget->text().isEmpty()) {
1854 if (searchTimer) {
1855 searchTimer->stop();
1856 }
1857 if (performedSearch) {
1858 performedSearch=false;
1859 }
1860 emit searchItems();
1861 } else {
1862 if (!searchTimer) {
1863 searchTimer=new QTimer(this);
1864 searchTimer->setSingleShot(true);
1865 connect(searchTimer, SIGNAL(timeout()), this, SLOT(doSearch()));
1866 }
1867 int len=searchWidget->text().trimmed().length();
1868 searchTimer->start(qMin(qMax(minSearchDebounce, len<2 ? 1000u : len<4 ? 750u : 500u), 5000u));
1869 }
1870 }
1871
doSearch()1872 void ItemView::doSearch()
1873 {
1874 if (searchTimer) {
1875 searchTimer->stop();
1876 }
1877 performedSearch=true;
1878 emit searchItems();
1879 }
1880
searchActive(bool a)1881 void ItemView::searchActive(bool a)
1882 {
1883 emit searchIsActive(a);
1884 if (!a && performedSearch) {
1885 performedSearch=false;
1886 }
1887 if (!a && view()->isVisible()) {
1888 view()->setFocus();
1889 }
1890 controlViewFrame();
1891 }
1892
setTitle()1893 void ItemView::setTitle()
1894 {
1895 QModelIndex index=view()->rootIndex();
1896 QAbstractItemModel *model=view()->model();
1897 if (!model) {
1898 return;
1899 }
1900 QString sub = model->data(index, Cantata::Role_TitleSubText).toString();
1901 if (sub.isEmpty()) {
1902 sub = model->data(index, Cantata::Role_SubText).toString();
1903 }
1904 title->update(model->data(index, Mode_IconTop==mode ? Cantata::Role_GridCoverSong : Cantata::Role_CoverSong).value<Song>(),
1905 model->data(index, Qt::DecorationRole).value<QIcon>(),
1906 model->data(index, Cantata::Role_TitleText).toString(),
1907 sub,
1908 model->data(index, Cantata::Role_TitleActions).toBool());
1909 }
1910
controlViewFrame()1911 void ItemView::controlViewFrame()
1912 {
1913 view()->setProperty(ProxyStyle::constModifyFrameProp,
1914 title->isVisible() || title->property(constAlwaysShowProp).toBool()
1915 ? ProxyStyle::VF_Side
1916 : (searchWidget->isActive() ? ProxyStyle::VF_Side : (ProxyStyle::VF_Side|ProxyStyle::VF_Top)));
1917 }
1918
1919 #include "moc_itemview.cpp"
1920