diff --git a/src/aboutDialog.cpp b/src/aboutDialog.cpp
index 4b5bb52..a57b665 100644
--- a/src/aboutDialog.cpp
+++ b/src/aboutDialog.cpp
@@ -74,7 +74,7 @@ NAboutDialog::NAboutDialog(QWidget *parent) : QDialog(parent)
     QSvgWidget svgWidget(":icon.svg");
     svgWidget.setStyleSheet("background: transparent");
     svgWidget.resize(96, 96);
-    iconLabel->setPixmap(svgWidget.grab());
+    iconLabel->setPixmap(QPixmap::grabWidget(&svgWidget));
 
     QHBoxLayout *iconLayout = new QHBoxLayout;
     iconLayout->addStretch();
diff --git a/src/action.cpp b/src/action.cpp
index 8c52d4d..ccb08d7 100644
--- a/src/action.cpp
+++ b/src/action.cpp
@@ -23,7 +23,7 @@ bool NAction::isEnabled() const
 
 void NAction::setEnabled(bool enable)
 {
-    for (QxtGlobalShortcut *shortcut : m_globalShortcuts) {
+    foreach (QxtGlobalShortcut *shortcut, m_globalShortcuts) {
         shortcut->setEnabled(enable);
     }
     QAction::setEnabled(enable);
@@ -47,7 +47,7 @@ QList<QKeySequence> NAction::sequences() const
 QStringList NAction::shortcuts() const
 {
     QStringList shortcuts;
-    for (const QKeySequence &seq : sequences()) {
+    foreach (const QKeySequence &seq, sequences()) {
         shortcuts << seq.toString();
     }
     return shortcuts;
@@ -61,7 +61,7 @@ void NAction::setSequences(const QList<QKeySequence> &sequences)
 void NAction::setShortcuts(const QStringList &shortcuts)
 {
     QList<QKeySequence> sequences;
-    for (const QString &str : shortcuts) {
+    foreach (const QString &str, shortcuts) {
         sequences << QKeySequence(str);
     }
     setSequences(sequences);
@@ -70,7 +70,7 @@ void NAction::setShortcuts(const QStringList &shortcuts)
 QList<QKeySequence> NAction::globalSequences() const
 {
     QList<QKeySequence> sequences;
-    for (QxtGlobalShortcut *shortcut : m_globalShortcuts) {
+    foreach (QxtGlobalShortcut *shortcut, m_globalShortcuts) {
         sequences << shortcut->shortcut();
     }
     return sequences;
@@ -79,7 +79,7 @@ QList<QKeySequence> NAction::globalSequences() const
 QStringList NAction::globalShortcuts() const
 {
     QStringList shortcuts;
-    for (const QKeySequence &seq : globalSequences()) {
+    foreach (const QKeySequence &seq, globalSequences()) {
         shortcuts << seq.toString();
     }
     return shortcuts;
@@ -87,14 +87,14 @@ QStringList NAction::globalShortcuts() const
 
 void NAction::setGlobalSequences(const QList<QKeySequence> &sequences)
 {
-    for (QxtGlobalShortcut *shortcut : m_globalShortcuts) {
+    foreach (QxtGlobalShortcut *shortcut, m_globalShortcuts) {
         delete shortcut;
     }
     m_globalShortcuts.clear();
 
-    for (const QKeySequence &seq : sequences) {
+    foreach (const QKeySequence &seq, sequences) {
         QxtGlobalShortcut *s = new QxtGlobalShortcut(this);
-        connect(s, &QxtGlobalShortcut::activated, this, &QAction::trigger);
+        connect(s, SIGNAL(activated()), this, SIGNAL(triggered()));
         s->setShortcut(seq);
         m_globalShortcuts << s;
     }
@@ -103,8 +103,14 @@ void NAction::setGlobalSequences(const QList<QKeySequence> &sequences)
 void NAction::setGlobalShortcuts(const QStringList &shortcuts)
 {
     QList<QKeySequence> sequences;
-    for (const QString &str : shortcuts) {
-        sequences << QKeySequence(str);
+    foreach (const QString &str, shortcuts) {
+        // Qt4: Don't create shortcuts for empty or invalid strings
+        if (!str.isEmpty() && str != "@Invalid()") {
+            QKeySequence seq(str);
+            if (!seq.isEmpty()) {
+                sequences << seq;
+            }
+        }
     }
     setGlobalSequences(sequences);
 }
diff --git a/src/action.h b/src/action.h
index 700a138..f2534ef 100644
--- a/src/action.h
+++ b/src/action.h
@@ -25,7 +25,9 @@ class NAction : public QAction
     Q_OBJECT
 
 public:
-    using QAction::QAction;
+    NAction(QObject *parent = 0) : QAction(parent), m_isCustomizable(false) {}
+    NAction(const QString &text, QObject *parent = 0) : QAction(text, parent), m_isCustomizable(false) {}
+    NAction(const QIcon &icon, const QString &text, QObject *parent = 0) : QAction(icon, text, parent), m_isCustomizable(false) {}
 
     bool isEnabled() const;
     void setEnabled(bool enable);
@@ -44,7 +46,7 @@ public:
     void setGlobalShortcuts(const QStringList &shortcuts);
 
 private:
-    bool m_isCustomizable = false;
+    bool m_isCustomizable;
     QList<QxtGlobalShortcut *> m_globalShortcuts;
 };
 
diff --git a/src/actionManager.cpp b/src/actionManager.cpp
index 3103eb4..8fa5ee7 100644
--- a/src/actionManager.cpp
+++ b/src/actionManager.cpp
@@ -16,6 +16,7 @@
 #include "actionManager.h"
 
 #include "action.h"
+#include "common.h"
 #include "coverWidget.h"
 #include "mainWindow.h"
 #include "playbackEngineInterface.h"
@@ -26,6 +27,7 @@
 #include "trash.h"
 
 #include <QApplication>
+#include <QDir>
 #include <QMenu>
 #include <QMenuBar>
 #include <QMessageBox>
@@ -65,7 +68,7 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
         showHideAction->setObjectName("ShowHideAction");
         showHideAction->setStatusTip(tr("Toggle window visibility"));
         showHideAction->setCustomizable(true);
-        connect(showHideAction, &NAction::triggered, player, &NPlayer::toggleWindowVisibility);
+        connect(showHideAction, SIGNAL(triggered()), player, SLOT(toggleWindowVisibility()));
         m_trayIconMenu->addAction(showHideAction);
     }
 
@@ -79,8 +82,8 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
         playAction->setObjectName("PlayAction");
         playAction->setStatusTip(tr("Start playback"));
         playAction->setCustomizable(true);
-        connect(playAction, &NAction::triggered, player->playbackEngine(),
-                &NPlaybackEngineInterface::play);
+        connect(playAction, SIGNAL(triggered()), player->playbackEngine(),
+                SLOT(play()));
         m_trayIconMenu->addAction(playAction);
         controlsMenu->addAction(playAction);
     }
@@ -93,8 +96,8 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
         pauseAction->setObjectName("PauseAction");
         pauseAction->setStatusTip(tr("Pause playback"));
         pauseAction->setCustomizable(true);
-        connect(pauseAction, &NAction::triggered, player->playbackEngine(),
-                &NPlaybackEngineInterface::pause);
+        connect(pauseAction, SIGNAL(triggered()), player->playbackEngine(),
+                SLOT(pause()));
         m_trayIconMenu->addAction(pauseAction);
         controlsMenu->addAction(pauseAction);
     }
@@ -107,7 +110,7 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
         playPauseAction->setObjectName("PlayPauseAction");
         playPauseAction->setStatusTip(tr("Toggle playback"));
         playPauseAction->setCustomizable(true);
-        connect(playPauseAction, &NAction::triggered, player, &NPlayer::playPause);
+        connect(playPauseAction, SIGNAL(triggered()), player, SLOT(playPause()));
         m_trayIconMenu->addAction(playPauseAction);
         controlsMenu->addAction(playPauseAction);
     }
@@ -120,8 +123,8 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
         stopAction->setObjectName("StopAction");
         stopAction->setStatusTip(tr("Stop playback"));
         stopAction->setCustomizable(true);
-        connect(stopAction, &NAction::triggered, player->playbackEngine(),
-                &NPlaybackEngineInterface::stop);
+        connect(stopAction, SIGNAL(triggered()), player->playbackEngine(),
+                SLOT(stop()));
         m_trayIconMenu->addAction(stopAction);
         controlsMenu->addAction(stopAction);
     }
@@ -134,8 +137,8 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
         prevAction->setObjectName("PrevAction");
         prevAction->setStatusTip(tr("Play previous track in playlist"));
         prevAction->setCustomizable(true);
-        connect(prevAction, &NAction::triggered, player->playlistWidget(),
-                &NPlaylistWidget::playPrevItem);
+        connect(prevAction, SIGNAL(triggered()), player->playlistWidget(),
+                SLOT(playPrevItem()));
         m_trayIconMenu->addAction(prevAction);
         controlsMenu->addAction(prevAction);
     }
@@ -148,8 +151,8 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
         nextAction->setObjectName("NextAction");
         nextAction->setStatusTip(tr("Play next track in playlist"));
         nextAction->setCustomizable(true);
-        connect(nextAction, &NAction::triggered, player->playlistWidget(),
-                &NPlaylistWidget::playNextItem);
+        connect(nextAction, SIGNAL(triggered()), player->playlistWidget(),
+                SLOT(playNextItem()));
         m_trayIconMenu->addAction(nextAction);
         controlsMenu->addAction(nextAction);
     }
@@ -161,7 +164,7 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
         NAction *addFilesAction = new NAction(QIcon::fromTheme("add", winIcons.value(171)),
                                               tr("Add Files..."), player);
         addFilesAction->setShortcut(QKeySequence("Ctrl+O"));
-        connect(addFilesAction, &NAction::triggered, player, &NPlayer::showOpenFileDialog);
+        connect(addFilesAction, SIGNAL(triggered()), player, SLOT(showOpenFileDialog()));
         m_contextMenu->addAction(addFilesAction);
         fileMenu->addAction(addFilesAction);
     }
@@ -170,7 +173,7 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
         NAction *addDirAction = new NAction(QIcon::fromTheme("folder-add", winIcons.value(3)),
                                             tr("Add Directory..."), player);
         addDirAction->setShortcut(QKeySequence("Ctrl+Shift+O"));
-        connect(addDirAction, &NAction::triggered, player, &NPlayer::showOpenDirDialog);
+        connect(addDirAction, SIGNAL(triggered()), player, SLOT(showOpenDirDialog()));
         m_contextMenu->addAction(addDirAction);
         fileMenu->addAction(addDirAction);
     }
@@ -180,7 +183,7 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
                                                                    winIcons.value(175)),
                                                   tr("Save Playlist..."), player);
         savePlaylistAction->setShortcut(QKeySequence("Ctrl+S"));
-        connect(savePlaylistAction, &NAction::triggered, player, &NPlayer::showSavePlaylistDialog);
+        connect(savePlaylistAction, SIGNAL(triggered()), player, SLOT(showSavePlaylistDialog()));
         m_contextMenu->addAction(savePlaylistAction);
         fileMenu->addAction(savePlaylistAction);
     }
@@ -192,7 +195,7 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
         NAction *preferencesAction = new NAction(QIcon::fromTheme("configure", winIcons.value(109)),
                                                  tr("Preferences..."), player);
         preferencesAction->setShortcut(QKeySequence("Ctrl+P"));
-        connect(preferencesAction, &NAction::triggered, player, &NPlayer::showPreferencesDialog);
+        connect(preferencesAction, SIGNAL(triggered()), player, SLOT(showPreferencesDialog()));
         m_trayIconMenu->addAction(preferencesAction);
         m_contextMenu->addAction(preferencesAction);
         fileMenu->addAction(preferencesAction);
@@ -204,10 +207,7 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
         NAction *showCoverAction = new NAction(tr("Show Cover Art"), player);
         showCoverAction->setCheckable(true);
         showCoverAction->setObjectName("ShowCoverAction");
-        connect(showCoverAction, &NAction::toggled, [player](bool checked) {
-            player->settings()->setValue("ShowCoverArt", checked);
-            player->coverWidget()->setVisible(checked);
-        });
+        connect(showCoverAction, SIGNAL(toggled(bool)), this, SLOT(on_showCoverAction_toggled(bool)));
         showCoverAction->setChecked(player->settings()->value("ShowCoverArt").toBool());
         windowSubMenu->addAction(showCoverAction);
         windowMenu->addAction(showCoverAction);
@@ -217,8 +217,8 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
         NAction *showPlaybackControlsAction = new NAction(tr("Show Playback Controls"), player);
         showPlaybackControlsAction->setCheckable(true);
         showPlaybackControlsAction->setObjectName("ShowPlaybackControls");
-        connect(showPlaybackControlsAction, &NAction::toggled, player->mainWindow(),
-                &NMainWindow::showPlaybackControls);
+        connect(showPlaybackControlsAction, SIGNAL(toggled(bool)), player->mainWindow(),
+                SLOT(showPlaybackControls(bool)));
         showPlaybackControlsAction->setChecked(
             player->settings()->value("ShowPlaybackControls").toBool());
         windowSubMenu->addAction(showPlaybackControlsAction);
@@ -228,7 +228,7 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
     {
         NAction *aboutAction = new NAction(QIcon::fromTheme("help", winIcons.value(76)),
                                            tr("About"), player);
-        connect(aboutAction, &NAction::triggered, player, &NPlayer::showAboutDialog);
+        connect(aboutAction, SIGNAL(triggered()), player, SLOT(showAboutDialog()));
         m_contextMenu->addAction(aboutAction);
         fileMenu->addAction(aboutAction);
     }
@@ -239,8 +239,8 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
         NAction *exitAction = new NAction(QIcon::fromTheme("exit", winIcons.value(259)), tr("Exit"),
                                           player);
         exitAction->setShortcut(QKeySequence("Ctrl+Q"));
-        connect(exitAction, &NAction::triggered, QCoreApplication::instance(),
-                &QCoreApplication::quit);
+        connect(exitAction, SIGNAL(triggered()), QCoreApplication::instance(),
+                SLOT(quit()));
         m_contextMenu->addAction(exitAction);
         m_trayIconMenu->addAction(exitAction);
         fileMenu->addAction(exitAction);
@@ -250,15 +250,7 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
         NAction *playingOnTopAction = new NAction(tr("On Top During Playback"), player);
         playingOnTopAction->setCheckable(true);
         playingOnTopAction->setObjectName("PlayingOnTopAction");
-        connect(playingOnTopAction, &NAction::toggled, [player](bool checked) {
-            player->settings()->setValue("WhilePlayingOnTop", checked);
-
-            bool alwaysOnTop = player->settings()->value("AlwaysOnTop").toBool();
-            if (!alwaysOnTop) {
-                player->mainWindow()->setOnTop(checked && player->playbackEngine()->state() ==
-                                                              N::PlaybackPlaying);
-            }
-        });
+        connect(playingOnTopAction, SIGNAL(toggled(bool)), this, SLOT(on_playingOnTopAction_toggled(bool)));
         playingOnTopAction->setChecked(player->settings()->value("WhilePlayingOnTop").toBool());
         windowSubMenu->addAction(playingOnTopAction);
         windowMenu->addAction(playingOnTopAction);
@@ -268,14 +260,7 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
         NAction *alwaysOnTopAction = new NAction(tr("Always On Top"), player);
         alwaysOnTopAction->setCheckable(true);
         alwaysOnTopAction->setObjectName("AlwaysOnTopAction");
-        connect(alwaysOnTopAction, &NAction::toggled, [player](bool checked) {
-            player->settings()->setValue("AlwaysOnTop", checked);
-
-            bool whilePlaying = player->settings()->value("WhilePlayingOnTop").toBool();
-            if (!whilePlaying || player->playbackEngine()->state() != N::PlaybackPlaying) {
-                player->mainWindow()->setOnTop(checked);
-            }
-        });
+        connect(alwaysOnTopAction, SIGNAL(toggled(bool)), this, SLOT(on_alwaysOnTopAction_toggled(bool)));
         alwaysOnTopAction->setChecked(player->settings()->value("AlwaysOnTop").toBool());
         windowSubMenu->addAction(alwaysOnTopAction);
         windowMenu->addAction(alwaysOnTopAction);
@@ -286,8 +271,8 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
         fullScreenAction->setStatusTip(tr("Hide all controls except waveform"));
         fullScreenAction->setObjectName("FullScreenAction");
         fullScreenAction->setCustomizable(true);
-        connect(fullScreenAction, &NAction::triggered, player->mainWindow(),
-                &NMainWindow::toggleFullScreen);
+        connect(fullScreenAction, SIGNAL(triggered()), player->mainWindow(),
+                SLOT(toggleFullScreen()));
         windowSubMenu->addAction(fullScreenAction);
         windowMenu->addAction(fullScreenAction);
     }
