From 0f726d98e20b7c28bcd33e5af9c5b4054dc42e9c Mon Sep 17 00:00:00 2001 From: Thomas Lange Date: Sat, 25 Jan 2025 12:28:59 +0100 Subject: [PATCH] moonstone: Remove plugin This interface plugin has always been experimental and was never enabled by default. Since there was no active development over the years, drop it. See also: https://github.com/orgs/audacious-media-player/discussions/1550 --- meson_options.txt | 5 - src/meson.build | 4 - src/moonstone/Makefile | 23 -- src/moonstone/info_bar.cc | 351 -------------------- src/moonstone/info_bar.h | 87 ----- src/moonstone/main_window.cc | 220 ------------- src/moonstone/main_window.h | 92 ------ src/moonstone/meson.build | 21 -- src/moonstone/moonstone.cc | 80 ----- src/moonstone/playlist.cc | 494 ---------------------------- src/moonstone/playlist.h | 100 ------ src/moonstone/playlist_header.cc | 323 ------------------ src/moonstone/playlist_header.h | 60 ---- src/moonstone/playlist_model.cc | 329 ------------------ src/moonstone/playlist_model.h | 105 ------ src/moonstone/playlist_selection.cc | 196 ----------- src/moonstone/playlist_selection.h | 135 -------- src/moonstone/playlist_tabs.cc | 375 --------------------- src/moonstone/playlist_tabs.h | 102 ------ src/moonstone/time_slider.cc | 144 -------- src/moonstone/time_slider.h | 70 ---- src/moonstone/tool_bar.cc | 69 ---- src/moonstone/tool_bar.h | 81 ----- 23 files changed, 3466 deletions(-) delete mode 100644 src/moonstone/Makefile delete mode 100644 src/moonstone/info_bar.cc delete mode 100644 src/moonstone/info_bar.h delete mode 100644 src/moonstone/main_window.cc delete mode 100644 src/moonstone/main_window.h delete mode 100644 src/moonstone/meson.build delete mode 100644 src/moonstone/moonstone.cc delete mode 100644 src/moonstone/playlist.cc delete mode 100644 src/moonstone/playlist.h delete mode 100644 src/moonstone/playlist_header.cc delete mode 100644 src/moonstone/playlist_header.h delete mode 100644 src/moonstone/playlist_model.cc delete mode 100644 src/moonstone/playlist_model.h delete mode 100644 src/moonstone/playlist_selection.cc delete mode 100644 src/moonstone/playlist_selection.h delete mode 100644 src/moonstone/playlist_tabs.cc delete mode 100644 src/moonstone/playlist_tabs.h delete mode 100644 src/moonstone/time_slider.cc delete mode 100644 src/moonstone/time_slider.h delete mode 100644 src/moonstone/tool_bar.cc delete mode 100644 src/moonstone/tool_bar.h diff --git a/meson_options.txt b/meson_options.txt index 578bdba62e..3a88ba4f48 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -124,8 +124,3 @@ option('gl-spectrum', type: 'boolean', value: true, description: 'Whether the OpenGL spectrum visualization plugin is enabled') option('vumeter', type: 'boolean', value: true, description: 'Whether the VU Meter visualization plugin is enabled') - - -# interface plugins -option('moonstone', type: 'boolean', value: false, - description: 'Whether the Moonstone UI plugin is enabled') diff --git a/src/meson.build b/src/meson.build index 9940874b17..31a1d799e3 100644 --- a/src/meson.build +++ b/src/meson.build @@ -118,10 +118,6 @@ if conf.has('USE_QT') subdir('qthotkey') endif - if get_option('moonstone') - subdir('moonstone') - endif - if get_option('qtaudio') subdir('qtaudio') endif diff --git a/src/moonstone/Makefile b/src/moonstone/Makefile deleted file mode 100644 index a1272164a2..0000000000 --- a/src/moonstone/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -PLUGIN = moonstone${PLUGIN_SUFFIX} - -SRCS = moonstone.cc \ - main_window.cc \ - info_bar.cc \ - tool_bar.cc \ - time_slider.cc \ - playlist.cc \ - playlist_tabs.cc \ - playlist_header.cc \ - playlist_model.cc \ - playlist_selection.cc - -include ../../buildsys.mk -include ../../extra.mk - -plugindir := ${plugindir}/${GENERAL_PLUGIN_DIR} - -LD = ${CXX} - -CFLAGS += ${PLUGIN_CFLAGS} -CPPFLAGS += ${PLUGIN_CPPFLAGS} -I../.. ${QT_CFLAGS} -LIBS += ${QT_LIBS} -laudqt diff --git a/src/moonstone/info_bar.cc b/src/moonstone/info_bar.cc deleted file mode 100644 index f751c0e83a..0000000000 --- a/src/moonstone/info_bar.cc +++ /dev/null @@ -1,351 +0,0 @@ -/* - * info_bar.cc - * Copyright 2014, 2020 Ariadne Conill - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions, and the following disclaimer in the documentation - * provided with the distribution. - * - * This software is provided "as is" and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising from - * the use of this software. - */ - -#include - -#include "info_bar.h" - -#include -#include -#include -#include - -#include -#include -#include - -namespace Moonstone { - -static constexpr int FadeSteps = 10; - -static constexpr int VisBands = 12; -static constexpr int VisDelay = 2; /* delay before falloff in frames */ -static constexpr int VisFalloff = 2; /* falloff in decibels per frame */ - -struct BarColors -{ - QColor main, shadow; -}; - -struct PixelSizes -{ - int Spacing, IconSize, Height, BandWidth, BandSpacing, VisWidth, VisScale, - VisCenter; - - PixelSizes(int dpi) - : Spacing(aud::rescale(dpi, 12, 1)), - IconSize(2 * aud::rescale(dpi, 3, 1)), // should be divisible by 2 - Height(IconSize + 2 * Spacing), BandWidth(aud::rescale(dpi, 16, 1)), - BandSpacing(aud::rescale(dpi, 48, 1)), - VisWidth(VisBands * (BandWidth + BandSpacing) - BandSpacing + - 2 * Spacing), - VisScale(aud::rescale(IconSize, 8, 5)), VisCenter(VisScale + Spacing) - { - } -}; - -class InfoVis : public QWidget, Visualizer -{ -public: - InfoVis(QWidget * parent = nullptr); - ~InfoVis(); - - void enable(bool enabled); - - const QGradient & gradient() const { return m_gradient; } - const PixelSizes & pixelSizes() const { return ps; } - -protected: - void render_freq(const float * freq); - void clear(); - - void changeEvent(QEvent * event); - void paintEvent(QPaintEvent *); - -private: - void update_colors(); - - const PixelSizes ps; - QLinearGradient m_gradient; - BarColors m_bar_colors[VisBands]; - - float m_bars[VisBands]{}; - char m_delay[VisBands]{}; -}; - -void InfoVis::update_colors() -{ - auto & base = palette().color(QPalette::Window); - auto & highlight = palette().color(QPalette::Highlight); - - m_gradient.setStops(audqt::dark_bg_gradient(base)); - - for (int i = 0; i < VisBands; i++) - { - m_bar_colors[i].main = audqt::vis_bar_color(highlight, i, VisBands); - m_bar_colors[i].shadow = m_bar_colors[i].main.darker(333); - } -} - -InfoVis::InfoVis(QWidget * parent) - : QWidget(parent), Visualizer(Freq), ps(audqt::sizes.OneInch), - m_gradient(0, 0, 0, ps.Height) -{ - update_colors(); - setAttribute(Qt::WA_OpaquePaintEvent); - setMinimumHeight(ps.Height); - resize(ps.VisWidth + 2 * ps.Spacing, parentWidget()->height()); -} - -InfoVis::~InfoVis() { enable(false); } - -void InfoVis::render_freq(const float * freq) -{ - /* xscale[i] = pow (256, i / VIS_BANDS) - 0.5; */ - const float xscale[VisBands + 1] = {0.5, 1.09, 2.02, 3.5, 5.85, - 9.58, 15.5, 24.9, 39.82, 63.5, - 101.09, 160.77, 255.5}; - - for (int i = 0; i < VisBands; i++) - { - /* 40 dB range */ - float x = 40 + compute_freq_band(freq, xscale, i, VisBands); - - m_bars[i] -= aud::max(0, VisFalloff - m_delay[i]); - - if (m_delay[i]) - m_delay[i]--; - - if (x > m_bars[i]) - { - m_bars[i] = x; - m_delay[i] = VisDelay; - } - } - - repaint(); -} - -void InfoVis::clear() -{ - memset(m_bars, 0, sizeof m_bars); - memset(m_delay, 0, sizeof m_delay); - - update(); -} - -void InfoVis::changeEvent(QEvent * event) -{ - if (event->type() == QEvent::PaletteChange) - update_colors(); - - QWidget::changeEvent(event); -} - -void InfoVis::paintEvent(QPaintEvent *) -{ - QPainter p(this); - p.fillRect(0, 0, ps.VisWidth, height(), m_gradient); - - for (int i = 0; i < VisBands; i++) - { - int x = ps.Spacing + i * (ps.BandWidth + ps.BandSpacing); - int v = aud::clamp((int)(m_bars[i] * ps.VisScale / 40), 0, ps.VisScale); - int m = aud::min(ps.VisCenter + v, ps.Height); - - p.fillRect(x, ps.VisCenter - v, ps.BandWidth, v, m_bar_colors[i].main); - p.fillRect(x, ps.VisCenter, ps.BandWidth, m - ps.VisCenter, - m_bar_colors[i].shadow); - } -} - -void InfoVis::enable(bool enabled) -{ - if (enabled) - aud_visualizer_add(this); - else - { - aud_visualizer_remove(this); - clear(); - } -} - -InfoBar::InfoBar(QWidget * parent, ToolBar * toolbar) - : QWidget(parent), m_vis(new InfoVis(this)), ps(m_vis->pixelSizes()), - m_stopped(true) -{ - update_vis(); - setFixedHeight(ps.Height + toolbar->height()); - - m_vis->raise(); - - for (SongData & d : sd) - { - d.title.setTextFormat(Qt::PlainText); - d.artist.setTextFormat(Qt::PlainText); - d.album.setTextFormat(Qt::PlainText); - d.alpha = 0; - } - - if (aud_drct_get_ready()) - { - m_stopped = false; - update_title(); - update_album_art(); - - /* skip fade-in */ - sd[Cur].alpha = FadeSteps; - } - - m_toolbar = toolbar; - m_toolbar->setParent(this); - - m_toolbar->move(0, height() - m_toolbar->height()); - m_toolbar->resize(width(), m_toolbar->height()); - m_toolbar->raise(); -} - -void InfoBar::resizeEvent(QResizeEvent *) -{ - for (SongData & d : sd) - d.title.setText(QString()); - - m_vis->move(width() - ps.VisWidth, 0); - m_vis->resize(ps.VisWidth, height()); - - m_toolbar->move(0, height() - m_toolbar->height()); - m_toolbar->resize(width(), m_toolbar->height()); -} - -void InfoBar::paintEvent(QPaintEvent *) -{ - QPainter p(this); - - p.fillRect(0, 0, width() - ps.VisWidth, height(), m_vis->gradient()); - - for (SongData & d : sd) - { - p.setOpacity((qreal)d.alpha / FadeSteps); - - if (!d.art.isNull()) - { - auto sz = d.art.size() / d.art.devicePixelRatio(); - int left = ps.Spacing + (ps.IconSize - sz.width()) / 2; - int top = ps.Spacing + (ps.IconSize - sz.height()) / 2; - p.drawPixmap(left, top, d.art); - } - - QFont font = p.font(); - font.setPointSize(18); - p.setFont(font); - - if (d.title.text().isNull() && !d.orig_title.isNull()) - { - QFontMetrics metrics = p.fontMetrics(); - d.title = QStaticText(metrics.elidedText( - d.orig_title, Qt::ElideRight, - width() - ps.VisWidth - ps.Height - ps.Spacing)); - } - - p.setPen(QColor(255, 255, 255)); - p.drawStaticText(ps.Height, ps.Spacing, d.title); - - font.setPointSize(9); - p.setFont(font); - - p.drawStaticText(ps.Height, ps.Spacing + ps.IconSize / 2, d.artist); - - p.setPen(QColor(179, 179, 179)); - p.drawStaticText(ps.Height, ps.Spacing + ps.IconSize * 3 / 4, d.album); - } -} - -void InfoBar::update_title() -{ - Tuple tuple = aud_drct_get_tuple(); - - sd[Cur].title.setText(QString()); - sd[Cur].orig_title = tuple.get_str(Tuple::Title); - sd[Cur].artist.setText((const char *)tuple.get_str(Tuple::Artist)); - sd[Cur].album.setText((const char *)tuple.get_str(Tuple::Album)); - - update(); -} - -void InfoBar::update_album_art() -{ - sd[Cur].art = audqt::art_request_current(ps.IconSize, ps.IconSize); -} - -void InfoBar::next_song() -{ - sd[Prev] = std::move(sd[Cur]); - sd[Cur].alpha = 0; -} - -void InfoBar::do_fade() -{ - bool done = true; - - if (aud_drct_get_playing() && sd[Cur].alpha < FadeSteps) - { - sd[Cur].alpha++; - done = false; - } - - if (sd[Prev].alpha > 0) - { - sd[Prev].alpha--; - done = false; - } - - update(); - - if (done) - fade_timer.stop(); -} - -void InfoBar::playback_ready_cb() -{ - if (!m_stopped) - next_song(); - - m_stopped = false; - update_title(); - update_album_art(); - - update(); - fade_timer.start(); -} - -void InfoBar::playback_stop_cb() -{ - next_song(); - m_stopped = true; - - update(); - fade_timer.start(); -} - -void InfoBar::update_vis() -{ - m_vis->enable(aud_get_bool("qtui", "infoarea_show_vis")); -} - -} diff --git a/src/moonstone/info_bar.h b/src/moonstone/info_bar.h deleted file mode 100644 index 16aecca528..0000000000 --- a/src/moonstone/info_bar.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * info_bar.h - * Copyright 2014, 2020 Ariadne Conill - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions, and the following disclaimer in the documentation - * provided with the distribution. - * - * This software is provided "as is" and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising from - * the use of this software. - */ - -#ifndef INFO_BAR_H -#define INFO_BAR_H - -#include "tool_bar.h" - -#include -#include -#include - -#include - -namespace Moonstone { - -class InfoVis; -struct PixelSizes; - -class InfoBar : public QWidget -{ -public: - InfoBar(QWidget * parent = nullptr, ToolBar * toolbar = nullptr); - - void resizeEvent(QResizeEvent *); - void paintEvent(QPaintEvent *); - -private: - void update_title(); - void update_album_art(); - void next_song(); - void do_fade(); - - void playback_ready_cb(); - void playback_stop_cb(); - void update_vis(); - - const HookReceiver hook1{"tuple change", this, - &InfoBar::update_title}, - hook2{"playback ready", this, &InfoBar::playback_ready_cb}, - hook3{"playback stop", this, &InfoBar::playback_stop_cb}, - hook4{"qtui toggle infoarea_vis", this, &InfoBar::update_vis}; - - const Timer fade_timer{TimerRate::Hz30, this, &InfoBar::do_fade}; - - InfoVis * m_vis; - const PixelSizes & ps; - - struct SongData - { - QPixmap art; - QString orig_title; - QStaticText title, artist, album; - int alpha; - }; - - enum - { - Prev = 0, - Cur = 1 - }; /* index into SongData array */ - - SongData sd[2]; - bool m_stopped; - - ToolBar * m_toolbar; -}; - -} - -#endif diff --git a/src/moonstone/main_window.cc b/src/moonstone/main_window.cc deleted file mode 100644 index 3736f1dcd9..0000000000 --- a/src/moonstone/main_window.cc +++ /dev/null @@ -1,220 +0,0 @@ -/* - * main_window.cc - * Copyright 2014 Michał Lipski - * Copyright 2020 Ariadne Conill - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions, and the following disclaimer in the documentation - * provided with the distribution. - * - * This software is provided "as is" and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising from - * the use of this software. - */ - -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include "main_window.h" -#include "info_bar.h" -#include "tool_bar.h" -#include "time_slider.h" -#include "playlist.h" -#include "playlist_tabs.h" -#include "playlist_selection.h" - -namespace Moonstone { - -MainWindow::MainWindow() : - m_center_widget(new QWidget(this)), - m_center_layout(audqt::make_vbox(m_center_widget, 0)), - m_splitter(new QSplitter(this)), - m_playlist_tabs(new PlaylistTabs), - m_playlists_view(new PlaylistsView) -{ - resize(1000, 600); - - setCentralWidget(m_center_widget); - - auto slider = new TimeSlider(this); - - const ToolBarItem items[] = { -#if 0 - ToolBarAction("edit-find", N_("Search Library"), N_("Search Library"), - toggle_search_tool, &m_search_action), -#endif - ToolBarAction("document-open", N_("Open Files"), N_("Open Files"), - []() { audqt::fileopener_show(audqt::FileMode::Open); }), - ToolBarAction("list-add", N_("Add Files"), N_("Add Files"), - []() { audqt::fileopener_show(audqt::FileMode::Add); }), - ToolBarSeparator(), - ToolBarAction("media-skip-backward", N_("Previous"), N_("Previous"), - aud_drct_pl_prev), - ToolBarAction("media-playback-start", N_("Play"), N_("Play"), - aud_drct_play_pause, &m_play_pause_action), - ToolBarAction("media-playback-stop", N_("Stop"), N_("Stop"), - aud_drct_stop, &m_stop_action), - ToolBarAction( - "media-playback-stop", N_("Stop After This Song"), - N_("Stop After This Song"), - [](bool on) { aud_set_bool("stop_after_current_song", on); }, - &m_stop_after_action), - ToolBarAction("media-skip-forward", N_("Next"), N_("Next"), - aud_drct_pl_next), - ToolBarAction( - "media-record", N_("Record Stream"), N_("Record Stream"), - [](bool on) { aud_set_bool("record", on); }, &m_record_action), - ToolBarSeparator(), - ToolBarCustom(slider), - ToolBarCustom(slider->label()), - ToolBarSeparator(), - ToolBarAction( - "media-playlist-repeat", N_("Repeat"), N_("Repeat"), - [](bool on) { aud_set_bool("repeat", on); }, &m_repeat_action), - ToolBarAction( - "media-playlist-shuffle", N_("Shuffle"), N_("Shuffle"), - [](bool on) { aud_set_bool("shuffle", on); }, &m_shuffle_action), - ToolBarCustom(audqt::volume_button_new(this))}; - - m_toolbar = new ToolBar(this, items); - m_infobar = new InfoBar(this, m_toolbar); - - m_center_layout->addWidget(m_infobar); - m_center_layout->addWidget(m_splitter); - - m_splitter->addWidget(m_playlists_view); - m_splitter->addWidget(m_playlist_tabs); - m_splitter->setStretchFactor(1, 2); - - update_toggles(); -} - -void MainWindow::closeEvent(QCloseEvent * e) -{ - bool handled = false; - - hook_call("window close", &handled); - - if (!handled) - { - e->accept(); - aud_quit(); - } - else - e->ignore(); -} - -void MainWindow::update_toggles() -{ -#if 0 - if (m_search_tool) - m_search_action->setChecked(aud_plugin_get_enabled(m_search_tool)); -#endif - - bool stop_after = aud_get_bool("stop_after_current_song"); - m_stop_action->setVisible(!stop_after); - m_stop_after_action->setVisible(stop_after); - m_stop_after_action->setChecked(stop_after); - - m_record_action->setVisible(aud_drct_get_record_enabled()); - m_record_action->setChecked(aud_get_bool("record")); - - m_repeat_action->setChecked(aud_get_bool("repeat")); - m_shuffle_action->setChecked(aud_get_bool("shuffle")); -} - -void MainWindow::update_play_pause() -{ - if (!aud_drct_get_playing() || aud_drct_get_paused()) - { - m_play_pause_action->setIcon(QIcon::fromTheme("media-playback-start")); - m_play_pause_action->setText(_("Play")); - m_play_pause_action->setToolTip(_("Play")); - } - else - { - m_play_pause_action->setIcon(QIcon::fromTheme("media-playback-pause")); - m_play_pause_action->setText(_("Pause")); - m_play_pause_action->setToolTip(_("Pause")); - } -} - -void MainWindow::title_change_cb() -{ - auto title = aud_drct_get_title(); - if (title) - { - set_title(QString(title) + QString(" - Audacious")); - m_buffering_timer.stop(); - } -} - -void MainWindow::playback_begin_cb() -{ - update_play_pause(); - - auto last_widget = m_playlist_tabs->playlistWidget(m_last_playing.index()); - if (last_widget) - last_widget->updatePlaybackIndicator(); - - auto playing = Playlist::playing_playlist(); - - auto widget = m_playlist_tabs->playlistWidget(playing.index()); - if (widget) - widget->scrollToCurrent(); - if (widget && widget != last_widget) - widget->updatePlaybackIndicator(); - - m_last_playing = playing; - - m_buffering_timer.queue(250, [this]() { set_title(_("Buffering ...")); }); -} - -void MainWindow::pause_cb() -{ - update_play_pause(); - - auto widget = m_playlist_tabs->playlistWidget(m_last_playing.index()); - if (widget) - widget->updatePlaybackIndicator(); -} - -void MainWindow::playback_stop_cb() -{ - set_title("Audacious"); - m_buffering_timer.stop(); - - update_play_pause(); - - auto last_widget = m_playlist_tabs->playlistWidget(m_last_playing.index()); - if (last_widget) - last_widget->updatePlaybackIndicator(); - - m_last_playing = Playlist(); -} - -void MainWindow::set_title(const QString & title) -{ - int instance = aud_get_instance(); - if (instance == 1) - QMainWindow::setWindowTitle(title); - else - QMainWindow::setWindowTitle( - QString("%1 (%2)").arg(title).arg(instance)); -} - -} diff --git a/src/moonstone/main_window.h b/src/moonstone/main_window.h deleted file mode 100644 index 707303ee93..0000000000 --- a/src/moonstone/main_window.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * main_window.h - * Copyright 2014 Michał Lipski - * Copyright 2020 Ariadne Conill - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions, and the following disclaimer in the documentation - * provided with the distribution. - * - * This software is provided "as is" and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising from - * the use of this software. - */ - -#ifndef MAIN_WINDOW_H -#define MAIN_WINDOW_H - -#include -#include -#include - -#include "../ui-common/dialogs-qt.h" - -#include -#include -#include - -namespace Moonstone { - -class InfoBar; -class ToolBar; -class PlaylistTabs; -class PlaylistsView; - -class MainWindow : public QMainWindow { -public: - MainWindow(); - -private: - QWidget * m_center_widget; - QVBoxLayout * m_center_layout; - InfoBar * m_infobar; - ToolBar * m_toolbar; - - QSplitter * m_splitter; - PlaylistTabs * m_playlist_tabs; - PlaylistsView * m_playlists_view; - - QAction *m_search_action; - QAction *m_play_pause_action, *m_stop_action, *m_stop_after_action; - QAction *m_record_action; - QAction *m_repeat_action, *m_shuffle_action; - - QueuedFunc m_buffering_timer; - Playlist m_last_playing; - - void closeEvent(QCloseEvent * e) override; - - void set_title(const QString & title); - - void update_toggles(); - void update_play_pause(); - - void title_change_cb(); - void playback_begin_cb(); - void playback_ready_cb(); - void pause_cb(); - void playback_stop_cb(); - - const HookReceiver // - hook1{"title change", this, &MainWindow::title_change_cb}, - hook2{"playback begin", this, &MainWindow::playback_begin_cb}, - hook3{"playback ready", this, &MainWindow::title_change_cb}, - hook4{"playback pause", this, &MainWindow::pause_cb}, - hook5{"playback unpause", this, &MainWindow::pause_cb}, - hook6{"playback stop", this, &MainWindow::playback_stop_cb}, - hook7{"set stop_after_current_song", this, &MainWindow::update_toggles}, - hook8{"enable record", this, &MainWindow::update_toggles}, - hook9{"set record", this, &MainWindow::update_toggles}, - hook10{"set repeat", this, &MainWindow::update_toggles}, - hook11{"set shuffle", this, &MainWindow::update_toggles}; -}; - -} - -#endif diff --git a/src/moonstone/meson.build b/src/moonstone/meson.build deleted file mode 100644 index dfbdaf5958..0000000000 --- a/src/moonstone/meson.build +++ /dev/null @@ -1,21 +0,0 @@ -moonstone_sources = [ - 'moonstone.cc', - 'main_window.cc', - 'info_bar.cc', - 'tool_bar.cc', - 'time_slider.cc', - 'playlist.cc', - 'playlist_header.cc', - 'playlist_model.cc', - 'playlist_tabs.cc', - 'playlist_selection.cc' -] - - -shared_module('moonstone', - moonstone_sources, - dependencies: [audacious_dep, qt_dep, audqt_dep], - name_prefix: '', - install: true, - install_dir: general_plugin_dir -) diff --git a/src/moonstone/moonstone.cc b/src/moonstone/moonstone.cc deleted file mode 100644 index 881b9b673d..0000000000 --- a/src/moonstone/moonstone.cc +++ /dev/null @@ -1,80 +0,0 @@ -/* - * moonstone.cc - * Copyright 2020 Ariadne Conill - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions, and the following disclaimer in the documentation - * provided with the distribution. - * - * This software is provided "as is" and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising from - * the use of this software. - */ - -#include -#include - -#include -#include -#include - -#include -#include - -#include "main_window.h" - -static QPointer s_window; - -class MoonstoneUI : public audqt::QtIfacePlugin -{ -public: - constexpr MoonstoneUI() - : audqt::QtIfacePlugin( - {N_("Moonstone"), PACKAGE, nullptr, nullptr, PluginQtOnly}) - { - } - - bool init() - { - audqt::init(); -// aud_config_set_defaults("moonstoneui", moonstoneui_defaults); - s_window = new Moonstone::MainWindow; - return true; - } - - void cleanup() - { - delete s_window; - audqt::cleanup(); - } - - void run() { QApplication::exec(); } - - void show(bool show) - { - if (!s_window) - return; - - s_window->setVisible(show); - - if (show) - { - s_window->activateWindow(); - s_window->raise(); - } - } - - void quit() - { - QObject::connect(s_window.data(), &QObject::destroyed, QApplication::quit); - s_window->deleteLater(); - } -}; - -EXPORT MoonstoneUI aud_plugin_instance; diff --git a/src/moonstone/playlist.cc b/src/moonstone/playlist.cc deleted file mode 100644 index 514a239d60..0000000000 --- a/src/moonstone/playlist.cc +++ /dev/null @@ -1,494 +0,0 @@ -/* - * playlist.cc - * Copyright 2014 Michał Lipski - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions, and the following disclaimer in the documentation - * provided with the distribution. - * - * This software is provided "as is" and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising from - * the use of this software. - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "playlist.h" -#include "playlist_header.h" -#include "playlist_model.h" - -#include "../ui-common/menu-ops.h" -#include "../ui-common/qt-compat.h" - -namespace Moonstone { - -PlaylistWidget::PlaylistWidget(QWidget * parent, Playlist playlist) - : audqt::TreeView(parent), m_playlist(playlist), - model(new PlaylistModel(this, playlist)), - proxyModel(new PlaylistProxyModel(this, playlist)) -{ - /* setting up filtering model */ - proxyModel->setSourceModel(model); - - inUpdate = true; /* prevents changing focused row */ - setModel(proxyModel); - inUpdate = false; - - auto header = new PlaylistHeader(this); - setHeader(header); - /* this has to come after setHeader() to take effect */ - header->setSectionsClickable(true); - - setAllColumnsShowFocus(true); - setAlternatingRowColors(true); - setAttribute(Qt::WA_MacShowFocusRect, false); - setUniformRowHeights(true); - setFrameShape(QFrame::NoFrame); - setSelectionMode(ExtendedSelection); - setDragDropMode(DragDrop); - setMouseTracking(true); - - connect(this, &QTreeView::activated, this, &PlaylistWidget::activate); - - updateSettings(); - header->updateColumns(); - - /* get initial selection and focus from core */ - inUpdate = true; - updateSelection(0, 0); - inUpdate = false; -} - -PlaylistWidget::~PlaylistWidget() -{ - delete model; - delete proxyModel; -} - -QModelIndex PlaylistWidget::rowToIndex(int row) -{ - if (row < 0) - return QModelIndex(); - - return proxyModel->mapFromSource(model->index(row, firstVisibleColumn)); -} - -int PlaylistWidget::indexToRow(const QModelIndex & index) -{ - if (!index.isValid()) - return -1; - - return proxyModel->mapToSource(index).row(); -} - -QModelIndex PlaylistWidget::visibleIndexNear(int row) -{ - QModelIndex index = rowToIndex(row); - if (index.isValid()) - return index; - - int n_entries = m_playlist.n_entries(); - - for (int r = row + 1; r < n_entries; r++) - { - index = rowToIndex(r); - if (index.isValid()) - return index; - } - - for (int r = row - 1; r >= 0; r--) - { - index = rowToIndex(r); - if (index.isValid()) - return index; - } - - return index; -} - -void PlaylistWidget::activate(const QModelIndex & index) -{ - if (index.isValid()) - { - m_playlist.set_position(indexToRow(index)); - m_playlist.start_playback(); - } -} - -void PlaylistWidget::contextMenuEvent(QContextMenuEvent * event) -{ - if (contextMenu) - contextMenu->popup(event->globalPos()); -} - -void PlaylistWidget::keyPressEvent(QKeyEvent * event) -{ - auto CtrlShiftAlt = - Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier; - if (!(event->modifiers() & CtrlShiftAlt)) - { - switch (event->key()) - { - case Qt::Key_Right: - aud_drct_seek(aud_drct_get_time() + - aud_get_int("step_size") * 1000); - return; - case Qt::Key_Left: - aud_drct_seek(aud_drct_get_time() - - aud_get_int("step_size") * 1000); - return; - case Qt::Key_Space: - aud_drct_play_pause(); - return; - case Qt::Key_Delete: - //pl_remove_selected(); - return; - case Qt::Key_Z: - aud_drct_pl_prev(); - return; - case Qt::Key_X: - aud_drct_play(); - return; - case Qt::Key_C: - aud_drct_pause(); - return; - case Qt::Key_V: - aud_drct_stop(); - return; - case Qt::Key_B: - aud_drct_pl_next(); - return; - } - } - - audqt::TreeView::keyPressEvent(event); -} - -void PlaylistWidget::mouseMoveEvent(QMouseEvent * event) -{ - int row = indexToRow(indexAt(event->pos())); - - if (row < 0) - hidePopup(); - else if (aud_get_bool("show_filepopup_for_tuple") && m_popup_pos != row) - triggerPopup(row); - - audqt::TreeView::mouseMoveEvent(event); -} - -void PlaylistWidget::leaveEvent(QEvent * event) -{ - hidePopup(); - - audqt::TreeView::leaveEvent(event); -} - -/* Since Qt doesn't support both DragDrop and InternalMove at once, - * this hack is needed to set the drag icon to "move" for internal drags. */ -void PlaylistWidget::dragMoveEvent(QDragMoveEvent * event) -{ - if (event->source() == this) - event->setDropAction(Qt::MoveAction); - - audqt::TreeView::dragMoveEvent(event); - - if (event->source() == this) - event->setDropAction(Qt::MoveAction); -} - -void PlaylistWidget::dropEvent(QDropEvent * event) -{ - /* let Qt forward external drops to the PlaylistModel */ - if (event->source() != this) - return audqt::TreeView::dropEvent(event); - - int from = indexToRow(currentIndex()); - if (from < 0) - return; - - int to; - switch (dropIndicatorPosition()) - { - case AboveItem: - to = indexToRow(indexAt(QtCompat::pos(event))); - break; - case BelowItem: - to = indexToRow(indexAt(QtCompat::pos(event))) + 1; - break; - case OnViewport: - to = m_playlist.n_entries(); - break; - default: - return; - } - - /* Adjust the shift amount so that the selected entry closest to the - * destination ends up at the destination. */ - if (to > from) - to -= m_playlist.n_selected(from, to - from); - else - to += m_playlist.n_selected(to, from - to); - - m_playlist.shift_entries(from, to - from); - - event->acceptProposedAction(); -} - -void PlaylistWidget::currentChanged(const QModelIndex & current, - const QModelIndex & previous) -{ - audqt::TreeView::currentChanged(current, previous); - - if (!inUpdate) - m_playlist.set_focus(indexToRow(current)); -} - -void PlaylistWidget::selectionChanged(const QItemSelection & selected, - const QItemSelection & deselected) -{ - audqt::TreeView::selectionChanged(selected, deselected); - - if (!inUpdate) - { - for (const QModelIndex & idx : selected.indexes()) - m_playlist.select_entry(indexToRow(idx), true); - for (const QModelIndex & idx : deselected.indexes()) - m_playlist.select_entry(indexToRow(idx), false); - } -} - -/* returns true if the focus changed or the playlist scrolled */ -bool PlaylistWidget::scrollToCurrent(bool force) -{ - bool scrolled = false; - int entry = m_playlist.get_position(); - - if (entry >= 0 && (aud_get_bool("qtui", "autoscroll") || force)) - { - if (m_playlist.get_focus() != entry) - scrolled = true; - - m_playlist.select_all(false); - m_playlist.select_entry(entry, true); - m_playlist.set_focus(entry); - - auto index = rowToIndex(entry); - auto rect = visualRect(index); - - scrollTo(index); - - if (visualRect(index) != rect) - scrolled = true; - } - - return scrolled; -} - -void PlaylistWidget::updatePlaybackIndicator() -{ - if (currentPos >= 0) - model->entriesChanged(currentPos, 1); -} - -void PlaylistWidget::getSelectedRanges(int rowsBefore, int rowsAfter, - QItemSelection & selected, - QItemSelection & deselected) -{ - int entries = m_playlist.n_entries(); - - QItemSelection ranges[2]; - QModelIndex first, last; - bool prev = false; - - for (int row = rowsBefore; row < entries - rowsAfter; row++) - { - auto idx = rowToIndex(row); - if (!idx.isValid()) - continue; - - bool sel = m_playlist.entry_selected(row); - - if (sel != prev && first.isValid()) - ranges[prev].merge(QItemSelection(first, last), - QItemSelectionModel::Select); - - if (sel != prev || !first.isValid()) - first = idx; - - last = idx; - prev = sel; - } - - if (first.isValid()) - ranges[prev].merge(QItemSelection(first, last), - QItemSelectionModel::Select); - - selected = std::move(ranges[true]); - deselected = std::move(ranges[false]); -} - -void PlaylistWidget::updateSelection(int rowsBefore, int rowsAfter) -{ - QItemSelection selected, deselected; - getSelectedRanges(rowsBefore, rowsAfter, selected, deselected); - - auto sel = selectionModel(); - - if (!selected.isEmpty()) - sel->select(selected, sel->Select | sel->Rows); - if (!deselected.isEmpty()) - sel->select(deselected, sel->Deselect | sel->Rows); - - sel->setCurrentIndex(rowToIndex(m_playlist.get_focus()), sel->NoUpdate); -} - -void PlaylistWidget::playlistUpdate() -{ - auto update = m_playlist.update_detail(); - - if (update.level == Playlist::NoUpdate) - return; - - inUpdate = true; - - int entries = m_playlist.n_entries(); - int changed = entries - update.before - update.after; - - if (update.level == Playlist::Structure) - { - int old_entries = model->rowCount(); - int removed = old_entries - update.before - update.after; - - if (currentPos >= old_entries - update.after) - currentPos += entries - old_entries; - else if (currentPos >= update.before) - currentPos = -1; - - model->entriesRemoved(update.before, removed); - model->entriesAdded(update.before, changed); - } - else if (update.level == Playlist::Metadata || update.queue_changed) - model->entriesChanged(update.before, changed); - - if (update.queue_changed) - { - for (int i = m_playlist.n_queued(); i--;) - { - int entry = m_playlist.queue_get_entry(i); - if (entry < update.before || entry >= entries - update.after) - model->entriesChanged(entry, 1); - } - } - - int pos = m_playlist.get_position(); - - if (pos != currentPos) - { - if (currentPos >= 0) - model->entriesChanged(currentPos, 1); - if (pos >= 0) - model->entriesChanged(pos, 1); - - currentPos = pos; - } - - updateSelection(update.before, update.after); - - inUpdate = false; -} - -void PlaylistWidget::setFilter(const char * text) -{ - // Save the current focus before filtering - int focus = m_playlist.get_focus(); - - // Empty the model before updating the filter. This prevents Qt from - // performing a series of "rows added" or "rows deleted" updates, which can - // be very slow (worst case O(N^2) complexity) on a large playlist. - model->entriesRemoved(0, model->rowCount()); - - // Update the filter - proxyModel->setFilter(text); - - // Repopulate the model - model->entriesAdded(0, m_playlist.n_entries()); - - // If the previously focused row is no longer visible with the new filter, - // try to find a nearby one that is, and focus it. - auto index = visibleIndexNear(focus); - - if (index.isValid()) - { - focus = indexToRow(index); - m_playlist.set_focus(focus); - m_playlist.select_all(false); - m_playlist.select_entry(focus, true); - scrollTo(index); - } -} - -void PlaylistWidget::setFirstVisibleColumn(int col) -{ - inUpdate = true; - firstVisibleColumn = col; - - // make sure current and selected indexes point to a visible column - updateSelection(0, 0); - - inUpdate = false; -} - -void PlaylistWidget::moveFocus(int distance) -{ - int visibleRows = proxyModel->rowCount(); - if (!visibleRows) - return; - - int row = currentIndex().row(); - row = aud::clamp(row + distance, 0, visibleRows - 1); - setCurrentIndex(proxyModel->index(row, 0)); -} - -void PlaylistWidget::showPopup() -{ - audqt::infopopup_show(m_playlist, m_popup_pos); -} - -void PlaylistWidget::triggerPopup(int pos) -{ - audqt::infopopup_hide(); - - m_popup_pos = pos; - m_popup_timer.queue(aud_get_int("filepopup_delay") * 100, - [this]() { showPopup(); }); -} - -void PlaylistWidget::hidePopup() -{ - audqt::infopopup_hide(); - - m_popup_pos = -1; - m_popup_timer.stop(); -} - -void PlaylistWidget::updateSettings() -{ - setHeaderHidden(true); -} - -} diff --git a/src/moonstone/playlist.h b/src/moonstone/playlist.h deleted file mode 100644 index 60aa61dea5..0000000000 --- a/src/moonstone/playlist.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * playlist.h - * Copyright 2014 Michał Lipski - * Copyright 2020 Ariadne Conill - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions, and the following disclaimer in the documentation - * provided with the distribution. - * - * This software is provided "as is" and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising from - * the use of this software. - */ - -#ifndef PLAYLIST_H -#define PLAYLIST_H - -#include -#include -#include -#include - -class QContextMenuEvent; -class QMenu; - -namespace Moonstone { - -class PlaylistModel; -class PlaylistProxyModel; - -class PlaylistWidget : public audqt::TreeView -{ -public: - PlaylistWidget(QWidget * parent, Playlist playlist); - ~PlaylistWidget(); - - Playlist playlist() const { return m_playlist; } - - bool scrollToCurrent(bool force = false); - void updatePlaybackIndicator(); - void playlistUpdate(); - void setFilter(const char * text); - void setFirstVisibleColumn(int col); - void moveFocus(int distance); - - void setContextMenu(QMenu * menu) { contextMenu = menu; } - -private: - Playlist m_playlist; - PlaylistModel * model; - PlaylistProxyModel * proxyModel; - QMenu * contextMenu = nullptr; - - int currentPos = -1; - bool inUpdate = false; - int firstVisibleColumn = 0; - - int m_popup_pos = -1; - QueuedFunc m_popup_timer; - - QModelIndex rowToIndex(int row); - int indexToRow(const QModelIndex & index); - QModelIndex visibleIndexNear(int row); - - void getSelectedRanges(int rowsBefore, int rowsAfter, - QItemSelection & selected, - QItemSelection & deselected); - void updateSelection(int rowsBefore, int rowsAfter); - - void activate(const QModelIndex & index); - void contextMenuEvent(QContextMenuEvent * event); - void keyPressEvent(QKeyEvent * event); - void mouseMoveEvent(QMouseEvent * event); - void leaveEvent(QEvent * event); - void dragMoveEvent(QDragMoveEvent * event); - void dropEvent(QDropEvent * event); - void currentChanged(const QModelIndex & current, - const QModelIndex & previous); - void selectionChanged(const QItemSelection & selected, - const QItemSelection & deselected); - - void showPopup(); - void triggerPopup(int pos); - void hidePopup(); - - void updateSettings(); - - const HookReceiver hook1{ - "moonstone update playlist settings", this, &PlaylistWidget::updateSettings}; -}; - -} - -#endif diff --git a/src/moonstone/playlist_header.cc b/src/moonstone/playlist_header.cc deleted file mode 100644 index fe2199dd02..0000000000 --- a/src/moonstone/playlist_header.cc +++ /dev/null @@ -1,323 +0,0 @@ -/* - * playlist_header.cc - * Copyright 2017 John Lindgren and Eugene Paskevich - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions, and the following disclaimer in the documentation - * provided with the distribution. - * - * This software is provided "as is" and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising from - * the use of this software. - */ - -#include "playlist_header.h" -#include "playlist.h" -#include "playlist_model.h" - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace Moonstone { - -static const char * const s_col_keys[] = { - "playing", "number", "title", "artist", "year", "album", - "album-artist", "track", "genre", "queued", "length", "path", - "filename", "custom", "bitrate", "comment", "disc"}; - -static const int s_default_widths[] = { - 25, // now playing - 25, // entry number - 275, // title - 175, // artist - 50, // year - 175, // album - 175, // album artist - 75, // track - 100, // genre - 25, // queue position - 75, // length - 275, // path - 275, // filename - 275, // custom title - 75, // bitrate - 275, // comment - 75 // disc -}; - -static const Playlist::SortType s_sort_types[] = { - Playlist::n_sort_types, // now playing - Playlist::n_sort_types, // entry number - Playlist::Title, // title - Playlist::Artist, // artist - Playlist::Date, // year - Playlist::Album, // album - Playlist::AlbumArtist, // album artist - Playlist::Track, // track - Playlist::Genre, // genre - Playlist::n_sort_types, // queue position - Playlist::Length, // length - Playlist::Path, // path - Playlist::Filename, // file name - Playlist::FormattedTitle, // custom title - Playlist::n_sort_types, // bitrate - Playlist::Comment, // comment - Playlist::Disc // disc -}; - -static_assert(aud::n_elems(s_col_keys) == PlaylistModel::n_cols, - "update s_col_keys"); -static_assert(aud::n_elems(s_default_widths) == PlaylistModel::n_cols, - "update s_default_widths"); -static_assert(aud::n_elems(s_sort_types) == PlaylistModel::n_cols, - "update s_sort_types"); - -static Index s_cols; -static int s_col_widths[PlaylistModel::n_cols]; - -static void loadConfig(bool force = false) -{ - static bool loaded = false; - - if (loaded && !force) - return; - - auto columns = - str_list_to_index("playing number artist album title length", " "); - int n_columns = aud::min(columns.len(), (int)PlaylistModel::n_cols); - - s_cols.clear(); - - for (int c = 0; c < n_columns; c++) - { - int i = 0; - while (i < PlaylistModel::n_cols && strcmp(columns[c], s_col_keys[i])) - i++; - - if (i < PlaylistModel::n_cols) - s_cols.append(i); - } - - for (int i = 0; i < PlaylistModel::n_cols; i++) - s_col_widths[i] = audqt::to_native_dpi(s_default_widths[i]); - - loaded = true; -} - -static void saveConfig() -{ - Index index; - for (int col : s_cols) - index.append(String(s_col_keys[col])); - - int widths[PlaylistModel::n_cols]; - for (int i = 0; i < PlaylistModel::n_cols; i++) - widths[i] = audqt::to_portable_dpi(s_col_widths[i]); - - aud_set_str("qtui", "playlist_columns", index_to_str_list(index, " ")); - aud_set_str("qtui", "column_widths", - int_array_to_str(widths, PlaylistModel::n_cols)); -} - -PlaylistHeader::PlaylistHeader(PlaylistWidget * playlist) - : QHeaderView(Qt::Horizontal, playlist), m_playlist(playlist) -{ - loadConfig(); - - setSectionsMovable(true); - setStretchLastSection(true); - - connect(this, &QHeaderView::sectionClicked, this, - &PlaylistHeader::sectionClicked); - connect(this, &QHeaderView::sectionResized, this, - &PlaylistHeader::sectionResized); - connect(this, &QHeaderView::sectionMoved, this, - &PlaylistHeader::sectionMoved); -} - -static void toggleColumn(int col, bool on) -{ - int pos = s_cols.find(col); - - if (on) - { - if (pos >= 0) - return; - - s_cols.append(col); - } - else - { - if (pos < 0) - return; - - s_cols.remove(pos, 1); - } - - saveConfig(); - - // update all playlists - hook_call("qtui update playlist columns", nullptr); -} - -static void resetToDefaults() -{ - aud_set_str("qtui", "playlist_columns", "artist title"); - aud_set_str("qtui", "column_widths", ""); - - loadConfig(true); - - // update all playlists - hook_call("qtui update playlist columns", nullptr); -} - -void PlaylistHeader::contextMenuEvent(QContextMenuEvent * event) -{ - auto menu = new QMenu(this); - QAction * actions[PlaylistModel::n_cols]; - - for (int col = 0; col < PlaylistModel::n_cols; col++) - { - actions[col] = new QAction(_(PlaylistModel::labels[col]), menu); - actions[col]->setCheckable(true); - - connect(actions[col], &QAction::toggled, - [col](bool on) { toggleColumn(col, on); }); - - menu->addAction(actions[col]); - } - - for (int col : s_cols) - actions[col]->setChecked(true); - - auto sep = new QAction(menu); - sep->setSeparator(true); - menu->addAction(sep); - - auto reset = new QAction(_("Reset to Defaults"), menu); - connect(reset, &QAction::triggered, resetToDefaults); - menu->addAction(reset); - - menu->popup(event->globalPos()); -} - -void PlaylistHeader::updateColumns() -{ - m_inUpdate = true; - - int n_shown = s_cols.len(); - - // Due to QTBUG-33974, column #0 cannot be moved by the user. - // As a workaround, hide column #0 and start the real columns at #1. - // However, Qt will hide the header completely if no columns are visible. - // This is bad since the user can't right-click to add any columns again. - // To prevent this, show column #0 if no real columns are visible. - m_playlist->setColumnHidden(0, (n_shown > 0)); - - bool shown[PlaylistModel::n_cols]{}; - - for (int i = 0; i < n_shown; i++) - { - int col = s_cols[i]; - moveSection(visualIndex(1 + col), 1 + i); - shown[col] = true; - } - - // last column expands to fit, so size is not restored - int last = (n_shown > 0) ? s_cols[n_shown - 1] : -1; - - for (int col = 0; col < PlaylistModel::n_cols; col++) - { - if (col != last) - m_playlist->setColumnWidth(1 + col, s_col_widths[col]); - - m_playlist->setColumnHidden(1 + col, !shown[col]); - } - - // width of last column should be set to 0 initially, - // but doing so repeatedly causes flicker - if (last >= 0 && last != m_lastCol) - m_playlist->setColumnWidth(1 + last, 0); - - // this should come after all setColumnHidden() calls - m_playlist->setFirstVisibleColumn((n_shown > 0) ? 1 + s_cols[0] : 0); - - m_inUpdate = false; - m_lastCol = last; -} - -void PlaylistHeader::sectionClicked(int logicalIndex) -{ - int col = logicalIndex - 1; - if (col < 0 || col >= PlaylistModel::n_cols) - return; - - if (s_sort_types[col] != Playlist::n_sort_types) - m_playlist->playlist().sort_entries(s_sort_types[col]); -} - -void PlaylistHeader::sectionMoved(int logicalIndex, int oldVisualIndex, - int newVisualIndex) -{ - if (m_inUpdate) - return; - - int old_pos = oldVisualIndex - 1; - int new_pos = newVisualIndex - 1; - - if (old_pos < 0 || old_pos > s_cols.len() || new_pos < 0 || - new_pos > s_cols.len()) - return; - - int col = logicalIndex - 1; - if (col != s_cols[old_pos]) - return; - - s_cols.remove(old_pos, 1); - s_cols.insert(&col, new_pos, 1); - - saveConfig(); - - // update all the other playlists - hook_call("qtui update playlist columns", nullptr); -} - -void PlaylistHeader::sectionResized(int logicalIndex, int /*oldSize*/, - int newSize) -{ - if (m_inUpdate) - return; - - int col = logicalIndex - 1; - if (col < 0 || col >= PlaylistModel::n_cols) - return; - - // last column expands to fit, so size is not saved - int pos = s_cols.find(col); - if (pos < 0 || pos == s_cols.len() - 1) - return; - - s_col_widths[col] = newSize; - - saveConfig(); - - // update all the other playlists - hook_call("qtui update playlist columns", nullptr); -} - -} diff --git a/src/moonstone/playlist_header.h b/src/moonstone/playlist_header.h deleted file mode 100644 index 7ac3f840e7..0000000000 --- a/src/moonstone/playlist_header.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * playlist_header.h - * Copyright 2017 John Lindgren and Eugene Paskevich - * Copyright 2020 Ariadne Conill - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions, and the following disclaimer in the documentation - * provided with the distribution. - * - * This software is provided "as is" and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising from - * the use of this software. - */ - -#ifndef PLAYLIST_HEADER_H -#define PLAYLIST_HEADER_H - -#include -#include - -class QAction; -class QContextMenuEvent; -class QMenu; - -namespace Moonstone { - -class PlaylistWidget; - -class PlaylistHeader : public QHeaderView -{ -public: - PlaylistHeader(PlaylistWidget * parent); - - /* this should be called by the playlist after adding the header */ - void updateColumns(); - -private: - PlaylistWidget * m_playlist; - bool m_inUpdate = false; - int m_lastCol = -1; - - void sectionClicked(int logicalIndex); - void sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex); - void sectionResized(int logicalIndex, int /*oldSize*/, int newSize); - - void contextMenuEvent(QContextMenuEvent * event); - - const HookReceiver hook1{ - "qtui update playlist columns", this, &PlaylistHeader::updateColumns}; -}; - -} - -#endif // PLAYLIST_HEADER_H diff --git a/src/moonstone/playlist_model.cc b/src/moonstone/playlist_model.cc deleted file mode 100644 index 95d2ec4f74..0000000000 --- a/src/moonstone/playlist_model.cc +++ /dev/null @@ -1,329 +0,0 @@ -/* - * playlist_model.cc - * Copyright 2014 Michał Lipski - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions, and the following disclaimer in the documentation - * provided with the distribution. - * - * This software is provided "as is" and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising from - * the use of this software. - */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "playlist_model.h" - -#define ICON_SIZE 16 - -namespace Moonstone { - -const char * const PlaylistModel::labels[] = { - N_("Now Playing"), N_("Entry Number"), N_("Title"), - N_("Artist"), N_("Year"), N_("Album"), - N_("Album Artist"), N_("Track"), N_("Genre"), - N_("Queue Position"), N_("Length"), N_("File Path"), - N_("File Name"), N_("Custom Title"), N_("Bitrate"), - N_("Comment"), N_("Disc")}; - -static const Tuple::Field s_fields[] = { - Tuple::Invalid, Tuple::Invalid, Tuple::Title, Tuple::Artist, - Tuple::Year, Tuple::Album, Tuple::AlbumArtist, Tuple::Track, - Tuple::Genre, Tuple::Invalid, Tuple::Length, Tuple::Path, - Tuple::Basename, Tuple::FormattedTitle, Tuple::Bitrate, Tuple::Comment, - Tuple::Disc}; - -static_assert(aud::n_elems(PlaylistModel::labels) == PlaylistModel::n_cols, - "update PlaylistModel::labels"); -static_assert(aud::n_elems(s_fields) == PlaylistModel::n_cols, - "update s_fields"); - -static inline QPixmap get_icon(const char * name) -{ - return QIcon::fromTheme(name).pixmap(audqt::to_native_dpi(ICON_SIZE)); -} - -PlaylistModel::PlaylistModel(QObject * parent, Playlist playlist) - : QAbstractListModel(parent), m_playlist(playlist), - m_rows(playlist.n_entries()) -{ -} - -int PlaylistModel::rowCount(const QModelIndex & parent) const { return m_rows; } - -int PlaylistModel::columnCount(const QModelIndex & parent) const -{ - return 1 + n_cols; -} - -QVariant PlaylistModel::alignment(int col) const -{ - switch (col) - { - case NowPlaying: - return Qt::AlignCenter; - case Length: - return static_cast(Qt::AlignRight | - Qt::AlignVCenter); - default: - return static_cast(Qt::AlignLeft | - Qt::AlignVCenter); - } -} - -QVariant PlaylistModel::data(const QModelIndex & index, int role) const -{ - int col = index.column() - 1; - if (col < 0 || col >= n_cols) - return QVariant(); - - Tuple tuple; - int val = -1; - - switch (role) - { - case Qt::DisplayRole: - if (s_fields[col] != Tuple::Invalid) - { - tuple = m_playlist.entry_tuple(index.row(), Playlist::NoWait); - - switch (tuple.get_value_type(s_fields[col])) - { - case Tuple::Empty: - return QVariant(); - case Tuple::String: - return QString(tuple.get_str(s_fields[col])); - case Tuple::Int: - val = tuple.get_int(s_fields[col]); - break; - } - } - - switch (col) - { - case NowPlaying: - return QVariant(); - case EntryNumber: - return QString("%1").arg(index.row() + 1); - case QueuePos: - return queuePos(index.row()); - case Length: - return QString(str_format_time(val)); - case Bitrate: - return QString("%1 kbit/s").arg(val); - default: - return QString("%1").arg(val); - } - - case Qt::TextAlignmentRole: - return alignment(col); - - case Qt::DecorationRole: - if (col == NowPlaying && index.row() == m_playlist.get_position()) - { - const char * icon_name = "media-playback-stop"; - - if (m_playlist == Playlist::playing_playlist()) - icon_name = aud_drct_get_paused() ? "media-playback-pause" - : "media-playback-start"; - - return get_icon(icon_name); - } - else if (col == NowPlaying && index.row() == 0) - { - /* put a blank pixmap in the top row for size calculations */ - QPixmap blank(ICON_SIZE, ICON_SIZE); - blank.fill(Qt::transparent); - return blank; - } - break; - } - return QVariant(); -} - -QVariant PlaylistModel::headerData(int section, Qt::Orientation orientation, - int role) const -{ - if (orientation != Qt::Horizontal) - return QVariant(); - - int col = section - 1; - if (col < 0 || col >= n_cols) - return QVariant(); - - switch (role) - { - case Qt::DisplayRole: - switch (col) - { - case NowPlaying: - case EntryNumber: - case QueuePos: - return QVariant(); - } - - return QString(_(labels[col])); - - case Qt::TextAlignmentRole: - return alignment(col); - - default: - return QVariant(); - } -} - -Qt::DropActions PlaylistModel::supportedDropActions() const -{ - return Qt::CopyAction | Qt::MoveAction; -} - -Qt::ItemFlags PlaylistModel::flags(const QModelIndex & index) const -{ - if (index.isValid()) - return Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled; - else - return Qt::ItemIsSelectable | Qt::ItemIsDropEnabled | Qt::ItemIsEnabled; -} - -QStringList PlaylistModel::mimeTypes() const -{ - return QStringList("text/uri-list"); -} - -QMimeData * PlaylistModel::mimeData(const QModelIndexList & indexes) const -{ - /* we assume that contains the selected entries */ - m_playlist.cache_selected(); - - QList urls; - int prev = -1; - - for (auto & index : indexes) - { - int row = index.row(); - if (row != prev) /* skip multiple cells in same row */ - { - urls.append(QString(m_playlist.entry_filename(row))); - prev = row; - } - } - - auto data = new QMimeData; - data->setUrls(urls); - return data; -} - -bool PlaylistModel::dropMimeData(const QMimeData * data, Qt::DropAction action, - int row, int column, - const QModelIndex & parent) -{ - if (action != Qt::CopyAction || !data->hasUrls()) - return false; - - Index items; - for (auto & url : data->urls()) - items.append(String(url.toEncoded())); - - m_playlist.insert_items(row, std::move(items), false); - return true; -} - -void PlaylistModel::entriesAdded(int row, int count) -{ - if (count < 1) - return; - - int last = row + count - 1; - beginInsertRows(QModelIndex(), row, last); - m_rows += count; - endInsertRows(); -} - -void PlaylistModel::entriesRemoved(int row, int count) -{ - if (count < 1) - return; - - int last = row + count - 1; - beginRemoveRows(QModelIndex(), row, last); - m_rows -= count; - endRemoveRows(); -} - -void PlaylistModel::entriesChanged(int row, int count) -{ - if (count < 1) - return; - - int bottom = row + count - 1; - auto topLeft = createIndex(row, 0); - auto bottomRight = createIndex(bottom, columnCount() - 1); - emit dataChanged(topLeft, bottomRight); -} - -QString PlaylistModel::queuePos(int row) const -{ - int at = m_playlist.queue_find_entry(row); - if (at < 0) - return QString(); - else - return QString("#%1").arg(at + 1); -} - -/* ---------------------------------- */ - -void PlaylistProxyModel::setFilter(const char * filter) -{ - m_searchTerms = str_list_to_index(filter, " "); - invalidateFilter(); -} - -bool PlaylistProxyModel::filterAcceptsRow(int source_row, - const QModelIndex &) const -{ - if (!m_searchTerms.len()) - return true; - - Tuple tuple = m_playlist.entry_tuple(source_row); - - String strings[] = {tuple.get_str(Tuple::Title), - tuple.get_str(Tuple::Artist), - tuple.get_str(Tuple::Album)}; - - for (auto & term : m_searchTerms) - { - bool found = false; - - for (auto & s : strings) - { - if (s && strstr_nocase_utf8(s, term)) - { - found = true; - break; - } - } - - if (!found) - return false; - } - - return true; -} - -} diff --git a/src/moonstone/playlist_model.h b/src/moonstone/playlist_model.h deleted file mode 100644 index 7dedadc7e1..0000000000 --- a/src/moonstone/playlist_model.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * playlist_model.h - * Copyright 2014 Michał Lipski - * Copyright 2020 Ariadne Conill - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions, and the following disclaimer in the documentation - * provided with the distribution. - * - * This software is provided "as is" and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising from - * the use of this software. - */ - -#ifndef PLAYLIST_MODEL_H -#define PLAYLIST_MODEL_H - -#include -#include - -#include - -namespace Moonstone { - -class PlaylistModel : public QAbstractListModel -{ -public: - enum - { - NowPlaying, - EntryNumber, - Title, - Artist, - Year, - Album, - AlbumArtist, - Track, - Genre, - QueuePos, - Length, - Path, - Filename, - CustomTitle, - Bitrate, - Comment, - Disc, - n_cols - }; - - static const char * const labels[]; - - PlaylistModel(QObject * parent, Playlist playlist); - - int rowCount(const QModelIndex & parent = QModelIndex()) const; - int columnCount(const QModelIndex & parent = QModelIndex()) const; - QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; - QVariant headerData(int section, Qt::Orientation orientation, - int role = Qt::DisplayRole) const; - - Qt::DropActions supportedDropActions() const; - Qt::ItemFlags flags(const QModelIndex & index) const; - - QStringList mimeTypes() const; - QMimeData * mimeData(const QModelIndexList & indexes) const; - bool dropMimeData(const QMimeData * data, Qt::DropAction action, int row, - int column, const QModelIndex & parent); - - void entriesAdded(int row, int count); - void entriesRemoved(int row, int count); - void entriesChanged(int row, int count); - -private: - Playlist m_playlist; - int m_rows; - - QVariant alignment(int col) const; - QString queuePos(int row) const; -}; - -class PlaylistProxyModel : public QSortFilterProxyModel -{ -public: - PlaylistProxyModel(QObject * parent, Playlist playlist) - : QSortFilterProxyModel(parent), m_playlist(playlist) - { - } - - void setFilter(const char * filter); - -private: - bool filterAcceptsRow(int source_row, const QModelIndex &) const; - - Playlist m_playlist; - Index m_searchTerms; -}; - -} - -#endif diff --git a/src/moonstone/playlist_selection.cc b/src/moonstone/playlist_selection.cc deleted file mode 100644 index 58a796c50a..0000000000 --- a/src/moonstone/playlist_selection.cc +++ /dev/null @@ -1,196 +0,0 @@ -/* - * playlist_selection.cc - * Copyright 2020 Ariadne Conill - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions, and the following disclaimer in the documentation - * provided with the distribution. - * - * This software is provided "as is" and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising from - * the use of this software. - */ - -#include "playlist_selection.h" - -#include "../ui-common/qt-compat.h" - -namespace Moonstone { - -QVariant PlaylistsModel::data (const QModelIndex & index, int role) const -{ - switch (role) - { - case Qt::DisplayRole: - { - auto list = Playlist::by_index (index.row ()); - switch (index.column ()) - { - case ColumnTitle: - return QString (list.get_title ()); - case ColumnEntries: - return list.n_entries (); - } - } - break; - - case Qt::FontRole: - if (index.row () == m_playing) - return m_bold; - break; - - case Qt::TextAlignmentRole: - if (index.column () == ColumnEntries) - return Qt::AlignRight; - break; - } - - return QVariant (); -} - -QVariant PlaylistsModel::headerData (int section, Qt::Orientation orientation, int role) const -{ - if (orientation == Qt::Horizontal && role == Qt::DisplayRole) - { - switch (section) - { - case ColumnTitle: - return QString (_("Title")); - case ColumnEntries: - return QString (_("Entries")); - } - } - - return QVariant (); -} - -void PlaylistsModel::update_rows (int row, int count) -{ - if (count < 1) - return; - - auto topLeft = createIndex (row, 0); - auto bottomRight = createIndex (row + count - 1, NColumns - 1); - emit dataChanged (topLeft, bottomRight); -} - -void PlaylistsModel::update_playing () -{ - int playing = Playlist::playing_playlist ().index (); - - if (playing != m_playing) - { - if (m_playing >= 0) - update_rows (m_playing, 1); - if (playing >= 0) - update_rows (playing, 1); - - m_playing = playing; - } -} - -void PlaylistsModel::update (const Playlist::UpdateLevel level) -{ - int rows = Playlist::n_playlists (); - - if (level == Playlist::Structure) - { - if (rows < m_rows) - { - beginRemoveRows (QModelIndex (), rows, m_rows - 1); - m_rows = rows; - endRemoveRows (); - } - else if (rows > m_rows) - { - beginInsertRows (QModelIndex (), m_rows, rows - 1); - m_rows = rows; - endInsertRows (); - } - } - - if (level >= Playlist::Metadata) - { - update_rows (0, m_rows); - m_playing = Playlist::playing_playlist ().index (); - } - else - update_playing (); -} - -PlaylistsView::PlaylistsView () -{ - m_model.setFont (font ()); - - m_in_update ++; - setModel (& m_model); - update_sel (); - m_in_update --; - - auto hdr = header (); - hdr->setStretchLastSection (false); - hdr->setSectionResizeMode (PlaylistsModel::ColumnTitle, QHeaderView::Stretch); - hdr->setSectionResizeMode (PlaylistsModel::ColumnEntries, QHeaderView::Interactive); - hdr->resizeSection (PlaylistsModel::ColumnEntries, audqt::to_native_dpi (64)); - - setDragDropMode (InternalMove); - setFrameShape (QFrame::NoFrame); - setIndentation (0); - setHeaderHidden (true); - - connect (this, &QTreeView::activated, this, &PlaylistsView::activate); -} - -void PlaylistsView::currentChanged (const QModelIndex & current, const QModelIndex & previous) -{ - audqt::TreeView::currentChanged (current, previous); - if (! m_in_update) - Playlist::by_index (current.row ()).activate (); -} - -void PlaylistsView::dropEvent (QDropEvent * event) -{ - if (event->source () != this || event->proposedAction () != Qt::MoveAction) - return; - - int from = currentIndex ().row (); - if (from < 0) - return; - - int to; - switch (dropIndicatorPosition ()) - { - case AboveItem: to = indexAt (QtCompat::pos(event)).row (); break; - case BelowItem: to = indexAt (QtCompat::pos(event)).row () + 1; break; - case OnViewport: to = Playlist::n_playlists (); break; - default: return; - } - - Playlist::reorder_playlists (from, (to > from) ? to - 1 : to, 1); - event->acceptProposedAction (); -} - -void PlaylistsView::update (Playlist::UpdateLevel level) -{ - m_in_update ++; - m_model.update (level); - update_sel (); - m_in_update --; -} - -void PlaylistsView::update_sel () -{ - m_in_update ++; - auto sel = selectionModel (); - auto current = m_model.index (Playlist::active_playlist ().index (), 0); - sel->setCurrentIndex (current, sel->ClearAndSelect | sel->Rows); - m_in_update --; -} - -} diff --git a/src/moonstone/playlist_selection.h b/src/moonstone/playlist_selection.h deleted file mode 100644 index 1170857062..0000000000 --- a/src/moonstone/playlist_selection.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * playlist_selection.h - * Copyright 2015 John Lindgren - * Copyright 2020 Ariadne Conill - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions, and the following disclaimer in the documentation - * provided with the distribution. - * - * This software is provided "as is" and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising from - * the use of this software. - */ - -#ifndef PLAYLIST_SELECTION_H - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -namespace Moonstone { - -class PlaylistsModel : public QAbstractListModel -{ -public: - enum { - ColumnTitle, - ColumnEntries, - NColumns - }; - - PlaylistsModel () : - m_rows (Playlist::n_playlists ()), - m_playing (Playlist::playing_playlist ().index ()) - { - } - - void setFont (const QFont & font) - { - m_bold = font; - m_bold.setBold (true); - - if (m_playing >= 0) - update_rows (m_playing, 1); - } - - void update (Playlist::UpdateLevel level); - -protected: - int rowCount (const QModelIndex & parent) const { return m_rows; } - int columnCount (const QModelIndex & parent) const { return NColumns; } - - Qt::DropActions supportedDropActions () const { return Qt::MoveAction; } - - Qt::ItemFlags flags (const QModelIndex & index) const - { - if (index.isValid ()) - return Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled; - else - return Qt::ItemIsSelectable | Qt::ItemIsDropEnabled | Qt::ItemIsEnabled; - } - - QVariant data (const QModelIndex & index, int role) const; - QVariant headerData (int section, Qt::Orientation orientation, int role) const; - -private: - void update_rows (int row, int count); - void update_playing (); - - const HookReceiver - activate_hook {"playlist set playing", this, & PlaylistsModel::update_playing}; - - int m_rows, m_playing; - QFont m_bold; -}; - -class PlaylistsView : public audqt::TreeView -{ -public: - PlaylistsView (); - -protected: - void changeEvent (QEvent * event) override - { - if (event->type () == QEvent::FontChange) - m_model.setFont (font ()); - - audqt::TreeView::changeEvent (event); - } - - void currentChanged (const QModelIndex & current, const QModelIndex & previous) override; - void dropEvent (QDropEvent * event) override; - -private: - PlaylistsModel m_model; - - void update (Playlist::UpdateLevel level); - void update_sel (); - - void activate (const QModelIndex & index) - { - if (index.isValid ()) - Playlist::by_index (index.row ()).start_playback (); - } - - const HookReceiver - update_hook {"playlist update", this, & PlaylistsView::update}; - const HookReceiver - activate_hook {"playlist activate", this, & PlaylistsView::update_sel}; - - int m_in_update = 0; -}; - -} - -#endif diff --git a/src/moonstone/playlist_tabs.cc b/src/moonstone/playlist_tabs.cc deleted file mode 100644 index 09d37b117b..0000000000 --- a/src/moonstone/playlist_tabs.cc +++ /dev/null @@ -1,375 +0,0 @@ -/* - * playlist_tabs.cc - * Copyright 2014 Michał Lipski - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions, and the following disclaimer in the documentation - * provided with the distribution. - * - * This software is provided "as is" and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising from - * the use of this software. - */ - -#include "playlist_tabs.h" -#include "playlist.h" - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -namespace Moonstone { - -class LayoutWidget : public QWidget -{ -public: - LayoutWidget(QWidget * parent, Playlist playlist, QMenu * contextMenu); - - PlaylistWidget * playlistWidget() const { return m_playlistWidget; } - -private: - PlaylistWidget * m_playlistWidget; -}; - -LayoutWidget::LayoutWidget(QWidget * parent, Playlist playlist, - QMenu * contextMenu) - : QWidget(parent), m_playlistWidget(new PlaylistWidget(this, playlist)) -{ - auto layout = audqt::make_vbox(this, 0); - layout->addWidget(m_playlistWidget); - - m_playlistWidget->setContextMenu(contextMenu); -} - -/* --------------------------------- */ - -PlaylistTabs::PlaylistTabs(QWidget * parent) - : QTabWidget(parent), - m_pl_menu(new QMenu(this)), - m_tabbar(new PlaylistTabBar(this)) -{ - installEventFilter(this); - - // set up tab bar - m_tabbar->setFocusPolicy(Qt::NoFocus); - setTabBar(m_tabbar); - - addRemovePlaylists(); - m_tabbar->updateTitles(); - m_tabbar->updateIcons(); - setCurrentIndex(Playlist::active_playlist().index()); - - connect(this, &QTabWidget::currentChanged, this, - &PlaylistTabs::currentChangedTrigger); -} - -PlaylistWidget * PlaylistTabs::currentPlaylistWidget() const -{ - return ((LayoutWidget *)currentWidget())->playlistWidget(); -} - -PlaylistWidget * PlaylistTabs::playlistWidget(int idx) const -{ - auto w = (LayoutWidget *)widget(idx); - return w ? w->playlistWidget() : nullptr; -} - -void PlaylistTabs::addRemovePlaylists() -{ - int tabs = count(); - int playlists = Playlist::n_playlists(); - - for (int i = 0; i < tabs; i++) - { - auto w = (LayoutWidget *)widget(i); - int list_idx = w->playlistWidget()->playlist().index(); - - if (list_idx < 0) - { - removeTab(i); - delete w; - tabs--; - i--; - } - else if (list_idx != i) - { - bool found = false; - - for (int j = i + 1; j < tabs; j++) - { - w = (LayoutWidget *)widget(j); - list_idx = w->playlistWidget()->playlist().index(); - - if (list_idx == i) - { - removeTab(j); - insertTab(i, w, QString()); - found = true; - break; - } - } - - if (!found) - { - insertTab( - i, new LayoutWidget(this, Playlist::by_index(i), m_pl_menu), - QString()); - tabs++; - } - } - } - - while (tabs < playlists) - { - addTab(new LayoutWidget(this, Playlist::by_index(tabs), m_pl_menu), - QString()); - tabs++; - } -} - -void PlaylistTabs::currentChangedTrigger(int idx) -{ - Playlist::by_index(idx).activate(); -} - -bool PlaylistTabs::eventFilter(QObject * obj, QEvent * e) -{ - if (e->type() == QEvent::KeyPress) - { - QKeyEvent * ke = (QKeyEvent *)e; - - if (ke->key() == Qt::Key_Escape) - return m_tabbar->cancelRename(); - } - - return QTabWidget::eventFilter(obj, e); -} - -void PlaylistTabs::renameCurrent() -{ - auto playlist = currentPlaylistWidget()->playlist(); - - if (m_tabbar->isVisible()) - m_tabbar->startRename(playlist); - else - audqt::playlist_show_rename(playlist); -} - -void PlaylistTabs::playlist_activate_cb() -{ - setCurrentIndex(Playlist::active_playlist().index()); - m_tabbar->cancelRename(); -} - -void PlaylistTabs::playlist_update_cb(Playlist::UpdateLevel global_level) -{ - if (global_level == Playlist::Structure) - addRemovePlaylists(); - if (global_level >= Playlist::Metadata) - m_tabbar->updateTitles(); - - for (int i = 0; i < count(); i++) - playlistWidget(i)->playlistUpdate(); - - setCurrentIndex(Playlist::active_playlist().index()); -} - -void PlaylistTabs::playlist_position_cb(Playlist list) -{ - auto widget = playlistWidget(list.index()); - if (widget) - widget->scrollToCurrent(); -} - -PlaylistTabBar::PlaylistTabBar(QWidget * parent) : QTabBar(parent) -{ - hide(); - - connect(this, &QTabBar::tabMoved, this, &PlaylistTabBar::tabMoved); - connect(this, &QTabBar::tabCloseRequested, [](int idx) { - audqt::playlist_confirm_delete(Playlist::by_index(idx)); - }); -} - -void PlaylistTabBar::updateTitles() -{ - int tabs = count(); - for (int i = 0; i < tabs; i++) - updateTabText(i); -} - -void PlaylistTabBar::updateIcons() -{ - QIcon icon; - int playing = Playlist::playing_playlist().index(); - if (playing >= 0) - icon = QIcon::fromTheme(aud_drct_get_paused() ? "media-playback-pause" - : "media-playback-start"); - - int tabs = count(); - for (int i = 0; i < tabs; i++) - { - /* hide icon when editing so it doesn't get shown on the wrong side */ - setTabIcon(i, (i == playing && !getTabEdit(i)) ? icon : QIcon()); - } -} - -void PlaylistTabBar::startRename(Playlist playlist) -{ - int idx = playlist.index(); - QLineEdit * edit = getTabEdit(idx); - - if (!edit) - { - edit = new QLineEdit((const char *)playlist.get_title()); - - connect(edit, &QLineEdit::returnPressed, [this, playlist, edit]() { - playlist.set_title(edit->text().toUtf8()); - cancelRename(); - }); - - setupTab(idx, edit, &m_leftbtn); - updateIcons(); - } - - edit->selectAll(); - edit->setFocus(); -} - -bool PlaylistTabBar::cancelRename() -{ - bool cancelled = false; - - for (int i = 0; i < count(); i++) - { - QLineEdit * edit = getTabEdit(i); - if (!edit) - continue; - - setupTab(i, m_leftbtn, nullptr); - m_leftbtn = nullptr; - cancelled = true; - updateIcons(); - } - - return cancelled; -} - -void PlaylistTabBar::mousePressEvent(QMouseEvent * e) -{ - if (e->button() == Qt::MiddleButton) - { - int index = tabAt(e->pos()); - if (index >= 0) - { - audqt::playlist_confirm_delete(Playlist::by_index(index)); - e->accept(); - } - } - - QTabBar::mousePressEvent(e); -} - -void PlaylistTabBar::mouseDoubleClickEvent(QMouseEvent * e) -{ - int idx = tabAt(e->pos()); - if (idx < 0 || e->button() != Qt::LeftButton) - return; - - Playlist::by_index(idx).start_playback(); -} - -void PlaylistTabBar::contextMenuEvent(QContextMenuEvent * e) -{ - int idx = tabAt(e->pos()); - if (idx < 0) - return; - - auto menu = new QMenu(this); - auto playlist = Playlist::by_index(idx); - - auto play_act = new QAction(QIcon::fromTheme("media-playback-start"), - audqt::translate_str(N_("_Play")), menu); - auto rename_act = - new QAction(QIcon::fromTheme("insert-text"), - audqt::translate_str(N_("_Rename ...")), menu); - auto remove_act = new QAction(QIcon::fromTheme("edit-delete"), - audqt::translate_str(N_("Remo_ve")), menu); - - QObject::connect(play_act, &QAction::triggered, - [playlist]() { playlist.start_playback(); }); - QObject::connect(rename_act, &QAction::triggered, [this, playlist]() { - if (playlist.exists()) - startRename(playlist); - }); - QObject::connect(remove_act, &QAction::triggered, [playlist]() { - if (playlist.exists()) - audqt::playlist_confirm_delete(playlist); - }); - - menu->addAction(play_act); - menu->addAction(rename_act); - menu->addAction(remove_act); - menu->setAttribute(Qt::WA_DeleteOnClose); - menu->popup(e->globalPos()); -} - -QLineEdit * PlaylistTabBar::getTabEdit(int idx) -{ - return dynamic_cast(tabButton(idx, QTabBar::LeftSide)); -} - -void PlaylistTabBar::updateTabText(int idx) -{ - QString title; - - if (!getTabEdit(idx)) - { - auto playlist = Playlist::by_index(idx); - - // escape ampersands for setTabText () - title = QString(playlist.get_title()).replace("&", "&&"); - - if (aud_get_bool("qtui", "entry_count_visible")) - title += QString(" (%1)").arg(playlist.n_entries()); - } - - setTabText(idx, title); -} - -void PlaylistTabBar::setupTab(int idx, QWidget * button, QWidget ** oldp) -{ - QWidget * old = tabButton(idx, QTabBar::LeftSide); - setTabButton(idx, QTabBar::LeftSide, button); - - if (oldp) - *oldp = old; - else - { - old->setParent(nullptr); - old->deleteLater(); - } - - updateTabText(idx); -} - -void PlaylistTabBar::tabMoved(int from, int to) -{ - Playlist::reorder_playlists(from, to, 1); -} - -} diff --git a/src/moonstone/playlist_tabs.h b/src/moonstone/playlist_tabs.h deleted file mode 100644 index 82592daa3d..0000000000 --- a/src/moonstone/playlist_tabs.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * playlist_tabs.h - * Copyright 2014 Michał Lipski - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions, and the following disclaimer in the documentation - * provided with the distribution. - * - * This software is provided "as is" and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising from - * the use of this software. - */ - -#ifndef PLAYLIST_TABS_H -#define PLAYLIST_TABS_H - -#include -#include - -#include -#include - -class QLineEdit; -class QMenu; - -namespace Moonstone { - -class PlaylistTabBar; -class PlaylistWidget; - -class PlaylistTabs : public QTabWidget -{ -public: - PlaylistTabs(QWidget * parent = nullptr); - - PlaylistWidget * currentPlaylistWidget() const; - PlaylistWidget * playlistWidget(int idx) const; - - void currentChangedTrigger(int idx); - void tabEditedTrigger(); - -protected: - bool eventFilter(QObject * obj, QEvent * e); - -private: - QMenu * m_pl_menu; - PlaylistTabBar * m_tabbar; - - void addRemovePlaylists(); - void renameCurrent(); - - void playlist_activate_cb(); - void playlist_update_cb(Playlist::UpdateLevel global_level); - void playlist_position_cb(Playlist list); - - const HookReceiver activate_hook{ - "playlist activate", this, &PlaylistTabs::playlist_activate_cb}; - const HookReceiver update_hook{ - "playlist update", this, &PlaylistTabs::playlist_update_cb}; - const HookReceiver position_hook{ - "playlist position", this, &PlaylistTabs::playlist_position_cb}; -}; - -class PlaylistTabBar : public QTabBar -{ -public: - PlaylistTabBar(QWidget * parent = nullptr); - - void updateTitles(); - void updateIcons(); - void startRename(Playlist playlist); - bool cancelRename(); - -protected: - void mousePressEvent(QMouseEvent * e) override; - void mouseDoubleClickEvent(QMouseEvent * e) override; - void contextMenuEvent(QContextMenuEvent * e) override; - -private: - QLineEdit * getTabEdit(int idx); - void updateTabText(int idx); - void setupTab(int idx, QWidget * button, QWidget ** oldp); - void tabMoved(int from, int to); - - const HookReceiver pause_hook{"playback pause", this, - &PlaylistTabBar::updateIcons}, - unpause_hook{"playback unpause", this, &PlaylistTabBar::updateIcons}, - set_playing_hook{"playlist set playing", this, - &PlaylistTabBar::updateIcons}; - - QWidget * m_leftbtn = nullptr; -}; - -} - -#endif diff --git a/src/moonstone/time_slider.cc b/src/moonstone/time_slider.cc deleted file mode 100644 index 710458e547..0000000000 --- a/src/moonstone/time_slider.cc +++ /dev/null @@ -1,144 +0,0 @@ -/* - * time_slider.cc - * Copyright 2014 John Lindgren - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions, and the following disclaimer in the documentation - * provided with the distribution. - * - * This software is provided "as is" and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising from - * the use of this software. - */ - -#include "time_slider.h" - -#include -#include -#include -#include - -#include -#include -#include - -TimeSliderLabel::TimeSliderLabel(QWidget * parent) : QLabel(parent) -{ - setStyleSheet("font-weight: bold"); -} -TimeSliderLabel::~TimeSliderLabel() {} - -void TimeSliderLabel::mouseDoubleClickEvent(QMouseEvent * event) -{ - if (event->button() == Qt::LeftButton) - { - aud_toggle_bool("qtui", "show_remaining_time"); - hook_call("qtui toggle remaining time", nullptr); - event->accept(); - } - - QLabel::mouseDoubleClickEvent(event); -} - -class TimeSliderStyle : public QProxyStyle -{ -public: - int styleHint(QStyle::StyleHint hint, const QStyleOption * option = nullptr, - const QWidget * widget = nullptr, - QStyleHintReturn * returnData = nullptr) const - { - int styleHint = - QProxyStyle::styleHint(hint, option, widget, returnData); - - if (hint == QStyle::SH_Slider_AbsoluteSetButtons) - styleHint |= Qt::LeftButton; - - return styleHint; - } -}; - -TimeSlider::TimeSlider(QWidget * parent) - : QSlider(Qt::Horizontal, parent), m_label(new TimeSliderLabel(parent)) -{ - setFocusPolicy(Qt::NoFocus); - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - setStyle(new TimeSliderStyle()); - - m_label->setContentsMargins(audqt::sizes.FourPt, 0, 0, 0); - m_label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::MinimumExpanding); - - connect(this, &QSlider::sliderMoved, this, &TimeSlider::moved); - connect(this, &QSlider::sliderPressed, this, &TimeSlider::pressed); - connect(this, &QSlider::sliderReleased, this, &TimeSlider::released); - - start_stop(); -} - -TimeSlider::~TimeSlider() {} - -void TimeSlider::set_label(int time, int length) -{ - QString text; - - if (length >= 0) - if (aud_get_bool("qtui", "show_remaining_time")) - text = str_concat({str_format_time(time - length), " / ", - str_format_time(length)}); - else - text = str_concat( - {str_format_time(time), " / ", str_format_time(length)}); - else - text = str_format_time(time); - - m_label->setText(text); -} - -void TimeSlider::start_stop() -{ - bool ready = aud_drct_get_ready(); - bool paused = aud_drct_get_paused(); - - setEnabled(ready); - m_label->setEnabled(ready); - - update(); - - if (ready && !paused) - m_timer.start(); - else - m_timer.stop(); -} - -void TimeSlider::update() -{ - if (aud_drct_get_ready()) - { - if (!isSliderDown()) - { - int time = aud_drct_get_time(); - int length = aud_drct_get_length(); - - setRange(0, length); - setValue(time); - - set_label(time, length); - } - } - else - { - setRange(0, 0); - set_label(0, 0); - } -} - -void TimeSlider::moved(int value) { set_label(value, aud_drct_get_length()); } - -void TimeSlider::pressed() { set_label(value(), aud_drct_get_length()); } - -void TimeSlider::released() { aud_drct_seek(value()); } diff --git a/src/moonstone/time_slider.h b/src/moonstone/time_slider.h deleted file mode 100644 index 4abbc7c12d..0000000000 --- a/src/moonstone/time_slider.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * time_slider.h - * Copyright 2014 John Lindgren - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions, and the following disclaimer in the documentation - * provided with the distribution. - * - * This software is provided "as is" and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising from - * the use of this software. - */ - -#ifndef TIME_SLIDER_H -#define TIME_SLIDER_H - -#include -#include - -#include - -class QMouseEvent; - -class TimeSliderLabel : public QLabel -{ -public: - TimeSliderLabel(QWidget * parent); - ~TimeSliderLabel(); - -protected: - void mouseDoubleClickEvent(QMouseEvent * event); -}; - -class TimeSlider : public QSlider -{ -public: - TimeSlider(QWidget * parent); - ~TimeSlider(); - - TimeSliderLabel * label() { return m_label; } - -private: - void set_label(int time, int length); - - void start_stop(); - void update(); - void moved(int value); - void pressed(); - void released(); - - TimeSliderLabel * m_label; - - const Timer m_timer{TimerRate::Hz4, this, &TimeSlider::update}; - - const HookReceiver hook1{"playback ready", this, - &TimeSlider::start_stop}, - hook2{"playback pause", this, &TimeSlider::start_stop}, - hook3{"playback unpause", this, &TimeSlider::start_stop}, - hook4{"playback seek", this, &TimeSlider::update}, - hook5{"playback stop", this, &TimeSlider::start_stop}, - hook6{"qtui toggle remaining time", this, &TimeSlider::start_stop}; -}; - -#endif diff --git a/src/moonstone/tool_bar.cc b/src/moonstone/tool_bar.cc deleted file mode 100644 index 916430c6f3..0000000000 --- a/src/moonstone/tool_bar.cc +++ /dev/null @@ -1,69 +0,0 @@ -/* - * tool_bar.cc - * Copyright 2014, 2020 Ariadne Conill - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions, and the following disclaimer in the documentation - * provided with the distribution. - * - * This software is provided "as is" and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising from - * the use of this software. - */ - -#include "tool_bar.h" - -#include -#include - -#include -#include - -namespace Moonstone { - -ToolBar::ToolBar(QWidget * parent, ArrayRef items) - : QToolBar(parent) -{ - setContextMenuPolicy(Qt::PreventContextMenu); - setMovable(false); - - for (const ToolBarItem & item : items) - { - if (item.widget) - addWidget(item.widget); - else if (item.sep) - addSeparator(); - else if (item.icon_name) - { - QAction * a = new QAction(QIcon::fromTheme(item.icon_name), - audqt::translate_str(item.name), this); - - if (item.tooltip_text) - a->setToolTip(audqt::translate_str(item.tooltip_text)); - - if (item.callback) - connect(a, &QAction::triggered, item.callback); - - if (item.toggled) - { - a->setCheckable(true); - connect(a, &QAction::toggled, item.toggled); - } - - addAction(a); - - if (item.action_ptr) - *item.action_ptr = a; - } - } - - setStyleSheet("QToolBar { background: rgba(255, 255, 255, 0.6); }"); -} - -} diff --git a/src/moonstone/tool_bar.h b/src/moonstone/tool_bar.h deleted file mode 100644 index 6575308cff..0000000000 --- a/src/moonstone/tool_bar.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * tool_bar.h - * Copyright 2014, 2020 Ariadne Conill - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions, and the following disclaimer in the documentation - * provided with the distribution. - * - * This software is provided "as is" and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising from - * the use of this software. - */ - -#ifndef TOOL_BAR_H -#define TOOL_BAR_H - -#include - -#include - -namespace Moonstone { - -struct ToolBarItem -{ - const char * icon_name; - const char * name; - const char * tooltip_text; - - void (*callback)(); - void (*toggled)(bool on); - - QWidget * widget; - - bool sep; - - QAction ** action_ptr; -}; - -class ToolBar : public QToolBar -{ -public: - ToolBar(QWidget * parent, ArrayRef items); -}; - -constexpr ToolBarItem ToolBarAction(const char * icon_name, const char * name, - const char * tooltip_text, - void (*callback)(), - QAction ** action_ptr = nullptr) -{ - return {icon_name, name, tooltip_text, callback, - nullptr, nullptr, false, action_ptr}; -} - -constexpr ToolBarItem ToolBarAction(const char * icon_name, const char * name, - const char * tooltip_text, - void (*toggled)(bool), - QAction ** action_ptr = nullptr) -{ - return {icon_name, name, tooltip_text, nullptr, - toggled, nullptr, false, action_ptr}; -} - -constexpr ToolBarItem ToolBarCustom(QWidget * item) -{ - return {nullptr, nullptr, nullptr, nullptr, nullptr, item}; -} - -constexpr ToolBarItem ToolBarSeparator() -{ - return {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, true}; -} - -} - -#endif