/*
*  qm_player.cpp
*  QUIMUP main player window
*  © 2008-2024 Johan Spee
*  SPDX-License-Identifier: GPL-3.0-or-later
*/

#include "qm_player.h"
#include "qm_widget_ids.h"
#include "qm_modes_ids.h"

qm_Player::qm_Player(qm_Config *cfg)
{
    config = cfg;

    if (objectName().isEmpty())
        setObjectName("quimup player");

    browser_window = nullptr;
    settings_window = nullptr;
    the_trayicon = nullptr;
    mpd_cmd = nullptr;
    current_songinfo = nullptr;
    info_scroller = nullptr;
    lb_time = nullptr;
    pb_progress = nullptr;
    current_fonts = nullptr;

    // 1st
    set_themed_icons(config->set_dark_theme);

    // 2nd
    setup_widgets();

    // 3rd
    init_vars();
    retranslate();

    // 4th
    config_actions();

    // 5th connect MPD
    mpd_cmd->configure_connection();
    // connect and set status
    if (config->auto_connect)
    {
        printf ("Connecting, as configured\n");
        mpd_cmd->mpd_try_connect(true);
    }
    else
    {
        printf ("NOT Connecting, as configured\n");
        on_mpdconnect_sgnl(false, false);
    }

    // 6th show
    if (config->start_minimized)
        this->showMinimized();
    else
        this->show();

    window_W_max = this->width();
    window_H_max = this->height();

    if (config->player_maxmode)
    {
        window_H = window_H_max;
        setFixedSize(window_W_max, window_H);
    }
    else
    { // minimode
        window_H = window_H_min;
        center_widget->hide();
        setFixedSize(window_W_max, window_H);
        config->player_maxmode = false;
    }

    QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
    setSizePolicy(sizePolicy);

    this->setAttribute(Qt::WA_QuitOnClose, false);

    // trigger window-manager update: hide & show
    this->hide();
    if (!config->start_minimized)
    {
        this->show();
        this->activateWindow();
    }
}


void qm_Player::config_actions()
{
    config->tag_editor_installed   = is_app_installed(config->tag_editor);
    config->image_viewer_installed = is_app_installed(config->image_viewer);
    config->file_manager_installed = is_app_installed(config->file_manager);

    a_user1->setText(config->usract1_label);
    if (config->usract1_enabled)
        a_user1->setVisible(true);
    else
        a_user1->setVisible(false);

    a_user2->setText(config->usract2_label);
    if (config->usract2_enabled)
        a_user2->setVisible(true);
    else
        a_user2->setVisible(false);

    disenable_menu_items();

    // signal library to adjust menu
    browser_window->library_view->set_actions();
}

// from mpd_cmd
void qm_Player::on_mpdconnect_sgnl(bool isconnected, bool isremote)
{
    if (isconnected)
    {
        b_mpd_connected = true;
        b_remote_server = isremote;
        settings_window->set_connected(mpd_cmd, true, isremote);
        browser_window->set_connected(mpd_cmd, true);
        browser_window->playlist_view->set_connected(mpd_cmd, true);
        browser_window->library_view->set_connected(mpd_cmd, true);
        if (b_use_trayicon)
            the_trayicon->set_connected(mpd_cmd, true);
        set_status(0, "");
    }
    else
    {
        b_mpd_connected = false;
        b_remote_server = false;
        settings_window->set_connected(mpd_cmd, false, isremote);
        browser_window->set_connected(mpd_cmd, false);
        browser_window->playlist_view->set_connected(mpd_cmd, false);
        browser_window->library_view->set_connected(mpd_cmd, false);
        if (b_use_trayicon)
            the_trayicon->set_connected(mpd_cmd, false);
        set_status(-1, tr("not connected") );
    }

    // disenable_menu_items() in set_status()
}


