1 /***************************************************************************
2 * Copyright (C) 2006-2021 by Ilya Kotov *
3 * forkotov02@ya.ru *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
20 #include <QPixmap>
21 #include <QResizeEvent>
22 #include <QPainter>
23 #include <QFont>
24 #include <QFontMetrics>
25 #include <QSettings>
26 #include <QMenu>
27 #include <QUrl>
28 #include <QApplication>
29 #include <QHelpEvent>
30 #include <QTimer>
31 #include <QScrollBar>
32 #include <QMimeData>
33 #include <qmmpui/playlistitem.h>
34 #include <qmmpui/playlistmodel.h>
35 #include <qmmpui/qmmpuisettings.h>
36 #include <qmmpui/mediaplayer.h>
37 #include "listwidget.h"
38 #include "playlistheader.h"
39 #include "actionmanager.h"
40 #include "popupwidget.h"
41
42 #define INVALID_INDEX -1
43
ListWidget(PlayListModel * model,QWidget * parent)44 ListWidget::ListWidget(PlayListModel *model, QWidget *parent) : QWidget(parent),
45 m_pressed_index(INVALID_INDEX),
46 m_drop_index(INVALID_INDEX),
47 m_anchor_index(INVALID_INDEX),
48 m_model(model)
49 {
50 setFocusPolicy(Qt::StrongFocus);
51 m_ui_settings = QmmpUiSettings::instance();
52 m_timer = new QTimer(this);
53 m_timer->setInterval(50);
54 m_header = new PlayListHeader(this);
55 m_scrollBar = new QScrollBar(Qt::Vertical, this);
56 m_hslider = new QScrollBar(Qt::Horizontal, this);
57 m_hslider->setPageStep(50);
58
59 setAcceptDrops(true);
60 setMouseTracking(true);
61
62 readSettings();
63 connect(m_ui_settings, SIGNAL(repeatableTrackChanged(bool)), SLOT(updateRepeatIndicator()));
64 connect(m_timer, SIGNAL(timeout()), SLOT(autoscroll()));
65 connect(m_scrollBar, SIGNAL(valueChanged (int)), SLOT(setViewPosition(int)));
66 connect(m_hslider, SIGNAL(valueChanged(int)), m_header, SLOT(scroll(int)));
67 connect(m_hslider, SIGNAL(valueChanged(int)), this, SLOT(update()));
68 connect(m_model, SIGNAL(scrollToRequest(int)), SLOT(scrollTo(int)));
69 connect(m_model, SIGNAL(listChanged(int)), SLOT(updateList(int)));
70 connect(m_model, SIGNAL(sortingByColumnFinished(int,bool)), m_header, SLOT(showSortIndicator(int,bool)));
71 SET_ACTION(ActionManager::PL_SHOW_HEADER, this, SLOT(readSettings()));
72 }
73
~ListWidget()74 ListWidget::~ListWidget()
75 {
76 qDeleteAll(m_rows);
77 m_rows.clear();
78 }
79
readSettings()80 void ListWidget::readSettings()
81 {
82 QSettings settings(Qmmp::configFile(), QSettings::IniFormat);
83 settings.beginGroup("Simple");
84 m_show_protocol = settings.value ("pl_show_protocol", false).toBool();
85 bool show_popup = settings.value("pl_show_popup", false).toBool();
86
87 m_header->readSettings();
88 m_header->setVisible(ACTION(ActionManager::PL_SHOW_HEADER)->isChecked());
89 m_header->setGeometry(0,0,width(), m_header->requiredHeight());
90
91 if (m_update)
92 {
93 m_drawer.readSettings();
94 updateList(PlayListModel::STRUCTURE);
95 if(m_popupWidget)
96 {
97 m_popupWidget->deleteLater();
98 m_popupWidget = nullptr;
99 }
100 }
101 else
102 {
103 m_update = true;
104 }
105
106 if(show_popup)
107 m_popupWidget = new PlayListPopup::PopupWidget(this);
108 }
109
visibleRows() const110 int ListWidget::visibleRows() const
111 {
112 return m_row_count;
113 }
114
firstVisibleIndex() const115 int ListWidget::firstVisibleIndex() const
116 {
117 return m_first;
118 }
119
anchorIndex() const120 int ListWidget::anchorIndex() const
121 {
122 return m_anchor_index;
123 }
124
setAnchorIndex(int index)125 void ListWidget::setAnchorIndex(int index)
126 {
127 m_anchor_index = index;
128 updateList(PlayListModel::SELECTION);
129 }
130
menu()131 QMenu *ListWidget::menu()
132 {
133 return m_menu;
134 }
135
setMenu(QMenu * menu)136 void ListWidget::setMenu(QMenu *menu)
137 {
138 m_menu = menu;
139 }
140
model()141 PlayListModel *ListWidget::model()
142 {
143 Q_ASSERT(m_model);
144 return m_model;
145 }
146
filterMode() const147 bool ListWidget::filterMode() const
148 {
149 return m_filterMode;
150 }
151
setModel(PlayListModel * selected,PlayListModel * previous)152 void ListWidget::setModel(PlayListModel *selected, PlayListModel *previous)
153 {
154 if(m_filterMode)
155 {
156 m_filterMode = false;
157 m_first = 0;
158 m_filteredItems.clear();
159 }
160
161 if(previous)
162 {
163 previous->setProperty("first_visible", m_first);
164 disconnect(previous, nullptr, this, nullptr); //disconnect previous model
165 disconnect(previous,nullptr,m_header,nullptr);
166 }
167 qApp->processEvents();
168 m_model = selected;
169 m_count = m_model->count();
170 m_firstItem = nullptr;
171
172 if(m_model->property("first_visible").isValid())
173 {
174 m_first = m_model->property("first_visible").toInt();
175 updateList(PlayListModel::STRUCTURE);
176 }
177 else
178 {
179 m_first = 0;
180 updateList(PlayListModel::STRUCTURE | PlayListModel::CURRENT);
181 }
182 connect (m_model, SIGNAL(scrollToRequest(int)), SLOT(scrollTo(int)));
183 connect (m_model, SIGNAL(listChanged(int)), SLOT(updateList(int)));
184 connect (m_model, SIGNAL(sortingByColumnFinished(int,bool)), m_header, SLOT(showSortIndicator(int,bool)));
185 }
186
paintEvent(QPaintEvent *)187 void ListWidget::paintEvent(QPaintEvent *)
188 {
189 QPainter painter(this);
190 m_drawer.fillBackground(&painter, width(), height());
191 painter.setLayoutDirection(Qt::LayoutDirectionAuto);
192 bool rtl = (layoutDirection() == Qt::RightToLeft);
193 int scroll_bar_width = m_scrollBar->isVisibleTo(this) ? m_scrollBar->sizeHint().width() : 0;
194
195 painter.setClipRect(5,0,width() - scroll_bar_width - 9, height());
196 painter.translate(rtl ? m_header->offset() : -m_header->offset(), 0);
197
198 for (int i = 0; i < m_rows.size(); ++i )
199 {
200 m_drawer.drawBackground(&painter, m_rows[i], i);
201
202 if(m_rows[i]->flags & ListWidgetRow::GROUP)
203 {
204 m_drawer.drawSeparator(&painter, m_rows[i], rtl);
205 continue;
206 }
207
208 m_drawer.drawTrack(&painter, m_rows[i], rtl);
209 }
210 //draw drop line
211 if(m_drop_index != INVALID_INDEX)
212 {
213 m_drawer.drawDropLine(&painter, m_drop_index - m_first, width(),
214 m_header->isVisible() ? m_header->height() : 0);
215 }
216 }
217
mouseDoubleClickEvent(QMouseEvent * e)218 void ListWidget::mouseDoubleClickEvent (QMouseEvent *e)
219 {
220 int y = e->y();
221 int index = indexAt(y);
222
223 if (INVALID_INDEX != index)
224 {
225 if(m_filterMode)
226 {
227 m_filterMode = false;
228 m_filteredItems.clear();
229 scrollTo(index);
230 }
231
232 m_model->setCurrent(index);
233 MediaPlayer *player = MediaPlayer::instance();
234 player->playListManager()->selectPlayList(m_model);
235 player->playListManager()->activatePlayList(m_model);
236 player->stop();
237 player->play();
238 emit doubleClicked();
239 update();
240 }
241 }
242
mousePressEvent(QMouseEvent * e)243 void ListWidget::mousePressEvent(QMouseEvent *e)
244 {
245 if(m_popupWidget)
246 m_popupWidget->hide();
247
248 int index = indexAt(e->y());
249
250 if (INVALID_INDEX != index && m_model->count() > index)
251 {
252 m_pressed_index = index;
253 if(e->button() == Qt::RightButton)
254 {
255 if(!m_model->isSelected(index))
256 {
257 m_anchor_index = m_pressed_index;
258 m_model->clearSelection();
259 m_model->setSelected(index, true);
260 }
261 if(m_model->isGroup(index) && m_model->selectedTracks().isEmpty())
262 {
263 m_anchor_index = m_pressed_index;
264 PlayListGroup *group = m_model->group(index);
265 m_model->setSelected(group->tracks());
266 }
267 QWidget::mousePressEvent(e);
268 return;
269 }
270
271 if (m_model->isSelected(index) && (e->modifiers() == Qt::NoModifier))
272 {
273 m_select_on_release = true;
274 QWidget::mousePressEvent(e);
275 return;
276 }
277
278 if ((Qt::ShiftModifier & e->modifiers()))
279 {
280 int prev_anchor_index = m_anchor_index;
281 m_anchor_index = m_pressed_index;
282 m_model->setSelected(m_pressed_index, prev_anchor_index, true);
283 }
284 else //ShiftModifier released
285 {
286 m_anchor_index = m_pressed_index;
287 if ((Qt::ControlModifier & e->modifiers()))
288 {
289 m_model->setSelected(index, !m_model->isSelected(index));
290 }
291 else //ControlModifier released
292 {
293 m_model->clearSelection();
294 m_model->setSelected(index, true);
295 }
296
297 }
298 update();
299 }
300 QWidget::mousePressEvent(e);
301 }
302
resizeEvent(QResizeEvent * e)303 void ListWidget::resizeEvent(QResizeEvent *e)
304 {
305 m_header->setGeometry(0,0,width(), m_header->requiredHeight());
306 if(e->oldSize().height() < 10)
307 updateList(PlayListModel::STRUCTURE | PlayListModel::CURRENT); //recenter to current on first resize
308 else
309 updateList(PlayListModel::STRUCTURE);
310 QWidget::resizeEvent(e);
311 }
312
wheelEvent(QWheelEvent * e)313 void ListWidget::wheelEvent (QWheelEvent *e)
314 {
315 if(m_hslider->underMouse())
316 return;
317
318 if (m_model->count() <= m_row_count)
319 return;
320 if ((m_first == 0 && e->delta() > 0) ||
321 ((m_first == m_model->count() - m_row_count) && e->delta() < 0))
322 return;
323 m_first -= e->delta()/40; //40*3 TODO: add step to config
324 if (m_first < 0)
325 m_first = 0;
326
327 if (m_first > m_model->count() - m_row_count)
328 m_first = m_model->count() - m_row_count;
329
330 updateList(PlayListModel::STRUCTURE);
331 }
332
showEvent(QShowEvent *)333 void ListWidget::showEvent(QShowEvent *)
334 {
335 if(!m_rows.isEmpty())
336 updateList(PlayListModel::METADATA);
337 }
338
event(QEvent * e)339 bool ListWidget::event (QEvent *e)
340 {
341 if(m_popupWidget)
342 {
343 if(e->type() == QEvent::ToolTip)
344 {
345 QHelpEvent *helpEvent = (QHelpEvent *) e;
346 int index = indexAt(helpEvent->y());
347 if(index < 0 || !m_model->isTrack(index))
348 {
349 m_popupWidget->deactivate();
350 return QWidget::event(e);
351 }
352 e->accept();
353 m_popupWidget->prepare(m_model->track(index), helpEvent->globalPos());
354 return true;
355 }
356 else if(e->type() == QEvent::Leave)
357 m_popupWidget->deactivate();
358 }
359 if(e->type() == QEvent::StyleChange)
360 readSettings();
361
362 return QWidget::event(e);
363 }
364
updateList(int flags)365 void ListWidget::updateList(int flags)
366 {
367 m_hslider->setRange(0, m_header->maxScrollValue());
368 m_hslider->setValue(m_header->offset());
369
370 if(updateRowCount())
371 flags |= PlayListModel::STRUCTURE;
372
373 if(flags & PlayListModel::STRUCTURE && m_filterMode)
374 {
375 m_filteredItems = m_model->findTracks(m_filterString);
376 }
377
378 if(flags & PlayListModel::CURRENT)
379 recenterTo(m_model->currentIndex());
380
381 QList<PlayListItem *> items;
382 int count = m_filterMode ? m_filteredItems.count() : m_model->count();
383
384 if(flags & PlayListModel::STRUCTURE || flags & PlayListModel::CURRENT)
385 {
386 m_scrollBar->blockSignals(true);
387 if(m_row_count >= count)
388 {
389 m_first = 0;
390 m_scrollBar->setMaximum(0);
391 m_scrollBar->setValue(0);
392 emit positionChanged(0,0);
393 }
394 else if(m_first + m_row_count >= count)
395 {
396 //try to restore first visible first
397 if(!m_filterMode && (m_count > 0) &&
398 (m_count != m_model->count()) && m_firstItem)
399 {
400 restoreFirstVisible();
401 }
402 if(m_first + m_row_count >= count)
403 m_first = qMax(0, count - m_row_count);
404 m_scrollBar->setMaximum(count - m_row_count);
405 m_scrollBar->setValue(m_first);
406 emit positionChanged(m_first, m_first);
407 }
408 else if(!m_filterMode && (m_count > 0) && (m_count != m_model->count()) &&
409 m_firstItem && m_model->item(m_first) != m_firstItem)
410 {
411 restoreFirstVisible();
412 m_scrollBar->setMaximum(count - m_row_count);
413 m_scrollBar->setValue(m_first);
414 emit positionChanged(m_first, m_model->count() - m_row_count);
415 }
416 else
417 {
418 m_scrollBar->setMaximum(count - m_row_count);
419 m_scrollBar->setValue(m_first);
420 emit positionChanged(m_first, count - m_row_count);
421 }
422 m_scrollBar->blockSignals(false);
423
424 if(!m_filterMode)
425 {
426 m_firstItem = m_model->isEmpty() ? nullptr : m_model->item(m_first);
427 m_count = m_model->count();
428 }
429 items = m_filterMode ? m_filteredItems.mid(m_first, m_row_count) : m_model->mid(m_first, m_row_count);
430
431 while(m_rows.count() < qMin(m_row_count, items.count()))
432 m_rows << new ListWidgetRow;
433 while(m_rows.count() > qMin(m_row_count, items.count()))
434 delete m_rows.takeFirst();
435
436 m_scrollBar->setVisible(count > m_row_count);
437 }
438 else
439 {
440 items = m_filterMode ? m_filteredItems.mid(m_first, m_row_count) : m_model->mid(m_first, m_row_count);
441 }
442
443 if(flags & PlayListModel::STRUCTURE)
444 m_header->hideSortIndicator();
445
446 if(flags & PlayListModel::STRUCTURE || flags & PlayListModel::METADATA)
447 {
448 //song numbers width
449 m_drawer.calculateNumberWidth(m_model->trackCount());
450 m_drawer.setSingleColumnMode(m_model->columnCount() == 1);
451 m_header->setNumberWidth(m_drawer.numberWidth());
452 }
453
454 updateScrollBars();
455
456 int scroll_bar_width = m_scrollBar->isVisibleTo(this) ? m_scrollBar->sizeHint().width() : 0;
457 int trackStateColumn = m_header->trackStateColumn();
458 int rowWidth = width() + m_header->maxScrollValue() - 10 - scroll_bar_width;
459 bool rtl = layoutDirection() == Qt::RightToLeft;
460 m_header->setScrollBarWidth(scroll_bar_width);
461 m_hslider->setVisible(m_header->maxScrollValue() > 0);
462
463 for(int i = 0; i < items.count(); ++i)
464 {
465 ListWidgetRow *row = m_rows[i];
466 row->autoResize = m_header->hasAutoResizeColumn();
467 row->trackStateColumn = trackStateColumn;
468 items[i]->isSelected() ? row->flags |= ListWidgetRow::SELECTED :
469 row->flags &= ~ListWidgetRow::SELECTED;
470
471 i == (m_anchor_index - m_first) ? row->flags |= ListWidgetRow::ANCHOR :
472 row->flags &= ~ListWidgetRow::ANCHOR;
473
474 if(flags == PlayListModel::SELECTION)
475 continue;
476
477 if(rtl)
478 {
479 row->rect = QRect(width() - 5 - rowWidth, (m_header->isVisibleTo(this) ? m_header->height() : 0) + i * m_drawer.rowHeight(),
480 rowWidth, m_drawer.rowHeight() - 1);
481 }
482 else
483 {
484 row->rect = QRect(5, (m_header->isVisibleTo(this) ? m_header->height() : 0) + i * m_drawer.rowHeight(),
485 rowWidth, m_drawer.rowHeight() - 1);
486 }
487
488 row->titles = items[i]->formattedTitles();
489 row->sizes = m_header->sizes();
490 row->alignment = m_header->alignment();
491
492 items[i] == m_model->currentTrack() ? row->flags |= ListWidgetRow::CURRENT :
493 row->flags &= ~ListWidgetRow::CURRENT;
494
495 if(items[i]->isGroup())
496 {
497 row->flags |= ListWidgetRow::GROUP;
498 row->number = -1;
499 row->length.clear();
500 }
501 else
502 {
503 row->flags &= ~ListWidgetRow::GROUP;
504 row->number = items.at(i)->trackIndex() + 1;
505 row->length = items.at(i)->formattedLength();
506 row->extraString = getExtraString(items.at(i));
507 }
508 m_drawer.prepareRow(row); //elide titles
509 }
510 update();
511 }
512
autoscroll()513 void ListWidget::autoscroll()
514 {
515 if(m_filterMode)
516 return;
517
518 SimpleSelection sel = m_model->getSelection(m_pressed_index);
519 if ((sel.m_top == 0 && m_scroll_direction == TOP && sel.count() > 1) ||
520 (sel.m_bottom == m_model->count() - 1 && m_scroll_direction == DOWN && sel.count() > 1))
521 return;
522
523 if(m_scroll_direction == DOWN)
524 {
525 int row = m_first + m_row_count;
526 (m_first + m_row_count < m_model->count()) ? m_first ++ : m_first;
527 m_model->moveItems(m_pressed_index,row);
528 m_pressed_index = row;
529 }
530 else if(m_scroll_direction == TOP && m_first > 0)
531 {
532 m_first--;
533 m_model->moveItems(m_pressed_index, m_first);
534 m_pressed_index = m_first;
535 }
536 }
537
updateRepeatIndicator()538 void ListWidget::updateRepeatIndicator()
539 {
540 updateList(PlayListModel::CURRENT | PlayListModel::STRUCTURE);
541 }
542
scrollTo(int index)543 void ListWidget::scrollTo(int index)
544 {
545 if (m_row_count && !m_filterMode)
546 {
547 recenterTo(index);
548 updateList(PlayListModel::STRUCTURE);
549 }
550 }
551
setViewPosition(int sc)552 void ListWidget::setViewPosition(int sc)
553 {
554 if (m_model->count() <= m_row_count)
555 return;
556 m_first = sc;
557 updateList(PlayListModel::STRUCTURE);
558 }
559
setFilterString(const QString & str)560 void ListWidget::setFilterString(const QString &str)
561 {
562 m_filterString = str;
563 if(str.isEmpty())
564 {
565 m_filteredItems.clear();
566 m_filterString.clear();
567 m_filterMode = false;
568 }
569 else
570 {
571 m_filterMode = true;
572 }
573 m_first = 0;
574 updateList(PlayListModel::STRUCTURE);
575 }
576
clear()577 void ListWidget::clear()
578 {
579 if(m_filterMode)
580 {
581 m_model->removeTracks(m_filteredItems);
582 m_filteredItems.clear();
583 }
584 else
585 {
586 m_model->clear();
587 }
588 }
589
removeSelected()590 void ListWidget::removeSelected()
591 {
592 if(m_filterMode)
593 {
594 QList<PlayListItem *> items;
595 for(PlayListItem *item : m_filteredItems)
596 {
597 if(item->isSelected())
598 items << item;
599 }
600 m_model->removeTracks(items);
601 }
602 else
603 {
604 m_model->removeSelected();
605 }
606 }
607
removeUnselected()608 void ListWidget::removeUnselected()
609 {
610 if(m_filterMode)
611 {
612 QList<PlayListItem *> items;
613 for(PlayListItem *item : m_filteredItems)
614 {
615 if(!item->isSelected())
616 items << item;
617 }
618 m_model->removeTracks(items);
619 }
620 else
621 {
622 m_model->removeUnselected();
623 }
624 }
625
updateSkin()626 void ListWidget::updateSkin()
627 {
628 m_drawer.loadSystemColors();
629 update();
630 }
631
dragEnterEvent(QDragEnterEvent * event)632 void ListWidget::dragEnterEvent(QDragEnterEvent *event)
633 {
634 if(event->mimeData()->hasFormat("text/uri-list") || event->mimeData()->hasFormat("application/json"))
635 event->acceptProposedAction();
636 }
637
dropEvent(QDropEvent * event)638 void ListWidget::dropEvent(QDropEvent *event)
639 {
640 if(!m_filterMode && (event->mimeData()->hasUrls() || event->mimeData()->hasFormat("application/json")))
641 {
642 event->acceptProposedAction();
643 QApplication::restoreOverrideCursor();
644
645 int index = indexAt(event->pos().y());
646 if(index == INVALID_INDEX)
647 index = qMin(m_first + m_row_count, m_model->count());
648
649 if(event->mimeData()->hasUrls())
650 {
651 QList<QUrl> list_urls = event->mimeData()->urls();
652 m_model->insert(index, list_urls);
653 }
654 else if(event->mimeData()->hasFormat("application/json"))
655 {
656 QByteArray json = event->mimeData()->data("application/json");
657 m_model->insert(index, json);
658 }
659 }
660 m_drop_index = INVALID_INDEX;
661 }
662
dragLeaveEvent(QDragLeaveEvent *)663 void ListWidget::dragLeaveEvent(QDragLeaveEvent *)
664 {
665 m_drop_index = INVALID_INDEX;
666 update();
667 }
668
dragMoveEvent(QDragMoveEvent * event)669 void ListWidget::dragMoveEvent(QDragMoveEvent *event)
670 {
671 int index = indexAt(event->pos().y());
672 if(index == INVALID_INDEX)
673 index = qMin(m_first + m_row_count, m_model->count());
674 if(index != m_drop_index)
675 {
676 m_drop_index = index;
677 update();
678 }
679 if (event->mimeData()->hasFormat("text/uri-list"))
680 event->acceptProposedAction();
681 }
682
getExtraString(PlayListItem * item)683 const QString ListWidget::getExtraString(PlayListItem *item)
684 {
685 if(item->isGroup())
686 return QString();
687
688 QString extra_string;
689 PlayListTrack *track = static_cast<PlayListTrack *>(item);
690
691 if (m_show_protocol && track->path().contains("://"))
692 extra_string = "[" + track->path().split("://").at(0) + "]";
693
694 if (m_model->isQueued(track))
695 {
696 int index = m_model->queuedIndex(track);
697 extra_string += "|"+QString::number(index + 1)+"|";
698 }
699
700 if(m_model->currentTrack() == track && m_ui_settings->isRepeatableTrack())
701 extra_string += "|R|";
702 else if(m_model->isStopAfter(track))
703 extra_string += "|S|";
704
705 return extra_string.trimmed(); //remove white space
706 }
707
updateRowCount()708 bool ListWidget::updateRowCount()
709 {
710 int h = height();
711 if(m_header->isVisibleTo(this))
712 h -= m_header->requiredHeight();
713 if(m_hslider->isVisibleTo(this))
714 h -= m_hslider->height();
715 int row_count = qMax(0, h / m_drawer.rowHeight());
716 if(m_row_count != row_count)
717 {
718 m_row_count = row_count;
719 return true;
720 }
721 return false;
722 }
723
restoreFirstVisible()724 void ListWidget::restoreFirstVisible()
725 {
726 if(m_first < m_model->count() && m_firstItem == m_model->item(m_first))
727 return;
728
729 int delta = m_model->count() - m_count;
730
731 //try to find and restore first visible index
732 if(delta > 0)
733 {
734 int from = qMin(m_model->count() - 1, m_first + 1);
735 for(int i = from; i <= qMin(m_model->count() - 1, m_first + delta); ++i)
736 {
737 if(m_model->item(i) == m_firstItem)
738 {
739 m_first = i;
740 break;
741 }
742 }
743 }
744 else
745 {
746 int from = qMin(m_model->count() - 1, m_first - 1);
747 for(int i = from; i >= qMax(0, m_first + delta); --i)
748 {
749 if(m_model->item(i) == m_firstItem)
750 {
751 m_first = i;
752 break;
753 }
754 }
755 }
756 }
757
updateScrollBars()758 void ListWidget::updateScrollBars()
759 {
760 bool rtl = layoutDirection() == Qt::RightToLeft;
761
762 int vslider_width = m_scrollBar->isVisibleTo(this) ? m_scrollBar->sizeHint().width() : 0;
763 int hslider_height = m_hslider->isVisibleTo(this) ? m_hslider->sizeHint().height() : 0;
764
765 if(rtl)
766 {
767 m_scrollBar->setGeometry(0, 0, m_scrollBar->sizeHint().width(), height() - hslider_height);
768 m_hslider->setGeometry(vslider_width, height() - m_hslider->sizeHint().height(),
769 width() - vslider_width, m_hslider->sizeHint().height());
770 }
771 else
772 {
773 m_scrollBar->setGeometry(width() - m_scrollBar->sizeHint().width(), 0,
774 m_scrollBar->sizeHint().width(), height() - hslider_height);
775 m_hslider->setGeometry(0, height() - m_hslider->sizeHint().height(), width() - vslider_width,
776 m_hslider->sizeHint().height());
777 }
778 }
779
mouseMoveEvent(QMouseEvent * e)780 void ListWidget::mouseMoveEvent(QMouseEvent *e)
781 {
782 if(m_filterMode)
783 return;
784
785 if(e->buttons() == Qt::LeftButton)
786 {
787 if (m_prev_y > e->y())
788 m_scroll_direction = TOP;
789 else if (m_prev_y < e->y())
790 m_scroll_direction = DOWN;
791 else
792 m_scroll_direction = NONE;
793
794 if(e->y() < 0 || e->y() > height())
795 {
796 if(!m_timer->isActive())
797 m_timer->start();
798 return;
799 }
800 m_timer->stop();
801
802 int index = indexAt(e->y());
803
804 if (INVALID_INDEX != index)
805 {
806 m_anchor_index = index;
807 SimpleSelection sel = m_model->getSelection(m_pressed_index);
808 if(sel.count() > 1 && m_scroll_direction == TOP)
809 {
810 if(sel.m_top == 0 || sel.m_top == m_first)
811 return;
812 }
813 else if(sel.count() > 1 && m_scroll_direction == DOWN)
814 {
815 if(sel.m_bottom == m_model->count() - 1 || sel.m_bottom == m_first + m_row_count)
816 return;
817 }
818 m_model->moveItems(m_pressed_index,index);
819
820 m_prev_y = e->y();
821 m_pressed_index = index;
822 }
823 }
824 else if(m_popupWidget)
825 {
826 int index = indexAt(e->y());
827 if(index < 0 || !m_model->isTrack(index) || m_popupWidget->url() != m_model->track(index)->path())
828 m_popupWidget->deactivate();
829 }
830 }
831
mouseReleaseEvent(QMouseEvent * e)832 void ListWidget::mouseReleaseEvent(QMouseEvent *e)
833 {
834 if (m_select_on_release)
835 {
836 m_model->clearSelection();
837 m_model->setSelected(m_pressed_index,true);
838 m_anchor_index = m_pressed_index;
839 m_select_on_release = false;
840 }
841 m_pressed_index = INVALID_INDEX;
842 m_scroll_direction = NONE;
843 m_timer->stop();
844 QWidget::mouseReleaseEvent(e);
845 }
846
indexAt(int y) const847 int ListWidget::indexAt(int y) const
848 {
849 y -= m_header->isVisible() ? m_header->height() : 0;
850
851 if(m_filterMode)
852 {
853 for (int i = 0; i < qMin(m_row_count, m_filteredItems.count() - m_first); ++i)
854 {
855 if ((y >= i * m_drawer.rowHeight()) && (y <= (i+1) * m_drawer.rowHeight()))
856 return m_model->indexOf(m_filteredItems.at(m_first + i));
857 }
858 }
859 else
860 {
861 for (int i = 0; i < qMin(m_row_count, m_model->count() - m_first); ++i)
862 {
863 if ((y >= i * m_drawer.rowHeight()) && (y <= (i+1) * m_drawer.rowHeight()))
864 return m_first + i;
865 }
866 }
867 return INVALID_INDEX;
868 }
869
contextMenuEvent(QContextMenuEvent * event)870 void ListWidget::contextMenuEvent(QContextMenuEvent * event)
871 {
872 if (menu())
873 menu()->exec(event->globalPos());
874 }
875
recenterTo(int index)876 void ListWidget::recenterTo(int index)
877 {
878 if (m_row_count && !m_filterMode)
879 {
880 if (m_first + m_row_count < index + 1)
881 m_first = qMin(m_model->count() - m_row_count, index - m_row_count/2);
882 else if (m_first > index)
883 m_first = qMax (index - m_row_count/2, 0);
884 }
885 }
886