Skip to content

Commit

Permalink
shortkey for clear/cut/copy/paste tracks (library, playlists, crates …
Browse files Browse the repository at this point in the history
…and autodj), for quick focus change between sidebar and tableview, for unselecting all tracks; add method to add tracks by id
  • Loading branch information
m0dB committed Jul 15, 2023
1 parent 3de2963 commit 1cd0d05
Show file tree
Hide file tree
Showing 25 changed files with 260 additions and 26 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1008,6 +1008,7 @@ add_library(mixxx-lib STATIC EXCLUDE_FROM_ALL
src/track/taglib/trackmetadata_xiph.cpp
src/util/battery/battery.cpp
src/util/cache.cpp
src/util/clipboard.cpp
src/util/cmdlineargs.cpp
src/util/colorcomponents.cpp
src/util/color/color.cpp
Expand Down
25 changes: 25 additions & 0 deletions src/library/basetracktablemodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "moc_basetracktablemodel.cpp"
#include "track/track.h"
#include "util/assert.h"
#include "util/clipboard.h"
#include "util/datetime.h"
#include "util/db/sqlite.h"
#include "util/logger.h"
Expand Down Expand Up @@ -362,6 +363,30 @@ int BaseTrackTableModel::columnCount(const QModelIndex& parent) const {
return countValidColumnHeaders();
}

void BaseTrackTableModel::cutTracks(const QModelIndexList& indices) {
copyTracks(indices);
removeTracks(indices);
}

void BaseTrackTableModel::copyTracks(const QModelIndexList& indices) const {
Clipboard::begin();
for (const QModelIndex& index : indices) {
if (index.isValid()) {
Clipboard::add(QUrl::fromLocalFile(getTrackLocation(index)));
}
}
Clipboard::end();
}

int BaseTrackTableModel::pasteTracks(const QModelIndex& insertionIndex) {
const QList<QUrl> urls = Clipboard::urls();
const QList<TrackId> trackIds = m_pTrackCollectionManager->resolveTrackIdsFromUrls(urls, false);
if (trackIds.isEmpty()) {
return 0;
}
return addTracksWithTrackIds(insertionIndex, trackIds);
}

bool BaseTrackTableModel::isColumnHiddenByDefault(
int column) {
return column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_ALBUMARTIST) ||
Expand Down
4 changes: 4 additions & 0 deletions src/library/basetracktablemodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ class BaseTrackTableModel : public QAbstractTableModel, public TrackModel {
return m_columnCache.fieldIndex(fieldName);
}

void cutTracks(const QModelIndexList& indices) override;
void copyTracks(const QModelIndexList& indices) const override;
int pasteTracks(const QModelIndex& index) override;

bool isColumnHiddenByDefault(
int column) override;

Expand Down
4 changes: 4 additions & 0 deletions src/library/library.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,10 @@ void Library::bindLibraryWidget(
&Library::slotLoadTrackToPlayer);
m_pLibraryWidget->registerView(m_sTrackViewName, pTrackTableView);

connect(m_pLibraryWidget,
&WLibrary::setLibraryFocus,
m_pLibraryControl,
&LibraryControl::setLibraryFocus);
connect(this,
&Library::switchToView,
m_pLibraryWidget,
Expand Down
13 changes: 5 additions & 8 deletions src/library/playlisttablemodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,15 +189,12 @@ void PlaylistTableModel::selectPlaylist(int playlistId) {
setSort(defaultSortColumn(), defaultSortOrder());
}

int PlaylistTableModel::addTracks(const QModelIndex& index,
const QList<QString>& locations) {
if (locations.isEmpty()) {
int PlaylistTableModel::addTracksWithTrackIds(const QModelIndex& index,
const QList<TrackId>& trackIds) {
if (trackIds.isEmpty()) {
return 0;
}

QList<TrackId> trackIds = m_pTrackCollectionManager->resolveTrackIdsFromLocations(
locations);

const int positionColumn = fieldIndex(ColumnCache::COLUMN_PLAYLISTTRACKSTABLE_POSITION);
int position = index.sibling(index.row(), positionColumn).data().toInt();

Expand All @@ -209,9 +206,9 @@ int PlaylistTableModel::addTracks(const QModelIndex& index,
int tracksAdded = m_pTrackCollectionManager->internalCollection()->getPlaylistDAO().insertTracksIntoPlaylist(
trackIds, m_iPlaylistId, position);

if (locations.size() - tracksAdded > 0) {
if (trackIds.size() - tracksAdded > 0) {
qDebug() << "PlaylistTableModel::addTracks could not add"
<< locations.size() - tracksAdded
<< trackIds.size() - tracksAdded
<< "to playlist" << m_iPlaylistId;
}
return tracksAdded;
Expand Down
2 changes: 1 addition & 1 deletion src/library/playlisttablemodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class PlaylistTableModel final : public TrackSetTableModel {
/// This function should only be used by AUTODJ
void removeTracks(const QModelIndexList& indices) final;
/// Returns the number of successful additions.
int addTracks(const QModelIndex& index, const QList<QString>& locations) final;
int addTracksWithTrackIds(const QModelIndex& index, const QList<TrackId>& trackIds) final;
bool isLocked() final;

Capabilities getCapabilities() const final;
Expand Down
6 changes: 3 additions & 3 deletions src/library/sidebarmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ QModelIndex SidebarModel::translateIndex(
}

void SidebarModel::slotDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) {
//qDebug() << "slotDataChanged topLeft:" << topLeft << "bottomRight:" << bottomRight;
// qDebug() << "slotDataChanged topLeft:" << topLeft << "bottomRight:" << bottomRight;
QModelIndex topLeftTranslated = translateSourceIndex(topLeft);
QModelIndex bottomRightTranslated = translateSourceIndex(bottomRight);
emit dataChanged(topLeftTranslated, bottomRightTranslated);
Expand All @@ -501,8 +501,8 @@ void SidebarModel::slotRowsInserted(const QModelIndex& parent, int start, int en
Q_UNUSED(parent);
Q_UNUSED(start);
Q_UNUSED(end);
//qDebug() << "slotRowsInserted" << parent << start << end;
//QModelIndex newParent = translateSourceIndex(parent);
// qDebug() << "slotRowsInserted" << parent << start << end;
// QModelIndex newParent = translateSourceIndex(parent);
endInsertRows();
}

Expand Down
15 changes: 15 additions & 0 deletions src/library/trackmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,16 @@ class TrackModel {
virtual void removeTracks(const QModelIndexList& indices) {
Q_UNUSED(indices);
}
virtual void cutTracks(const QModelIndexList& indices) {
Q_UNUSED(indices);
}
virtual void copyTracks(const QModelIndexList& indices) const {
Q_UNUSED(indices);
}
virtual int pasteTracks(const QModelIndex& index) {
Q_UNUSED(index);
return 0;
}
virtual void hideTracks(const QModelIndexList& indices) {
Q_UNUSED(indices);
}
Expand All @@ -154,6 +164,11 @@ class TrackModel {
Q_UNUSED(locations);
return 0;
}
virtual int addTracksWithTrackIds(const QModelIndex& index, const QList<TrackId>& tracks) {
Q_UNUSED(index);
Q_UNUSED(tracks);
return 0;
}
virtual void moveTrack(const QModelIndex& sourceIndex,
const QModelIndex& destIndex) {
Q_UNUSED(sourceIndex);
Expand Down
2 changes: 1 addition & 1 deletion src/library/trackset/baseplaylistfeature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ void BasePlaylistFeature::connectPlaylistDAO() {
&BasePlaylistFeature::slotPlaylistTableRenamed);
}

int BasePlaylistFeature::playlistIdFromIndex(const QModelIndex& index) {
int BasePlaylistFeature::playlistIdFromIndex(const QModelIndex& index) const {
TreeItem* item = static_cast<TreeItem*>(index.internalPointer());
if (item == nullptr) {
return kInvalidPlaylistId;
Expand Down
2 changes: 1 addition & 1 deletion src/library/trackset/baseplaylistfeature.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ class BasePlaylistFeature : public BaseTrackSetFeature {
virtual void decorateChild(TreeItem* pChild, int playlistId) = 0;
virtual void addToAutoDJ(PlaylistDAO::AutoDJSendLoc loc);

int playlistIdFromIndex(const QModelIndex& index);
int playlistIdFromIndex(const QModelIndex& index) const;
// Get the QModelIndex of a playlist based on its id. Returns QModelIndex()
// on failure.
QModelIndex indexFromPlaylistId(int playlistId);
Expand Down
8 changes: 3 additions & 5 deletions src/library/trackset/crate/cratetablemodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,17 +150,15 @@ TrackModel::Capabilities CrateTableModel::getCapabilities() const {
return caps;
}

int CrateTableModel::addTracks(
const QModelIndex& index, const QList<QString>& locations) {
int CrateTableModel::addTracksWithTrackIds(
const QModelIndex& index, const QList<TrackId>& trackIds) {
Q_UNUSED(index);
// If a track is dropped but it isn't in the library, then add it because
// the user probably dropped a file from outside Mixxx into this crate.
QList<TrackId> trackIds =
m_pTrackCollectionManager->resolveTrackIdsFromLocations(locations);
if (!m_pTrackCollectionManager->internalCollection()->addCrateTracks(
m_selectedCrate, trackIds)) {
qWarning() << "CrateTableModel::addTracks could not add"
<< locations.size() << "tracks to crate" << m_selectedCrate;
<< trackIds.size() << "tracks to crate" << m_selectedCrate;
return 0;
}

Expand Down
2 changes: 1 addition & 1 deletion src/library/trackset/crate/cratetablemodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class CrateTableModel final : public TrackSetTableModel {

void removeTracks(const QModelIndexList& indices) final;
/// Returns the number of unsuccessful additions.
int addTracks(const QModelIndex& index, const QList<QString>& locations) final;
int addTracksWithTrackIds(const QModelIndex& index, const QList<TrackId>& tracks) final;

Capabilities getCapabilities() const final;
QString modelKey(bool noSearch) const override;
Expand Down
12 changes: 12 additions & 0 deletions src/library/trackset/tracksettablemodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,15 @@ bool TrackSetTableModel::isColumnInternal(int column) {
column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_COVERART_DIGEST) ||
column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_COVERART_HASH);
}

int TrackSetTableModel::addTracks(const QModelIndex& index,
const QList<QString>& locations) {
if (locations.isEmpty()) {
return 0;
}

QList<TrackId> trackIds = m_pTrackCollectionManager->resolveTrackIdsFromLocations(
locations);

return addTracksWithTrackIds(index, trackIds);
}
2 changes: 2 additions & 0 deletions src/library/trackset/tracksettablemodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ class TrackSetTableModel : public BaseSqlTableModel {
const char* settingsNamespace);

bool isColumnInternal(int column) override;

int addTracks(const QModelIndex& index, const QList<QString>& locations) final;
};
35 changes: 35 additions & 0 deletions src/util/clipboard.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include "clipboard.h"

#include <QApplication>
#include <QClipboard>

QString& Clipboard::text() {
static QString s_text;
return s_text;
}

void Clipboard::begin() {
text() = "";
}

void Clipboard::add(const QUrl& url) {
text().append(url.toString() + "\n");
}

void Clipboard::end() {
QApplication::clipboard()->setText(text());
}

QList<QUrl> Clipboard::urls() {
const QString text = QApplication::clipboard()->text();
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
QStringList strings = text.split("\n", Qt::SkipEmptyParts);
#else
QStringList strings = text.split("\n", QString::SkipEmptyParts);
#endif
QList<QUrl> result;
for (QString string : strings) {

Check failure on line 31 in src/util/clipboard.cpp

View workflow job for this annotation

GitHub Actions / clazy

Missing reference in range-for with non trivial type (QString) [-Wclazy-range-loop-reference]
result.append(QUrl(string));
}
return result;
}
14 changes: 14 additions & 0 deletions src/util/clipboard.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once

#include <QString>
#include <QUrl>

class Clipboard {
static QString& text();

public:
static QList<QUrl> urls();
static void begin();
static void end();
static void add(const QUrl& url);
};
6 changes: 6 additions & 0 deletions src/widget/wlibrary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,9 @@ bool WLibrary::event(QEvent* pEvent) {
}
return QStackedWidget::event(pEvent);
}

void WLibrary::keyPressEvent(QKeyEvent* event) {
if (event->key() == Qt::Key_Left && event->modifiers() & Qt::ControlModifier) {
emit setLibraryFocus(FocusWidget::Sidebar);
}
}
5 changes: 5 additions & 0 deletions src/widget/wlibrary.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <QStackedWidget>
#include <QString>

#include "library/library_decl.h"
#include "library/libraryview.h"
#include "skin/legacy/skincontext.h"
#include "util/compatibility/qmutex.h"
Expand Down Expand Up @@ -48,6 +49,9 @@ class WLibrary : public QStackedWidget, public WBaseWidget {
return m_bShowButtonText;
}

signals:
FocusWidget setLibraryFocus(FocusWidget newFocus);

public slots:
// Show the view registered with the given name. Does nothing if the current
// view is the specified view, or if the name does not specify any
Expand All @@ -59,6 +63,7 @@ class WLibrary : public QStackedWidget, public WBaseWidget {

protected:
bool event(QEvent* pEvent) override;
void keyPressEvent(QKeyEvent* event) override;

private:
QT_RECURSIVE_MUTEX m_mutex;
Expand Down
8 changes: 8 additions & 0 deletions src/widget/wlibrarysidebar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,14 @@ void WLibrarySidebar::keyPressEvent(QKeyEvent* event) {
emit pressed(selIndex);
return;
}
case Qt::Key_Right: {
if (event->modifiers() & Qt::ControlModifier) {
emit setLibraryFocus(FocusWidget::TracksTable);
} else {
QTreeView::keyPressEvent(event);
}
return;
}
case Qt::Key_Left: {
QModelIndexList selectedIndices = selectionModel()->selectedRows();
if (selectedIndices.isEmpty()) {
Expand Down
5 changes: 4 additions & 1 deletion src/widget/wlibrarytableview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -267,12 +267,15 @@ void WLibraryTableView::focusInEvent(QFocusEvent* event) {
QTableView::focusInEvent(event);

if (event->reason() == Qt::TabFocusReason ||
event->reason() == Qt::BacktabFocusReason) {
event->reason() == Qt::BacktabFocusReason ||
event->reason() == Qt::OtherFocusReason) {
// On FocusIn caused by a tab action with no focused item, select the
// current or first track which can then instantly be loaded to a deck.
// This is especially helpful if the table has only one track, which can
// not be selected with up/down buttons, either physical or emulated via
// [Library],MoveVertical controls. See #9548
// Qt::OtherFocusReason is setFocus called by LibraryControl::setLibraryFocus,
// in response to shortkey Ctrl-Right
if (model()->rowCount() > 0) {
if (selectionModel()->hasSelection()) {
DEBUG_ASSERT(!selectionModel()->selectedIndexes().isEmpty());
Expand Down
2 changes: 2 additions & 0 deletions src/widget/wlibrarytableview.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <QString>
#include <QTableView>

#include "library/library_decl.h"
#include "library/libraryview.h"
#include "preferences/usersettings.h"
#include "track/track_decl.h"
Expand Down Expand Up @@ -58,6 +59,7 @@ class WLibraryTableView : public QTableView, public virtual LibraryView {
void trackSelected(TrackPointer pTrack);
void onlyCachedCoverArt(bool);
void scrollValueChanged(int);
FocusWidget setLibraryFocus(FocusWidget newFocus);

public slots:
void setTrackTableFont(const QFont& font);
Expand Down
10 changes: 10 additions & 0 deletions src/widget/wlibrarytextbrowser.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "widget/wlibrarytextbrowser.h"

#include <QKeyEvent>

#include "moc_wlibrarytextbrowser.cpp"

WLibraryTextBrowser::WLibraryTextBrowser(QWidget* parent)
Expand All @@ -13,3 +15,11 @@ bool WLibraryTextBrowser::hasFocus() const {
void WLibraryTextBrowser::setFocus() {
QWidget::setFocus();
}

void WLibraryTextBrowser::keyPressEvent(QKeyEvent* event) {
if (event->key() == Qt::Key_Left && event->modifiers() & Qt::ControlModifier) {
event->ignore();
return;
}
QTextBrowser::keyPressEvent(event);
}
4 changes: 4 additions & 0 deletions src/widget/wlibrarytextbrowser.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <QTextBrowser>

#include "library/library_decl.h"
#include "library/libraryview.h"

class WLibraryTextBrowser : public QTextBrowser, public LibraryView {
Expand All @@ -11,4 +12,7 @@ class WLibraryTextBrowser : public QTextBrowser, public LibraryView {
void onShow() override {}
bool hasFocus() const override;
void setFocus() override;
void keyPressEvent(QKeyEvent* event) override;
signals:
FocusWidget setLibraryFocus(FocusWidget newFocus);
};
Loading

0 comments on commit 1cd0d05

Please sign in to comment.