void qm_Player::setup_widgets()
{
    if (objectName().isEmpty())
        setObjectName(QString::fromUtf8("qm_Player"));

    setWindowIcon(QPixmap(":/qm_main_icon"));
    setWindowTitle("Quimup");

    art_label_W = art_label_H = config->albumart_size;

    // width of info_scroller, pb_progress, center_widget
    // a minimum of 338 is required for the buttons and volume slider
    widget_W_setup = config->albumart_size + 138;
    if (widget_W_setup < 338)
        widget_W_setup = 338;

    main_widget = new QWidget(this);
    vbox_all = new QVBoxLayout(main_widget);
    setCentralWidget(main_widget);
    vbox_all_margin = 6,    // used to calculate window_H_min
    vboxall_space = 4,      // used to calculate window_H_min
    vbox_all->setContentsMargins(vbox_all_margin, vbox_all_margin, vbox_all_margin, vbox_all_margin);
    vbox_all->setSpacing(vboxall_space);

    main_display = new QLabel(); // used as QFrame
    main_display->setFixedSize(widget_W_setup,50);
    main_display->setFrameStyle(QFrame::NoFrame | QFrame::Plain);

    vbox_in_main_display = new QVBoxLayout(main_display);
    vbox_in_main_display->setContentsMargins(0,0,0,0);
    vbox_in_main_display->setSpacing(0);

    info_scroller = new qm_Scroller();
    info_scroller->setFixedSize(widget_W_setup,24);
    info_scroller->setAlignment(Qt::AlignCenter|Qt::AlignVCenter);
    vbox_in_main_display->addWidget(info_scroller);

    hbox_info = new QHBoxLayout();
    hbox_info->setContentsMargins(0,0,0,0);
    hbox_info->setAlignment(Qt::AlignLeft|Qt::AlignVCenter);
    hbox_info->setSpacing(0);
    vbox_in_main_display->addLayout(hbox_info);

    lb_extension = new QLabel();
    lb_extension->setContentsMargins(4,0,4,0);
    lb_extension->setWordWrap(false);
    hbox_info->addWidget(lb_extension);

    lb_kbps = new QLabel();
    lb_kbps->setContentsMargins(4,0,4,0);
    lb_kbps->setWordWrap(false);
    hbox_info->addWidget(lb_kbps);

    lb_kHz = new QLabel();
    lb_kHz->setContentsMargins(4,0,4,0);
    lb_kHz->setWordWrap(false);
    hbox_info->addWidget(lb_kHz);

    spacer_info = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Fixed);
    hbox_info->addItem(spacer_info);

    lb_time = new qm_clickLabel();
    lb_time->setContentsMargins(4,0,4,0);
    lb_time->setWordWrap(false);
    lb_time->setTextFormat(Qt::RichText);
    hbox_info->addWidget(lb_time);

    vbox_all->addWidget(main_display);

    pb_progress = new qm_clickProgressbar();
    pb_progress->setOrientation (Qt::Horizontal);
    pb_progress->setFixedSize(widget_W_setup,8);
    pb_progress->setMinimum(0);
    // pb_stylesheet is applied in set_colors(), %1 %2 take args
    pb_stylesheet = QString(
        "QSlider::groove:horizontal {"
        "margin: 0 0 0 0;"
        "border: 0px;"
        "height: 6px; }"

        "QSlider::sub-page:horizontal {"
        "background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 1, stop: 0 %1, stop: 1 %2);"
        "border: 1px solid #aaa;"
        "height: 4px; }"

        "QSlider::add-page:horizontal {"
        "background: %1;"
        "border: 1px solid #aaa;"
        "height: 4px; }"

        "QSlider::handle:Horizontal {"
        "width:3px;"
        "border-radius:0px;"
        "background: %2;"
        "margin: 1px 0px 1px 0px; }"
        );

    vbox_all->addWidget(pb_progress);

    spacer_main = new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding);
    vbox_all->addItem(spacer_main);

    center_widget = new QLabel();
    center_widget->setFixedSize(widget_W_setup, art_label_H);

    hbox_center = new QHBoxLayout(center_widget);
    hbox_center->setContentsMargins(0,0,0,0);
    hbox_center->setSpacing(0);
    hbox_center->setAlignment(Qt::AlignLeft|Qt::AlignTop);

    lb_albumart = new qm_clickLabel();
    lb_albumart->setFixedSize(art_label_W, art_label_H);
    lb_albumart->setScaledContents(false);
    lb_albumart->setAlignment(Qt::AlignCenter|Qt::AlignVCenter);
    lb_albumart->setFrameStyle(QFrame::NoFrame | QFrame::Plain);
    hbox_center->addWidget(lb_albumart);

    wg_album_info = new QWidget();
    hbox_center->addWidget(wg_album_info);

    vbox_album_info = new QVBoxLayout(wg_album_info);
    vbox_album_info->setContentsMargins(0,0,0,0);
    vbox_album_info->setSpacing(0);
    vbox_album_info->setAlignment(Qt::AlignLeft|Qt::AlignTop);
    vbox_album_info->setGeometry(QRect(0, 0, widget_W_setup - art_label_W, art_label_H));
    lb_album = new QLabel();
    lb_album->setWordWrap(true);
    lb_album->setContentsMargins(4,2,0,0);
    lb_album->setMinimumSize(widget_W_setup - art_label_W, 4);
    lb_album->setAlignment(Qt::AlignLeft);
    vbox_album_info->addWidget(lb_album);
    lb_year = new QLabel();
    lb_year->setWordWrap(true);
    lb_year->setContentsMargins(4,2,0,0);
    lb_year->setMinimumSize(widget_W_setup - art_label_W, 4);
    lb_year->setAlignment(Qt::AlignLeft);
    vbox_album_info->addWidget(lb_year);
    sa_comment = new QScrollArea();
    sa_comment->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    sa_comment->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    sa_comment->setFixedWidth(widget_W_setup - art_label_W);
    sa_comment->setFrameStyle(QFrame::NoFrame | QFrame::Plain);
    sa_comment->setLineWidth(0);
    sa_comment->setWidgetResizable(true);
    vbox_album_info->addWidget(sa_comment);
    lb_comment = new QLabel();
    lb_comment->setContentsMargins(4,2,0,0);
    lb_comment->setWordWrap(true);
    lb_comment->setAlignment(Qt::AlignLeft|Qt::AlignTop);
    lb_comment->setFixedWidth(widget_W_setup - art_label_W);
    lb_comment->setLineWidth(1);
    sa_comment->setWidget(lb_comment);
    //hbox_center->addLayout(vbox_album_info);
    vbox_all->addWidget(center_widget);
    hbox_bottom = new QHBoxLayout();
    hbox_bottom->setContentsMargins(0,0,0,0);
    hbox_bottom->setSpacing(6);
    bt_prev = new QPushButton();
    bt_prev->setFixedSize(34,28);
    bt_prev->setIconSize(QSize(20,20));
    bt_prev->setIcon(ic_bt_prev);
    hbox_bottom->addWidget(bt_prev);
    bt_stop = new QPushButton();
    bt_stop->setFixedSize(34,28);
    bt_stop->setIconSize(QSize(20,20));
    bt_stop->setIcon(ic_bt_stop);
    hbox_bottom->addWidget(bt_stop);
    bt_playpause = new QPushButton();
    bt_playpause->setFixedSize(34,28);
    bt_playpause->setIconSize(QSize(20,20));
    bt_playpause->setIcon(ic_bt_pp_pause);
    bt_playpause->setDefault(false);
    hbox_bottom->addWidget(bt_playpause);
    bt_next = new QPushButton();
    bt_next->setFixedSize(34,28);
    bt_next->setIconSize(QSize(20,20));
    bt_next->setIcon(ic_bt_next);
    hbox_bottom->addWidget(bt_next);
    bt_browser = new QPushButton();
    bt_browser->setFixedSize(34,28);
    bt_browser->setIconSize(QSize(20,20));
    bt_browser->setIcon(ic_bt_browser);
    hbox_bottom->addWidget(bt_browser);
    vol_slider = new QSlider(Qt::Horizontal);
    vol_slider->setRange(0,100);
    vol_slider->setFixedSize(58,28);
    if (config->set_dark_theme)
    {
        vol_slider->setStyleSheet(
            "QSlider::groove:horizontal {"
            "margin: 1px 0 1px 0;"
            "border: 1px solid #aaa;"
            "background: transparent;"
            "height: 4px;"
            "border-radius: 2px; }"
            "QSlider::handle:Horizontal {"
            "width:4px;"
            "border-radius:2px;"
            "background:#c2cad6;"
            "margin: -5px 0px -5px 0px; }" );
    }
    else
    {
        vol_slider->setStyleSheet(
            "QSlider::groove:horizontal {"
            "margin: 1px 0 1px 0;"
            "border: 1px solid #aaa;"
            "background: transparent;"
            "height: 4px;"
            "border-radius: 2px; }"
            "QSlider::handle:Horizontal {"
            "width:4px;"
            "border-radius:2px;"
            "background:#414b59;"
            "margin: -5px 0px -5px 0px; }" );
    }
    hbox_bottom->addWidget(vol_slider);
    bt_minmax = new QPushButton();
    bt_minmax->setFixedSize(34,28);
    bt_minmax->setIconSize(QSize(20,20));
    bt_minmax->setIcon(ic_bt_minmax);
    hbox_bottom->addWidget(bt_minmax);
    bt_settings = new QPushButton();
    bt_settings->setFixedSize(34,28);
    bt_settings->setIconSize(QSize(20,20));
    bt_settings->setIcon(ic_bt_settings);
    hbox_bottom->addWidget(bt_settings);
    vbox_all->addLayout(hbox_bottom);

    // buttonmapper
    button_mapper = new QSignalMapper();

    button_mapper->setMapping( bt_playpause, ID_play );
    connect(bt_playpause, SIGNAL(clicked()), button_mapper, SLOT(map()));

    button_mapper->setMapping( bt_stop, ID_stop );
    connect(bt_stop, SIGNAL(clicked()), button_mapper, SLOT(map()));

    button_mapper->setMapping( bt_next, ID_next );
    connect(bt_next, SIGNAL(clicked()), button_mapper, SLOT(map()));

    button_mapper->setMapping( bt_prev, ID_prev );
    connect(bt_prev, SIGNAL(clicked()), button_mapper, SLOT(map()));

    button_mapper->setMapping( bt_browser, ID_plst );
    connect(bt_browser, SIGNAL(clicked()), button_mapper, SLOT(map()));

    button_mapper->setMapping( bt_settings, ID_sets );
    connect(bt_settings, SIGNAL(clicked()), button_mapper, SLOT(map()));

    button_mapper->setMapping(lb_time, ID_time );
    connect(lb_time, SIGNAL(clicked()), button_mapper, SLOT(map()));

    button_mapper->setMapping( bt_minmax, ID_mmax );
    connect(bt_minmax, SIGNAL(clicked()), button_mapper, SLOT(map()));

    connect( button_mapper, SIGNAL(mappedInt(int)), this, SLOT(on_signal(int) ) );
    // end buttonmapper

    connect(pb_progress, SIGNAL(pressed(QMouseEvent*)), this, SLOT(lock_progress(QMouseEvent*)));

    connect(pb_progress, SIGNAL(clicked()), this, SLOT(unlock_progress()));

    connect(lb_albumart, SIGNAL(doubleclicked()), this, SLOT(show_saved_albumart()));

    minimizer = new QTimer();
    connect(minimizer, SIGNAL(timeout()), this, SLOT(minimice()));
    maximizer = new QTimer();
    connect(maximizer, SIGNAL(timeout()), this, SLOT(maximice()));

    this->setContextMenuPolicy(Qt::CustomContextMenu);
    connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(on_contextmenu()) );

    // CONTEXT MENU
    context_menu = new QMenu(this);

    a_tagscan = new QAction(context_menu);
    a_tagscan->setText(tr("Rescan tags"));
    a_tagscan->setIcon(ic_a_tagscan);
    connect( a_tagscan, SIGNAL(triggered()), this, SLOT(rescan_tags() ) );
    context_menu->addAction(a_tagscan);

    context_menu->addSeparator();

    a_fileman = new QAction(context_menu);
    a_fileman->setText(tr("File Manager"));
    a_fileman->setIcon(ic_a_fileman);
    connect( a_fileman, SIGNAL(triggered()), this, SLOT(open_directory() ) );
    context_menu->addAction(a_fileman);

    a_tagedit = new QAction(context_menu);
    a_tagedit->setText(tr("Tag Editor"));
    a_tagedit->setIcon(ic_a_tagedit);
    connect( a_tagedit, SIGNAL(triggered()), this, SLOT(edit_tags() ) );
    context_menu->addAction(a_tagedit);

    a_viewer = new QAction(context_menu);
    a_viewer->setText(tr("Image Viewer"));
    a_viewer->setIcon(ic_a_viewer);
    connect( a_viewer, SIGNAL(triggered()), this, SLOT(open_albumart_dir() ) );
    context_menu->addAction(a_viewer);

    a_user1 = new QAction(context_menu);
    a_user1->setIcon(ic_a_user);
    connect( a_user1, SIGNAL(triggered()), this, SLOT(run_user_action1() ) );
    context_menu->addAction(a_user1);

    a_user2 = new QAction(context_menu);
    a_user2->setIcon(ic_a_user);
    connect( a_user2, SIGNAL(triggered()), this, SLOT(run_user_action2() ) );
     context_menu->addAction(a_user2);

    connect(vol_slider, SIGNAL(valueChanged(int)), this, SLOT(set_volume(int)));
}


void qm_Player::set_volume(int vol)
{
    if (b_mpd_sets_volume || !b_mpd_connected)
        return;

    if (vol != current_volume)
    {
        current_volume = vol;
        mpd_cmd->set_volume(vol);
        if (config->show_tooltips)
            vol_slider->setToolTip("Volume " + QString::number(vol) + " %");
    }
}


void qm_Player::init_vars()
{
    this->setAcceptDrops(true);
    current_fonts = new qm_fonts;

    size_step = art_label_H / 30;
    alpha_step = 255/30;
    window_H_min = main_display->height()
                 + pb_progress->height()
                 + hbox_bottom->sizeHint().height()
                 + spacer_main->sizeHint().height()
                 + (2 * vbox_all_margin)
                 + (3 * vboxall_space);

    // mpd_cmd 1st: before current_songinfo! & settings calls it in init_vars()
    mpd_cmd = new qm_mpdCommand(config);
    connect( mpd_cmd, SIGNAL(signal_new_song()), this, SLOT(on_new_song()));
    connect( mpd_cmd, SIGNAL(signal_status_info(mpd_status *)), this, SLOT(on_new_status(mpd_status *)));
    connect( mpd_cmd, SIGNAL(signal_connected (bool, bool)), this, SLOT(on_mpdconnect_sgnl(bool, bool)));

    // browser_window 2nd
    browser_window = new qm_Browser(config);
    connect(browser_window, SIGNAL(signal_keypressed(QKeyEvent*)), this, SLOT(on_browser_keypress(QKeyEvent*)));

    // settings_window 3rd
    settings_window = new qm_Settings(config);
    connect(settings_window, SIGNAL(signal_playlist_auto_colwidth()), browser_window->playlist_view, SLOT(set_auto_columns()));
    connect(settings_window, SIGNAL(signal_library_auto_colwidth()), browser_window->library_view, SLOT(set_auto_columns()));
    connect(settings_window, SIGNAL(signal_fonts()), this, SLOT(set_fonts()));
    connect(settings_window, SIGNAL(signal_colors()), this, SLOT(set_colors()));
    connect(settings_window, SIGNAL(signal_mark_whenplayed()), browser_window->playlist_view, SLOT(set_markplayed()));
    connect(settings_window, SIGNAL(signal_custom_art()), this, SLOT(on_new_custom_art()));
    connect(settings_window, SIGNAL(signal_actions_changed()), this, SLOT(config_actions()));

    if (config->version != config->version_previous) // first run (current version is set in main.cpp
    {
        config->version_previous = config->version;
        QMessageBox msgBox;
        msgBox.setWindowTitle("Quimup");
        msgBox.setIcon(QMessageBox::Information);
        msgBox.setText("<b>" + tr("Welcome to Quimup") + " " + config->version + "</b>");

        QString message = tr("For tips see the 'about' tab of the settings window.")
                          + "\n\n"
                          + tr("This message will not be shown again.");

        msgBox.setInformativeText(message);
        msgBox.exec();
    }

    b_wayland = config->this_is_wayland;

    setWindowTitle("Quimup"); // + config->version ?

    current_songinfo = new qm_songInfo;
    current_songinfo->type = TP_NOSONG;
    mpd_cmd->set_songinfo(current_songinfo);

    kbps_count_down = 0;
    current_status = -99; // allow all
    current_volume = 0;
    current_type = TP_NOSONG;
    current_song_pos = -1;
    if (config->show_tooltips)
        vol_slider->setToolTip("Volume 0 %");
    song_total_time = 0;
    song_previous_time = 0;
    b_skip_khz = false;
    b_repeat = false;
    b_random = false;
    b_single = false;
    b_consume = false;
    b_xfading = false;
    b_mpd_connected = false;
    b_mpd_sets_volume = false;
    b_stream = false;
    b_minmax_busy = false;
    b_settings_hidden = false;
    b_browser_hidden = false;
    b_nosong = true;
    b_lock_progress = false;
    b_really_close = false;
    b_need_new_song = false;
    b_reshow_browser = false;
    b_reshow_settings = false;
    current_art_path = "";

    if (config->use_trayicon)
    {
        the_trayicon = new qm_trayIcon(this, config);
        the_trayicon->show();

        connect(the_trayicon, SIGNAL(clicked()), this, SLOT(on_tray_clicked()));
        connect(the_trayicon, SIGNAL(signal_tray_menu(int)), this, SLOT(on_signal(int)));

        the_trayicon->set_mode(ID_idle);

        b_use_trayicon = true;
    }
    else
        b_use_trayicon = false;

    set_colors();
    set_fonts();

    if (!b_wayland)
        move(config->player_X, config->player_Y);

    // don't show album info until max mode
    if (!config->player_maxmode)
        wg_album_info->hide();

    vol_slider->setFocus(Qt::OtherFocusReason);
}


