Menu items to add files and streams by URL.

Fixes issue #10
This commit is contained in:
David Sansome 2010-02-24 22:26:01 +00:00
parent ad56fdc932
commit 294eae80dd
15 changed files with 474 additions and 5 deletions

View File

@ -57,5 +57,7 @@
<file>media-playback-start-32.png</file>
<file>lightbulb.png</file>
<file>shuffle.png</file>
<file>open_media.png</file>
<file>open_stream.png</file>
</qresource>
</RCC>

BIN
data/open_media.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 796 B

BIN
data/open_stream.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

62
src/addstreamdialog.cpp Normal file
View File

@ -0,0 +1,62 @@
#include "addstreamdialog.h"
#include "radiomodel.h"
#include "savedradio.h"
#include <QSettings>
#include <QUrl>
#include <QPushButton>
#include <QtDebug>
const char* AddStreamDialog::kSettingsGroup = "AddStreamDialog";
AddStreamDialog::AddStreamDialog(QWidget *parent)
: QDialog(parent),
saved_radio_(NULL)
{
ui_.setupUi(this);
connect(ui_.url, SIGNAL(textChanged(QString)), SLOT(TextChanged(QString)));
TextChanged(QString::null);
// Restore settings
QSettings s;
s.beginGroup(kSettingsGroup);
ui_.save->setChecked(s.value("save", true).toBool());
ui_.url->setText(s.value("url").toString());
// Connections to the saved streams service
saved_radio_ = qobject_cast<SavedRadio*>(
RadioModel::ServiceByName(SavedRadio::kServiceName));
connect(saved_radio_, SIGNAL(ShowAddStreamDialog()), SLOT(show()));
}
QUrl AddStreamDialog::url() const {
return QUrl::fromUserInput(ui_.url->text());
}
void AddStreamDialog::accept() {
if (ui_.save->isChecked()) {
saved_radio_->Add(url());
}
// Save settings
QSettings s;
s.beginGroup(kSettingsGroup);
s.setValue("save", ui_.save->isChecked());
s.setValue("url", url().toString());
QDialog::accept();
}
void AddStreamDialog::TextChanged(const QString &text) {
// Decide whether the URL is valid
QUrl url(QUrl::fromUserInput(text));
bool valid = url.isValid() &&
!url.scheme().isEmpty() &&
!url.toString().isEmpty();
ui_.button_box->button(QDialogButtonBox::Ok)->setEnabled(valid);
}

31
src/addstreamdialog.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef ADDSTREAMDIALOG_H
#define ADDSTREAMDIALOG_H
#include <QDialog>
#include "ui_addstreamdialog.h"
class SavedRadio;
class AddStreamDialog : public QDialog {
Q_OBJECT
public:
AddStreamDialog(QWidget *parent = 0);
QUrl url() const;
void accept();
private slots:
void TextChanged(const QString& text);
private:
static const char* kSettingsGroup;
Ui::AddStreamDialog ui_;
SavedRadio* saved_radio_;
};
#endif // ADDSTREAMDIALOG_H

97
src/addstreamdialog.ui Normal file
View File