@@ -297,8 +282,8 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
         shufflePlaylistAction->setObjectName("ShufflePlaylistAction");
         shufflePlaylistAction->setStatusTip(tr("Shuffle items in playlist"));
         shufflePlaylistAction->setCustomizable(true);
-        connect(shufflePlaylistAction, &NAction::triggered, player->playlistWidget(),
-                &NPlaylistWidget::shufflePlaylist);
+        connect(shufflePlaylistAction, SIGNAL(triggered()), player->playlistWidget(),
+                SLOT(shufflePlaylist()));
         playlistSubMenu->addAction(shufflePlaylistAction);
         controlsPlaylistSubMenu->addAction(shufflePlaylistAction);
     }
@@ -309,10 +294,10 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
         repeatPlaylistAction->setObjectName("RepeatPlaylistAction");
         repeatPlaylistAction->setStatusTip(tr("Toggle current item repeat"));
         repeatPlaylistAction->setCustomizable(true);
-        connect(repeatPlaylistAction, &NAction::triggered, player->playlistWidget(),
-                &NPlaylistWidget::setRepeatMode);
-        connect(player->playlistWidget(), &NPlaylistWidget::repeatModeChanged, repeatPlaylistAction,
-                &NAction::setChecked);
+        connect(repeatPlaylistAction, SIGNAL(triggered(bool)), player->playlistWidget(),
+                SLOT(setRepeatMode(bool)));
+        connect(player->playlistWidget(), SIGNAL(repeatModeChanged(bool)), repeatPlaylistAction,
+                SLOT(setChecked(bool)));
         repeatPlaylistAction->setChecked(player->settings()->value("Repeat").toBool());
         playlistSubMenu->addAction(repeatPlaylistAction);
         controlsPlaylistSubMenu->addAction(repeatPlaylistAction);
@@ -322,8 +307,7 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
         NAction *loopPlaylistAction = new NAction(tr("Loop playlist"), player);
         loopPlaylistAction->setCheckable(true);
         loopPlaylistAction->setObjectName("LoopPlaylistAction");
-        connect(loopPlaylistAction, &NAction::triggered,
-                [player](bool checked) { player->settings()->setValue("LoopPlaylist", checked); });
+        connect(loopPlaylistAction, SIGNAL(triggered(bool)), this, SLOT(on_loopPlaylistAction_triggered(bool)));
         loopPlaylistAction->setChecked(player->settings()->value("LoopPlaylist").toBool());
         playlistSubMenu->addAction(loopPlaylistAction);
         controlsPlaylistSubMenu->addAction(loopPlaylistAction);
@@ -335,8 +319,7 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
         scrollToItemPlaylistAction->setStatusTip(
             tr("Automatically scroll playlist to currently playing item"));
         scrollToItemPlaylistAction->setObjectName("ScrollToItemPlaylistAction");
-        connect(scrollToItemPlaylistAction, &NAction::triggered,
-                [player](bool checked) { player->settings()->setValue("ScrollToItem", checked); });
+        connect(scrollToItemPlaylistAction, SIGNAL(triggered(bool)), this, SLOT(on_scrollToItemPlaylistAction_triggered(bool)));
         scrollToItemPlaylistAction->setChecked(player->settings()->value("ScrollToItem").toBool());
         playlistSubMenu->addAction(scrollToItemPlaylistAction);
         controlsPlaylistSubMenu->addAction(scrollToItemPlaylistAction);
@@ -347,8 +330,7 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
                                                     player);
         nextFileEnableAction->setCheckable(true);
         nextFileEnableAction->setObjectName("NextFileEnableAction");
-        connect(nextFileEnableAction, &NAction::triggered,
-                [player](bool checked) { player->settings()->setValue("LoadNext", checked); });
+        connect(nextFileEnableAction, SIGNAL(triggered(bool)), this, SLOT(on_nextFileEnableAction_triggered(bool)));
         nextFileEnableAction->setChecked(player->settings()->value("LoadNext").toBool());
         playlistSubMenu->addAction(nextFileEnableAction);
         controlsPlaylistSubMenu->addAction(nextFileEnableAction);
@@ -357,9 +339,7 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
             new NAction(QString::fromUtf8("    ├  %1 ↓").arg(tr("By Name")), player);
         nextFileByNameAscdAction->setCheckable(true);
         nextFileByNameAscdAction->setObjectName("NextFileByNameAscdAction");
-        connect(nextFileByNameAscdAction, &NAction::triggered, [player](bool checked) {
-            player->settings()->setValue("LoadNextSort", (int)QDir::Name);
-        });
+        connect(nextFileByNameAscdAction, SIGNAL(triggered(bool)), this, SLOT(on_nextFileByNameAscdAction_triggered(bool)));
         playlistSubMenu->addAction(nextFileByNameAscdAction);
         controlsPlaylistSubMenu->addAction(nextFileByNameAscdAction);
 
@@ -367,9 +347,7 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
             new NAction(QString::fromUtf8("    ├  %1 ↑").arg(tr("By Name")), player);
         nextFileByNameDescAction->setCheckable(true);
         nextFileByNameDescAction->setObjectName("NextFileByNameDescAction");
-        connect(nextFileByNameDescAction, &NAction::triggered, [player](bool checked) {
-            player->settings()->setValue("LoadNextSort", (int)(QDir::Name | QDir::Reversed));
-        });
+        connect(nextFileByNameDescAction, SIGNAL(triggered(bool)), this, SLOT(on_nextFileByNameDescAction_triggered(bool)));
         playlistSubMenu->addAction(nextFileByNameDescAction);
         controlsPlaylistSubMenu->addAction(nextFileByNameDescAction);
 
@@ -377,9 +355,7 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
             new NAction(QString::fromUtf8("    ├  %1 ↓").arg(tr("By Date")), player);
         nextFileByDateAscd->setCheckable(true);
         nextFileByDateAscd->setObjectName("NextFileByDateAscd");
-        connect(nextFileByDateAscd, &NAction::triggered, [player](bool checked) {
-            player->settings()->setValue("LoadNextSort", (int)(QDir::Time | QDir::Reversed));
-        });
+        connect(nextFileByDateAscd, SIGNAL(triggered(bool)), this, SLOT(on_nextFileByDateAscdAction_triggered(bool)));
         playlistSubMenu->addAction(nextFileByDateAscd);
         controlsPlaylistSubMenu->addAction(nextFileByDateAscd);
 
@@ -387,9 +363,7 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
             new NAction(QString::fromUtf8("    └  %1 ↑").arg(tr("By Date")), player);
         nextFileByDateDesc->setCheckable(true);
         nextFileByDateDesc->setObjectName("NextFileByDateDesc");
-        connect(nextFileByDateDesc, &NAction::triggered, [player](bool checked) {
-            player->settings()->setValue("LoadNextSort", (int)(QDir::Time));
-        });
+        connect(nextFileByDateDesc, SIGNAL(triggered(bool)), this, SLOT(on_nextFileByDateDescAction_triggered(bool)));
         playlistSubMenu->addAction(nextFileByDateDesc);
         controlsPlaylistSubMenu->addAction(nextFileByDateDesc);
 
@@ -417,18 +391,7 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
         revealAction->setObjectName("RevealInFileManagerAction");
         revealAction->setStatusTip(tr("Open file manager for selected file"));
         revealAction->setCustomizable(true);
-        connect(revealAction, &NAction::triggered, [player]() {
-            QStringList files = player->playlistWidget()->selectedFiles();
-            if (files.isEmpty()) {
-                return;
-            }
-
-            QString error;
-            if (!player->revealInFileManager(files.first(), &error)) {
-                QMessageBox::warning(player->mainWindow(), tr("Reveal in File Manager Error"),
-                                     error, QMessageBox::Close);
-            }
-        });
+        connect(revealAction, SIGNAL(triggered()), this, SLOT(on_revealAction_triggered()));
         m_playlistContextMenu->addAction(revealAction);
     }
 
@@ -438,8 +401,8 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
         removeSelectedAction->setObjectName("RemoveFromPlaylistAction");
         removeSelectedAction->setStatusTip(tr("Remove selected files from playlist"));
         removeSelectedAction->setCustomizable(true);
-        connect(removeSelectedAction, &NAction::triggered, player->playlistWidget(),
-                &NPlaylistWidget::removeSelected);
+        connect(removeSelectedAction, SIGNAL(triggered()), player->playlistWidget(),
+                SLOT(removeSelected()));
         m_playlistContextMenu->addAction(removeSelectedAction);
     }
 
@@ -450,15 +413,7 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
         trashSelectedAction->setObjectName("MoveToTrashAction");
         trashSelectedAction->setStatusTip(tr("Move selected files to trash bin"));
         trashSelectedAction->setCustomizable(true);
-        connect(trashSelectedAction, &NAction::triggered, [player]() {
-            QStringList files = player->playlistWidget()->selectedFiles();
-            if (files.isEmpty()) {
-                return;
-            }
-
-            QStringList deleted = NTrash::moveToTrash(files);
-            player->playlistWidget()->removeFiles(deleted);
-        });
+        connect(trashSelectedAction, SIGNAL(triggered()), this, SLOT(on_trashSelectedAction_triggered()));
         m_playlistContextMenu->addAction(trashSelectedAction);
     }
 
@@ -468,13 +423,7 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
         tagEditorAction->setObjectName("TagEditorAction");
         tagEditorAction->setStatusTip(tr("Open tag editor for selected file"));
         tagEditorAction->setCustomizable(true);
-        connect(tagEditorAction, &NAction::triggered, [player]() {
-            QStringList files = player->playlistWidget()->selectedFiles();
-            if (files.isEmpty()) {
-                return;
-            }
-            player->showTagEditor(files.first());
-        });
+        connect(tagEditorAction, SIGNAL(triggered()), this, SLOT(on_tagEditorAction_triggered()));
         m_playlistContextMenu->addAction(tagEditorAction);
     }
 
@@ -485,19 +434,13 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
         jumpForwardAction->setObjectName(QString("Jump%1ForwardAction").arg(num));
         jumpForwardAction->setStatusTip(tr("Make a jump forward #%1").arg(num));
         jumpForwardAction->setCustomizable(true);
-        connect(jumpForwardAction, &NAction::triggered, [player, num]() {
-            qreal seconds = player->settings()->value(QString("Jump%1").arg(num)).toDouble();
-            player->playbackEngine()->jump(seconds * 1000);
-        });
+        connect(jumpForwardAction, SIGNAL(triggered()), this, SLOT(on_jumpForwardAction_triggered()));
 
         NAction *jumpBackwardsAction = new NAction(tr("Jump Backwards #%1").arg(num), player);
         jumpBackwardsAction->setObjectName(QString("Jump%1BackwardsAction").arg(num));
         jumpBackwardsAction->setStatusTip(tr("Make a jump backwards #%1").arg(num));
         jumpBackwardsAction->setCustomizable(true);
-        connect(jumpBackwardsAction, &NAction::triggered, [player, num]() {
-            qreal seconds = player->settings()->value(QString("Jump%1").arg(num)).toDouble();
-            player->playbackEngine()->jump(-seconds * 1000);
-        });
+        connect(jumpBackwardsAction, SIGNAL(triggered()), this, SLOT(on_jumpBackwardsAction_triggered()));
     }
 
     {
@@ -505,33 +448,19 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
         speedIncreaseAction->setObjectName("SpeedIncreaseAction");
         speedIncreaseAction->setStatusTip(tr("Increase playback speed"));
         speedIncreaseAction->setCustomizable(true);
-        connect(speedIncreaseAction, &NAction::triggered, [player]() {
-            qreal newSpeed = qMax(0.01, player->playbackEngine()->speed() +
-                                            player->settings()->value("SpeedStep").toDouble());
-            player->playbackEngine()->setSpeed(newSpeed);
-            player->showToolTip(tr("Speed: %1").arg(player->playbackEngine()->speed()));
-        });
+        connect(speedIncreaseAction, SIGNAL(triggered()), this, SLOT(on_speedIncreaseAction_triggered()));
 
         NAction *speedDecreaseAction = new NAction(tr("Speed Decrease"), player);
         speedDecreaseAction->setObjectName("SpeedDecreaseAction");
         speedDecreaseAction->setStatusTip(tr("Decrease playback speed"));
         speedDecreaseAction->setCustomizable(true);
-        connect(speedDecreaseAction, &NAction::triggered, [player]() {
-            qreal newSpeed = qMax(0.01, player->playbackEngine()->speed() -
-                                            player->settings()->value("SpeedStep").toDouble());
-            player->playbackEngine()->setSpeed(newSpeed);
-            player->showToolTip(tr("Speed: %1").arg(player->playbackEngine()->speed()));
-        });
+        connect(speedDecreaseAction, SIGNAL(triggered()), this, SLOT(on_speedDecreaseAction_triggered()));
 
         NAction *speedResetAction = new NAction(tr("Speed Reset"), player);
         speedResetAction->setObjectName("SpeedResetAction");
         speedResetAction->setStatusTip(tr("Reset playback speed to 1.0"));
         speedResetAction->setCustomizable(true);
-        connect(speedResetAction, &NAction::triggered, [player]() {
-            qreal newSpeed = 1.0;
-            player->playbackEngine()->setSpeed(newSpeed);
-            player->showToolTip(tr("Speed: %1").arg(player->playbackEngine()->speed()));
-        });
+        connect(speedResetAction, SIGNAL(triggered()), this, SLOT(on_speedResetAction_triggered()));
     }
 
     /*
@@ -540,37 +469,23 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
         pitchIncreaseAction->setObjectName("PitchIncreaseAction");
         pitchIncreaseAction->setStatusTip(tr("Increase playback pitch"));
         pitchIncreaseAction->setCustomizable(true);
-        connect(pitchIncreaseAction, &NAction::triggered, [player]() {
-            qreal newPitch = qMax(0.01, player->playbackEngine()->pitch() +
-                                            player->settings()->value("PitchStep").toDouble());
-            player->playbackEngine()->setPitch(newPitch);
-            player->showToolTip(tr("Pitch: %1").arg(player->playbackEngine()->pitch()));
-        });
+        connect(pitchIncreaseAction, SIGNAL(triggered()), this, SLOT(on_pitchIncreaseAction_triggered()));
 
         NAction *pitchDecreaseAction = new NAction(tr("Pitch Decrease"), player);
         pitchDecreaseAction->setObjectName("PitchDecreaseAction");
         pitchDecreaseAction->setStatusTip(tr("Decrease playback pitch"));
         pitchDecreaseAction->setCustomizable(true);
-        connect(pitchDecreaseAction, &NAction::triggered, [player]() {
-            qreal newPitch = qMax(0.01, player->playbackEngine()->pitch() -
-                                            player->settings()->value("PitchStep").toDouble());
-            player->playbackEngine()->setPitch(newPitch);
-            player->showToolTip(tr("Pitch: %1").arg(player->playbackEngine()->pitch()));
-        });
+        connect(pitchDecreaseAction, SIGNAL(triggered()), this, SLOT(on_pitchDecreaseAction_triggered()));
 
         NAction *pitchResetAction = new NAction(tr("Pitch Reset"), player);
         pitchResetAction->setObjectName("PitchResetAction");
         pitchResetAction->setStatusTip(tr("Reset pitch to 1.0"));
         pitchResetAction->setCustomizable(true);
-        connect(pitchResetAction, &NAction::triggered, [player]() {
-            qreal newPitch = 1.0;
-            player->playbackEngine()->setPitch(newPitch);
-            player->showToolTip(tr("Pitch: %1").arg(player->playbackEngine()->pitch()));
-        });
+        connect(pitchResetAction, SIGNAL(triggered()), this, SLOT(on_pitchResetAction_triggered()));
     }
     */
 