void qm_Player::minimice()
{
    if (alphacycler > 0)
    {
        alphacycler -= alpha_step;
        if (alphacycler < 0)
            alphacycler = 0;
    }

    window_H -= size_step;
    size_changed += size_step;
    if (window_H >= window_H_min)
    {
        int a = alphacycler;
        QImage ifading = im_albumart;
        for ( int j = 0; j < art_label_H - size_changed; j++ )
        {
            QRgb *px = (reinterpret_cast<QRgb *>(ifading.scanLine(j)) );
            for ( int k = 0; k < art_label_W; k++ )
            {
                uchar r = uchar( ( int(qRed(*px))  * a  +  cR * (255 - a)) / 255 );
                uchar g = uchar( ( int(qGreen(*px))* a  +  cG * (255 - a)) / 255 );
                uchar b = uchar( ( int(qBlue(*px)) * a  +  cB * (255 - a)) / 255 );
                *px = qRgba( r, g, b, 255);
                px++;
            }
        }

        px_mm_fading = QPixmap::fromImage(ifading);
        lb_albumart->setPixmap(px_mm_fading);

        this->setFixedSize(window_W_max, window_H);
        if  (art_label_H - size_changed >  0)
            center_widget->setFixedSize(widget_W_setup, art_label_H - size_changed);
        else
        {
            center_widget->hide();
            center_widget->setFixedSize(widget_W_setup, 0);
        }

    }
    else
    {
        // no need to render pixmap: hide()
        center_widget->setFixedSize(widget_W_setup, 0);
        center_widget->hide();
        b_minmax_busy = false;
        minimizer->stop();
        this->setFixedSize(window_W_max, window_H_min);
    }
}


void qm_Player::maximice()
{
    if (alphacycler < 255)
    {
        alphacycler += alpha_step;
        if (alphacycler >= 255)
            alphacycler =255;
    }

    window_H += size_step;
    size_changed += size_step;
    if (window_H <= window_H_max)
    {
        this->setFixedSize(window_W_max, window_H);

        if  (size_changed <= art_label_H)
            center_widget->setFixedSize(widget_W_setup, size_changed);
        else
            center_widget->setFixedSize(widget_W_setup, art_label_H);

        int a = alphacycler;
        QImage ifading = im_albumart;
        if  (size_changed <= art_label_H)
        {
            for ( int j = 0; j < size_changed; j++ )
            {
                QRgb *px = (reinterpret_cast<QRgb *>(ifading.scanLine(j)) );
                for ( int k = 0; k < art_label_W; k++ )
                {
                    uchar r = uchar( ( int(qRed(*px))   * a  +  cR * (255 - a)) / 255 );
                    uchar g = uchar( ( int(qGreen(*px)) * a  +  cG * (255 - a)) / 255 );
                    uchar b = uchar( ( int(qBlue(*px))  * a  +  cB * (255 - a)) / 255 );
                    *px = qRgba( r, g, b, 255);
                    px++;
                }
            }
        }

        px_mm_fading = QPixmap::fromImage(ifading);
        lb_albumart->setPixmap(px_mm_fading);

        // 1st time around
        if (size_changed == size_step)
            center_widget->show();
    }
    else
    {
        int a = 255;
        QImage ifading = im_albumart;
        for ( int j = 0; j < art_label_H; j++ )
        {
            QRgb *px = (reinterpret_cast<QRgb *>(ifading.scanLine(j)) );
            for ( int k = 0; k < art_label_W; k++ )
            {
                uchar r = uchar( ( int(qRed(*px))  * a  +  cR * (255 - a)) / 255 );
                uchar g = uchar( ( int(qGreen(*px))* a  +  cG * (255 - a)) / 255 );
                uchar b = uchar( ( int(qBlue(*px)) * a  +  cB * (255 - a)) / 255 );
                *px = qRgba( r, g, b, 255);
                px++;
            }
        }

        px_mm_fading = QPixmap::fromImage(ifading);
        lb_albumart->setPixmap(px_mm_fading);
        // show album info
        wg_album_info->show();
        b_minmax_busy = false;
        maximizer->stop();
        window_H = window_H_max;
        this->setFixedSize(window_W_max, window_H_max);
        center_widget->setFixedSize(widget_W_setup, art_label_H);
    }
}


void qm_Player::on_tray_clicked()
{
    /* on Wayland isMinimized() is not set
    and a minimized window still isVisible() */
    if (isMinimized())
    {
        showNormal();
    }
    else
    if (isVisible())
    {
        hide();
    }
    else
    {
        showNormal();
    }
}


void qm_Player::on_new_song()
{
    kbps_count_down = 0;
    b_skip_khz = false;

    if (current_songinfo->type == TP_NOSONG)
    {
        if (b_nosong)
            return;
        // else
        b_nosong = true;
        current_song_pos = -1;
        current_file_name = "";
        current_mpd_dir = "";
        browser_window->playlist_view->set_newsong(-1);
        disenable_menu_items();
        get_and_set_albumart(current_songinfo->type, current_mpd_dir);
        return;
    }
    else
    {
        browser_window->playlist_view->set_newsong(current_songinfo->song_pos);
        current_type = current_songinfo->type;
    }

    // in "idle" state we didn't set the title etc.
    if (current_status == MPD_STATE_UNKNOWN)
    {
        current_file_name = "";
        current_mpd_dir = "";
        b_need_new_song = true;
        get_and_set_albumart( TP_NOSONG, current_mpd_dir); // but current_songinfo->type != TP_NOSONG
        disenable_menu_items();
        return;
    }
    else
        b_need_new_song = false;

    if (current_song_pos != current_songinfo->song_pos)
    {
        current_song_pos = current_songinfo->song_pos;
        printf ("New song: %i\n", current_song_pos);
    }
    else
    {
        if (current_songinfo->type == TP_STREAM)
            printf ("Stream updated: %i\n", current_song_pos);
        else
            printf ("Song restarted: %i\n", current_song_pos);
    }

    b_nosong = false;

    current_file_name = current_songinfo->name;
     current_mpd_dir = current_songinfo->uri.left(current_songinfo->uri.lastIndexOf("/"));
    //           current_mpd_dir never ends with '/'
    // TP_SONGX: current_mpd_dir begins with '/'            /fist/second/dlast
    // TP_SONG:  current_mpd_dir does not begin with '/'     one/two/end
    // TP_STREAM current_mpd_dir begins with 'http://'      http://some.web.location/dir (or https://)
    //     config->mpd_musicpath does begin with '/'        /the/path/to/music
    //     config->mpd_musicpath does not end with '/'

    if (current_songinfo->type == TP_STREAM)
    {
        current_mpd_dir = "";
        pb_progress->setValue(0);
        lb_extension->setText("URL");
        set_album_text("");
        set_year_text("");
        set_comment_text(current_songinfo->comment); // "Online audio stream"
        // stream titles change, so reset it in the playlist
        browser_window->playlist_view->update_stream_title(current_song_pos, current_songinfo->title);

        // note: song->artist is NULL !

        if (!current_songinfo->title.isEmpty())
        {
            info_scroller->render_info(current_songinfo->title);
            if (b_use_trayicon)
                the_trayicon->set_tip("", current_songinfo->title, true);
        }
        else
        {
            if (!current_songinfo->name.isEmpty())
            {
                info_scroller->render_info(current_songinfo->name);
                if (b_use_trayicon)
                    the_trayicon->set_tip(current_songinfo->name, "", true);
            }
            else
            {
                info_scroller->render_info(tr("no info"));
                if (b_use_trayicon)
                    the_trayicon->set_tip(tr("stream"), tr("no info"), true);
            }
        }

        b_stream = true;
    }
    else
        b_stream = false;

    disenable_menu_items();

    // ALBUMART
    get_and_set_albumart(current_songinfo->type, current_mpd_dir);

     // Everything below is not for streams
    if (b_stream)
        return;

    // extension
    if (current_file_name.isEmpty())
    {
        lb_extension->setText("");
    }
    else
    {
        QString aString = current_file_name;
        int i = aString.lastIndexOf( '.' ) + 1;
        aString = aString.right(aString.length() - i);
        aString = aString.toUpper();
        lb_extension->setText(aString);
    }

    if (current_songinfo->time)
    {
        song_total_time = current_songinfo->time;
        pb_progress->setRange(0,song_total_time);
    }

    // artist
    QString artist = "";
    if (!current_songinfo->artist.isEmpty())
        artist = current_songinfo->artist;

    // title
    QString title = "";
    if (!current_songinfo->title.isEmpty())
        title = current_songinfo->title;


    if (artist.isEmpty() && title.isEmpty())
    {
        if (current_file_name.isEmpty())
        {
            info_scroller->render_info(tr("no info"));
            if (b_use_trayicon)
                the_trayicon->set_tip(tr("(No Info)"), "", true);
        }
        else
        {
            info_scroller->render_info(current_file_name);
            if (b_use_trayicon)
                the_trayicon->set_tip(current_file_name, "", true);
        }
    }
    else
    {
        info_scroller->render_info(artist, title);
        if (b_use_trayicon)
             the_trayicon->set_tip( artist, title);
    }

    if (!current_songinfo->album.isEmpty())
        set_album_text(current_songinfo->album);
    else
        set_album_text("");

    if (!current_songinfo->date.isEmpty())
        set_year_text(current_songinfo->date);
    else
        set_year_text("");

    if (!current_songinfo->comment.isEmpty())
        set_comment_text(current_songinfo->comment);
    else
        set_comment_text("");
}

