1 #include "widget/wlibrarysidebar.h"
2 
3 #include <QFileInfo>
4 #include <QHeaderView>
5 #include <QMimeData>
6 #include <QUrl>
7 #include <QtDebug>
8 
9 #include "library/sidebarmodel.h"
10 #include "moc_wlibrarysidebar.cpp"
11 #include "util/dnd.h"
12 
13 const int expand_time = 250;
14 
WLibrarySidebar(QWidget * parent)15 WLibrarySidebar::WLibrarySidebar(QWidget* parent)
16         : QTreeView(parent),
17           WBaseWidget(this) {
18     //Set some properties
19     setHeaderHidden(true);
20     setSelectionMode(QAbstractItemView::SingleSelection);
21     //Drag and drop setup
22     setDragEnabled(false);
23     setDragDropMode(QAbstractItemView::DragDrop);
24     setDropIndicatorShown(true);
25     setAcceptDrops(true);
26     setAutoScroll(true);
27     setAttribute(Qt::WA_MacShowFocusRect, false);
28     header()->setStretchLastSection(false);
29     header()->setSectionResizeMode(QHeaderView::ResizeToContents);
30     header()->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
31 }
32 
contextMenuEvent(QContextMenuEvent * event)33 void WLibrarySidebar::contextMenuEvent(QContextMenuEvent *event) {
34     //if (event->state() & Qt::RightButton) { //Dis shiz don werk on windowze
35     QModelIndex clickedItem = indexAt(event->pos());
36     emit rightClicked(event->globalPos(), clickedItem);
37     //}
38 }
39 
40 // Drag enter event, happens when a dragged item enters the track sources view
dragEnterEvent(QDragEnterEvent * event)41 void WLibrarySidebar::dragEnterEvent(QDragEnterEvent * event) {
42     qDebug() << "WLibrarySidebar::dragEnterEvent" << event->mimeData()->formats();
43     if (event->mimeData()->hasUrls()) {
44         // We don't have a way to ask the LibraryFeatures whether to accept a
45         // drag so for now we accept all drags. Since almost every
46         // LibraryFeature accepts all files in the drop and accepts playlist
47         // drops we default to those flags to DragAndDropHelper.
48         QList<TrackFile> files = DragAndDropHelper::supportedTracksFromUrls(
49                 event->mimeData()->urls(), false, true);
50         if (!files.isEmpty()) {
51             event->acceptProposedAction();
52             return;
53         }
54     }
55     event->ignore();
56     //QTreeView::dragEnterEvent(event);
57 }
58 
59 // Drag move event, happens when a dragged item hovers over the track sources view...
dragMoveEvent(QDragMoveEvent * event)60 void WLibrarySidebar::dragMoveEvent(QDragMoveEvent * event) {
61     //qDebug() << "dragMoveEvent" << event->mimeData()->formats();
62     // Start a timer to auto-expand sections the user hovers on.
63     QPoint pos = event->pos();
64     QModelIndex index = indexAt(pos);
65     if (m_hoverIndex != index) {
66         m_expandTimer.stop();
67         m_hoverIndex = index;
68         m_expandTimer.start(expand_time, this);
69     }
70     // This has to be here instead of after, otherwise all drags will be
71     // rejected -- rryan 3/2011
72     QTreeView::dragMoveEvent(event);
73     if (event->mimeData()->hasUrls()) {
74         const QList<QUrl> urls = event->mimeData()->urls();
75         // Drag and drop within this widget
76         if ((event->source() == this)
77                 && (event->possibleActions() & Qt::MoveAction)) {
78             // Do nothing.
79             event->ignore();
80         } else {
81             SidebarModel* sidebarModel = qobject_cast<SidebarModel*>(model());
82             bool accepted = true;
83             if (sidebarModel) {
84                 accepted = false;
85                 for (const QUrl& url : urls) {
86                     QModelIndex destIndex = indexAt(event->pos());
87                     if (sidebarModel->dragMoveAccept(destIndex, url)) {
88                         // We only need one URL to be valid for us
89                         // to accept the whole drag...
90                         // consider we have a long list of valid files, checking all will
91                         // take a lot of time that stales Mixxx and this makes the drop feature useless
92                         // Eg. you may have tried to drag two MP3's and an EXE, the drop is accepted here,
93                         // but the EXE is sorted out later after dropping
94                         accepted = true;
95                         break;
96                     }
97                 }
98             }
99             if (accepted) {
100                 event->acceptProposedAction();
101             } else {
102                 event->ignore();
103             }
104         }
105     } else {
106         event->ignore();
107     }
108 }
109 
timerEvent(QTimerEvent * event)110 void WLibrarySidebar::timerEvent(QTimerEvent *event) {
111     if (event->timerId() == m_expandTimer.timerId()) {
112         QPoint pos = viewport()->mapFromGlobal(QCursor::pos());
113         if (viewport()->rect().contains(pos)) {
114             QModelIndex index = indexAt(pos);
115             if (m_hoverIndex == index) {
116                 setExpanded(index, !isExpanded(index));
117             }
118         }
119         m_expandTimer.stop();
120         return;
121     }
122     QTreeView::timerEvent(event);
123 }
124 
125 // Drag-and-drop "drop" event. Occurs when something is dropped onto the track sources view
dropEvent(QDropEvent * event)126 void WLibrarySidebar::dropEvent(QDropEvent * event) {
127     if (event->mimeData()->hasUrls()) {
128         // Drag and drop within this widget
129         if ((event->source() == this)
130                 && (event->possibleActions() & Qt::MoveAction)) {
131             // Do nothing.
132             event->ignore();
133         } else {
134             //Reset the selected items (if you had anything highlighted, it clears it)
135             //this->selectionModel()->clear();
136             //Drag-and-drop from an external application or the track table widget
137             //eg. dragging a track from Windows Explorer onto the sidebar
138             SidebarModel* sidebarModel = qobject_cast<SidebarModel*>(model());
139             if (sidebarModel) {
140                 QModelIndex destIndex = indexAt(event->pos());
141                 // event->source() will return NULL if something is dropped from
142                 // a different application
143                 const QList<QUrl> urls = event->mimeData()->urls();
144                 if (sidebarModel->dropAccept(destIndex, urls, event->source())) {
145                     event->acceptProposedAction();
146                 } else {
147                     event->ignore();
148                 }
149             }
150         }
151         //emit trackDropped(name);
152         //repaintEverything();
153     } else {
154         event->ignore();
155     }
156 }
157 
158 
toggleSelectedItem()159 void WLibrarySidebar::toggleSelectedItem() {
160     QModelIndexList selectedIndices = this->selectionModel()->selectedRows();
161     if (selectedIndices.size() > 0) {
162         QModelIndex index = selectedIndices.at(0);
163         // Activate the item so its content shows in the main library.
164         emit pressed(index);
165         // Expand or collapse the item as necessary.
166         setExpanded(index, !isExpanded(index));
167     }
168 }
169 
isLeafNodeSelected()170 bool WLibrarySidebar::isLeafNodeSelected() {
171     QModelIndexList selectedIndices = this->selectionModel()->selectedRows();
172     if (selectedIndices.size() > 0) {
173         QModelIndex index = selectedIndices.at(0);
174         if(!index.model()->hasChildren(index)) {
175             return true;
176         }
177         const SidebarModel* sidebarModel = qobject_cast<const SidebarModel*>(index.model());
178         if (sidebarModel) {
179             return sidebarModel->hasTrackTable(index);
180         }
181     }
182     return false;
183 }
184 
keyPressEvent(QKeyEvent * event)185 void WLibrarySidebar::keyPressEvent(QKeyEvent* event) {
186     if (event->key() == Qt::Key_Return) {
187         toggleSelectedItem();
188         return;
189     } else if (event->key() == Qt::Key_Down || event->key() == Qt::Key_Up) {
190         // Let the tree view move up and down for us.
191         QTreeView::keyPressEvent(event);
192 
193         // But force the index to be activated/clicked after the selection
194         // changes. (Saves you from having to push "enter" after changing the
195         // selection.)
196         QModelIndexList selectedIndices = this->selectionModel()->selectedRows();
197 
198         //Note: have to get the selected indices _after_ QTreeView::keyPressEvent()
199         if (selectedIndices.size() > 0) {
200             QModelIndex index = selectedIndices.at(0);
201             emit pressed(index);
202         }
203         return;
204     //} else if (event->key() == Qt::Key_Enter && (event->modifiers() & Qt::AltModifier)) {
205     //    // encoder click via "GoToItem"
206     //    qDebug() << "GoToItem";
207     //    TODO(xxx) decide what todo here instead of in librarycontrol
208     }
209 
210     // Fall through to default handler.
211     QTreeView::keyPressEvent(event);
212 }
213 
selectIndex(const QModelIndex & index)214 void WLibrarySidebar::selectIndex(const QModelIndex& index) {
215     auto* pModel = new QItemSelectionModel(model());
216     pModel->select(index, QItemSelectionModel::Select);
217     setSelectionModel(pModel);
218 
219     if (index.parent().isValid()) {
220         expand(index.parent());
221     }
222     scrollTo(index);
223 }
224 
event(QEvent * pEvent)225 bool WLibrarySidebar::event(QEvent* pEvent) {
226     if (pEvent->type() == QEvent::ToolTip) {
227         updateTooltip();
228     }
229     return QTreeView::event(pEvent);
230 }
231 
slotSetFont(const QFont & font)232 void WLibrarySidebar::slotSetFont(const QFont& font) {
233     setFont(font);
234     // Resize the feature icons to be a bit taller than the label's capital
235     int iconSize = static_cast<int>(QFontMetrics(font).height() * 0.8);
236     setIconSize(QSize(iconSize, iconSize));
237 }
238