@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AddStreamDialog</class>
<widget class="QDialog" name="AddStreamDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>122</height>
</rect>
</property>
<property name="windowTitle">
<string>Add Stream</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Enter the URL of an internet radio stream:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="url"/>
</item>
<item>
<widget class="QCheckBox" name="save">
<property name="text">
<string>Save this stream in the Radio tab</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>7</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="button_box">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>button_box</sender>
<signal>accepted()</signal>
<receiver>AddStreamDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>button_box</sender>
<signal>rejected()</signal>
<receiver>AddStreamDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -15,6 +15,7 @@
#include "settingsdialog.h"
#include "libraryconfigdialog.h"
#include "about.h"
#include "addstreamdialog.h"
#include "qxtglobalshortcut.h"
@ -28,6 +29,7 @@
#include <QtDebug>
#include <QCloseEvent>
#include <QSignalMapper>
#include <QFileDialog>
#include <cmath>
@ -48,6 +50,7 @@ MainWindow::MainWindow(QWidget *parent)
player_(new Player(playlist_, radio_model_->GetLastFMService(), this)),
library_(new Library(player_->GetEngine(), this)),
settings_dialog_(new SettingsDialog(this)),
add_stream_dialog_(new AddStreamDialog(this)),
playlist_menu_(new QMenu(this)),
library_sort_model_(new QSortFilterProxyModel(this)),
track_position_timer_(new QTimer(this))
@ -104,6 +107,9 @@ MainWindow::MainWindow(QWidget *parent)
connect(ui_.action_configure, SIGNAL(triggered()), settings_dialog_, SLOT(show()));
connect(ui_.action_about, SIGNAL(triggered()), about_dialog_, SLOT(show()));
connect(ui_.action_shuffle, SIGNAL(triggered()), playlist_, SLOT(Shuffle()));
connect(ui_.action_open_media, SIGNAL(triggered()), SLOT(AddMedia()));
connect(ui_.action_add_media, SIGNAL(triggered()), SLOT(AddMedia()));
connect(ui_.action_add_stream, SIGNAL(triggered()), SLOT(AddStream()));
// Give actions to buttons
ui_.forward_button->setDefaultAction(ui_.action_next_track);
@ -243,6 +249,9 @@ MainWindow::MainWindow(QWidget *parent)
connect(settings_dialog_, SIGNAL(accepted()), player_, SLOT(ReloadSettings()));
connect(settings_dialog_, SIGNAL(accepted()), osd_, SLOT(ReloadSettings()));
// Add stream dialog
connect(add_stream_dialog_, SIGNAL(accepted()), SLOT(AddStreamAccepted()));
// Analyzer
ui_.analyzer->set_engine(player_->GetEngine());
@ -557,3 +566,40 @@ void MainWindow::LibraryScanFinished() {
void MainWindow::PlayerInitFinished() {
multi_loading_indicator_->TaskFinished(MultiLoadingIndicator::LoadingAudioEngine);
}
void MainWindow::AddMedia() {
QSettings s;
s.beginGroup(kSettingsGroup);
// Last used directory
QString directory = s.value("add_media_path", QDir::currentPath()).toString();
// Show dialog
QStringList file_names = QFileDialog::getOpenFileNames(this, "Add media", directory);
if (file_names.isEmpty())
return;
// Save last used directory
s.setValue("add_media_path", file_names[0]);
// Add media
QList<QUrl> urls;
foreach (const QString& path, file_names) {
QUrl url(path);
if (url.scheme().isEmpty())
url.setScheme("file");
urls << url;
}
playlist_->InsertPaths(urls);
}
void MainWindow::AddStream() {
add_stream_dialog_->show();
}
void MainWindow::AddStreamAccepted() {
QList<QUrl> urls;
urls << add_stream_dialog_->url();
playlist_->InsertStreamUrls(urls);
}

View File

@ -19,6 +19,7 @@ class EditTagDialog;
class MultiLoadingIndicator;
class SettingsDialog;
class About;
class AddStreamDialog;
class QSortFilterProxyModel;
class SystemTrayIcon;
@ -71,6 +72,10 @@ class MainWindow : public QMainWindow {
void PlayerInitFinished();
void AddMedia();
void AddStream();
void AddStreamAccepted();
private:
void SaveGeometry();
@ -93,6 +98,7 @@ class MainWindow : public QMainWindow {
Library* library_;
SettingsDialog* settings_dialog_;
AddStreamDialog* add_stream_dialog_;
QMenu* playlist_menu_;
QAction* playlist_play_pause_;

View File

@ -353,6 +353,9 @@
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
</layout>
@ -434,13 +437,15 @@
<x>0</x>
<y>0</y>
<width>804</width>
<height>23</height>
<height>21</height>
</rect>
</property>
<widget class="QMenu" name="menuMusic">
<property name="title">
<string>Music</string>
</property>
<addaction name="action_open_media"/>
<addaction name="separator"/>
<addaction name="action_previous_track"/>
<addaction name="action_play_pause"/>
<addaction name="action_stop"/>
@ -455,6 +460,9 @@
<property name="title">
<string>Playlist</string>
</property>
<addaction name="action_add_media"/>
<addaction name="action_add_stream"/>
<addaction name="separator"/>
<addaction name="action_clear_playlist"/>
<addaction name="action_shuffle"/>
</widget>
@ -657,6 +665,33 @@
<string>Shuffle playlist</string>
</property>
</action>
<action name="action_add_media">
<property name="icon">
<iconset resource="../data/data.qrc">
<normaloff>:/open_media.png</normaloff>:/open_media.png</iconset>
</property>
<property name="text">
<string>Add media...</string>
</property>
</action>
<action name="action_add_stream">
<property name="icon">
<iconset resource="../data/data.qrc">
<normaloff>:/open_stream.png</normaloff>:/open_stream.png</iconset>
</property>
<property name="text">
<string>Add stream...</string>
</property>
</action>
<action name="action_open_media">
<property name="icon">
<iconset resource="../data/data.qrc">
<normaloff>:/open_media.png</normaloff>:/open_media.png</iconset>
</property>
<property name="text">
<string>Open media...</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>

View File

@ -3,6 +3,8 @@
#include "songplaylistitem.h"
#include "radiomimedata.h"
#include "radioplaylistitem.h"
#include "radiomodel.h"
#include "savedradio.h"
#include <QtDebug>
#include <QMimeData>
@ -292,6 +294,15 @@ QModelIndex Playlist::InsertRadioStations(const QList<RadioItem*>& items, int af
return InsertItems(playlist_items, after);
}
QModelIndex Playlist::InsertStreamUrls(const QList<QUrl>& urls, int after) {
QList<PlaylistItem*> playlist_items;
foreach (const QUrl& url, urls) {
playlist_items << new RadioPlaylistItem(
RadioModel::ServiceByName(SavedRadio::kServiceName), url.toString(), url.toString(), QString());
}
return InsertItems(playlist_items, after);
}
QMimeData* Playlist::mimeData(const QModelIndexList& indexes) const {
QMimeData* data = new QMimeData;

View File

@ -73,6 +73,7 @@ class Playlist : public QAbstractListModel {
QModelIndex InsertItems(const QList<PlaylistItem*>& items, int after = -1);
QModelIndex InsertSongs(const SongList& items, int after = -1);
QModelIndex InsertRadioStations(const QList<RadioItem*>& items, int after = -1);
QModelIndex InsertStreamUrls(const QList<QUrl>& urls, int after = -1);
QModelIndex InsertPaths(QList<QUrl> urls, int after = -1);
void StopAfter(int row);
void ReloadItems(const QList<int>& rows);

View File

@ -3,6 +3,7 @@
#include "lastfmservice.h"
#include "somafmservice.h"
#include "radiomimedata.h"
#include "savedradio.h"
#include <QMimeData>
#include <QtDebug>
@ -18,6 +19,7 @@ RadioModel::RadioModel(QObject* parent)
AddService(new LastFMService(this));
AddService(new SomaFMService(this));
AddService(new SavedRadio(this));
}
void RadioModel::AddService(RadioService *service) {

115
src/savedradio.cpp Normal file
View File

@ -0,0 +1,115 @@
#include "savedradio.h"
#include <QSettings>
#include <QMenu>
const char* SavedRadio::kServiceName = "SavedRadio";
const char* SavedRadio::kSettingsGroup = "SavedRadio";
SavedRadio::SavedRadio(QObject* parent)
: RadioService(kServiceName, parent),
root_(NULL),
context_menu_(new QMenu)
{
add_action_ = context_menu_->addAction(QIcon(":media-playback-start.png"), tr("Add to playlist"), this, SLOT(AddToPlaylist()));
remove_action_ = context_menu_->addAction(QIcon(":list-remove.png"), tr("Remove"), this, SLOT(Remove()));
context_menu_->addSeparator();
context_menu_->addAction(QIcon(":open_stream.png"), tr("Add another stream..."), this, SIGNAL(ShowAddStreamDialog()));
LoadStreams();
}
SavedRadio::~SavedRadio() {
delete context_menu_;
}
RadioItem* SavedRadio::CreateRootItem(RadioItem* parent) {
root_ = new RadioItem(this, RadioItem::Type_Service, "Your radio streams", parent);
root_->icon = QIcon(":open_stream.png");
return root_;
}
void SavedRadio::LazyPopulate(RadioItem* item) {
switch (item->type) {
case RadioItem::Type_Service:
foreach (const QString& stream, streams_)
ItemForStream(stream, root_);
break;
default:
break;
}
item->lazy_loaded = true;
}
void SavedRadio::LoadStreams() {
// Load saved streams
QSettings s;
s.beginGroup(kSettingsGroup);
int count = s.beginReadArray("streams");
for (int i=0 ; i<count ; ++i) {
s.setArrayIndex(i);
streams_ << s.value("url").toString();
}
s.endArray();
}
void SavedRadio::SaveStreams() {
QSettings s;
s.beginGroup(kSettingsGroup);
int count = streams_.size();
s.beginWriteArray("streams", count);
for (int i=0 ; i<count ; ++i) {
s.setArrayIndex(i);
s.setValue("url", streams_[i]);
}
s.endArray();
}
void SavedRadio::ShowContextMenu(RadioItem* item, const QPoint& global_pos) {
context_item_ = item;
add_action_->setEnabled(item != root_);
remove_action_->setEnabled(item != root_);
context_menu_->popup(global_pos);
}
void SavedRadio::Remove() {
streams_.removeAll(context_item_->key);
context_item_->parent->DeleteNotify(context_item_->row);
SaveStreams();
}
void SavedRadio::StartLoading(const QUrl& url) {
emit StreamReady(url, url);
}
void SavedRadio::AddToPlaylist() {
emit AddItemToPlaylist(context_item_);
}
RadioItem* SavedRadio::ItemForStream(const QUrl& url, RadioItem* parent) {
RadioItem* s = new RadioItem(this, Type_Stream, url.toString(), parent);
s->lazy_loaded = true;
s->icon = QIcon(":last.fm/icon_radio.png");
s->playable = true;
return s;
}
void SavedRadio::Add(const QUrl &url) {
if (streams_.contains(url.toString()))
return;
streams_ << url.toString();
if (root_->lazy_loaded) {
RadioItem* s = ItemForStream(url, NULL);
s->InsertNotify(root_);
}
SaveStreams();
}

54
src/savedradio.h Normal file
View File

@ -0,0 +1,54 @@
#ifndef SAVEDRADIO_H
#define SAVEDRADIO_H
#include "radioservice.h"
class QMenu;
class SavedRadio : public RadioService {
Q_OBJECT
public:
SavedRadio(QObject* parent = 0);
~SavedRadio();
enum ItemType {
Type_Stream = 2000,
};
static const char* kServiceName;
static const char* kSettingsGroup;
RadioItem* CreateRootItem(RadioItem* parent);
void LazyPopulate(RadioItem* item);
void ShowContextMenu(RadioItem* item, const QPoint& global_pos);
void StartLoading(const QUrl& url);
void Add(const QUrl& url);
signals:
void ShowAddStreamDialog();
private slots:
void AddToPlaylist();
void Remove();
private:
void LoadStreams();
void SaveStreams();
RadioItem* ItemForStream(const QUrl& url, RadioItem* parent);
private:
RadioItem* root_;
QMenu* context_menu_;
RadioItem* context_item_;
QAction* add_action_;
QAction* remove_action_;
QStringList streams_;
};
#endif // SAVEDRADIO_H

View File

@ -52,7 +52,9 @@ SOURCES += main.cpp \
libraryconfigdialog.cpp \
lastfmconfigdialog.cpp \
about.cpp \
albumcoverfetcher.cpp
albumcoverfetcher.cpp \
addstreamdialog.cpp \
savedradio.cpp
HEADERS += mainwindow.h \
player.h \
library.h \
@ -105,7 +107,9 @@ HEADERS += mainwindow.h \
libraryconfigdialog.h \
lastfmconfigdialog.h \
about.h \
albumcoverfetcher.h
albumcoverfetcher.h \
addstreamdialog.h \
savedradio.h
FORMS += mainwindow.ui \
libraryconfig.ui \
fileview.ui \
@ -117,7 +121,8 @@ FORMS += mainwindow.ui \
settingsdialog.ui \
libraryconfigdialog.ui \
lastfmconfigdialog.ui \
about.ui
about.ui \
addstreamdialog.ui
RESOURCES += ../data/data.qrc \
translations.qrc
OTHER_FILES += ../data/schema.sql \
@ -144,7 +149,9 @@ win32|fedora-win32-cross {
LIBS += -llastfm
# Enable a bunch of warnings
QMAKE_CXXFLAGS += -Wall -Werror=non-virtual-dtor -Woverloaded-virtual
QMAKE_CXXFLAGS += -Wall \
-Werror=non-virtual-dtor \
-Woverloaded-virtual
# Other platform specific libraries
!win32:!fedora-win32-cross {