// can't use setText() directly: must hide/show
void qm_Player::set_year_text(QString text)
{
    if (!text.isEmpty())
    {
        if (lb_year->isHidden())
             lb_year->show();
        lb_year->setText(text);
    }
    else
        lb_year->hide();
}

// can't use setText() directly: must hide/show
void qm_Player::set_album_text(QString text)
{
    if (!text.isEmpty())
    {
        if (lb_album->isHidden())
             lb_album->show();
        lb_album->setText(text);
    }
    else
        lb_album->hide();
}

// can't use setText() directly: must resize
void qm_Player::set_comment_text(QString text)
{
    lb_comment->setText(text);
    int h = lb_comment->heightForWidth(122);
    lb_comment->resize(122, h);
}


void qm_Player::on_new_status(mpd_status *sInfo)
{
    if (sInfo == nullptr)
        return;

    // set the playback modes in the playlist menu
    if ( mpd_status_get_repeat(sInfo) != b_repeat)
    {
        browser_window->set_menu_repeat(mpd_status_get_repeat(sInfo));
        b_repeat = mpd_status_get_repeat(sInfo);
    }

    if (mpd_status_get_random(sInfo) != b_random)
    {
        browser_window->set_menu_random(mpd_status_get_random(sInfo));
        b_random = mpd_status_get_random(sInfo);
    }

    if (mpd_status_get_single(sInfo) != b_single)
    {
        browser_window->set_menu_single(mpd_status_get_single(sInfo));
        b_single = mpd_status_get_single(sInfo);
    }

    if (mpd_status_get_consume(sInfo) != b_consume)
    {
        browser_window->set_menu_consume(mpd_status_get_consume(sInfo));
        b_consume = mpd_status_get_consume(sInfo);
    }

    if (mpd_status_get_crossfade(sInfo) > 0 && !b_xfading)
    {
        b_xfading = true;
        browser_window->set_menu_xfade(b_xfading);
    }

    if (mpd_status_get_crossfade(sInfo) == 0 && b_xfading)
    {
        b_xfading = false;
        browser_window->set_menu_xfade(b_xfading);
    }

    // if status has changed...
    if (b_nosong && current_status != MPD_STATE_UNKNOWN)
        set_status(MPD_STATE_UNKNOWN);
    else
        if (mpd_status_get_state(sInfo) != current_status)
            set_status(mpd_status_get_state(sInfo));

    // set the other status info
    if (mpd_status_get_state(sInfo) != MPD_STATE_UNKNOWN && current_status != MPD_STATE_UNKNOWN)
    {
        int timenow = static_cast<int>(mpd_status_get_elapsed_time(sInfo));

        if (!b_stream && !b_lock_progress && song_total_time > 0)
        {
            if (timenow != song_previous_time)
            {
                song_previous_time = timenow;
                // some tracks play a bit too long (honestly)
                if (timenow > song_total_time)
                    timenow = song_total_time;
                pb_progress->setValue(timenow);
            }
        }

        if (b_stream || song_total_time == 0)
        {
            lb_time->setText("[&#187;]");
        }
        else
        {
            if (config->use_timeleft)
                timenow = song_total_time - timenow;

            QString nowtime = "";
            if (config->use_timeleft)
                nowtime += "-";
            nowtime += into_time(static_cast<int>(timenow));
            lb_time->setText(nowtime + " [" + into_time(static_cast<int>(song_total_time)) + "]");
        }

        if (kbps_count_down == 0)
        {
            kbps_count_down = 4;

            uint kbps = mpd_status_get_kbit_rate(sInfo);
            if (kbps) // 0 means unknown. Never NULL
                lb_kbps->setText(QString::number(kbps) + " kb/s");
            else
                lb_kbps->setText("");

            if (!b_skip_khz)
            {
                b_skip_khz = true;
                if (mpd_status_get_audio_format(sInfo) != nullptr) // 0 means unknown. Can return NULL, esp. with streams!
                {
                    double dbl_khz = static_cast<double>(mpd_status_get_audio_format(sInfo)->sample_rate) / 1000;
                    lb_kHz->setText(QString::number(dbl_khz) + " kHz");
                    b_skip_khz = true;
                }
                else
                    lb_kHz->setText("");
            }
            else
                b_skip_khz = false;
        }
        else
            kbps_count_down--;
    }

    if (current_volume != mpd_status_get_volume(sInfo))
    {
        b_mpd_sets_volume = true;
        /* slider Scale is set 0 to 100
            MPD also reports 0 to 100 */
        current_volume = mpd_status_get_volume(sInfo);
        vol_slider->setValue(current_volume);
        if (config->show_tooltips)
            vol_slider->setToolTip(tr("Volume") + " " + QString::number(current_volume) + " %");
        b_mpd_sets_volume = false;
    }
}


void qm_Player::retranslate()
{
    if (config->show_tooltips)
    {
        lb_time->setToolTip(tr("Click to toggle time mode"));
        bt_minmax->setToolTip(tr("Mini-Maxi mode"));
        bt_browser->setToolTip(tr("Media Browser & Playlist"));
        bt_settings->setToolTip(tr("Settings"));
    }
}

// Signal handler.
void qm_Player::on_signal(int sigID)
{
    switch (sigID)
    {
        case ID_prev:
        {
            if (current_status != MPD_STATE_PLAY || !b_mpd_connected)
                break;
            mpd_cmd->prev();
            break;
        }

        case ID_stop:
        {
            kbps_count_down = 0;
            b_skip_khz = false;

            if (current_status == MPD_STATE_UNKNOWN ||
                    current_status == MPD_STATE_STOP || !b_mpd_connected)
                break;

            set_status(MPD_STATE_STOP, "");
            info_scroller->pause(true);
            mpd_cmd->stop();
            break;
        }

        case ID_play:
        {
            kbps_count_down = 0;
            b_skip_khz = false;

            if (!b_mpd_connected)
                break;

            switch (current_status)
            {
                case MPD_STATE_PLAY:
                {
                    set_status(MPD_STATE_PAUSE, "");
                    mpd_cmd->pause();
                    break;
                }

                case MPD_STATE_PAUSE:
                {
                    set_status(MPD_STATE_PLAY, "");
                    mpd_cmd->resume();
                    break;
                }

                case MPD_STATE_STOP:
                {
                    set_status(MPD_STATE_PLAY, "");
                    mpd_cmd->play();
                    break;
                }

                case MPD_STATE_UNKNOWN:
                {
                    set_status(MPD_STATE_PLAY, "");
                    mpd_cmd->play();
                    break;
                }

                default:
                    break;
            }
            break;
        }

        case ID_next:
        {
            if (current_status != MPD_STATE_PLAY || !b_mpd_connected)
                break;
            mpd_cmd->next();
            break;
        }

        case ID_sets:
        {
            if (settings_window->isVisible())
                settings_window->hide();
            else
            {
                settings_window->showNormal();
                settings_window->setWindowState(Qt::WindowNoState);
            }
            break;
        }

        case ID_plst:
        {
            if (browser_window->isVisible())
                browser_window->hide();
            else
            {
                browser_window->showNormal();
                browser_window->setWindowState(Qt::WindowNoState);
            }
            break;
        }

        case ID_mmax:
        {
            if (b_minmax_busy)
                break;

            if (config->player_maxmode)
            {
                 b_minmax_busy = true;
                window_H = window_H_max;
                alphacycler = 255;
                size_changed = 0;
                minimizer->start(18);
                // wg_album_info->show() in final step of maximice()
                wg_album_info->hide();
                config->player_maxmode = false;
            }
            else
            {
                b_minmax_busy = true;
                window_H = window_H_min;
                alphacycler = 0;
                size_changed = 0;
                maximizer->start(18);
                config->player_maxmode = true;
            }
            break;
        }

        case ID_time:
        {
            config->use_timeleft = !config->use_timeleft;
            break;
        }

        case ID_exit:
        {
            b_really_close = true;

            // close does not happen when player was first closed from its window
            // (and thus hidden in closeEvent()) showNormal() fixes that. - Wayland bug?
            if (this->isHidden())
                this->showNormal();
            on_shutdown();
            break;
        }

        default:
            break;
    }
        // focus to the volume slider, where it does not show
        // on ID_exit vol_slider is already null -> segfault
        if (sigID != ID_exit)
            vol_slider->setFocus(Qt::OtherFocusReason);
}


