1 #include "playlistcontroller.h" 2 3 #include <QDebug> 4 #include <QHeaderView> 5 #include <QAbstractItemView> 6 #include <QScrollBar> 7 #include <QThread> 8 #include <QTimer> 9 #include <QMouseEvent> 10 11 namespace PlaylistUi { Controller(QTableView * v,QLineEdit * s,BusySpinner * sp,Config::Local & local_cfg,Config::Global & global_cfg,QObject * parent)12 Controller::Controller(QTableView *v, QLineEdit *s, BusySpinner *sp, Config::Local &local_cfg, Config::Global &global_cfg, QObject *parent) : QObject(parent), search(s), spinner(sp), local_conf(local_cfg), global_conf(global_cfg) { 13 restore_scroll_once = true; 14 view = v; 15 scroll_positions.clear(); 16 17 loadColumnsConfig(); 18 19 model = new Model(view->style(), columns_config, this); 20 proxy = new ProxyFilterModel(model, this); 21 view->setModel(proxy); 22 view->horizontalHeader()->hide(); 23 view->verticalHeader()->hide(); 24 view->setSelectionBehavior(QAbstractItemView::SelectRows); 25 view->setShowGrid(false); 26 //view->setFocusPolicy(Qt::NoFocus); 27 view->setEditTriggers(QAbstractItemView::NoEditTriggers); 28 //view->horizontalHeader()->setStretchLastSection(true); 29 30 view->setAlternatingRowColors(true); 31 view->setFocus(); 32 33 view->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed); 34 view->verticalHeader()->setDefaultSectionSize(view->verticalHeader()->minimumSectionSize()); 35 36 for (int c = 0; c < view->horizontalHeader()->count(); ++c) { 37 view->horizontalHeader()->setSectionResizeMode(c, QHeaderView::Fixed); 38 } 39 40 view->viewport()->installEventFilter(this); 41 view->installEventFilter(this); 42 43 connect(view, &QTableView::activated, [=](const QModelIndex &index) { 44 emit activated(model->itemAt(proxy->mapToSource(index))); 45 }); 46 47 connect(view->selectionModel(), &QItemSelectionModel::currentChanged, this, &Controller::on_currentSelectionChanged); 48 connect(view->selectionModel(), &QItemSelectionModel::selectionChanged, this, &Controller::on_selectionChanged); 49 50 connect(search, &QLineEdit::textChanged, this, &Controller::on_search); 51 search->setClearButtonEnabled(true); 52 53 connect(view->verticalScrollBar(), &QScrollBar::valueChanged, [=](int val) { 54 if (model->playlist() != nullptr) { 55 scroll_positions[model->playlist()->uid()] = val; 56 } 57 }); 58 59 context_menu = new PlaylistContextMenu(model, proxy, view, search, this); 60 connect(context_menu, &PlaylistContextMenu::playlistChanged, this, &Controller::changed); 61 62 view->setContextMenuPolicy(Qt::CustomContextMenu); 63 connect(view, &QTableView::customContextMenuRequested, context_menu, &PlaylistContextMenu::show); 64 } 65 loadColumnsConfig()66 void PlaylistUi::Controller::loadColumnsConfig() { 67 auto c = global_conf.columnsConfig(); 68 if (c.count() == 0) { 69 global_conf.saveColumnsConfig(columns_config); 70 } else { 71 columns_config = global_conf.columnsConfig(); 72 } 73 } 74 on_load(const std::shared_ptr<Playlist::Playlist> pi)75 void Controller::on_load(const std::shared_ptr<Playlist::Playlist> pi) { 76 if (pi == nullptr) { 77 return; 78 } 79 model->setPlaylist(pi); 80 81 if (scroll_positions.contains(pi->uid())) { 82 QTimer::singleShot(20, [=]() { // hack: https://stackoverflow.com/questions/50989433/qtableviewscrollto-immediately-after-model-reset-and-after-some-delay 83 view->verticalScrollBar()->setValue(scroll_positions[pi->uid()]); 84 }); 85 } 86 } 87 on_unload()88 void Controller::on_unload() { 89 model->setPlaylist(nullptr); 90 } 91 on_stop()92 void Controller::on_stop() { 93 model->highlight(0, Model::HighlightState::None); 94 } 95 on_start(const Track & t)96 void Controller::on_start(const Track &t) { 97 model->highlight(t.uid(), Model::HighlightState::Playing); 98 } 99 on_pause(const Track & t)100 void Controller::on_pause(const Track &t) { 101 model->highlight(t.uid(), Model::HighlightState::Paused); 102 } 103 on_scrollTo(const Track & track)104 void Controller::on_scrollTo(const Track &track) { 105 QModelIndex index = proxy->mapFromSource(model->indexOf(track.uid())); 106 if (index.isValid()) { 107 view->setCurrentIndex(index); 108 view->scrollTo(index, QAbstractItemView::PositionAtCenter); 109 emit selected(track); 110 } 111 } 112 on_appendToPlaylist(const QList<QDir> & filepaths)113 void Controller::on_appendToPlaylist(const QList<QDir> &filepaths) { 114 if (model->playlist() != nullptr) { 115 connect(&*model->playlist(), &Playlist::Playlist::concatAsyncFinished, this, &Controller::on_appendAsyncFinished); 116 model->playlist()->concatAsync(filepaths); 117 spinner->show(); 118 } 119 } 120 sortBy(const QString & criteria)121 void Controller::sortBy(const QString &criteria) { 122 if (model->playlist() != nullptr) { 123 model->playlist()->sortBy(criteria); 124 model->reload(); 125 emit changed(model->playlist()); 126 } 127 } 128 on_appendAsyncFinished(Playlist::Playlist * pl)129 void Controller::on_appendAsyncFinished(Playlist::Playlist *pl) { 130 disconnect(pl, &Playlist::Playlist::concatAsyncFinished, this, &Controller::on_appendAsyncFinished); 131 model->reload(); 132 emit changed(model->playlist()); 133 spinner->hide(); 134 } 135 eventFilter(QObject * obj,QEvent * event)136 bool Controller::eventFilter(QObject *obj, QEvent *event) { 137 if (obj == view->viewport()) { 138 eventFilterViewport(event); 139 } else if (obj == view) { 140 eventFilterTableView(event); 141 } 142 return QObject::eventFilter(obj, event); 143 } 144 eventFilterTableView(QEvent * event)145 void Controller::eventFilterTableView(QEvent *event) { 146 if (event->type() == QEvent::KeyPress) { 147 QKeyEvent* keyevent = dynamic_cast<QKeyEvent*>(event); 148 if (keyevent->key() == Qt::Key_Delete) { 149 context_menu->on_remove(); 150 } 151 } 152 } 153 eventFilterViewport(QEvent * event)154 void Controller::eventFilterViewport(QEvent *event) { 155 if (event->type() == QEvent::Resize) { 156 int total_width = view->width(); 157 view->horizontalHeader()->setMinimumSectionSize(20); 158 view->setColumnWidth(0, 20); 159 160 for (int col = 1; col <= columns_config.count(); col++) { 161 auto rel_width = columns_config.width(col); 162 if (rel_width > 0) { 163 view->setColumnWidth(col, static_cast<int>(total_width * rel_width)); 164 } 165 if (columns_config.stretch(col)) { 166 view->horizontalHeader()->setSectionResizeMode(col, QHeaderView::Stretch); 167 } 168 } 169 170 } else if (event->type() == QEvent::WindowActivate) { 171 if (restore_scroll_once) { 172 restore_scroll_once = false; 173 view->verticalScrollBar()->setValue(local_conf.playlistViewScrollPosition()); 174 } 175 } else if (event->type() == QEvent::MouseMove || event->type() == QEvent::MouseButtonPress) { 176 local_conf.savePlaylistViewScrollPosition(view->verticalScrollBar()->value()); 177 } 178 179 if (event->type() == QEvent::MouseButtonPress) { 180 QMouseEvent *me = dynamic_cast<QMouseEvent *>(event); 181 if (me->button() == Qt::BackButton) { 182 search->clear(); 183 } 184 } 185 } 186 on_search(const QString & term)187 void Controller::on_search(const QString &term) { 188 proxy->filter(term); 189 if (term.isEmpty()) { 190 QTimer::singleShot(20, [=]() { 191 if (!view->selectionModel()->selectedRows().isEmpty()) { 192 view->scrollTo(view->selectionModel()->selectedRows().first(), QAbstractItemView::PositionAtCenter); 193 } 194 }); 195 } 196 } 197 on_currentSelectionChanged(const QModelIndex & index,const QModelIndex & prev)198 void Controller::on_currentSelectionChanged(const QModelIndex &index, const QModelIndex &prev) { 199 Q_UNUSED(prev) 200 auto source_index = proxy->mapToSource(index); 201 if (index.isValid() && source_index.isValid() && source_index.row() < model->rowCount()) { 202 emit selected(model->itemAt(source_index)); 203 } 204 } 205 on_selectionChanged(const QItemSelection & selected,const QItemSelection & deselected)206 void Controller::on_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) { 207 Q_UNUSED(deselected) 208 Q_UNUSED(selected) 209 210 quint32 selection_time = 0; 211 for (auto i: view->selectionModel()->selectedRows()) { 212 auto source_index = proxy->mapToSource(i); 213 if (i.isValid() && source_index.isValid() && source_index.row() < model->rowCount()) { 214 selection_time += model->itemAt(source_index).duration(); 215 } 216 } 217 emit durationOfSelectedChanged(selection_time); 218 } 219 } 220