-    for (NAction *action : m_player->findChildren<NAction *>()) {
+    foreach (NAction *action, m_player->findChildren<NAction *>()) {
 #ifdef Q_OS_MAC
         // remove icons for macOS:
         action->setIcon(QIcon());
@@ -588,7 +503,7 @@ NActionManager::NActionManager(NPlayer *player) : QObject(player)
 
 void NActionManager::saveSettings()
 {
-    for (NAction *action : m_player->findChildren<NAction *>()) {
+    foreach (NAction *action, m_player->findChildren<NAction *>()) {
         if (action->objectName().isEmpty() || !action->isCustomizable()) {
             continue;
         }
@@ -612,3 +527,164 @@ QMenu *NActionManager::trayIconMenu()
 {
     return m_trayIconMenu;
 }
+
+// Slot implementations for lambdas converted for Qt4 compatibility
+
+void NActionManager::on_showCoverAction_toggled(bool checked)
+{
+    m_player->settings()->setValue("ShowCoverArt", checked);
+    m_player->coverWidget()->setVisible(checked);
+}
+
+void NActionManager::on_playingOnTopAction_toggled(bool checked)
+{
+    m_player->settings()->setValue("WhilePlayingOnTop", checked);
+
+    bool alwaysOnTop = m_player->settings()->value("AlwaysOnTop").toBool();
+    if (!alwaysOnTop) {
+        m_player->mainWindow()->setOnTop(checked && m_player->playbackEngine()->state() ==
+                                                      N::PlaybackPlaying);
+    }
+}
+
+void NActionManager::on_alwaysOnTopAction_toggled(bool checked)
+{
+    m_player->settings()->setValue("AlwaysOnTop", checked);
+
+    bool whilePlaying = m_player->settings()->value("WhilePlayingOnTop").toBool();
+    if (!whilePlaying || m_player->playbackEngine()->state() != N::PlaybackPlaying) {
+        m_player->mainWindow()->setOnTop(checked);
+    }
+}
+
+void NActionManager::on_loopPlaylistAction_triggered(bool checked)
+{
+    m_player->settings()->setValue("LoopPlaylist", checked);
+}
+
+void NActionManager::on_scrollToItemPlaylistAction_triggered(bool checked)
+{
+    m_player->settings()->setValue("ScrollToItem", checked);
+}
+
+void NActionManager::on_nextFileEnableAction_triggered(bool checked)
+{
+    m_player->settings()->setValue("LoadNext", checked);
+}
+
+void NActionManager::on_nextFileByNameAscdAction_triggered(bool checked)
+{
+    Q_UNUSED(checked);
+    m_player->settings()->setValue("LoadNextSort", (int)QDir::Name);
+}
+
+void NActionManager::on_nextFileByNameDescAction_triggered(bool checked)
+{
+    Q_UNUSED(checked);
+    m_player->settings()->setValue("LoadNextSort", (int)(QDir::Name | QDir::Reversed));
+}
+
+void NActionManager::on_nextFileByDateAscdAction_triggered(bool checked)
+{
+    Q_UNUSED(checked);
+    m_player->settings()->setValue("LoadNextSort", (int)(QDir::Time | QDir::Reversed));
+}
+
+void NActionManager::on_nextFileByDateDescAction_triggered(bool checked)
+{
+    Q_UNUSED(checked);
+    m_player->settings()->setValue("LoadNextSort", (int)(QDir::Time));
+}
+
+void NActionManager::on_revealAction_triggered()
+{
+    QStringList files = m_player->playlistWidget()->selectedFiles();
+    if (files.isEmpty()) {
+        return;
+    }
+
+    QString error;
+    if (!m_player->revealInFileManager(files.first(), &error)) {
+        QMessageBox::warning(m_player->mainWindow(), tr("Reveal in File Manager Error"),
+                             error, QMessageBox::Close);
+    }
+}
+
+void NActionManager::on_trashSelectedAction_triggered()
+{
+    QStringList files = m_player->playlistWidget()->selectedFiles();
+    if (files.isEmpty()) {
+        return;
+    }
+
+    QStringList deleted = NTrash::moveToTrash(files);
+    m_player->playlistWidget()->removeFiles(deleted);
+}
+
+void NActionManager::on_tagEditorAction_triggered()
+{
+    QStringList files = m_player->playlistWidget()->selectedFiles();
+    if (files.isEmpty()) {
+        return;
+    }
+
+    m_player->showTagEditor(files.first());
+}
+
+void NActionManager::on_jumpForwardAction_triggered()
+{
+    qint64 num = 10 * 1000;
+    m_player->playbackEngine()->jump(num);
+}
+
+void NActionManager::on_jumpBackwardsAction_triggered()
+{
+    qint64 num = 10 * 1000;
+    m_player->playbackEngine()->jump(-num);
+}
+
+void NActionManager::on_speedIncreaseAction_triggered()
+{
+    qreal newSpeed = qMax(0.01, m_player->playbackEngine()->speed() +
+                                    m_player->settings()->value("SpeedStep").toDouble());
+    m_player->playbackEngine()->setSpeed(newSpeed);
+    m_player->showToolTip(tr("Speed: %1").arg(m_player->playbackEngine()->speed()));
+}
+
+void NActionManager::on_speedDecreaseAction_triggered()
+{
+    qreal newSpeed = qMax(0.01, m_player->playbackEngine()->speed() -
+                                    m_player->settings()->value("SpeedStep").toDouble());
+    m_player->playbackEngine()->setSpeed(newSpeed);
+    m_player->showToolTip(tr("Speed: %1").arg(m_player->playbackEngine()->speed()));
+}
+
+void NActionManager::on_speedResetAction_triggered()
+{
+    qreal newSpeed = 1.0;
+    m_player->playbackEngine()->setSpeed(newSpeed);
+    m_player->showToolTip(tr("Speed: %1").arg(m_player->playbackEngine()->speed()));
+}
+
+void NActionManager::on_pitchIncreaseAction_triggered()
+{
+    qreal newPitch = qMax(0.01, m_player->playbackEngine()->pitch() +
+                                    m_player->settings()->value("PitchStep").toDouble());
+    m_player->playbackEngine()->setPitch(newPitch);
+    m_player->showToolTip(tr("Pitch: %1").arg(m_player->playbackEngine()->pitch()));
+}
+
+void NActionManager::on_pitchDecreaseAction_triggered()
+{
+    qreal newPitch = qMax(0.01, m_player->playbackEngine()->pitch() -
+                                    m_player->settings()->value("PitchStep").toDouble());
+    m_player->playbackEngine()->setPitch(newPitch);
+    m_player->showToolTip(tr("Pitch: %1").arg(m_player->playbackEngine()->pitch()));
+}
+
+void NActionManager::on_pitchResetAction_triggered()
+{
+    qreal newPitch = 1.0;
+    m_player->playbackEngine()->setPitch(newPitch);
+    m_player->showToolTip(tr("Pitch: %1").arg(m_player->playbackEngine()->pitch()));
+}
diff --git a/src/actionManager.h b/src/actionManager.h
index c43ea20..296fd43 100644
--- a/src/actionManager.h
+++ b/src/actionManager.h
@@ -32,6 +32,29 @@ public:
     QMenu *playlistContextMenu();
     QMenu *trayIconMenu();
 
+private slots:
+    void on_showCoverAction_toggled(bool checked);
+    void on_playingOnTopAction_toggled(bool checked);
+    void on_alwaysOnTopAction_toggled(bool checked);
+    void on_loopPlaylistAction_triggered(bool checked);
+    void on_scrollToItemPlaylistAction_triggered(bool checked);
+    void on_nextFileEnableAction_triggered(bool checked);
+    void on_nextFileByNameAscdAction_triggered(bool checked);
+    void on_nextFileByNameDescAction_triggered(bool checked);
+    void on_nextFileByDateAscdAction_triggered(bool checked);
+    void on_nextFileByDateDescAction_triggered(bool checked);
+    void on_revealAction_triggered();
+    void on_trashSelectedAction_triggered();
+    void on_tagEditorAction_triggered();
+    void on_jumpForwardAction_triggered();
+    void on_jumpBackwardsAction_triggered();
+    void on_speedIncreaseAction_triggered();
+    void on_speedDecreaseAction_triggered();
+    void on_speedResetAction_triggered();
+    void on_pitchIncreaseAction_triggered();
+    void on_pitchDecreaseAction_triggered();
+    void on_pitchResetAction_triggered();
+
 private:
     NPlayer *m_player;
     QMenu *m_contextMenu;
diff --git a/src/common.cpp b/src/common.cpp
index b625651..c9c5862 100644
--- a/src/common.cpp
+++ b/src/common.cpp
@@ -73,7 +73,9 @@ QString NCore::rcDir()
     if (!_rcDir_init) {
 #ifndef Q_OS_WIN
         QDir parentDir(QCoreApplication::applicationDirPath());
-        if (parentDir.dirName() == "bin") {
+        // Qt4: On macOS, check for .app bundle structure (Contents/MacOS)
+        // Also check for standard bin directory installations
+        if (parentDir.dirName() == "bin" || parentDir.dirName() == "MacOS") {
             _rcDir = QDir::homePath() + "/.nulloy";
         } else {
             _rcDir = QCoreApplication::applicationDirPath();
@@ -102,7 +104,8 @@ QString NCore::rcDir()
 #endif
         QDir dir(_rcDir);
         if (!dir.exists()) {
-            dir.mkdir(_rcDir);
+            // Qt4: Use mkpath() to create directory and any parent directories
+            QDir().mkpath(_rcDir);
         }
 
         _rcDir_init = true;
diff --git a/src/interfaces/coverReaderInterface.h b/src/interfaces/coverReaderInterface.h
index ee45a4e..13d2da4 100644
--- a/src/interfaces/coverReaderInterface.h
+++ b/src/interfaces/coverReaderInterface.h
@@ -16,6 +16,8 @@
 #ifndef N_COVER_ART_READER_INTERFACE_H
 #define N_COVER_ART_READER_INTERFACE_H
 
+#include <QImage>
+#include <QList>
 #include <QObject>
 
 class QString;
diff --git a/src/interfaces/tagReaderInterface.h b/src/interfaces/tagReaderInterface.h
index a216a31..4c48cf9 100644
--- a/src/interfaces/tagReaderInterface.h
+++ b/src/interfaces/tagReaderInterface.h
@@ -38,7 +38,7 @@ public:
 
     virtual bool isWriteSupported() const { return false; }
     virtual QMap<QString, QStringList> getTags() const { return QMap<QString, QStringList>(); }
-    virtual QMap<QString, QStringList> setTags(const QMap<QString, QStringList> &) {}
+    virtual QMap<QString, QStringList> setTags(const QMap<QString, QStringList> &) { return QMap<QString, QStringList>(); }
 };
 
 Q_DECLARE_INTERFACE(NTagReaderInterface, TAGREADER_INTERFACE)
diff --git a/src/main.cpp b/src/main.cpp
index 34868d8..4f0e791 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -21,7 +21,8 @@
 
 #ifndef _N_NO_SKINS_
 #include "skinFileSystem.h"
-Q_IMPORT_PLUGIN(NWidgetCollection)
+// Qt4 Q_IMPORT_PLUGIN must match Q_EXPORT_PLUGIN2 first parameter
+Q_IMPORT_PLUGIN(widgetcollection)
 #endif
 
 bool logToFile = false;
@@ -56,17 +57,16 @@ static void print_try()
     print_out("Try `" + NCore::applicationBasenameName() + " --help' for more information");
 }
 
-void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
+// Qt4 uses different message handler signature (no QMessageLogContext, const char* instead of QString)
+void messageHandler(QtMsgType type, const char *msg)
 {
-    Q_UNUSED(context);
-    print_err(msg);
+    QString message = QString::fromLocal8Bit(msg);
+    print_err(message);
 
     if (logToFile) {
         QString prefix;
         switch (type) {
-            case QtInfoMsg:
-                prefix = "Info";
-                break;
+            // QtInfoMsg doesn't exist in Qt4, added in Qt5
             case QtDebugMsg:
                 prefix = "Debug";
                 break;
@@ -85,7 +85,7 @@ void messageHandler(QtMsgType type, const QMessageLogContext &context, const QSt
         QTextStream stream(&logFile);
         stream << QString("%1 %2: %3")
                       .arg(QTime::currentTime().toString("hh:mm:ss.zzz"), prefix,
-                           msg.toLocal8Bit().constData())
+                           message.toLocal8Bit().constData())
                << endl;
         logFile.close();
     }
@@ -100,8 +100,7 @@ int main(int argc, char *argv[])
     QCoreApplication::addLibraryPath(QFileInfo(argv[0]).dir().path() + "/plugins/");
 #endif
 
-    QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
-    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
+    // Qt4 doesn't support HiDPI attributes (AA_UseHighDpiPixmaps and AA_EnableHighDpiScaling are Qt5+)
 
 #ifdef Q_OS_MAC
     // https://bugreports.qt-project.org/browse/QTBUG-32789
@@ -119,7 +118,8 @@ int main(int argc, char *argv[])
     instance.setOrganizationDomain("nulloy.com");
     instance.setQuitOnLastWindowClosed(false);
 
-    qInstallMessageHandler(messageHandler);
+    // Qt4 uses qInstallMsgHandler() instead of Qt5's qInstallMessageHandler()
+    qInstallMsgHandler(messageHandler);
 
     QStringList argList = instance.arguments();
     argList.takeFirst();
diff --git a/src/mainWindow.cpp b/src/mainWindow.cpp
index 0a7ec55..c53293e 100644
--- a/src/mainWindow.cpp
+++ b/src/mainWindow.cpp
@@ -19,7 +19,7 @@
 #include "settings.h"
 
 #ifndef _N_NO_SKINS_
-#include <QUiLoader>
+#include "customUiLoader.h"
 
 #include "skinFileSystem.h"
 #endif
@@ -35,6 +35,7 @@
 #endif
 #endif
 
+#include <QAbstractItemView>
 #include <QApplication>
 #include <QDesktopWidget>
 #include <QEvent>
@@ -54,7 +55,7 @@ NMainWindow::NMainWindow(const QString &uiFile, QWidget *parent) : QDialog(paren
     setObjectName("mainWindow");
 
 #ifndef _N_NO_SKINS_
-    QUiLoader loader;
+    NCustomUiLoader loader;
     QFile formFile(uiFile);
     formFile.open(QIODevice::ReadOnly);
     QWidget *form = loader.load(&formFile);
@@ -66,6 +67,31 @@ NMainWindow::NMainWindow(const QString &uiFile, QWidget *parent) : QDialog(paren
     setLayout(layout);
     setStyleSheet(form->styleSheet());
     form->setStyleSheet("");
+
+    // Qt4: Explicitly enable acceptDrops and dragEnabled for custom widgets AFTER reparenting
+    // QUiLoader in Qt4 may not properly apply these properties from .ui file
+    // Must be done after widgets are added to the main window layout
+    QList<QWidget *> allWidgets = findChildren<QWidget *>();
+    foreach (QWidget *widget, allWidgets) {
+        QString className = widget->metaObject()->className();
+        if (className == "NPlaylistWidget") {
+            widget->setAcceptDrops(true);
+            // Also ensure drag-drop mode is set for QListWidget-based widgets
+            QAbstractItemView *itemView = qobject_cast<QAbstractItemView *>(widget);
+            if (itemView) {
+                itemView->setDragDropMode(QAbstractItemView::DragDrop);
+                itemView->setDragEnabled(true);
+                itemView->setAcceptDrops(true);
+                itemView->setDropIndicatorShown(true);
+                // Qt4: Ensure viewport also accepts drops
+                if (itemView->viewport()) {
+                    itemView->viewport()->setAcceptDrops(true);
+                }
+            }
+        } else if (className == "NWaveformSlider") {
+            widget->setAcceptDrops(true);
+        }
+    }
 #else
     Q_UNUSED(uiFile)
     ui.setupUi(this);
@@ -450,7 +476,9 @@ void NMainWindow::wheelEvent(QWheelEvent *event)
 {
     QDialog::wheelEvent(event);
 
-    if (event->orientation() == Qt::Vertical) {
+    // Qt4: Only emit scrolled for global volume control if the event
+    // was not already handled by a child widget (like playlist scrolling)
+    if (event->orientation() == Qt::Vertical && !event->isAccepted()) {
         emit scrolled(event->delta());
     }
 }
diff --git a/src/messageBox.cpp b/src/messageBox.cpp
index 578d460..427d71e 100644
--- a/src/messageBox.cpp
+++ b/src/messageBox.cpp
@@ -20,7 +20,7 @@
 #include "messageBox.h"
 
 NMessageBox::NMessageBox(int width, QObject &centerInTarget, QWidget *parent)
-    : QMessageBox(parent), m_centerInWindow{centerInTarget}
+    : QMessageBox(parent), m_centerInWindow(centerInTarget)
 {
     m_width = width;
     m_resized = false;
diff --git a/src/messageBox.h b/src/messageBox.h
index 7e91728..61da432 100644
--- a/src/messageBox.h
+++ b/src/messageBox.h
@@ -18,10 +18,10 @@
 class NMessageBox : public QMessageBox
 {
 public:
-    NMessageBox(int width, QObject &centerInTarget, QWidget *parent = nullptr);
+    NMessageBox(int width, QObject &centerInTarget, QWidget *parent = 0);
 
 protected:
-    void resizeEvent(QResizeEvent *event) override;
+    void resizeEvent(QResizeEvent *event);
 
 private:
     int m_width;
diff --git a/src/platform/trash.cpp b/src/platform/trash.cpp
index 074fcbc..55fca41 100644
--- a/src/platform/trash.cpp
+++ b/src/platform/trash.cpp
@@ -20,6 +20,7 @@
 #include <QCheckBox>
 #include <QFile>
 #include <QFileInfo>
+#include <QGridLayout>
 #include <QMessageBox>
 #include <QProcess>
 
@@ -38,7 +39,11 @@ QStringList NTrash::moveToTrash(QStringList files)
                                    .arg(QFileInfo(file).fileName()),
                                QMessageBox::Yes | QMessageBox::Cancel, NULL);
             msgBox.setDefaultButton(QMessageBox::Cancel);
-            msgBox.setCheckBox(checkBox);
+            // Qt4 doesn't have setCheckBox(), add checkbox manually to layout
+            QGridLayout *layout = qobject_cast<QGridLayout *>(msgBox.layout());
+            if (layout) {
+                layout->addWidget(checkBox, layout->rowCount(), 0, 1, layout->columnCount());
+            }
             int res = msgBox.exec();
 
             if (res != QMessageBox::Yes) {
diff --git a/src/player.cpp b/src/player.cpp
index 32d1da6..e672201 100644
--- a/src/player.cpp
+++ b/src/player.cpp
@@ -71,7 +71,12 @@ NPlayer::NPlayer()
 
     QString styleName = m_settings->value("Style").toString();
     if (!styleName.isEmpty()) {
-        QApplication::setStyle(styleName);
+        // Qt4: Normalize style names (remove parenthetical additions)
+        // "Macintosh (aqua)" -> "Macintosh"
+        styleName = styleName.split('(').first().trimmed();
+        if (!styleName.isEmpty()) {
+            QApplication::setStyle(styleName);
+        }
     }
 
     NI18NLoader::init();
@@ -175,13 +180,12 @@ NPlayer::NPlayer()
     connectSignals();
 
     m_settingsSaveTimer = new QTimer(this);
-    connect(m_settingsSaveTimer, &QTimer::timeout, [this]() { saveSettings(); });
+    connect(m_settingsSaveTimer, SIGNAL(timeout()), this, SLOT(on_settingsSaveTimer_timeout()));
     m_settingsSaveTimer->start(5000); // 5 seconds
 
     m_writeDefaultPlaylistTimer = new QTimer(this);
     m_writeDefaultPlaylistTimer->setSingleShot(true);
-    connect(m_writeDefaultPlaylistTimer, &QTimer::timeout,
-            [this]() { writePlaylist(NCore::defaultPlaylistPath(), N::NulloyM3u); });
+    connect(m_writeDefaultPlaylistTimer, SIGNAL(timeout()), this, SLOT(on_writeDefaultPlaylistTimer_timeout()));
 }
 
 NPlayer::~NPlayer()
@@ -213,8 +217,8 @@ void NPlayer::connectSignals()
 
     connect(m_mainWindow, SIGNAL(closed()), this, SLOT(on_mainWindow_closed()));
 
-    connect(m_preferencesDialog, &NPreferencesDialog::settingsChanged, this,
-            &NPlayer::applySettings);
+    connect(m_preferencesDialog, SIGNAL(settingsChanged()), this,
+            SLOT(applySettings()));
 
     if (QAbstractButton *playButton = m_mainWindow->findChild<QAbstractButton *>("playButton")) {
         connect(playButton, SIGNAL(clicked()), this, SLOT(playPause()));
@@ -263,45 +267,24 @@ void NPlayer::connectSignals()
 
     connect(m_playlistWidget, SIGNAL(addMoreRequested()), this,
             SLOT(on_playlist_addMoreRequested()));
-    connect(m_playlistWidget, &NPlaylistWidget::durationChanged, [this](int durationSec) {
-        NPlaylistWidgetItem *item = m_playlistWidget->playingItem();
-        if (item) {
-            m_trackInfoReader->setSource(item->data(N::PathRole).toString());
-        }
-
-        m_trackInfoReader->updatePlaylistDuration(durationSec);
-
-        m_trackInfoWidget->updatePlaylistLabels();
-
-        QString format = NSettings::instance()->value("WindowTitleTrackInfo").toString();
-        if (!format.isEmpty()) {
-            QString title = m_trackInfoReader->toString(format);
-            m_mainWindow->setTitle(title);
-        }
-    });
-    connect(m_playlistWidget, &NPlaylistWidget::itemsChanged, [this]() {
-        m_writeDefaultPlaylistTimer->start(100); //
-    });
-    connect(m_playlistWidget, &NPlaylistWidget::playingItemChanged,
-            [this]() { savePlaybackState(); });
-    connect(m_playlistWidget, &NPlaylistWidget::playlistFinished, [this]() {
-        if (NSettings::instance()->value("QuitWhenFinished").toBool()) {
-            QCoreApplication::quit();
-        }
-    });
-
-    connect(m_waveformSlider, &NWaveformSlider::filesDropped,
-            [this](const QList<NPlaylistDataItem> &dataItems) {
-                m_playlistWidget->setItems(dataItems);
-                m_playlistWidget->playRow(0);
-            });
+    connect(m_playlistWidget, SIGNAL(durationChanged(int)), this,
+            SLOT(on_playlistWidget_durationChanged(int)));
+    connect(m_playlistWidget, SIGNAL(itemsChanged()), this,
+            SLOT(on_playlistWidget_itemsChanged()));
+    connect(m_playlistWidget, SIGNAL(playingItemChanged()),
+            this, SLOT(on_playlistWidget_playingItemChanged()));
+    connect(m_playlistWidget, SIGNAL(playlistFinished()), this,
+            SLOT(on_playlistWidget_playlistFinished()));
+
+    connect(m_waveformSlider, SIGNAL(filesDropped(QList<NPlaylistDataItem>)),
+            this, SLOT(on_waveformSlider_filesDropped(QList<NPlaylistDataItem>)));
     connect(m_waveformSlider, SIGNAL(sliderMoved(qreal)), m_playbackEngine,
             SLOT(setPosition(qreal)));
 
     connect(m_mainWindow, SIGNAL(customContextMenuRequested(const QPoint &)), this,
             SLOT(showContextMenu(const QPoint &)));
-    connect(m_playlistWidget, &NPlaylistWidget::contextMenuRequested, this,
-            &NPlayer::showPlaylistContextMenu);
+    connect(m_playlistWidget, SIGNAL(contextMenuRequested(QPoint)), this,
+            SLOT(showPlaylistContextMenu(QPoint)));
     connect(m_systemTray, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this,
             SLOT(on_trayIcon_activated(QSystemTrayIcon::ActivationReason)));
     connect(m_trayClickTimer, SIGNAL(timeout()), this, SLOT(on_trayClickTimer_timeout()));
@@ -406,6 +389,8 @@ void NPlayer::loadDefaultPlaylist()
         return;
     }
 
+    // Qt4: Don't auto-play on startup, only restore playlist position
+    // User must explicitly press play if they want to resume playback
     QStringList playlistRowValues = m_settings->value("PlaylistRow").toStringList();
     if (!playlistRowValues.isEmpty()) {
         int row = playlistRowValues.at(0).toInt();
@@ -415,26 +400,22 @@ void NPlayer::loadDefaultPlaylist()
             return;
         }
 
-        if (!m_settings->value("StartPaused").toBool()) {
-            m_playlistWidget->playRow(row);
-            m_playbackEngine->setPosition(pos);
-        } else { // do the work that would have been done upon m_playbackEngine::mediaChanged()
-            NPlaylistWidgetItem *item = m_playlistWidget->itemAtRow(row);
-            m_playlistWidget->setPlayingItem(item);
+        // Set the playing item and media but don't start playback
+        NPlaylistWidgetItem *item = m_playlistWidget->itemAtRow(row);
+        m_playlistWidget->setPlayingItem(item);
 
-            QString file = item->data(N::PathRole).toString();
-            int id = item->data(N::IdRole).toInt();
+        QString file = item->data(N::PathRole).toString();
+        int id = item->data(N::IdRole).toInt();
 
-            m_playbackEngine->setMedia(file, id);
-            m_playbackEngine->setPosition(pos);
+        m_playbackEngine->setMedia(file, id);
+        m_playbackEngine->setPosition(pos);
 
-            loadCoverArt(file);
+        loadCoverArt(file);
 
-            m_waveformSlider->setMedia(file);
-            m_waveformSlider->setValue(pos);
-            m_waveformSlider->setPausedState(true);
-            m_trackInfoWidget->updateFileLabels(file);
-        }
+        m_waveformSlider->setMedia(file);
+        m_waveformSlider->setValue(pos);
+        m_waveformSlider->setPausedState(true);
+        m_trackInfoWidget->updateFileLabels(file);
     }
 }
 
@@ -468,6 +449,8 @@ void NPlayer::saveSettings()
     m_mainWindow->saveSettings();
     savePlaybackState();
     m_actionManager->saveSettings();
+    // Qt4: Explicitly sync settings to disk after periodic save
+    m_settings->sync();
 }
 
 void NPlayer::savePlaybackState()
@@ -843,7 +826,9 @@ bool NPlayer::revealInFileManager(const QString &file, QString *error) const
         cmd.replace("%P", canonicalPath);
 
 #if !defined Q_OS_WIN
-        res = QProcess::execute("sh", QStringList{"-c", cmd});
+        QStringList args;
+        args << "-c" << cmd;
+        res = QProcess::execute("sh", args);
 #else
         res = QProcess::execute(cmd);
 #endif
@@ -852,16 +837,16 @@ bool NPlayer::revealInFileManager(const QString &file, QString *error) const
         QStringList args;
 #if defined Q_OS_WIN
         cmd = "explorer.exe";
-        args = QStringList{"/n", ",", "/select", ",", path.replace('/', '\\')};
+        args << "/n" << "," << "/select" << "," << path.replace('/', '\\');
 #elif defined Q_OS_LINUX
         cmd = "xdg-open";
-        args = QStringList{fileInfo.canonicalPath().replace("'", "'\\''")};
+        args << fileInfo.canonicalPath().replace("'", "'\\''");
 #elif defined Q_OS_MAC
         cmd = "open";
-        args = QStringList{"-R", path.replace("'", "'\\''")};
+        args << "-R" << path.replace("'", "'\\''");
 #endif
         res = QProcess::execute(cmd, args);
-        cmd += " " + args.join(' ');
+        cmd += " " + args.join(QString(" "));
     }
 
 #ifndef Q_OS_WIN
@@ -876,3 +861,54 @@ bool NPlayer::revealInFileManager(const QString &file, QString *error) const
 
     return true;
 }
+
+void NPlayer::on_settingsSaveTimer_timeout()
+{
+    saveSettings();
+}
+
+void NPlayer::on_writeDefaultPlaylistTimer_timeout()
+{
+    writePlaylist(NCore::defaultPlaylistPath(), N::NulloyM3u);
+}
+
+void NPlayer::on_playlistWidget_durationChanged(int durationSec)
+{
+    NPlaylistWidgetItem *item = m_playlistWidget->playingItem();
+    if (item) {
+        m_trackInfoReader->setSource(item->data(N::PathRole).toString());
+    }
+
+    m_trackInfoReader->updatePlaylistDuration(durationSec);
+
+    m_trackInfoWidget->updatePlaylistLabels();
+
+    QString format = NSettings::instance()->value("WindowTitleTrackInfo").toString();
+    if (!format.isEmpty()) {
+        QString title = m_trackInfoReader->toString(format);
+        m_mainWindow->setTitle(title);
+    }
+}
+
+void NPlayer::on_playlistWidget_itemsChanged()
+{
+    m_writeDefaultPlaylistTimer->start(100);
+}
+
+void NPlayer::on_playlistWidget_playingItemChanged()
+{
+    savePlaybackState();
+}
+
+void NPlayer::on_playlistWidget_playlistFinished()
+{
+    if (NSettings::instance()->value("QuitWhenFinished").toBool()) {
+        QCoreApplication::quit();
+    }
+}
+
+void NPlayer::on_waveformSlider_filesDropped(const QList<NPlaylistDataItem> &dataItems)
+{
+    m_playlistWidget->setItems(dataItems);
+    m_playlistWidget->playRow(0);
+}
diff --git a/src/player.h b/src/player.h
index dad9890..c0835c9 100644
--- a/src/player.h
+++ b/src/player.h
@@ -28,6 +28,7 @@ class NPlaylistWidget;
 class NWaveformSlider;
 class NCoverWidget;
 class NCoverReaderInterface;
+struct NPlaylistDataItem;
 class NVolumeSlider;
 class NPreferencesDialog;
 class NAboutDialog;
@@ -101,6 +102,14 @@ private slots:
     void on_trayClickTimer_timeout();
     void trayIconCountClicks(int clicks);
 
+    void on_settingsSaveTimer_timeout();
+    void on_writeDefaultPlaylistTimer_timeout();
+    void on_playlistWidget_durationChanged(int durationSec);
+    void on_playlistWidget_itemsChanged();
+    void on_playlistWidget_playingItemChanged();
+    void on_playlistWidget_playlistFinished();
+    void on_waveformSlider_filesDropped(const QList<NPlaylistDataItem> &dataItems);
+
 public slots:
     void quit();
     void playPause();
diff --git a/src/plugins/pluginGstreamer/containerGstreamer.cpp b/src/plugins/pluginGstreamer/containerGstreamer.cpp
index 2340267..1e8d3b0 100644
--- a/src/plugins/pluginGstreamer/containerGstreamer.cpp
+++ b/src/plugins/pluginGstreamer/containerGstreamer.cpp
@@ -43,3 +43,5 @@ QList<NPlugin *> NContainerGstreamer::plugins() const
 {
     return m_plugins;
 }
+
+Q_EXPORT_PLUGIN2(containerGstreamer, NContainerGstreamer)
diff --git a/src/plugins/pluginGstreamer/containerGstreamer.h b/src/plugins/pluginGstreamer/containerGstreamer.h
index 93b9f2c..cf41956 100644
--- a/src/plugins/pluginGstreamer/containerGstreamer.h
+++ b/src/plugins/pluginGstreamer/containerGstreamer.h
@@ -22,7 +22,6 @@ class NContainerGstreamer : public QObject, public NPluginContainer
 {
     Q_OBJECT
     Q_INTERFACES(NPluginContainer)
-    Q_PLUGIN_METADATA(IID "com.nulloy.NContainerGstreamer")
 
 private:
     QList<NPlugin *> m_plugins;
diff --git a/src/plugins/pluginGstreamer/playbackEngineGstreamer.cpp b/src/plugins/pluginGstreamer/playbackEngineGstreamer.cpp
index af1c932..ec57a57 100644
--- a/src/plugins/pluginGstreamer/playbackEngineGstreamer.cpp
+++ b/src/plugins/pluginGstreamer/playbackEngineGstreamer.cpp
@@ -121,18 +121,11 @@ void NPlaybackEngineGStreamer::init()
     m_emitStateTimer = new QTimer(this);
     m_emitStateTimer->setSingleShot(true);
     m_emitStateTimer->setInterval(STATE_CHANGE_DEBOUNCE_MSEC);
-    connect(m_emitStateTimer, &QTimer::timeout,
-            [this]() { emit stateChanged(fromGstState(m_gstState)); });
+    connect(m_emitStateTimer, SIGNAL(timeout()), this, SLOT(on_emitStateTimer_timeout()));
 
     m_gstBusPopTimer = new QTimer(this);
     m_gstBusPopTimer->setInterval(GST_BUS_POP_MSEC);
-    connect(m_gstBusPopTimer, &QTimer::timeout, [this]() {
-        GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(m_playbin));
-        GstMessage *msg;
-        while ((msg = gst_bus_pop(bus)) != NULL) {
-            processGstMessage(msg);
-        }
-    });
+    connect(m_gstBusPopTimer, SIGNAL(timeout()), this, SLOT(on_gstBusPopTimer_timeout()));
 
     m_init = true;
 }
@@ -376,7 +369,7 @@ void NPlaybackEngineGStreamer::processGstMessage(GstMessage *msg)
                 gchar *detail = gst_missing_plugin_message_get_installer_detail(msg);
                 if (detail) {
                     QStringList fields = QString::fromUtf8(detail).split('|').mid(3);
-                    str += QString::fromUtf8(detail).split('|').mid(3).join("<br/>");
+                    str += fields.join("<br/>");
                     g_free(detail);
                 } else {
                     str += tr("Unknown plugin");
@@ -486,3 +479,17 @@ bool NPlaybackEngineGStreamer::_nextMediaRequestBlocked()
 {
     return m_nextMediaRequestBlock;
 }
+
+void NPlaybackEngineGStreamer::on_emitStateTimer_timeout()
+{
+    emit stateChanged(fromGstState(m_gstState));
+}
+
+void NPlaybackEngineGStreamer::on_gstBusPopTimer_timeout()
+{
+    GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(m_playbin));
+    GstMessage *msg;
+    while ((msg = gst_bus_pop(bus)) != NULL) {
+        processGstMessage(msg);
+    }
+}
diff --git a/src/plugins/pluginGstreamer/playbackEngineGstreamer.h b/src/plugins/pluginGstreamer/playbackEngineGstreamer.h
index 35be620..05b8fff 100644
--- a/src/plugins/pluginGstreamer/playbackEngineGstreamer.h
+++ b/src/plugins/pluginGstreamer/playbackEngineGstreamer.h
@@ -57,6 +57,10 @@ private:
     void processGstMessage(GstMessage *msg);
     void fail();
 
+private slots:
+    void on_emitStateTimer_timeout();
+    void on_gstBusPopTimer_timeout();
+
 public:
     NPlaybackEngineGStreamer(QObject *parent = NULL) : NPlaybackEngineInterface(parent) {}
     ~NPlaybackEngineGStreamer();
diff --git a/src/plugins/pluginTaglib/containerTaglib.cpp b/src/plugins/pluginTaglib/containerTaglib.cpp
index f6ce5d9..7dba764 100644
--- a/src/plugins/pluginTaglib/containerTaglib.cpp
+++ b/src/plugins/pluginTaglib/containerTaglib.cpp
@@ -33,3 +33,5 @@ QList<NPlugin *> NContainerTaglib::plugins() const
 {
     return m_plugins;
 }
+
+Q_EXPORT_PLUGIN2(containerTaglib, NContainerTaglib)
diff --git a/src/plugins/pluginTaglib/containerTaglib.h b/src/plugins/pluginTaglib/containerTaglib.h
index a0b4999..c3a2d3a 100644
--- a/src/plugins/pluginTaglib/containerTaglib.h
+++ b/src/plugins/pluginTaglib/containerTaglib.h
@@ -22,7 +22,6 @@ class NContainerTaglib : public QObject, public NPluginContainer
 {
     Q_OBJECT
     Q_INTERFACES(NPluginContainer)
-    Q_PLUGIN_METADATA(IID "com.nulloy.NContainerTaglib")
 
 private:
     QList<NPlugin *> m_plugins;
diff --git a/src/plugins/pluginTaglib/coverReaderTaglib.cpp b/src/plugins/pluginTaglib/coverReaderTaglib.cpp
index 398c968..37667fd 100644
--- a/src/plugins/pluginTaglib/coverReaderTaglib.cpp
+++ b/src/plugins/pluginTaglib/coverReaderTaglib.cpp
@@ -79,7 +79,7 @@ QList<QImage> NCoverReaderTaglib::fromApe(TagLib::APE::Tag *tag) const
     QList<QImage> images;
     const TagLib::APE::ItemListMap &map = tag->itemListMap();
 
-    for (auto iter = map.begin(); iter != map.end(); ++iter) {
+    for (TagLib::APE::ItemListMap::ConstIterator iter = map.begin(); iter != map.end(); ++iter) {
         TagLib::String key = iter->first;
         if (!key.startsWith("COVER ART")) {
             continue;
@@ -105,8 +105,8 @@ QList<QImage> NCoverReaderTaglib::fromAsf(TagLib::ASF::Tag *tag) const
 
     const TagLib::ASF::AttributeList &list = map[str];
 
-    for (auto attribute : list) {
-        TagLib::ASF::Picture pic = attribute.toPicture();
+    for (TagLib::ASF::AttributeList::ConstIterator it = list.begin(); it != list.end(); ++it) {
+        TagLib::ASF::Picture pic = it->toPicture();
         if (pic.isValid()) {
             images << fromTagBytes(pic.picture());
         }
@@ -120,8 +120,8 @@ QList<QImage> NCoverReaderTaglib::fromFlac(TagLib::FLAC::File *file) const
     QList<QImage> images;
     const TagLib::List<TagLib::FLAC::Picture *> &list = file->pictureList();
 
-    for (TagLib::FLAC::Picture *pic : list) {
-        images << fromTagBytes(pic->data());
+    for (TagLib::List<TagLib::FLAC::Picture *>::ConstIterator it = list.begin(); it != list.end(); ++it) {
+        images << fromTagBytes((*it)->data());
     }
     return images;
 }
@@ -134,8 +134,8 @@ QList<QImage> NCoverReaderTaglib::fromId3(TagLib::ID3v2::Tag *tag) const
         return images;
     }
 
-    for (auto *frame : list) {
-        auto pictureFrame = static_cast<TagLib::ID3v2::AttachedPictureFrame *>(frame);
+    for (TagLib::ID3v2::FrameList::ConstIterator it = list.begin(); it != list.end(); ++it) {
+        TagLib::ID3v2::AttachedPictureFrame *pictureFrame = static_cast<TagLib::ID3v2::AttachedPictureFrame *>(*it);
         images << fromTagBytes(pictureFrame->picture());
     }
     return images;
@@ -150,8 +150,8 @@ QList<QImage> NCoverReaderTaglib::fromMp4(TagLib::MP4::Tag *tag) const
     }
 
     TagLib::MP4::CoverArtList coverList = tag->itemMap()[str].toCoverArtList();
-    for (auto coverArt : coverList) {
-        images << fromTagBytes(coverArt.data());
+    for (TagLib::MP4::CoverArtList::ConstIterator it = coverList.begin(); it != coverList.end(); ++it) {
+        images << fromTagBytes(it->data());
     }
 
     return images;
@@ -160,7 +160,7 @@ QList<QImage> NCoverReaderTaglib::fromMp4(TagLib::MP4::Tag *tag) const
 QList<QImage> NCoverReaderTaglib::fromVorbis(TagLib::Tag *tag) const
 {
     QList<QImage> images;
-    if (auto *comment = dynamic_cast<TagLib::Ogg::XiphComment *>(tag)) {
+    if (TagLib::Ogg::XiphComment *comment = dynamic_cast<TagLib::Ogg::XiphComment *>(tag)) {
         TagLib::String str = "COVERART";
 
         if (!comment->contains(str)) {
@@ -193,29 +193,29 @@ QList<QImage> NCoverReaderTaglib::getImages() const
 
     TagLib::File *tagFile = NTaglib::_tagRef->file();
 
-    if (auto *file = dynamic_cast<TagLib::APE::File *>(tagFile)) {
+    if (TagLib::APE::File *file = dynamic_cast<TagLib::APE::File *>(tagFile)) {
         if (file->APETag()) {
             images = fromApe(file->APETag());
         }
-    } else if (auto *file = dynamic_cast<TagLib::ASF::File *>(tagFile)) {
+    } else if (TagLib::ASF::File *file = dynamic_cast<TagLib::ASF::File *>(tagFile)) {
         if (file->tag()) {
             images = fromAsf(file->tag());
         }
-    } else if (auto *file = dynamic_cast<TagLib::FLAC::File *>(tagFile)) {
+    } else if (TagLib::FLAC::File *file = dynamic_cast<TagLib::FLAC::File *>(tagFile)) {
         images = fromFlac(file);
 
         if (images.isEmpty() && file->ID3v2Tag()) {
             images = fromId3(file->ID3v2Tag());
         }
-    } else if (auto *file = dynamic_cast<TagLib::MP4::File *>(tagFile)) {
+    } else if (TagLib::MP4::File *file = dynamic_cast<TagLib::MP4::File *>(tagFile)) {
         if (file->tag()) {
             images = fromMp4(file->tag());
         }
-    } else if (auto *file = dynamic_cast<TagLib::MPC::File *>(tagFile)) {
+    } else if (TagLib::MPC::File *file = dynamic_cast<TagLib::MPC::File *>(tagFile)) {
         if (file->APETag()) {
             images = fromApe(file->APETag());
         }
-    } else if (auto *file = dynamic_cast<TagLib::MPEG::File *>(tagFile)) {
+    } else if (TagLib::MPEG::File *file = dynamic_cast<TagLib::MPEG::File *>(tagFile)) {
         if (file->ID3v2Tag()) {
             images = fromId3(file->ID3v2Tag());
         }
@@ -223,11 +223,11 @@ QList<QImage> NCoverReaderTaglib::getImages() const
         if (images.isEmpty() && file->APETag()) {
             images = fromApe(file->APETag());
         }
-    } else if (auto *file = dynamic_cast<TagLib::Ogg::Vorbis::File *>(tagFile)) {
+    } else if (TagLib::Ogg::Vorbis::File *file = dynamic_cast<TagLib::Ogg::Vorbis::File *>(tagFile)) {
         if (file->tag()) {
             images = fromVorbis(file->tag());
         }
-    } else if (auto *file = dynamic_cast<TagLib::WavPack::File *>(tagFile)) {
+    } else if (TagLib::WavPack::File *file = dynamic_cast<TagLib::WavPack::File *>(tagFile)) {
         if (file->APETag()) {
             images = fromApe(file->APETag());
         }
diff --git a/src/plugins/pluginTaglib/tagReaderTaglib.cpp b/src/plugins/pluginTaglib/tagReaderTaglib.cpp
index c54f711..26af1f1 100644
--- a/src/plugins/pluginTaglib/tagReaderTaglib.cpp
+++ b/src/plugins/pluginTaglib/tagReaderTaglib.cpp
@@ -35,7 +35,7 @@ QString NTaglib::_filePath;
 NTagReaderTaglib::NTagReaderTaglib(QObject *parent) : NTagReaderInterface(parent)
 {
     m_isValid = false;
-    m_codec = nullptr;
+    m_codec = 0;
     m_utf8Codec = QTextCodec::codecForName("UTF-8");
 }
 
@@ -141,19 +141,19 @@ QString NTagReaderTaglib::getTag(QChar ch) const
         }
         case 'b': { // bit depth
             TagLib::AudioProperties *ap = NTaglib::_tagRef->audioProperties();
-            if (auto *prop = dynamic_cast<TagLib::APE::Properties *>(ap)) {
+            if (TagLib::APE::Properties *prop = dynamic_cast<TagLib::APE::Properties *>(ap)) {
                 return QString::number(prop->bitsPerSample());
-            } else if (auto *prop = dynamic_cast<TagLib::FLAC::Properties *>(ap)) {
+            } else if (TagLib::FLAC::Properties *prop = dynamic_cast<TagLib::FLAC::Properties *>(ap)) {
                 return QString::number(prop->bitsPerSample());
-            } else if (auto *prop = dynamic_cast<TagLib::MP4::Properties *>(ap)) {
+            } else if (TagLib::MP4::Properties *prop = dynamic_cast<TagLib::MP4::Properties *>(ap)) {
                 return QString::number(prop->bitsPerSample());
-            } else if (auto *prop = dynamic_cast<TagLib::RIFF::AIFF::Properties *>(ap)) {
+            } else if (TagLib::RIFF::AIFF::Properties *prop = dynamic_cast<TagLib::RIFF::AIFF::Properties *>(ap)) {
                 return QString::number(prop->bitsPerSample());
-            } else if (auto *prop = dynamic_cast<TagLib::RIFF::WAV::Properties *>(ap)) {
+            } else if (TagLib::RIFF::WAV::Properties *prop = dynamic_cast<TagLib::RIFF::WAV::Properties *>(ap)) {
                 return QString::number(prop->bitsPerSample());
-            } else if (auto *prop = dynamic_cast<TagLib::TrueAudio::Properties *>(ap)) {
+            } else if (TagLib::TrueAudio::Properties *prop = dynamic_cast<TagLib::TrueAudio::Properties *>(ap)) {
                 return QString::number(prop->bitsPerSample());
-            } else if (auto *prop = dynamic_cast<TagLib::WavPack::Properties *>(ap)) {
+            } else if (TagLib::WavPack::Properties *prop = dynamic_cast<TagLib::WavPack::Properties *>(ap)) {
                 return QString::number(prop->bitsPerSample());
             } else {
                 return "";
@@ -264,11 +264,11 @@ QMap<QString, QStringList>
 NTagReaderTaglib::TMapToQMap(const TagLib::Map<TagLib::String, TagLib::StringList> &tmap) const
 {
     QMap<QString, QStringList> qmap;
-    for (auto iter = tmap.begin(); iter != tmap.end(); ++iter) {
+    for (TagLib::Map<TagLib::String, TagLib::StringList>::ConstIterator iter = tmap.begin(); iter != tmap.end(); ++iter) {
         QStringList values;
         TagLib::StringList tlist = iter->second;
-        for (auto iter = tlist.begin(); iter != tlist.end(); ++iter) {
-            values << toUnicode((*iter));
+        for (TagLib::StringList::ConstIterator iter2 = tlist.begin(); iter2 != tlist.end(); ++iter2) {
+            values << toUnicode((*iter2));
         }
         qmap[TStringToQString(iter->first)] = values;
     }
diff --git a/src/preferencesDialog.cpp b/src/preferencesDialog.cpp
index 658a5a4..11de850 100644
--- a/src/preferencesDialog.cpp
+++ b/src/preferencesDialog.cpp
@@ -96,8 +96,8 @@ NPreferencesDialog::NPreferencesDialog(NPlayer *player, QWidget *parent) : QDial
 #ifdef _N_NO_UPDATE_CHECK_
     ui.autoCheckUpdatesContainer->hide();
 #else
-    connect(&NUpdateChecker::instance(), &NUpdateChecker::versionChanged, this,
-            &NPreferencesDialog::setVersionLabel);
+    connect(&NUpdateChecker::instance(), SIGNAL(versionChanged(QString)), this,
+            SLOT(setVersionLabel(QString)));
 #endif
 
 #if defined Q_OS_WIN || defined Q_OS_MAC
@@ -111,8 +111,8 @@ NPreferencesDialog::NPreferencesDialog(NPlayer *player, QWidget *parent) : QDial
     ui.quitOnCloseContainer->hide();
 #endif
 
-    QRegularExpression re("Container$");
-    for (QWidget *widget : ui.generalTab->findChildren<QWidget *>(re)) {
+    QRegExp re("Container$");
+    foreach (QWidget *widget, ui.generalTab->findChildren<QWidget *>(re)) {
         if (widget->objectName() == "fileFiltersContainer") {
             continue;
         }
@@ -158,7 +158,7 @@ NPreferencesDialog::NPreferencesDialog(NPlayer *player, QWidget *parent) : QDial
     ui.styleRestartLabel->setVisible(false);
     connect(ui.styleComboBox, SIGNAL(activated(int)), ui.styleRestartLabel, SLOT(show()));
 
-    ui.waveformTrackInfoTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
+    ui.waveformTrackInfoTable->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
 
     int i = 0;
     foreach (int mib, QTextCodec::availableMibs()) {
@@ -398,7 +398,7 @@ void NPreferencesDialog::on_languageComboBox_activated(int index)
     QString newText = NI18NLoader::translate(locale.language(), "PreferencesDialog",
                                              "Switching languages requires restart");
     ui.languageRestartLabel->setText(
-        ui.languageRestartLabel->text().replace(QRegExp("(.*)&nbsp;.*"), "\\1&nbsp;" + newText));
+        ui.languageRestartLabel->text().replace(QRegExp("(.*)&nbsp;.*"), "\\\\1&nbsp;" + newText));
 }
 
 QString NPreferencesDialog::selectedContainer(N::PluginType type)
@@ -566,7 +566,9 @@ void NPreferencesDialog::loadSettings()
 
     // shortcuts >>
     QList<NAction *> actionList;
-    for (NAction *action : m_player->findChildren<NAction *>()) {
+    QList<NAction *> allActions = m_player->findChildren<NAction *>();
+    for (int i = 0; i < allActions.size(); ++i) {
+        NAction *action = allActions.at(i);
         if (action->objectName().isEmpty() || !action->isCustomizable()) {
             continue;
         }
@@ -678,6 +680,9 @@ void NPreferencesDialog::saveSettings()
     ui.shortcutEditorWidget->applyShortcuts();
     // << shortcuts
 
+    // Qt4: Explicitly sync settings to disk after all changes
+    NSettings::instance()->sync();
+
     emit settingsChanged();
 
     // systray check >>
diff --git a/src/scriptEngine.cpp b/src/scriptEngine.cpp
index ace9224..b08bb44 100644
--- a/src/scriptEngine.cpp
+++ b/src/scriptEngine.cpp
@@ -203,7 +203,7 @@ NScriptEngine::NScriptEngine(NPlayer *player) : QScriptEngine(player)
     setDefaultPrototype(qMetaTypeId<QWidget *>(), newQObject(&widgetPrototype));
     setDefaultPrototype(qMetaTypeId<QLayout *>(), newQObject(&layoutPrototype));
     setDefaultPrototype(qMetaTypeId<QSplitter *>(), newQObject(&splitterPrototype));
-    qScriptRegisterSequenceMetaType<QList<QWidget *>>(this);
+    qScriptRegisterSequenceMetaType<QList<QWidget *> >(this);
 
     qScriptRegisterMetaType<N::PlaybackState>(this, enumToScriptValue, enumFromScriptValue);
     QScriptValue N = newQMetaObject(&N::staticMetaObject);
diff --git a/src/settings.cpp b/src/settings.cpp
index 82c0f43..f7f0abf 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -18,10 +18,11 @@
 #include <QApplication>
 #include <QCoreApplication>
 #include <QDebug>
+#include <QDesktopServices>
 #include <QDir>
 #include <QSettings>
-#include <QStandardPaths>
 #include <QStyle>
+#include <QTextCodec>
 #include <QVariant>
 
 #include "action.h"
@@ -37,7 +38,8 @@ NSettings::NSettings(QObject *parent)
     Q_ASSERT_X(!m_instance, "NSettings", "NSettings instance already exists.");
     m_instance = this;
 
-    setIniCodec("UTF-8");
+    // Qt4: setIniCodec() requires QTextCodec* parameter
+    setIniCodec(QTextCodec::codecForName("UTF-8"));
 
     QString version = value("SettingsVersion").toString();
     if (version.isEmpty() || version < MIN_VERSION) {
@@ -81,13 +83,9 @@ NSettings::NSettings(QObject *parent)
     initValue("PitchStep", 0.01);
     */
 
-    {
-        QStringList fullScreenKeys;
-        foreach (QKeySequence seq, QKeySequence::keyBindings(QKeySequence::FullScreen)) {
-            fullScreenKeys << seq.toString();
-        }
-        initValue("Shortcuts/FullScreenAction", QStringList() << fullScreenKeys);
-    }
+    // Qt4 doesn't have QKeySequence::keyBindings or QKeySequence::FullScreen
+    // Use common fullscreen shortcuts manually
+    initValue("Shortcuts/FullScreenAction", QStringList() << "F11");
 
     initValue("PlaylistTrackInfo", "%F{ (%d)}");
     initValue("WindowTitleTrackInfo",
@@ -110,7 +108,7 @@ NSettings::NSettings(QObject *parent)
     initValue("DisplayLogDialog", true);
     initValue("DisplayTagEditorConfirmDialog", true);
     initValue("DisplayMoveToTrashConfirmDialog", true);
-    initValue("LastDirectory", QStandardPaths::standardLocations(QStandardPaths::MusicLocation));
+    initValue("LastDirectory", QDesktopServices::storageLocation(QDesktopServices::MusicLocation));
     initValue("ScrollToItem", true);
     initValue("LoopPlaylist", false);
     initValue("LoadNext", false);
@@ -178,7 +176,11 @@ void NSettings::setValue(const QString &key, const QVariant &value)
 void NSettings::initValue(const QString &key, const QVariant &defaultValue)
 {
     QVariant val = value(key, defaultValue);
-    val.convert(defaultValue.type());
+    // Qt4: convert() modifies in-place and returns bool success
+    if (!val.convert(defaultValue.type())) {
+        // Conversion failed, use default value
+        val = defaultValue;
+    }
     setValue(key, val);
 }
 
diff --git a/src/shortcutEditorWidget.cpp b/src/shortcutEditorWidget.cpp
index e341b51..a26f6d0 100644
--- a/src/shortcutEditorWidget.cpp
+++ b/src/shortcutEditorWidget.cpp
@@ -68,10 +68,10 @@ void NShortcutEditorWidget::init(const QList<NAction *> &actionList)
 
     resizeColumnToContents(Name);
     resizeColumnToContents(Description);
-    horizontalHeader()->setSectionResizeMode(Name, QHeaderView::Fixed);
-    horizontalHeader()->setSectionResizeMode(Description, QHeaderView::Fixed);
-    horizontalHeader()->setSectionResizeMode(Shortcut, QHeaderView::Stretch);
-    horizontalHeader()->setSectionResizeMode(GlobalShortcut, QHeaderView::Stretch);
+    horizontalHeader()->setResizeMode(Name, QHeaderView::Fixed);
+    horizontalHeader()->setResizeMode(Description, QHeaderView::Fixed);
+    horizontalHeader()->setResizeMode(Shortcut, QHeaderView::Stretch);
+    horizontalHeader()->setResizeMode(GlobalShortcut, QHeaderView::Stretch);
     horizontalHeader()->setStretchLastSection(true);
 }
 
diff --git a/src/src.pri b/src/src.pri
index 6ca941d..fd474f8 100644
--- a/src/src.pri
+++ b/src/src.pri
@@ -1,4 +1,4 @@
-QT += script gui svg core-private
+QT += script gui svg
 
 INCLUDEPATH += $$SRC_DIR $$SRC_DIR/interfaces
 
@@ -35,6 +35,8 @@ unix:!mac:PKGCONFIG += x11
 !no-skins {
     include($$SRC_DIR/skins/skins.pri)
     QT += uitools
+    # Qt4 on macOS requires explicit QtUiTools library linking
+    mac:LIBS += -lQtUiTools
     INCLUDEPATH += $$SRC_DIR/widgetCollection
     LIBS += -L$$SRC_DIR/widgetCollection -lwidget_collection
     PRE_TARGETDEPS += $$SRC_DIR/widgetCollection/libwidget_collection.a
@@ -57,7 +59,9 @@ unix:!mac:PKGCONFIG += x11
     FORMS += $$SRC_DIR/skins/native/form.ui
 }
 
-no-update-check:DEFINES += _N_NO_UPDATE_CHECK_
+# Qt4: Always disable update checking (MacPorts handles updates)
+DEFINES += _N_NO_UPDATE_CHECK_
+# no-update-check:DEFINES += _N_NO_UPDATE_CHECK_
 
 include(version.pri)
 DEFINES += _N_VERSION_=\""\\\"$${N_VERSION}\\\""\"
diff --git a/src/tagEditorDialog.cpp b/src/tagEditorDialog.cpp
index 1914d0e..bace3a7 100644
--- a/src/tagEditorDialog.cpp
+++ b/src/tagEditorDialog.cpp
@@ -29,6 +29,7 @@
 #include <QMetaEnum>
 #include <QPushButton>
 #include <QTextEdit>
+#include <QRegExp>
 
 NTagEditorDialog::NTagEditorDialog(const QString &file, QWidget *parent) : QDialog(parent)
 {
@@ -77,23 +78,15 @@ NTagEditorDialog::NTagEditorDialog(const QString &file, QWidget *parent) : QDial
         }
         ++i;
     }
-    connect(ui.encodingResetButton, &QPushButton::clicked, [=] {
-        ui.encodingComboBox->setCurrentIndex(m_encodingUtf8Index);
-        on_encodingComboBox_activated(-1);
-    });
-    connect(ui.editAsUtf8Button, &QPushButton::clicked, [=] {
-        ui.encodingComboBox->setCurrentIndex(m_encodingUtf8Index);
-        m_encodingPreviousIndex = m_encodingUtf8Index;
-        m_hasChanges = true;
-        setReadOnlyMode(false);
-    });
+    connect(ui.encodingResetButton, SIGNAL(clicked()), this, SLOT(onEncodingResetClicked()));
+    connect(ui.editAsUtf8Button, SIGNAL(clicked()), this, SLOT(onEditAsUtf8Clicked()));
     on_encodingComboBox_activated(-1);
 
     setWindowTitle("\"" + QFileInfo(file).fileName() + "\" — " + tr("Tag Editor"));
 
     m_coverReader->setSource(m_file);
     QList<QImage> images = m_coverReader->getImages();
-    for (QImage image : images) {
+    foreach (QImage image, images) {
         QLabel *label = new QLabel;
         label->setFixedSize(100, 100);
         label->setScaledContents(true);
@@ -107,11 +100,7 @@ NTagEditorDialog::NTagEditorDialog(const QString &file, QWidget *parent) : QDial
     ui.artworkListWidget->setMinimumHeight(111);
 
     ui.buttonBox->button(QDialogButtonBox::Reset)->setText(tr("Revert"));
-    connect(ui.buttonBox->button(QDialogButtonBox::Reset), &QPushButton::clicked, [=] {
-        ui.encodingComboBox->setCurrentIndex(m_encodingSettingsIndex);
-        readTags();
-        setReadOnlyMode(ui.encodingComboBox->currentIndex() != m_encodingUtf8Index);
-    });
+    connect(ui.buttonBox->button(QDialogButtonBox::Reset), SIGNAL(clicked()), this, SLOT(onRevertClicked()));
 
     exec();
 }
@@ -161,7 +150,7 @@ void NTagEditorDialog::readTags()
     }
 
     // create raw widgets for standard tags:
-    QRegularExpression widgetNameRegex("Tag");
+    QRegExp widgetNameRegex("Tag");
     m_tagReader->setSource(m_file);
     QMap<QString, QStringList> tags = m_tagReader->getTags();
     m_tagReader->setSource(""); // release the file
@@ -182,11 +171,7 @@ void NTagEditorDialog::readTags()
             QLineEdit *rawTagLineEdit = new QLineEdit(value);
             rawTagsFormLayout->addRow(label, rawTagLineEdit);
 
-            connect(rawTagLineEdit, &QLineEdit::textEdited, [=](const QString &newValue) {
-                m_hasChanges = true;
-                ui.buttonBox->button(QDialogButtonBox::Save)->setEnabled(true);
-                ui.buttonBox->button(QDialogButtonBox::Reset)->setEnabled(true);
-            });
+            connect(rawTagLineEdit, SIGNAL(textEdited(QString)), this, SLOT(onTagTextChanged()));
 
             if (label.isEmpty()) { // connect widgets only for the first tag value
                 continue;
@@ -196,36 +181,24 @@ void NTagEditorDialog::readTags()
             if (QLineEdit *standardTagLineEdit = ui.generalTab->findChild<QLineEdit *>(
                     widgetNamePrefix + "LineEdit")) {
                 standardTagLineEdit->setText(values.first());
-                QMetaObject::Connection connection =
-                    connect(standardTagLineEdit, &QLineEdit::textEdited,
-                            [=](const QString &newValue) {
-                                rawTagLineEdit->setText(newValue);
-                                m_hasChanges = true;
-                                ui.buttonBox->button(QDialogButtonBox::Save)->setEnabled(true);
-                                ui.buttonBox->button(QDialogButtonBox::Reset)->setEnabled(true);
-                            });
-                connect(rawTagLineEdit, &QObject::destroyed,
-                        [=] { QObject::disconnect(connection); });
-                connect(rawTagLineEdit, &QLineEdit::textEdited,
-                        [=](const QString &newValue) { standardTagLineEdit->setText(newValue); });
+
+                // Store bidirectional widget associations using properties
+                standardTagLineEdit->setProperty("rawTagWidget", qVariantFromValue((QObject*)rawTagLineEdit));
+                rawTagLineEdit->setProperty("standardTagWidget", qVariantFromValue((QObject*)standardTagLineEdit));
+                rawTagLineEdit->setProperty("isLineEdit", true);
+
+                connect(standardTagLineEdit, SIGNAL(textEdited(QString)), this, SLOT(onTagTextChanged()));
             } else if (QPlainTextEdit *standardTagPlainTextEdit =
                            ui.generalTab->findChild<QPlainTextEdit *>(widgetNamePrefix +
                                                                       "PlainTextEdit")) {
                 standardTagPlainTextEdit->setPlainText(values.first());
-                QMetaObject::Connection connection =
-                    connect(standardTagPlainTextEdit, &QPlainTextEdit::textChanged, [=] {
-                        rawTagLineEdit->setText(standardTagPlainTextEdit->toPlainText());
-                        m_hasChanges = true;
-                        ui.buttonBox->button(QDialogButtonBox::Save)->setEnabled(true);
-                        ui.buttonBox->button(QDialogButtonBox::Reset)->setEnabled(true);
-                    });
-                connect(rawTagLineEdit, &QObject::destroyed,
-                        [=] { QObject::disconnect(connection); });
-                connect(rawTagLineEdit, &QLineEdit::textEdited, [=](const QString &newValue) {
-                    QTextCursor cursor = standardTagPlainTextEdit->textCursor();
-                    standardTagPlainTextEdit->setPlainText(newValue);
-                    standardTagPlainTextEdit->setTextCursor(cursor);
-                });
+
+                // Store bidirectional widget associations using properties
+                standardTagPlainTextEdit->setProperty("rawTagWidget", qVariantFromValue((QObject*)rawTagLineEdit));
+                rawTagLineEdit->setProperty("standardTagWidget", qVariantFromValue((QObject*)standardTagPlainTextEdit));
+                rawTagLineEdit->setProperty("isLineEdit", false);
+
+                connect(standardTagPlainTextEdit, SIGNAL(textChanged()), this, SLOT(onTagTextChanged()));
             } else {
                 QMessageBox::critical(NULL, tr("Match error"),
                                       tr("No widget for tag: %1").arg(enumKey), QMessageBox::Close);
@@ -271,7 +244,8 @@ bool NTagEditorDialog::writeTags()
                 valueList.clear();
             }
             QLabel *label = qobject_cast<QLabel *>(item->widget());
-            tagName = label->text().chopped(1);
+            // Qt4 doesn't have QString::chopped(), use left() instead
+            tagName = label->text().left(label->text().length() - 1);
         }
         QString value = qobject_cast<QLineEdit *>(
                             rawTagsFormLayout->itemAt(i, QFormLayout::FieldRole)->widget())
@@ -295,7 +269,8 @@ bool NTagEditorDialog::writeTags()
             msgBox.exec();
         } else {
             QMessageBox msgBox(QMessageBox::Warning, tr("Save Fail"),
-                               tr("Saving aborted. Failed tags: %1").arg(unsaved.keys().join(", ")),
+                               // Qt4 QList doesn't have join(), convert to QStringList first
+                               tr("Saving aborted. Failed tags: %1").arg(QStringList(unsaved.keys()).join(", ")),
                                QMessageBox::Close, this);
             msgBox.exec();
         }
@@ -307,6 +282,80 @@ bool NTagEditorDialog::writeTags()
 
 NTagEditorDialog::~NTagEditorDialog() {}
 
+void NTagEditorDialog::onEncodingResetClicked()
+{
+    ui.encodingComboBox->setCurrentIndex(m_encodingUtf8Index);
+    on_encodingComboBox_activated(-1);
+}
+
+void NTagEditorDialog::onEditAsUtf8Clicked()
+{
+    ui.encodingComboBox->setCurrentIndex(m_encodingUtf8Index);
+    m_encodingPreviousIndex = m_encodingUtf8Index;
+    m_hasChanges = true;
+    setReadOnlyMode(false);
+}
+
+void NTagEditorDialog::onRevertClicked()
+{
+    ui.encodingComboBox->setCurrentIndex(m_encodingSettingsIndex);
+    readTags();
+    setReadOnlyMode(ui.encodingComboBox->currentIndex() != m_encodingUtf8Index);
+}
+
+void NTagEditorDialog::onTagTextChanged()
+{
+    m_hasChanges = true;
+    ui.buttonBox->button(QDialogButtonBox::Save)->setEnabled(true);
+    ui.buttonBox->button(QDialogButtonBox::Reset)->setEnabled(true);
+
+    QObject *senderWidget = sender();
+    if (!senderWidget) {
+        return;
+    }
+
+    // Check if this widget has an associated widget to sync with
+    QVariant rawTagVariant = senderWidget->property("rawTagWidget");
+    QVariant standardTagVariant = senderWidget->property("standardTagWidget");
+
+    if (rawTagVariant.isValid()) {
+        // This is a standard tag widget, update the raw tag widget
+        QLineEdit *rawTagLineEdit = qobject_cast<QLineEdit *>(
+            qvariant_cast<QObject*>(rawTagVariant));
+        if (rawTagLineEdit) {
+            QLineEdit *standardLineEdit = qobject_cast<QLineEdit *>(senderWidget);
+            QPlainTextEdit *standardPlainTextEdit = qobject_cast<QPlainTextEdit *>(senderWidget);
+
+            if (standardLineEdit) {
+                rawTagLineEdit->setText(standardLineEdit->text());
+            } else if (standardPlainTextEdit) {
+                rawTagLineEdit->setText(standardPlainTextEdit->toPlainText());
+            }
+        }
+    } else if (standardTagVariant.isValid()) {
+        // This is a raw tag widget, update the standard tag widget
+        QLineEdit *rawTagLineEdit = qobject_cast<QLineEdit *>(senderWidget);
+        if (rawTagLineEdit) {
+            QObject *standardWidget = qvariant_cast<QObject*>(standardTagVariant);
+            bool isLineEdit = senderWidget->property("isLineEdit").toBool();
+
+            if (isLineEdit) {
+                QLineEdit *standardLineEdit = qobject_cast<QLineEdit *>(standardWidget);
+                if (standardLineEdit) {
+                    standardLineEdit->setText(rawTagLineEdit->text());
+                }
+            } else {
+                QPlainTextEdit *standardPlainTextEdit = qobject_cast<QPlainTextEdit *>(standardWidget);
+                if (standardPlainTextEdit) {
+                    QTextCursor cursor = standardPlainTextEdit->textCursor();
+                    standardPlainTextEdit->setPlainText(rawTagLineEdit->text());
+                    standardPlainTextEdit->setTextCursor(cursor);
+                }
+            }
+        }
+    }
+}
+
 void NTagEditorDialog::onSaveClicked()
 {
     if (NSettings::instance()->value("DisplayTagEditorConfirmDialog").toBool()) {
@@ -316,7 +365,11 @@ void NTagEditorDialog::onSaveClicked()
                            tr("Do you want to save your changes?"),
                            QMessageBox::Save | QMessageBox::Cancel, this);
         msgBox.setDefaultButton(QMessageBox::Save);
-        msgBox.setCheckBox(checkBox);
+        // Qt4 doesn't have setCheckBox(), add checkbox manually to layout
+        QGridLayout *layout = qobject_cast<QGridLayout *>(msgBox.layout());
+        if (layout) {
+            layout->addWidget(checkBox, layout->rowCount(), 0, 1, layout->columnCount());
+        }
         int res = msgBox.exec();
 
         if (res != QMessageBox::Save) {
diff --git a/src/tagEditorDialog.h b/src/tagEditorDialog.h
index 08889d9..eeae7df 100644
--- a/src/tagEditorDialog.h
+++ b/src/tagEditorDialog.h
@@ -41,6 +41,7 @@ private:
     bool m_hasChanges;
     void readTags();
     bool writeTags();
+    void onTagTextChanged();
 
 public:
     NTagEditorDialog(const QString &file, QWidget *parent = 0);
@@ -50,6 +51,9 @@ private slots:
     void on_encodingComboBox_activated(int index);
     void onSaveClicked();
     void setReadOnlyMode(bool readOnly);
+    void onEncodingResetClicked();
+    void onEditAsUtf8Clicked();
+    void onRevertClicked();
 };
 
 #endif
diff --git a/src/trackInfoReader.cpp b/src/trackInfoReader.cpp
index 167f842..a648176 100644
--- a/src/trackInfoReader.cpp
+++ b/src/trackInfoReader.cpp
@@ -23,10 +23,11 @@ QString NTrackInfoReader::formatTime(int durationSec)
     int minutes = (durationSec - seconds) / 60;
     int hours = minutes / 60;
     minutes = minutes % 60;
+    // Qt4 doesn't have QString::asprintf() (added in Qt 5.5), use QString::sprintf() instead
     if (hours > 0) {
-        return QString::asprintf("%d:%02d:%02d", hours, minutes, seconds);
+        return QString().sprintf("%d:%02d:%02d", hours, minutes, seconds);
     } else {
-        return QString::asprintf("%d:%02d", minutes, seconds);
+        return QString().sprintf("%d:%02d", minutes, seconds);
     }
 }
 
diff --git a/src/updateChecker.cpp b/src/updateChecker.cpp
index 652b5fa..106e23b 100644
--- a/src/updateChecker.cpp
+++ b/src/updateChecker.cpp
@@ -33,7 +33,7 @@ NUpdateChecker &NUpdateChecker::instance()
 NUpdateChecker::NUpdateChecker()
 {
     m_networkManager = new QNetworkAccessManager(this);
-    connect(m_networkManager, &QNetworkAccessManager::finished, this, &NUpdateChecker::on_finished);
+    connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(on_finished(QNetworkReply*)));
 }
 
 QString NUpdateChecker::version() const
diff --git a/src/waveformPeaks.cpp b/src/waveformPeaks.cpp
index 4f0627d..031493e 100644
--- a/src/waveformPeaks.cpp
+++ b/src/waveformPeaks.cpp
@@ -32,7 +32,7 @@ void NWaveformPeaks::reset()
     m_counter = 0;
     m_completed = false;
 
-    m_vector = QVector<QPair<qreal, qreal>>(MAX_RES, qMakePair(0.0, 0.0));
+    m_vector = QVector<QPair<qreal, qreal> >(MAX_RES, qMakePair(0.0, 0.0));
 }
 
 int NWaveformPeaks::size() const
diff --git a/src/waveformPeaks.h b/src/waveformPeaks.h
index e7fd7bc..19afcde 100644
--- a/src/waveformPeaks.h
+++ b/src/waveformPeaks.h
@@ -23,7 +23,7 @@
 class NWaveformPeaks
 {
 private:
-    QVector<QPair<qreal, qreal>> m_vector;
+    QVector<QPair<qreal, qreal> > m_vector;
     bool m_completed;
     int m_index;
     int m_factor;
diff --git a/src/widgetCollection/playlistWidget.cpp b/src/widgetCollection/playlistWidget.cpp
index 401aa14..c65940c 100644
--- a/src/widgetCollection/playlistWidget.cpp
+++ b/src/widgetCollection/playlistWidget.cpp
@@ -17,6 +17,7 @@
 
 #include <QContextMenuEvent>
 #include <QDrag>
+#include <QKeyEvent>
 #include <QMap>
 #include <QScrollBar>
 
@@ -45,9 +46,7 @@ NPlaylistWidget::NPlaylistWidget(QWidget *parent) : QListWidget(parent)
     Q_ASSERT(m_playbackEngine);
 
     // triggered bu user input (double click or enter, depending on platform):
-    connect(this, &QListWidget::itemActivated, [this](QListWidgetItem *item) {
-        playItem(reinterpret_cast<NPlaylistWidgetItem *>(item));
-    });
+    connect(this, SIGNAL(itemActivated(QListWidgetItem*)), this, SLOT(on_itemActivated(QListWidgetItem*)));
     connect(m_playbackEngine, SIGNAL(mediaFinished(const QString &, int)), this,
             SLOT(on_playbackEngine_mediaFinished(const QString &, int)));
     connect(m_playbackEngine, SIGNAL(mediaFailed(const QString &, int)), this,
@@ -74,10 +73,7 @@ NPlaylistWidget::NPlaylistWidget(QWidget *parent) : QListWidget(parent)
 
     m_processVisibleItemsTimer = new QTimer(this);
     m_processVisibleItemsTimer->setSingleShot(true);
-    connect(m_processVisibleItemsTimer, &QTimer::timeout, [this]() {
-        processVisibleItems();
-        calculateDuration();
-    });
+    connect(m_processVisibleItemsTimer, SIGNAL(timeout()), this, SLOT(on_processVisibleItemsTimer_timeout()));
     connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this,
             SLOT(startProcessVisibleItemsTimer()));
 }
@@ -125,7 +121,7 @@ void NPlaylistWidget::processVisibleItems()
 QStringList NPlaylistWidget::selectedFiles() const
 {
     QStringList files;
-    for (QListWidgetItem *item : selectedItems()) {
+    foreach (QListWidgetItem *item, selectedItems()) {
         files << item->data(N::PathRole).toString();
     }
     return files;
@@ -137,6 +133,33 @@ void NPlaylistWidget::wheelEvent(QWheelEvent *event)
     event->accept();
 }
 
+void NPlaylistWidget::keyPressEvent(QKeyEvent *event)
+{
+    // Qt4: Handle Delete and Backspace keys for removing selected items
+    // On macOS, Cmd+Backspace is the standard delete shortcut
+    bool isDeleteKey = (event->key() == Qt::Key_Delete ||
+                        event->key() == Qt::Key_Backspace);
+
+#ifdef Q_OS_MAC
+    // macOS: Cmd+Backspace
+    bool isDeleteShortcut = (event->key() == Qt::Key_Backspace &&
+                             event->modifiers() & Qt::MetaModifier);
+#else
+    // Linux/Windows: Ctrl+Backspace
+    bool isDeleteShortcut = (event->key() == Qt::Key_Backspace &&
+                             event->modifiers() & Qt::ControlModifier);
+#endif
+
+    if ((isDeleteKey || isDeleteShortcut) && selectedItems().count() > 0) {
+        removeSelected();
+        event->accept();
+        return;
+    }
+
+    // For all other keys, use default QListWidget behavior
+    QListWidget::keyPressEvent(event);
+}
+
 void NPlaylistWidget::contextMenuEvent(QContextMenuEvent *event)
 {
     if (selectedItems().size() != 0 && itemAt(event->pos())) {
@@ -156,7 +179,7 @@ void NPlaylistWidget::removeFiles(const QStringList &files)
     }
 
     std::reverse(rowsToRemove.begin(), rowsToRemove.end());
-    for (int row : rowsToRemove) {
+    foreach (int row, rowsToRemove) {
         delete takeItem(row);
     }
 
@@ -742,6 +765,12 @@ void NPlaylistWidget::dragEnterEvent(QDragEnterEvent *event)
     }
 
     QListWidget::dragEnterEvent(event);
+
+    // Qt4: Explicitly accept external file drops (from Finder/file manager)
+    // QListWidget::dragEnterEvent only accepts internal drags (item reordering)
+    if (event->mimeData() && event->mimeData()->hasUrls()) {
+        event->acceptProposedAction();
+    }
 }
 
 void NPlaylistWidget::dragMoveEvent(QDragMoveEvent *event)
@@ -751,6 +780,11 @@ void NPlaylistWidget::dragMoveEvent(QDragMoveEvent *event)
     }
 
     QListWidget::dragMoveEvent(event);
+
+    // Qt4: Explicitly accept external file drops during drag move
+    if (event->mimeData() && event->mimeData()->hasUrls()) {
+        event->acceptProposedAction();
+    }
 }
 
 void NPlaylistWidget::dragLeaveEvent(QDragLeaveEvent *event)
@@ -819,3 +853,14 @@ void NPlaylistWidget::setFileDropRadius(int radius)
     m_fileDropRadius = radius;
 }
 // << STYLESHEET PROPERTIES
+
+void NPlaylistWidget::on_itemActivated(QListWidgetItem *item)
+{
+    playItem(reinterpret_cast<NPlaylistWidgetItem *>(item));
+}
+
+void NPlaylistWidget::on_processVisibleItemsTimer_timeout()
+{
+    processVisibleItems();
+    calculateDuration();
+}
diff --git a/src/widgetCollection/playlistWidget.h b/src/widgetCollection/playlistWidget.h
index f78340a..0979aff 100644
--- a/src/widgetCollection/playlistWidget.h
+++ b/src/widgetCollection/playlistWidget.h
@@ -22,7 +22,7 @@
 
 #include "global.h"
 
-class NPlaylistDataItem;
+struct NPlaylistDataItem;
 class NPlaylistWidgetItem;
 class NTrackInfoReader;
 class NPlaybackEngineInterface;
@@ -61,6 +61,7 @@ private:
 
 protected:
     void wheelEvent(QWheelEvent *event);
+    void keyPressEvent(QKeyEvent *event);
 
 protected slots:
     void rowsInserted(const QModelIndex &parent, int start, int end);
@@ -70,6 +71,8 @@ private slots:
 
     void on_playbackEngine_mediaChanged(const QString &file, int id);
     void on_playbackEngine_prepareNextMediaRequested();
+    void on_itemActivated(QListWidgetItem *item);
+    void on_processVisibleItemsTimer_timeout();
     void on_playbackEngine_mediaFinished(const QString &file, int id);
     void on_playbackEngine_mediaFailed(const QString &file, int id);
 
@@ -125,13 +128,13 @@ public:
         DragStartInside,
         DragStartOutside
     };
-    Q_ENUM(DragStart)
+    Q_ENUMS(DragStart)
     enum DropEnd
     {
         DropEndInside,
         DropEndOutside
     };
-    Q_ENUM(DropEnd)
+    Q_ENUMS(DropEnd)
 private:
     DragStart m_dragStart;
     DropEnd m_dropEnd;
diff --git a/src/widgetCollection/playlistWidgetItem.cpp b/src/widgetCollection/playlistWidgetItem.cpp
index 6768019..7821bbc 100644
--- a/src/widgetCollection/playlistWidgetItem.cpp
+++ b/src/widgetCollection/playlistWidgetItem.cpp
@@ -124,7 +124,7 @@ void NPlaylistWidgetItemDelegate::paint(QPainter *painter, const QStyleOptionVie
                                         const QModelIndex &index) const
 {
     QStyleOptionViewItem opt = option;
-    const NPlaylistWidget *playlistWidget = qobject_cast<const NPlaylistWidget *>(opt.widget);
+    const NPlaylistWidget *playlistWidget = qobject_cast<const NPlaylistWidget *>(parent());
 
     if (index.data(N::FailedRole).toBool()) { // FailedRole has higher priority than PlayingRole
         QColor color = playlistWidget->failedTextColor();
diff --git a/src/widgetCollection/slider.h b/src/widgetCollection/slider.h
index fa15f9b..dcd2dc9 100644
--- a/src/widgetCollection/slider.h
+++ b/src/widgetCollection/slider.h
@@ -27,9 +27,9 @@ public:
     qreal valueAtPos(int pos);
 
 protected:
-    virtual void mousePressEvent(QMouseEvent *event) override;
-    virtual void mouseMoveEvent(QMouseEvent *event) override;
-    virtual void wheelEvent(QWheelEvent *event) override;
+    virtual void mousePressEvent(QMouseEvent *event);
+    virtual void mouseMoveEvent(QMouseEvent *event);
+    virtual void wheelEvent(QWheelEvent *event);
 
 private slots:
     void setValue(int){};
diff --git a/src/widgetCollection/volumeSlider.h b/src/widgetCollection/volumeSlider.h
index b51062a..84ae9e2 100644
--- a/src/widgetCollection/volumeSlider.h
+++ b/src/widgetCollection/volumeSlider.h
@@ -26,9 +26,9 @@ public:
     NVolumeSlider(QWidget *parent);
 
 private:
-    void mousePressEvent(QMouseEvent *event) override;
-    void mouseMoveEvent(QMouseEvent *event) override;
-    void wheelEvent(QWheelEvent *e) override;
+    void mousePressEvent(QMouseEvent *event);
+    void mouseMoveEvent(QMouseEvent *event);
+    void wheelEvent(QWheelEvent *e);
     QString toolTipText(int value) const;
 
 private slots:
diff --git a/src/widgetCollection/waveformSlider.cpp b/src/widgetCollection/waveformSlider.cpp
index d784ec6..02f6cf2 100644
--- a/src/widgetCollection/waveformSlider.cpp
+++ b/src/widgetCollection/waveformSlider.cpp
@@ -119,9 +119,8 @@ void NWaveformSlider::checkForUpdate()
 
     if (m_needsUpdate) {
         QPainter painter;
-        qreal dpr = devicePixelRatioF();
+        qreal dpr = 1.0;  // Qt4 doesn't have 1.0
         QImage image(width() * dpr, height() * dpr, QImage::Format_ARGB32_Premultiplied);
-        image.setDevicePixelRatio(dpr);
         QImage waveImage;
         waveImage = m_backgroundImage = m_progressPlayingImage = m_progressPausedImage =
             m_remainingPlayingImage = m_remainingPausedImage = image;
@@ -200,7 +199,7 @@ void NWaveformSlider::paintEvent(QPaintEvent *)
 
     if (m_hasMedia) {
         qreal x = (qreal)value() / maximum() * width();
-        qreal dpr = devicePixelRatioF();
+        qreal dpr = 1.0;
         QTransform transform;
         transform.scale(dpr, dpr);
 
diff --git a/src/widgetCollection/waveformSlider.h b/src/widgetCollection/waveformSlider.h
index 7473fcb..01ba016 100644
--- a/src/widgetCollection/waveformSlider.h
+++ b/src/widgetCollection/waveformSlider.h
@@ -20,7 +20,7 @@
 #include <QPainter>
 #include <QVector>
 
-class NPlaylistDataItem;
+struct NPlaylistDataItem;
 class NWaveformBuilderInterface;
 
 class NWaveformSlider : public QAbstractSlider
diff --git a/src/widgetCollection/widgetCollection.cpp b/src/widgetCollection/widgetCollection.cpp
index 4bfb613..6643810 100644
--- a/src/widgetCollection/widgetCollection.cpp
+++ b/src/widgetCollection/widgetCollection.cpp
@@ -27,10 +27,10 @@
 
 // clang-format off
 #define N_WIDGET_PLUGIN(CLASS_NAME) \
-class CLASS_NAME##Plugin : public QObject, public NWidgetPlugin \
+class CLASS_NAME##Plugin : public NWidgetPlugin \
 { \
 public: \
-    CLASS_NAME##Plugin(QObject *parent = 0) : QObject(parent), NWidgetPlugin(#CLASS_NAME) {} \
+    CLASS_NAME##Plugin(QObject *parent = 0) : NWidgetPlugin(#CLASS_NAME) { Q_UNUSED(parent); } \
     virtual QWidget* createWidget(QWidget *parent) { return new CLASS_NAME(parent); } \
     virtual bool isContainer() const { return false; } \
 }; \
@@ -79,7 +79,7 @@ void NWidgetPlugin::initialize(QDesignerFormEditorInterface *core)
     m_initialized = true;
 }
 
-NWidgetCollection::NWidgetCollection(QObject *parent) : QObject(parent)
+NWidgetCollection::NWidgetCollection(QObject *parent) : QObject(parent), QDesignerCustomWidgetCollectionInterface()
 {
     m_plugins.push_back(new NWaveformSliderPlugin(this));
     m_plugins.push_back(new QSizeGripPlugin(this));
@@ -89,3 +89,5 @@ NWidgetCollection::NWidgetCollection(QObject *parent) : QObject(parent)
     m_plugins.push_back(new NLabelPlugin(this));
     m_plugins.push_back(new NCoverWidgetPlugin(this));
 }
+
+Q_EXPORT_PLUGIN2(widgetcollection, NWidgetCollection)
diff --git a/src/widgetCollection/widgetCollection.h b/src/widgetCollection/widgetCollection.h
index 7dd84e4..f7c562b 100644
--- a/src/widgetCollection/widgetCollection.h
+++ b/src/widgetCollection/widgetCollection.h
@@ -25,9 +25,11 @@
 #include <QtUiPlugin/QDesignerCustomWidgetInterface>
 #endif
 
-class NWidgetPlugin : public QDesignerCustomWidgetInterface
+class NWidgetPlugin : public QObject, public QDesignerCustomWidgetInterface
 {
-    Q_INTERFACES(QDesignerCustomWidgetInterface)
+    Q_OBJECT
+    // Q_INTERFACES not needed for Qt4 static plugins using Q_EXPORT_PLUGIN2
+    // Only the collection class (NWidgetCollection) needs Q_INTERFACES
 
 private:
     bool m_initialized;
@@ -42,6 +44,8 @@ public:
     QString name() const { return m_className; }
     void initialize(QDesignerFormEditorInterface *core);
     bool isInitialized() const { return m_initialized; }
+    virtual QWidget *createWidget(QWidget *parent) = 0;
+    virtual bool isContainer() const = 0;
     virtual QIcon icon() const { return QIcon(); }
     virtual QString whatsThis() const { return QString(); }
     virtual QString toolTip() const { return QString(); }
@@ -53,8 +57,7 @@ public:
 class NWidgetCollection : public QObject, public QDesignerCustomWidgetCollectionInterface
 {
     Q_OBJECT
-    Q_INTERFACES(QDesignerCustomWidgetCollectionInterface)
-    Q_PLUGIN_METADATA(IID "com.nulloy.NWidgetCollection")
+    // Q_INTERFACES not needed for Qt4 static plugins using Q_EXPORT_PLUGIN2
 
 public:
     NWidgetCollection(QObject *parent = 0);
diff --git a/src/widgetCollection/widgetCollection.pro b/src/widgetCollection/widgetCollection.pro
index 2e45ae6..4229fb9 100644
--- a/src/widgetCollection/widgetCollection.pro
+++ b/src/widgetCollection/widgetCollection.pro
@@ -2,7 +2,9 @@ TEMPLATE = lib
 TARGET = widget_collection
 DESTDIR = $$PWD
 
-QT += designer gui
+# Qt4 doesn't have separate designer module, Qt5+ does
+greaterThan(QT_MAJOR_VERSION, 4): QT += designer
+QT += gui
 CONFIG += plugin static
 
 include(widgetCollection.pri)
diff --git a/tests/testPlaylistWidget.cpp b/tests/testPlaylistWidget.cpp
index f767eba..6562762 100644
--- a/tests/testPlaylistWidget.cpp
+++ b/tests/testPlaylistWidget.cpp
@@ -61,8 +61,8 @@ private slots:
     void cleanup()
     {
         delete m_playlistWidget;
-        m_playlistWidget = nullptr;
-        m_playbackEngine = nullptr;
+        m_playlistWidget = 0;
+        m_playbackEngine = 0;
     }
 
     void testPlaylistRemoval()
diff --git a/src/customUiLoader.h b/src/customUiLoader.h
new file mode 100644
index 0000000..7c22024
--- /dev/null
+++ b/src/customUiLoader.h
@@ -0,0 +1,83 @@
+/********************************************************************
+**  Nulloy Music Player, http://nulloy.com
+**  Copyright (C) 2010-2024 Sergey Vlasov <sergey@vlasov.me>
+**
+**  This program can be distributed under the terms of the GNU
+**  General Public License version 3.0 as published by the Free
+**  Software Foundation and appearing in the file LICENSE.GPL3
+**  included in the packaging of this file.  Please review the
+**  following information to ensure the GNU General Public License
+**  version 3.0 requirements will be met:
+**
+**  http://www.gnu.org/licenses/gpl-3.0.html
+**
+*********************************************************************/
+
+#ifndef N_CUSTOM_UI_LOADER_H
+#define N_CUSTOM_UI_LOADER_H
+
+#include <QtUiTools/QUiLoader>
+
+#ifndef _N_NO_SKINS_
+
+#include "widgetCollection/coverWidget.h"
+#include "widgetCollection/label.h"
+#include "widgetCollection/playlistWidget.h"
+#include "widgetCollection/volumeSlider.h"
+#include "widgetCollection/waveformSlider.h"
+
+// Custom QUiLoader for Qt4 static plugin widgets
+// Qt4's QUiLoader doesn't automatically discover static plugins,
+// so we need to manually instantiate our custom widgets
+class NCustomUiLoader : public QUiLoader
+{
+public:
+    NCustomUiLoader(QObject *parent = 0) : QUiLoader(parent) {}
+
+    // Override availableWidgets to include our custom widgets
+    // This tells QUiLoader that these widgets exist
+    QStringList availableWidgets() const
+    {
+        QStringList widgets = QUiLoader::availableWidgets();
+        widgets << "NLabel";
+        widgets << "NWaveformSlider";
+        widgets << "NVolumeSlider";
+        widgets << "NPlaylistWidget";
+        widgets << "NCoverWidget";
+        return widgets;
+    }
+
+protected:
+    QWidget *createWidget(const QString &className, QWidget *parent = 0,
+                          const QString &name = QString())
+    {
+        if (className == "NLabel") {
+            NLabel *widget = new NLabel(parent);
+            widget->setObjectName(name);
+            return widget;
+        } else if (className == "NWaveformSlider") {
+            NWaveformSlider *widget = new NWaveformSlider(parent);
+            widget->setObjectName(name);
+            return widget;
+        } else if (className == "NVolumeSlider") {
+            NVolumeSlider *widget = new NVolumeSlider(parent);
+            widget->setObjectName(name);
+            return widget;
+        } else if (className == "NPlaylistWidget") {
+            NPlaylistWidget *widget = new NPlaylistWidget(parent);
+            widget->setObjectName(name);
+            return widget;
+        } else if (className == "NCoverWidget") {
+            NCoverWidget *widget = new NCoverWidget(parent);
+            widget->setObjectName(name);
+            return widget;
+        }
+
+        // For all other widgets, use default QUiLoader behavior
+        return QUiLoader::createWidget(className, parent, name);
+    }
+};
+
+#endif // _N_NO_SKINS_
+
+#endif // N_CUSTOM_UI_LOADER_H