void qm_Player::set_status(int status, QString message)
{
    if (!message.isNull() && !message.isEmpty())
        info_scroller->render_info(message);

    if (status == current_status)
        return;

    // in "idle" mode only respond to play and not connected
    if (current_status == MPD_STATE_UNKNOWN && status != MPD_STATE_PLAY && status != -1)
        return;

    if (b_need_new_song)
        mpd_cmd->reset_current_song();

    browser_window->playlist_view->set_status(status);

    switch (status)
    {
        case -1: // not connected
        {
            bt_playpause->setIcon(ic_bt_pp_play);
            lb_time->setText("");
            lb_kHz->setText("");
            lb_extension->setText("");
            lb_kbps->setText("");
            set_album_text("");
            set_year_text("");
            set_comment_text("");
            get_and_set_albumart(TP_NOSONG, "");
            pb_progress->setValue(0);
            if (b_use_trayicon)
            {
                the_trayicon->set_mode(ID_nocn);
            }
            disenable_menu_items();
            break;
        }

        case MPD_STATE_STOP: // 1
        {
            bt_playpause->setIcon(ic_bt_pp_play);
            info_scroller->pause(true);
            pb_progress->setValue(0);
            if (b_use_trayicon)
                the_trayicon->set_mode(ID_stop);
            break;
        }

        case MPD_STATE_PLAY: // 2
        {
            bt_playpause->setIcon(ic_bt_pp_pause);
            info_scroller->pause(false);

            if (b_use_trayicon)
                the_trayicon->set_mode(ID_play);
            break;
        }

        case MPD_STATE_PAUSE: // 3
        {
            bt_playpause->setIcon(ic_bt_pp_play);
            info_scroller->pause(true);
            if (b_use_trayicon)
                the_trayicon->set_mode(ID_paus);
            break;
        }

        default: // 0 (MPD_STATE_UNKNOWN) "Idle"
        {
            info_scroller->render_info("");
            bt_playpause->setIcon(ic_bt_pp_play);
            lb_time->setText("");
            lb_kHz->setText("");
            lb_extension->setText("");
            lb_kbps->setText("");
            set_album_text("");
            set_year_text("");
            set_comment_text("");
            get_and_set_albumart(TP_NOSONG, "");
            pb_progress->setValue(0);
            if (b_use_trayicon)
            {
                the_trayicon->set_mode(ID_idle);
            }
            disenable_menu_items();
            break;
        }
    }
    current_status = status;
}


QString qm_Player::into_time(int time)
{
    QString formattedTime = "";
    int mins = time / 60;
    formattedTime += QString::number((mins)) + ":";
    int secs = time % 60;
    if (secs < 10)
        formattedTime += "0";
    formattedTime += QString::number((secs));
    return formattedTime;
}


void qm_Player::set_colors()
{
    QPalette pal_main(this->palette());
    QPalette pal_album (this->palette());
    QPalette pal_default(this->palette());

    // QUIMUP colors
    if (config->colors_default)
    {
        QColor fg_color_title;
        QColor bg_color_title;
        QColor fg_color_album;
        QColor bg_color_album;

        if (config->set_dark_theme)
        {
            fg_color_title = QColor(config->color_alt_fg_title);
            bg_color_title = QColor(config->color_alt_bg_title);
            fg_color_album = QColor(config->color_alt_fg_album);
            bg_color_album = QColor(config->color_alt_bg_album);
        }
        else
        {
            fg_color_title = QColor(config->color_def_fg_title);
            bg_color_title = QColor(config->color_def_bg_title);
            fg_color_album = QColor(config->color_def_fg_album);
            bg_color_album = QColor(config->color_def_bg_album);
        }

        // display
        pal_main.setColor(QPalette::Window, QColor(bg_color_title));
        pal_main.setColor(QPalette::WindowText, QColor(fg_color_title));
        // siblings are transparent by default
        main_display->setPalette(pal_main);
        main_display->setAutoFillBackground(true);
        lb_kbps->setPalette(pal_main);
        lb_kbps->setAutoFillBackground(true);
        lb_time->setPalette(pal_main);
        lb_time->setAutoFillBackground(true);
        info_scroller->setPalette(pal_main);
        info_scroller->setAutoFillBackground(true);
        lb_kHz->setPalette(pal_main);
        lb_kHz->setAutoFillBackground(true);
        lb_extension->setPalette(pal_main);
        lb_extension->setAutoFillBackground(true);
        // browser titles
        if (config->colfor_browser)
        {
            pal_album.setColor(QPalette::Window, bg_color_album);
            pal_album.setColor(QPalette::WindowText, fg_color_album);
            browser_window->set_colors(pal_album);
        }
        else
            browser_window->set_colors(pal_default);
        // progressbar
        if (config->colfor_progressbar)
        {
            QString fgname = fg_color_title.name(QColor::HexRgb);
            QString bgname = bg_color_title.name(QColor::HexRgb);
            if (config->set_dark_theme)
                pb_progress->setStyleSheet( pb_stylesheet.arg(bgname).arg(fgname) );
            else
                pb_progress->setStyleSheet( pb_stylesheet.arg(fgname).arg(bgname) );
        }

        // album info
        if (config->colfor_albuminfo)
        {
            pal_album.setColor(QPalette::Window, bg_color_album);
            pal_album.setColor(QPalette::WindowText, fg_color_album);
            center_widget->setAutoFillBackground(true);
            center_widget->setPalette(pal_album);
            // to fill transparency in (album art) images:
            cR = bg_color_album.red();
            cG = bg_color_album.green();
            cB = bg_color_album.blue();
        }
    }
    else
    if (config->colors_cutom)
    {
        // display
        QColor fg_color_title = QColor(config->color_cus_fg_title);
        QColor bg_color_title = QColor(config->color_cus_bg_title);
        pal_main.setColor(QPalette::Window, QColor(bg_color_title));
        pal_main.setColor(QPalette::WindowText, QColor(fg_color_title));
        // siblings are transparent by default
        main_display->setPalette(pal_main);
        main_display->setAutoFillBackground(true);
        lb_kbps->setPalette(pal_main);
        lb_kbps->setAutoFillBackground(true);
        lb_time->setPalette(pal_main);
        lb_time->setAutoFillBackground(true);
        info_scroller->setPalette(pal_main);
        info_scroller->setAutoFillBackground(true);
        lb_kHz->setPalette(pal_main);
        lb_kHz->setAutoFillBackground(true);
        lb_extension->setPalette(pal_main);
        lb_extension->setAutoFillBackground(true);
        // browser titles
        if (config->colfor_browser)
        {
            QColor fg_color_album = QColor(config->color_cus_fg_album);
            QColor bg_color_album = QColor(config->color_cus_bg_album);
            pal_album.setColor(QPalette::Window, bg_color_album);
            pal_album.setColor(QPalette::WindowText, fg_color_album);
            browser_window->set_colors(pal_album);
        }
        else
            browser_window->set_colors(pal_default);
        // progressbar
        if (config->colfor_progressbar)
        {
            QString fgname = fg_color_title.name(QColor::HexRgb);
            QString bgname = bg_color_title.name(QColor::HexRgb);
            if (config->set_dark_theme)
                pb_progress->setStyleSheet( pb_stylesheet.arg(bgname).arg(fgname) );
            else
                pb_progress->setStyleSheet( pb_stylesheet.arg(fgname).arg(bgname) );
        }

        // album info
        if (config->colfor_albuminfo)
        {
            QColor fg_color_album = QColor(config->color_cus_fg_album);
            QColor bg_color_album = QColor(config->color_cus_bg_album);
            pal_album.setColor(QPalette::Window, bg_color_album);
            pal_album.setColor(QPalette::WindowText, fg_color_album);
            center_widget->setAutoFillBackground(true);
            center_widget->setPalette(pal_album);
            // to fill transparency in (album art) images:
            cR = bg_color_album.red();
            cG = bg_color_album.green();
            cB = bg_color_album.blue();
        }
    }
    else // config->colors_system
    {
        // display
        main_display->setPalette(pal_default);
        main_display->setAutoFillBackground(false);
        lb_kbps->setPalette(pal_default);
        lb_kbps->setAutoFillBackground(false);
        lb_time->setPalette(pal_default);
        lb_time->setAutoFillBackground(false);
        info_scroller->setPalette(pal_default);
        info_scroller->setAutoFillBackground(false);
        lb_kHz->setPalette(pal_default);
        lb_kHz->setAutoFillBackground(false);
        lb_extension->setPalette(pal_default);
        lb_extension->setAutoFillBackground(false);
        center_widget->setAutoFillBackground(false);
        // browser titles
        browser_window->set_colors(pal_default);
        // album info
        if (config->colfor_albuminfo)
        {
            center_widget->setAutoFillBackground(false);
            center_widget->setPalette(pal_default);
            // to fill transparency in (album art) images:
            QColor bg_color = pal_default.color(QPalette::Window);
            cR = bg_color.red();
            cG = bg_color.green();
            cB = bg_color.blue();
        }
        // progressbar
        if (config->colfor_progressbar)
        {
            QColor fg_color = pal_default.color(QPalette::Window);
            QColor bg_color = pal_default.color(QPalette::Text);
            QString fgname = fg_color.name(QColor::HexRgb);
            QString bgname = bg_color.name(QColor::HexRgb);
            if (config->set_dark_theme)
                pb_progress->setStyleSheet( pb_stylesheet.arg(bgname).arg(fgname) );
            else
                pb_progress->setStyleSheet( pb_stylesheet.arg(fgname).arg(bgname) );
        }
    }

    if (!config->colfor_progressbar)
    {
        if (config->set_dark_theme)
            pb_progress->setStyleSheet( pb_stylesheet.arg("#414b59").arg("#c2cad6") );
        else
            pb_progress->setStyleSheet( pb_stylesheet.arg("#c2cad6").arg("#414b59") );
    }

    if (!config->colfor_albuminfo)
    {
        QColor bg_color = pal_default.color(QPalette::Window);
        cR = bg_color.red();
        cG = bg_color.green();
        cB = bg_color.blue();
        center_widget->setAutoFillBackground(false);
        center_widget->setPalette(pal_default);
    }

    get_and_set_albumart(current_type, current_art_path);
}

//  Mouse pressed on progress bar
void qm_Player::lock_progress(QMouseEvent *event)
{
    if (!b_stream && current_status == MPD_STATE_PLAY && b_mpd_connected)
    {
        b_lock_progress = true;
        // Get mouse x pos within widget
        double x = event->position().x();
        int pgress = (x * song_total_time) / 324; // 324 is width of widget
        pb_progress->setValue(pgress);
    }
}

// Mouse released on progress bar.
void qm_Player::unlock_progress()
{
    if (!b_stream && current_status == MPD_STATE_PLAY && b_mpd_connected)
    {
        mpd_cmd->set_seek( static_cast<uint>(pb_progress->value()) );
        b_lock_progress = false;
    }
}


void qm_Player::set_fonts()
{
    QFont font;

    // Title scroller
    font.setWeight(QFont::Normal);
    font.setItalic(false);
    if (current_fonts->title_size != config->font_title_size
        || current_fonts->title_bold != config->font_title_bold
        || current_fonts->title_italic != config->font_title_italic)
    {
        info_scroller->setFont(config->font_title_size, config->font_title_bold, config->font_title_italic);
    }

    // Codec info
    font.setWeight(QFont::Normal);
    font.setItalic(false);
    if (current_fonts->codecinfo_size != config->font_codecinfo_size
        || current_fonts->codecinfo_bold != config->font_codecinfo_bold)
    {
        font.setPointSize(config->font_codecinfo_size);
        if (config->font_codecinfo_bold)
            font.setWeight(QFont::DemiBold);

        lb_extension->setFont (font);
        lb_kHz->setFont (font);
        lb_kbps->setFont (font);
    }

    // Time counter
    font.setWeight(QFont::Normal);
    font.setItalic(false);
    if (current_fonts->time_size != config->font_time_size
        || current_fonts->time_bold != config->font_time_bold)
    {
        font.setPointSize(config->font_time_size);
        if (config->font_time_bold)
            font.setWeight(QFont::DemiBold);

        lb_time->setFont (font);
    }

    // Album info
    font.setWeight(QFont::Normal);
    font.setItalic(false);
    if (current_fonts->album_size != config->font_album_size
        || current_fonts->album_bold != config->font_album_bold)
    {
        font.setPointSize(config->font_album_size);
        if (config->font_album_bold)
            font.setWeight(QFont::DemiBold);
        lb_album->setFont (font);
    }

    // Year
    font.setWeight(QFont::Normal);
    font.setItalic(false);
    if (current_fonts->year_size != config->font_year_size ||
        current_fonts->year_bold != config->font_year_bold)
    {
        font.setPointSize(config->font_year_size);
        if (config->font_year_bold)
            font.setWeight(QFont::DemiBold);
        lb_year->setFont (font);
    }

    // Comment
    font.setWeight(QFont::Normal);
    font.setItalic(false);
    if (current_fonts->comment_size != config->font_comment_size ||
        current_fonts->comment_italic != config->font_comment_italic)
    {
        font.setPointSize(config->font_comment_size);
        if (config->font_comment_italic)
            font.setItalic(true);
        lb_comment->setFont (font);

        // resize comment label
        if (!current_songinfo->comment.isEmpty())
            set_comment_text(current_songinfo->comment);
        else
            set_comment_text("");
    }

    // Library and Playlist
    if (current_fonts->browser_size != config->font_browser_size)
    {
        font.setItalic(false);
        font.setWeight(QFont::Normal);
        font.setPointSize(config->font_browser_size);
        browser_window->playlist_view->setFont(font);
        browser_window->playlist_view->set_pos_time_column_width();
        browser_window->playlist_view->resize_columns();
        browser_window->library_view->setFont(font);
        browser_window->library_view->set_last_column_width();
        browser_window->library_view->resize_columns();
    }

    // current_fonts are used to check if changes apply
    current_fonts->title_size = config->font_title_size;
    current_fonts->title_italic = config->font_title_italic;
    current_fonts->title_bold = config->font_title_bold;
    current_fonts->codecinfo_size = config->font_codecinfo_size;
    current_fonts->codecinfo_bold = config->font_codecinfo_bold;
    current_fonts->time_size = config->font_time_size;
    current_fonts->time_bold = config->font_time_bold;
    current_fonts->album_size = config->font_album_size;
    current_fonts->album_bold = config->font_album_bold;
    current_fonts->year_size = config->font_year_size;
    current_fonts->year_bold = config->font_year_bold;
    current_fonts->browser_size = config->font_browser_size;
    current_fonts->comment_size = config->font_comment_size;
    current_fonts->comment_italic = config->font_comment_italic;
}

// TP_SONG    'mpd_path' is current_mpd_dir (relative to MPD's music dir), does not end with /
// TP_SONGX   'mpd_path' is current_mpd_dir (relative to '/' root), does not end with /
// TP_NOSONG  'mpd_path' is ""
// TP_STREAM  'mpd_path' is ""
// current_art_path is used in set_colors() and on_new_custom_art()
void qm_Player::get_and_set_albumart(int type, QString mpd_path)
{
    current_art_path = mpd_path;

    im_albumart = QImage(); // reset
    bool b_art_from_mpd = false;
    bool b_art_from_file = false;
    bool b_art_custom = false;
    bool b_art_default = false;
    bool b_art_set = false;

    if (type == TP_NOSONG)
    {
        // This is called in set_colors() on start up: when NO SONG has yet played
        // and in set_status(disconnected or unknown): when NO SONG is still playing
        b_art_set = im_albumart.load(":/pl_nosong_art");
        b_art_default = b_art_set;
    }
    else
    if (type == TP_STREAM)
    {
        b_art_set = im_albumart.load(":/pl_stream_art");
        b_art_default = b_art_set;
    }
    else // TP_SONGX or TP_SONG
    {
        // [1] try MPD
        b_art_set = mpd_cmd->get_album_art(&im_albumart);
        b_art_from_mpd = b_art_set;

        if (!b_art_set)
        {
            if (!b_remote_server && !mpd_path.isEmpty())
            {
                // [2] try local file
                b_art_set = get_art_from_local_file(type, mpd_path);
                b_art_from_file =  b_art_set;
            }

            if (!b_art_set)
            {
                // [3] try custom file
                if (config->albumart_custom)
                {
                    QFile file;
                    file.setFileName(config->custom_albumart_file);
                    if (!file.exists())
                    {
                        if (config->cout_extensive)
                            printf ("Custom albumart image does not exist\n");
                    }
                    else
                    {
                        b_art_set = im_albumart.load(config->custom_albumart_file);
                        b_art_custom = b_art_set;

                        if (!b_art_set && config->cout_extensive)
                                printf ("Could not load custom albumart image\n");
                    }
                }

                if (!b_art_set)
                {
                    // [4] use default image
                    b_art_set = im_albumart.load(":/pl_nosong_art");
                    b_art_default = b_art_set;
                }
            }
        }
    }

    if (!b_art_set) // should never happen
    {
        printf ("Error: Could not set albumart! (using default)\n");
        b_art_default = im_albumart.load(":/pl_nosong_art");
    }

    // save image as qm_temp.png (to show on doubleclick)
    // [qt.gui.imageio: libpng warning: known incorrect sRGB profile] originates here
    QString file = QDir::home().absolutePath() + "/.config/quimup/qm_temp.png" ;

    if (!im_albumart.save(file, "PNG", -1))
    {
        if (config->cout_extensive)
            printf ("Could not save ~/.config/quimup/qm_temp.png\n");
    }
    else
    {
        if (config->cout_extensive)
            printf ("Saved ~/.config/quimup/qm_temp.png\n");
    }

    // fit image inside the label
    if (im_albumart.width() != art_label_W ||  im_albumart.height() != art_label_H)
        im_albumart = im_albumart.scaled( art_label_H, art_label_W, Qt::KeepAspectRatio, Qt::SmoothTransformation );

    // 1. im_albumart must have an alpha channel (for min-max fading)
    // 2. Transparency must be filled with the background color(for min-max fading)
    // 3. Possibly im_albumart.width()  < art_label_W
    //          or im_albumart.height() < art_label_H

    // 1. Format_RGB32 has alfa channel
    QImage im_temp(art_label_W, art_label_H, QImage::Format_RGB32);
    // 2. fill im_temp with background color
    QColor bgcolor(QColor(cR, cG, cB, 255));
    im_temp.fill(bgcolor);

    // 3. draw the possibly smaller im_albumart centered in im_temp
    int offset_x = (art_label_W - im_albumart.width())/2;
    int offset_y = (art_label_H - im_albumart.height())/2;
    QPainter pntr(&im_temp);
    pntr.drawImage(offset_x,offset_y, im_albumart);
    // recreate im_albumart from im_temp
    im_albumart = im_temp.copy(0,0,art_label_W, art_label_H);

    // add border
    if (!b_art_default)
    {
        for (int  h = 0; h < art_label_H; h++)
        {
            for (int  w = 0; w < art_label_W; w++)
            {
                if (h < 1 || h > art_label_H-2 || w < 1 || w > art_label_W-2)
                {
                    im_albumart.setPixel(w, h, 0xFF6b8a99);
                }
            }
        }
    }

    // show the image. minimice() or maximice() will set alpha
    px_album_art  = QPixmap::fromImage(im_albumart);
    lb_albumart->setPixmap(px_album_art);

    QString str = "";
    if (config->image_viewer_installed)
        str = "\n" + tr("Double click to open");
    else
        str = "\n" + tr("(viewer was not found)");

    // set tooltip
    if (config->show_tooltips)
    {
        if (b_art_from_mpd)
        {
            lb_albumart->setToolTip(tr("Album art from MPD") + str);
            //  printf is done in mpd_comd->get_album_art()
        }
        else
        if (b_art_from_file)
        {
            lb_albumart->setToolTip(tr("Album art from file") + str);
            if (config->cout_extensive)
                printf ("Using local image file as album art\n");
        }
        else
        if (b_art_default)
        {
            lb_albumart->setToolTip(tr("Default album art") + str);
            if (config->cout_extensive)
                printf ("Using default image file as album art\n");
        }
        else
        if (b_art_custom)
        {
            lb_albumart->setToolTip(tr("Custom album art") + str);
            if (config->cout_extensive)
                printf ("Using custom image file as album art\n");
        }
        else // should never happen
        {
            lb_albumart->setToolTip(tr("Album art"));
            printf ("Error: could not set album art\n");
        }
    }
}


void qm_Player::on_contextmenu()
{
    context_menu->exec(QCursor::pos(), nullptr);
}


void qm_Player::open_albumart_dir()
{
    if (!b_mpd_connected)
        return;

    if (config->mpd_musicdir_status != 1)
    {
        printf ("Album art: Directory is not accessible\n");
        return;
    }

    if (!config->image_viewer_installed)
        return;

    QString full_path;
    if (current_type == TP_SONGX)
    {
        full_path = current_mpd_dir;
    }
    else
        if (current_type == TP_SONG)
        {
            full_path = config->mpd_musicpath + "/" + current_mpd_dir;
        }
        else
            return;

    QDir dir(full_path);
    if (!dir.exists())
    {
        if (config->cout_extensive)
            printf ("Album art: directory not found: %s\n", full_path.toStdString().c_str());
        return;
    }

    QProcess *proc = nullptr;
    proc->startDetached(config->image_viewer, QStringList() << full_path );
}

// this also works on a remote connection
void qm_Player::show_saved_albumart()
{
    // this also works on a remote connection

    if (!config->image_viewer_installed)
        return;

    QString artfile = QDir::home().absolutePath() + "/.config/quimup/qm_temp.png" ;
    QFile file(artfile);
    if (!file.exists())
    {
        printf ("Album art: ~/.config/quimup/qm_temp.png not found\n");
        return;
    }

    QProcess *proc = nullptr;
    QStringList qsl = {artfile}; // adding , "&>/dev/null" does not work
    proc->startDetached(config->image_viewer, qsl);
}


void qm_Player::rescan_tags()
{
    if (current_type != TP_SONG)
       return;

    if (config->mpd_rescan_allowed)
    {
        qm_commandList cmdList;
        qm_mpd_command newCommand;

        newCommand.uri = current_mpd_dir;
        newCommand.cmd = CMD_SCN;
        cmdList.push_back(newCommand);

        if (config->cout_extensive)
            printf ("Rescanning: %s\n", current_mpd_dir.toUtf8().constData());

        mpd_cmd->execute_commands(cmdList, false);
    }
    else
        printf("rescan_tags(): rescan not allowed\n");
}


void qm_Player::edit_tags()
{
    if (!b_mpd_connected)
        return;

    if (config->mpd_musicdir_status != 1)
    {
        printf ("Album art: Directory is not accessible\n");
        return;
    }

    if (!config->tag_editor_installed)
        return;

    QString full_path;
    if (current_type == TP_SONGX)
    {
        full_path = current_mpd_dir;
    }
    else
    if (current_type == TP_SONG)
    {
        full_path = config->mpd_musicpath + "/" + current_mpd_dir;
    }
    else
        return;

    QDir dir(full_path);
    if (!dir.exists())
    {
        if (config->cout_extensive)
            printf ("Tag edit: directory not found: %s\n", full_path.toStdString().c_str());
        return;
    }

    QProcess *proc = nullptr;
    proc->startDetached(config->tag_editor, QStringList() << full_path);
}


void qm_Player::open_directory()
{
    if (!b_mpd_connected)
        return;

    if (config->mpd_musicdir_status != 1)
    {
        printf ("Album art: Directory is not accessible\n");
        return;
    }

    if (!config->file_manager_installed)
        return;

    QString full_path;
    if (current_type == TP_SONGX)
    {
        full_path = current_mpd_dir;
    }
    else
    if (current_type == TP_SONG)
    {
        full_path = config->mpd_musicpath + "/" + current_mpd_dir;
    }
    else
        return;

    QDir dir(full_path);
    if (!dir.exists())
    {
        if (config->cout_extensive)
            printf ("Open directory: directory not found: %s\n", full_path.toStdString().c_str());
        return;
    }

    QProcess *proc = nullptr;
    proc->startDetached(config->file_manager, QStringList() << full_path);
}

// called by the core
void qm_Player::wakeup_call(bool show_browser)
{

    this->showNormal();
    if (show_browser)
    {
        browser_window->showNormal();
        browser_window->activateWindow();
    }
    else
        this->activateWindow();
}


void qm_Player::showEvent(QShowEvent *event)
{
    if (b_reshow_browser)
        browser_window->showNormal();
    if (b_reshow_settings)
    {
        settings_window->showNormal();
        settings_window->move(config->settings_X, config->settings_Y);
    }

    this->activateWindow();

    event->accept();
}


void qm_Player::hideEvent(QHideEvent *event)
{
    if (browser_window->isVisible())
    {
        b_reshow_browser = true;
        browser_window->hide();
    }
    else
        b_reshow_browser = false;

    if (settings_window->isVisible())
    {
        b_reshow_settings = true;
        settings_window->hide();
    }
    else
        b_reshow_settings = false;

    if (!b_wayland)
    {
        config->player_X = this->x();
        config->player_Y = this->y();
    }

    event->accept();
}


void qm_Player::closeEvent( QCloseEvent *event )
{

    if (!b_really_close && b_use_trayicon)
    {
         this->hide();
    }
    else
    {
        printf("Player closing\n");

        if (mpd_cmd != nullptr)
        {
            // close mpd connection
            mpd_cmd->mpd_disconnect(false);
            // stop reconnect thread
            mpd_cmd->stop_connect_thread();
        }

        // x() and y() do not work on wayland
        if (b_wayland && config->cout_extensive)
            printf ("We are in Wayland: not saving window positions\n");

        if (this->isVisible() && ! b_wayland)
        {
            config->player_X = this->x();
            config->player_Y = this->y();
        }
        if (browser_window->isVisible())
        {
            if (!b_wayland)
            {
                config->browser_X = browser_window->x();
                config->browser_Y = browser_window->y();
            }
            config->browser_W = browser_window->width();
            config->browser_H = browser_window->height();
        }
        if (settings_window->isVisible())
        {
            if (!b_wayland)
            {
                config->settings_X = settings_window->x();
                config->settings_Y = settings_window->y();
            }
            config->settings_W = settings_window->width();
            config->settings_H = settings_window->height();
        }

        if (config->onquit_quitmpd && config->mpd_is_installed)
        {
            // no harm done if MPD is not running

            if (config->onquit_mpd_command.isEmpty() || config->onquit_mpd_command.isNull())
                return;

            printf("Executing command: %s\n", config->onquit_mpd_command.toStdString().c_str());
            QProcess *proc = nullptr;
            QString cmd = config->onquit_mpd_command.section(" ", 0, 0);
            QStringList args;
            args = config->onquit_mpd_command.split((" "));
            if (!args.at(0).isNull())
                args.remove(0); // only keep the argumens
            proc->startDetached(cmd, args);
        }

        config->save_config();

        if (config->cout_extensive)
            printf ("Removing /.config/quimup/qm_temp.png\n");
        QFile file (QDir::home().absolutePath() + "/.config/quimup/qm_temp.png");
        file.remove();

        printf ("Thank you and goodbye!\n");
        qApp->quit();
    }

    event->accept();
}


void qm_Player::on_shutdown()
{
    b_really_close = true;
    close();
}


// signal > directly to keyPressEvent() does not work
void qm_Player::on_browser_keypress(QKeyEvent *event)
{
    switch (event->key())
    {
        case Qt::Key_MediaPause:
        {
            if (config->cout_extensive)
                printf ("Key_MediaPause pressed\n");
            on_signal(ID_play);
            break;

        }
        case Qt::Key_MediaPlay:
        {
            if (config->cout_extensive)
                printf ("Key_MediaPlay pressed\n");
            on_signal(ID_play);
            break;

        }
        case Qt::Key_MediaNext:
        {
            if (config->cout_extensive)
                printf ("Key_MediaNext pressed\n");
            on_signal(ID_next);
            break;

        }
        case Qt::Key_MediaPrevious:
        {
            if (config->cout_extensive)
                printf ("Key_MediaPrevious pressed\n");
            on_signal(ID_prev);
            break;

        }
        case Qt::Key_MediaStop:
        {
            if (config->cout_extensive)
                printf ("Key_MediaStoppressed\n");
            on_signal(ID_stop);
            break;
        }
        default:
            break;
    }
}


void qm_Player::keyPressEvent(QKeyEvent *event)
{
    switch (event->key())
    {
        case Qt::Key_Q:
        {
            if (event->modifiers() & Qt::ControlModifier)
                on_shutdown();
            break;
        }
        case Qt::Key_MediaPause:
        {
            if (config->cout_extensive)
                printf ("Key_MediaPause pressed\n");
            on_signal(ID_play);
            break;

        }
        case Qt::Key_MediaPlay:
        {
            if (config->cout_extensive)
                printf ("Key_MediaPlay pressed\n");
            on_signal(ID_play);
            break;

        }
        case Qt::Key_MediaNext:
        {
            if (config->cout_extensive)
                printf ("Key_MediaNext pressed\n");
            on_signal(ID_next);
            break;

        }
        case Qt::Key_MediaPrevious:
        {
            if (config->cout_extensive)
                printf ("Key_MediaPrevious pressed\n");
            on_signal(ID_prev);
            break;

        }
        case Qt::Key_MediaStop:
        {
            if (config->cout_extensive)
                printf ("Key_MediaStop pressed\n");
            on_signal(ID_stop);
            break;

        }
        default:
            QMainWindow::keyPressEvent(event);
    }
}

// Drag & drop
void qm_Player::dragEnterEvent(QDragEnterEvent *event)
{
    if (config->mpd_socket_conn && event->source() == nullptr &&  event->mimeData()->hasUrls())
        event->accept();
    else
        event->ignore();
}

// Drag & drop
void qm_Player::dragMoveEvent(QDragMoveEvent *event)
{
    if (config->mpd_socket_conn && event->source() == nullptr &&  event->mimeData()->hasUrls())
        event->accept();
    else
        event->ignore();
}

// Drag & drop
void qm_Player::dragLeaveEvent(QDragLeaveEvent *event)
{
    event->accept();
}

// Drag & drop
void qm_Player::dropEvent(QDropEvent *event)
{
    if (config->mpd_socket_conn && event->source() == nullptr &&  event->mimeData()->hasUrls())
        browser_window->playlist_view->on_open_drop_request(event);

    event->ignore();
}


void qm_Player::set_themed_icons(bool dark_theme)
{
    if (!dark_theme)
    {
        ic_bt_prev = QIcon(":/pl_sy_prev");
        ic_bt_stop = QIcon(":/pl_sy_stop");
        ic_bt_next = QIcon(":/pl_sy_next");
        ic_bt_pp_play = QIcon(":/pl_sy_play");
        ic_bt_pp_pause = QIcon(":/pl_sy_pause");
        ic_bt_browser = QIcon(":/pl_playlist");
        ic_bt_minmax = QIcon(":/pl_minmax");
        ic_bt_settings = QIcon(":/pl_config");
        ic_a_tagedit = QIcon(":/pl_br_tagedit");
        ic_a_fileman = QIcon(":/pl_br_dir");
        ic_a_viewer = QIcon(":/pl_view");
        ic_a_tagscan = QIcon(":/pl_br_update");
        ic_a_user = QIcon(":/pl_usraction");
    }
    else
    {
        ic_bt_prev = QIcon(":/pl_sy_prev.alt");
        ic_bt_stop = QIcon(":/pl_sy_stop.alt");
        ic_bt_next = QIcon(":/pl_sy_next.alt");
        ic_bt_pp_play = QIcon(":/pl_sy_play.alt");
        ic_bt_pp_pause = QIcon(":/pl_sy_pause.alt");
        ic_bt_browser = QIcon(":/pl_playlist.alt");
        ic_bt_minmax = QIcon(":/pl_minmax.alt");
        ic_bt_settings = QIcon(":/pl_config.alt");
        ic_a_tagedit = QIcon(":/pl_br_tagedit.alt");
        ic_a_fileman = QIcon(":/pl_br_dir.alt");
        ic_a_viewer = QIcon(":/pl_view.alt");
        ic_a_tagscan = QIcon(":/pl_br_update.alt");
        ic_a_user = QIcon(":/pl_usraction.alt");
    }
}


bool qm_Player::is_app_installed(QString app)
{
    QString output;
    QProcess proc;
    proc.setProgram("which");
    proc.setArguments({app.toStdString().c_str()});
    proc.start();
    if (!proc.waitForFinished())
        output = "";
    else
        output = proc.readAllStandardOutput();

    output = output.trimmed();
    bool is_installed = false;

    if (output.contains("/usr/bin/" + app) || output.contains("/usr/local/bin/" + app))
    {
        is_installed = true;
    }
    else
    {
        // try to catch e.g ~/bin/app, ~/scripts/app
        if (output.contains("/home/") && output.contains("/" + app))
            is_installed = true;
    }

    // in case a full path is used
    if (!is_installed)
    {
        QFile file;
        file.setFileName(app);
        if ( file.exists() )
            is_installed = true;
    }

    if (is_installed)
    {
        if (config->cout_extensive)
            printf ("%s is installed\n", app.toStdString().c_str());
    }
    else
    {
        if (config->cout_extensive)
            printf ("%s is not installed\n", app.toStdString().c_str());
    }

    return is_installed;
}


bool qm_Player::get_art_from_local_file(int type, QString mpd_path)
{
    if (mpd_path.isEmpty())
        return false;

    QString local_path;

    if (type == TP_SONGX)
    {
        // path is relative to '/' (system root)
        local_path = mpd_path;
    }
    else
        if (type == TP_SONG)
        {
            // path is realtive to mpd's music directory
            local_path = config->mpd_musicpath + "/" + mpd_path;
        }

    QDir dir(local_path);
    if (!dir.exists())
    {
        printf ("Album art directory: not found\n");
        return false;
    }

    if(!dir.isReadable())
    {
        printf ("Album art directory: not readable\n");
        return false;
    }

    dir.setSorting(QDir::Type);

    QStringList extensions;
    extensions << ".jpg" << ".jpeg" << ".png" << ".gif" << ".bmp" << ".svg" ;

    // filter is not case sensitive
    QStringList namefilter;
    QString album_name = "*" + current_songinfo->album + "*";
    namefilter << "cover*" << "albumart*" << "folder*" << "front*" << "booklet*" << "portada*" << "couverture*" << "capa*" << album_name ;

    // Note: QDir::Unsorted keeps the order of namefilter
    QStringList filenames = dir.entryList(namefilter, QDir::Files, QDir::Unsorted);

    if (filenames.isEmpty())
    {
        if (config->cout_extensive)
            printf ("Album art: could not find any matching local bitmaps\n");
        return false;
    }

    bool success = false;
    for (int x = 0; x < filenames.size(); ++x)
    {
        for (int i = 0; i < extensions.size(); ++i)
        {
            if ( filenames.at(x).contains(extensions.at(i), Qt::CaseInsensitive) )
            {
                success = true;
                local_path.append("/");
                local_path.append(filenames.at(x));
                break;
            }
        }
        if (success)
            break;
     }

    if (!success && config->cout_extensive)
            printf ("Album art: could not find any matching local bitmaps\n");

    // load the image
    if (success)
    {
        if (!im_albumart.load(local_path))
        {
            if (config->cout_extensive)
            {
                printf ("Album art: failed loading file: %s\n", local_path.toStdString().c_str());
            }
            success = false;
        }
    }

    return success;
}

void qm_Player::run_user_action1()
{
    // TP_SONG: 'mpd_path' is relative to MPD's music dir
    // TP_SONGX: 'mpd_path' is relative to '/' root
    // TP_NOSONG, TP_STREAM: 'mpd_path' is ""

    // exceptions are also checked in disenable_menu_items()
    if (!b_mpd_connected)
    {
        printf ("User action 1 failed: not connected to mpd\n");
        return;
    }

    if (config->usract1_command.isEmpty())
    {
        printf ("User action 1 failed: command is empty\n");
        return;
    }

    if (current_songinfo->type != TP_SONG && current_songinfo->type != TP_SONGX)
    {
        printf ("User action 1 failed: stream\n");
        return;
    }

    if (config->mpd_musicdir_status != 1)
    {
        printf ("User action 1 failed: Music directory is not accessible\n");
        return;
    }

    QProcess *proc = nullptr;
    QStringList qsl = config->usract1_command.split(" ");

    QString full_path;
    if (current_type == TP_SONGX)
    {
         full_path = current_mpd_dir;
    }
    else
    if (current_type == TP_SONG)
    {
        full_path = config->mpd_musicpath + "/" + current_mpd_dir;
    }

    QDir dir(full_path);
    if (!dir.exists())
    {
        printf ("User action 1 failed: directory not found: %s\n", full_path.toStdString().c_str());
        return;
    }

    if (config->usract1_fileparm)
    {
        // append filename
        if (current_file_name.isEmpty())
        {
            printf ("User action 1 failed: current file is unknown\n");
            return;
        }

        full_path = full_path + "/" + current_file_name;
    }

    qsl.append(full_path);

    QString executable = qsl.first();
    qsl.removeFirst();

    if (config->cout_extensive)
        printf ("User action 1: %s %s\n",(config->usract2_command).toStdString().c_str(), full_path.toStdString().c_str());

    proc->startDetached(executable, qsl);
}


void qm_Player::run_user_action2()
{
    // TP_SONG: 'mpd_path' is relative to MPD's music dir
    // TP_SONGX: 'mpd_path' is relative to '/' root
    // TP_NOSONG, TP_STREAM: 'mpd_path' is ""

    // exceptions are also checked in disenable_menu_items()
    if (!b_mpd_connected)
    {
        printf ("User action 2 failed: not connected to mpd\n");
        return;
    }

    if (config->usract2_command.isEmpty())
    {
        printf ("User action 1 failed: command is empty\n");
        return;
    }

    if (current_songinfo->type != TP_SONG && current_songinfo->type != TP_SONGX)
    {
        printf ("User action 2 failed: stream\n");
        return;
    }

    if (config->mpd_musicdir_status != 1)
    {
        printf ("User action 2 failed: Music directory is not accessible\n");
        return;
    }

    QProcess *proc = nullptr;
    QStringList qsl = config->usract2_command.split(" ");

    QString full_path;
    if (current_type == TP_SONGX)
    {
        full_path = current_mpd_dir;
    }
    else
        if (current_type == TP_SONG)
        {
            full_path = config->mpd_musicpath + "/" + current_mpd_dir;
        }

    QDir dir(full_path);
    if (!dir.exists())
    {
        printf ("User action 2 failed: directory not found: %s\n", full_path.toStdString().c_str());
        return;
    }

    if (config->usract2_fileparm)
    {
        // append filename
        if (current_file_name.isEmpty())
        {
            printf ("User action 2 failed: current file is unknown\n");
            return;
        }

        full_path = full_path + "/" + current_file_name;
    }

    qsl.append(full_path);

    QString executable = qsl.first();
    qsl.removeFirst();

    if (config->cout_extensive)
        printf ("User action 2: %s %s\n",(config->usract2_command).toStdString().c_str(), full_path.toStdString().c_str());

    proc->startDetached(executable, qsl);
}

void qm_Player::on_new_custom_art()
{
    get_and_set_albumart(current_type, current_art_path);
}

void qm_Player::disenable_menu_items()
{
    if (!b_mpd_connected || current_status == MPD_STATE_UNKNOWN)
    {
        a_fileman->setEnabled(false);
        a_viewer->setEnabled(false);
        a_tagedit->setEnabled(false);
        a_user1->setEnabled(false);
        a_user2->setEnabled(false);
        a_tagscan->setEnabled(false);
        return;
    }
    if (b_remote_server)
    {
        a_fileman->setEnabled(false);
        a_viewer->setEnabled(false);
        a_tagedit->setEnabled(false);
        a_user1->setEnabled(false);
        a_user2->setEnabled(false);
        if (current_songinfo->type == TP_SONG)
            a_tagscan->setEnabled(true);
        else
            a_tagscan->setEnabled(false);
        return;
    }

    if (current_songinfo->type == TP_SONG)
        a_tagscan->setEnabled(true);
    else
        a_tagscan->setEnabled(false);

    if(config->mpd_musicdir_status == MDIR_ACCESSIBLE &&
        (current_songinfo->type == TP_SONG || current_songinfo->type == TP_SONGX) )
    {
        a_fileman->setEnabled(config->file_manager_installed);
        a_viewer->setEnabled(config->image_viewer_installed);
        a_tagedit->setEnabled(config->tag_editor_installed);
        if (config->usract1_command.isEmpty())
            a_user1->setEnabled(false);
        else
            a_user1->setEnabled(config->usract1_enabled);
        if (config->usract2_command.isEmpty())
            a_user2->setEnabled(false);
        else
            a_user2->setEnabled(config->usract2_enabled);
    }
    else
    {
        a_fileman->setEnabled(false);
        a_viewer->setEnabled(false);
        a_tagedit->setEnabled(false);
        a_user1->setEnabled(false);
        a_user2->setEnabled(false);
    }
}

/*
// TEST: This never happens on Wayland
void qm_Player::moveEvent(QMoveEvent *event)
{
    printf("Player moved\n");
    int x = event->pos().x();
    int y = event->pos().y();
    printf("Player x: %d\n", x);
    printf("Player y: %d\n", y);
    event->accept();
}
*/

qm_Player::~qm_Player()
{
    delete (mpd_cmd);
    delete (browser_window);
    delete (settings_window);
    delete (the_trayicon);
    delete (pb_progress);
    delete (lb_time);
    delete (vol_slider);
    delete (current_fonts);
    delete (config);
}
