Use a save dialog option instead of quick change menu.

This is less confusing IMO. The dialog will shown up only if users decided to in the preferences, so that will not bother users who don't need this.
This reuses lot of things from Alan contribution from #4484
This commit is contained in:
Arnaud Bienner 2014-10-07 00:23:28 +02:00
parent 54d76506a7
commit 78804b12c6
26 changed files with 121 additions and 92 deletions

View File

@ -138,6 +138,8 @@ class Playlist : public QAbstractListModel {
Path_Automatic = 0, // Automatically select path type
Path_Absolute, // Always use absolute paths
Path_Relative, // Always use relative paths
Path_Ask_User, // Only used in preferences: to ask user which of the
// previous values he wants to use.
};
static const char* kCddaMimeType;

View File

@ -18,6 +18,7 @@
#include "playlistbackend.h"
#include "playlistcontainer.h"
#include "playlistmanager.h"
#include "playlistsaveoptionsdialog.h"
#include "playlistview.h"
#include "core/application.h"
#include "core/logging.h"
@ -172,9 +173,10 @@ void PlaylistManager::Load(const QString& filename) {
playlist->InsertUrls(urls << QUrl::fromLocalFile(filename));
}
void PlaylistManager::Save(int id, const QString& filename) {
void PlaylistManager::Save(int id, const QString& filename,
Playlist::Path path_type) {
if (playlists_.contains(id)) {
parser_->Save(playlist(id)->GetAllSongs(), filename);
parser_->Save(playlist(id)->GetAllSongs(), filename, path_type);
} else {
// Playlist is not in the playlist manager: probably save action was
// triggered
@ -184,16 +186,18 @@ void PlaylistManager::Save(int id, const QString& filename) {
watcher->setFuture(future);
NewClosure(watcher, SIGNAL(finished()), this,
SLOT(ItemsLoadedForSavePlaylist(QFutureWatcher<Song>*, QString)),
SLOT(ItemsLoadedForSavePlaylist(QFutureWatcher<Song>*, QString,
Playlist::Path)),
watcher, filename);
}
}
void PlaylistManager::ItemsLoadedForSavePlaylist(QFutureWatcher<Song>* watcher,
const QString& filename) {
const QString& filename,
Playlist::Path path_type) {
SongList song_list = watcher->future().results();
parser_->Save(song_list, filename);
parser_->Save(song_list, filename, path_type);
}
void PlaylistManager::SaveWithUI(int id, const QString& suggested_filename) {
@ -230,10 +234,23 @@ void PlaylistManager::SaveWithUI(int id, const QString& suggested_filename) {
return;
}
QSettings s;
s.beginGroup(Playlist::kSettingsGroup);
int p = s.value(Playlist::kPathType, Playlist::Path_Automatic).toInt();
Playlist::Path path = static_cast<Playlist::Path>(p);
if (path == Playlist::Path_Ask_User) {
PlaylistSaveOptionsDialog optionsDialog(nullptr);
optionsDialog.setModal(true);
if (optionsDialog.exec() != QDialog::Accepted) {
return;
}
path = optionsDialog.path_type();
}
settings.setValue("last_save_playlist", filename);
settings.endGroup();
Save(id == -1 ? current_id() : id, filename);
Save(id == -1 ? current_id() : id, filename, path);
}
void PlaylistManager::Rename(int id, const QString& new_name) {

View File

@ -76,7 +76,7 @@ class PlaylistManagerInterface : public QObject {
virtual void New(const QString& name, const SongList& songs = SongList(),
const QString& special_type = QString()) = 0;
virtual void Load(const QString& filename) = 0;
virtual void Save(int id, const QString& filename) = 0;
virtual void Save(int id, const QString& filename, Playlist::Path path_type) = 0;
virtual void Rename(int id, const QString& new_name) = 0;
virtual void Delete(int id) = 0;
virtual bool Close(int id) = 0;
@ -181,7 +181,7 @@ class PlaylistManager : public PlaylistManagerInterface {
void New(const QString& name, const SongList& songs = SongList(),
const QString& special_type = QString());
void Load(const QString& filename);
void Save(int id, const QString& filename);
void Save(int id, const QString& filename, Playlist::Path path_type);
// Display a file dialog to let user choose a file before saving the file
void SaveWithUI(int id, const QString& suggested_filename);
void Rename(int id, const QString& new_name);
@ -232,7 +232,8 @@ class PlaylistManager : public PlaylistManagerInterface {
void UpdateSummaryText();
void SongsDiscovered(const SongList& songs);
void ItemsLoadedForSavePlaylist(QFutureWatcher<Song>* watcher,
const QString& filename);
const QString& filename,
Playlist::Path path_type);
private:
Playlist* AddPlaylist(int id, const QString& name,

View File

@ -1,5 +1,5 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
Copyright 2014, David Sansome <me@davidsansome.com>
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -29,44 +29,24 @@ PlaylistSaveOptionsDialog::PlaylistSaveOptionsDialog(QWidget* parent)
: QDialog(parent), ui(new Ui::PlaylistSaveOptionsDialog) {
ui->setupUi(this);
ui->filePaths->addItem(tr("Automatic"), PlaylistSaveOptions::Paths_Automatic);
ui->filePaths->addItem(tr("Relative"), PlaylistSaveOptions::Paths_Relative);
ui->filePaths->addItem(tr("Absolute"), PlaylistSaveOptions::Paths_Absolute);
ui->pathSeparators->addItem(tr("Automatic"),
PlaylistSaveOptions::Separators_Automatic);
ui->pathSeparators->addItem(tr("Windows-style") + " (\\)",
PlaylistSaveOptions::Separators_Windows);
ui->pathSeparators->addItem(tr("Unix-style") + " (/)",
PlaylistSaveOptions::Separators_Unix);
QSettings s;
s.beginGroup(kSettingsGroup);
ui->filePaths->setCurrentIndex(ui->filePaths->findData(
s.value("file_paths", PlaylistSaveOptions::Paths_Automatic)));
ui->pathSeparators->setCurrentIndex(ui->pathSeparators->findData(
s.value("path_separators", PlaylistSaveOptions::Separators_Automatic)));
ui->filePaths->addItem(tr("Automatic"), Playlist::Path_Automatic);
ui->filePaths->addItem(tr("Relative"), Playlist::Path_Relative);
ui->filePaths->addItem(tr("Absolute"), Playlist::Path_Absolute);
}
PlaylistSaveOptionsDialog::~PlaylistSaveOptionsDialog() { delete ui; }
void PlaylistSaveOptionsDialog::accept() {
QSettings s;
s.beginGroup(kSettingsGroup);
s.setValue("file_paths",
ui->filePaths->itemData(ui->filePaths->currentIndex()).toInt());
s.setValue(
"path_separators",
ui->pathSeparators->itemData(ui->pathSeparators->currentIndex()).toInt());
if (ui->remember_user_choice->isChecked()) {
QSettings s;
s.beginGroup(Playlist::kSettingsGroup);
s.setValue(Playlist::kPathType,
ui->filePaths->itemData(ui->filePaths->currentIndex()).toInt());
}
QDialog::accept();
}
PlaylistSaveOptions PlaylistSaveOptionsDialog::options() const {
PlaylistSaveOptions o;
o.filePathStyle = static_cast<PlaylistSaveOptions::FilePathStyle>(
ui->filePaths->itemData(ui->filePaths->currentIndex()).toInt());
o.pathSeparatorStyle = static_cast<PlaylistSaveOptions::PathSeparatorStyle>(
ui->pathSeparators->itemData(ui->pathSeparators->currentIndex()).toInt());
return o;
Playlist::Path PlaylistSaveOptionsDialog::path_type() const {
return static_cast<Playlist::Path>(ui->filePaths->itemData(ui->filePaths->currentIndex()).toInt());
}

View File

@ -1,5 +1,5 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
Copyright 2014, David Sansome <me@davidsansome.com>
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -20,7 +20,7 @@
#include <QDialog>
struct PlaylistSaveOptions;
#include "playlist.h"
namespace Ui {
class PlaylistSaveOptionsDialog;
@ -34,7 +34,7 @@ class PlaylistSaveOptionsDialog : public QDialog {
~PlaylistSaveOptionsDialog();
void accept();
PlaylistSaveOptions options() const;
Playlist::Path path_type() const;
private:
static const char* kSettingsGroup;

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>348</width>
<height>114</height>
<height>116</height>
</rect>
</property>
<property name="windowTitle">
@ -15,7 +15,7 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayout" columnstretch="0,1">
<layout class="QGridLayout" name="gridLayout" columnstretch="0,0">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
@ -23,18 +23,25 @@
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Path separators</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="filePaths"/>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="pathSeparators"/>
</layout>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_2">
<property name="topMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QCheckBox" name="remember_user_choice">
<property name="toolTip">
<string>This can be changed later through the preferences</string>
</property>
<property name="text">
<string>Remember my choice</string>
</property>
</widget>
</item>
</layout>
</item>

View File

@ -50,13 +50,14 @@ SongList AsxIniParser::Load(QIODevice* device, const QString& playlist_path,
}
void AsxIniParser::Save(const SongList& songs, QIODevice* device,
const QDir& dir) const {
const QDir& dir,
Playlist::Path path_type) const {
QTextStream s(device);
s << "[Reference]" << endl;
int n = 1;
for (const Song& song : songs) {
s << "Ref" << n << "=" << URLOrRelativeFilename(song.url(), dir) << endl;
s << "Ref" << n << "=" << URLOrFilename(song.url(), dir, path_type) << endl;
++n;
}
}

View File

@ -34,7 +34,8 @@ class AsxIniParser : public ParserBase {
SongList Load(QIODevice* device, const QString& playlist_path = "",
const QDir& dir = QDir()) const;
void Save(const SongList& songs, QIODevice* device,
const QDir& dir = QDir()) const;
const QDir& dir = QDir(),
Playlist::Path path_type = Playlist::Path_Automatic) const;
};
#endif // ASXINIPARSER_H

View File

@ -118,7 +118,7 @@ return_song:
}
void ASXParser::Save(const SongList& songs, QIODevice* device,
const QDir&) const {
const QDir&, Playlist::Path path_type) const {
QXmlStreamWriter writer(device);
writer.setAutoFormatting(true);
writer.setAutoFormattingIndent(2);

View File

@ -34,7 +34,8 @@ class ASXParser : public XMLParser {
SongList Load(QIODevice* device, const QString& playlist_path = "",
const QDir& dir = QDir()) const;
void Save(const SongList& songs, QIODevice* device,
const QDir& dir = QDir()) const;
const QDir& dir = QDir(),
Playlist::Path path_type = Playlist::Path_Automatic) const;
private:
Song ParseTrack(QXmlStreamReader* reader, const QDir& dir) const;

View File

@ -352,7 +352,8 @@ qint64 CueParser::IndexToMarker(const QString& index) const {
}
void CueParser::Save(const SongList& songs, QIODevice* device,
const QDir& dir) const {
const QDir& dir,
Playlist::Path path_type) const {
// TODO
}

View File

@ -54,7 +54,8 @@ class CueParser : public ParserBase {
SongList Load(QIODevice* device, const QString& playlist_path = "",
const QDir& dir = QDir()) const;
void Save(const SongList& songs, QIODevice* device,
const QDir& dir = QDir()) const;
const QDir& dir = QDir(),
Playlist::Path path_type = Playlist::Path_Automatic) const;
private:
// A single TRACK entry in .cue file.

View File

@ -105,7 +105,8 @@ bool M3UParser::ParseMetadata(const QString& line,
}
void M3UParser::Save(const SongList& songs, QIODevice* device,
const QDir& dir) const {
const QDir& dir,
Playlist::Path path_type) const {
device->write("#EXTM3U\n");
QSettings s;
@ -124,7 +125,7 @@ void M3UParser::Save(const SongList& songs, QIODevice* device,
.arg(song.title());
device->write(meta.toUtf8());
}
device->write(URLOrRelativeFilename(song.url(), dir).toUtf8());
device->write(URLOrFilename(song.url(), dir, path_type).toUtf8());
device->write("\n");
}
}

View File

@ -42,7 +42,8 @@ class M3UParser : public ParserBase {
SongList Load(QIODevice* device, const QString& playlist_path = "",
const QDir& dir = QDir()) const;
void Save(const SongList& songs, QIODevice* device,
const QDir& dir = QDir()) const;
const QDir& dir = QDir(),
Playlist::Path path_type = Playlist::Path_Automatic) const;
private:
enum M3UType {

View File

@ -86,22 +86,17 @@ Song ParserBase::LoadSong(const QString& filename_or_url, qint64 beginning,
return song;
}
QString ParserBase::URLOrRelativeFilename(const QUrl& url,
const QDir& dir) const {
QString ParserBase::URLOrFilename(const QUrl& url,
const QDir& dir,
Playlist::Path path_type) const {
if (url.scheme() != "file") return url.toString();
QSettings s;
s.beginGroup(Playlist::kSettingsGroup);
int p = s.value(Playlist::kPathType, Playlist::Path_Automatic).toInt();
const Playlist::Path path = static_cast<Playlist::Path>(p);
s.endGroup();
const QString filename = url.toLocalFile();
if (path != Playlist::Path_Absolute && QDir::isAbsolutePath(filename)) {
if (path_type != Playlist::Path_Absolute && QDir::isAbsolutePath(filename)) {
const QString relative = dir.relativeFilePath(filename);
if (!relative.startsWith("../") || path == Playlist::Path_Relative)
if (!relative.startsWith("../") || path_type == Playlist::Path_Relative)
return relative;
}
return filename;

View File

@ -22,6 +22,7 @@
#include <QDir>
#include "core/song.h"
#include "playlist/playlist.h"
class LibraryBackendInterface;
@ -51,7 +52,8 @@ class ParserBase : public QObject {
virtual SongList Load(QIODevice* device, const QString& playlist_path = "",
const QDir& dir = QDir()) const = 0;
virtual void Save(const SongList& songs, QIODevice* device,
const QDir& dir = QDir()) const = 0;
const QDir& dir = QDir(),
Playlist::Path path_type = Playlist::Path_Automatic) const = 0;
protected:
// Loads a song. If filename_or_url is a URL (with a scheme other than
@ -65,10 +67,12 @@ class ParserBase : public QObject {
void LoadSong(const QString& filename_or_url, qint64 beginning,
const QDir& dir, Song* song) const;
// If the URL is a file:// URL then returns its path relative to the
// directory. Otherwise returns the URL as is.
// If the URL is a file:// URL then returns its path, absolute or relative to
// the directory depending on the path_type option.
// Otherwise returns the URL as is.
// This function should always be used when saving a playlist.
QString URLOrRelativeFilename(const QUrl& url, const QDir& dir) const;
QString URLOrFilename(const QUrl& url, const QDir& dir,
Playlist::Path path_type) const;
private:
LibraryBackendInterface* library_;

View File

@ -131,7 +131,8 @@ SongList PlaylistParser::LoadFromDevice(QIODevice* device,
}
void PlaylistParser::Save(const SongList& songs,
const QString& filename) const {
const QString& filename,
Playlist::Path path_type) const {
QFileInfo info(filename);
// Find a parser that supports this file extension
@ -145,5 +146,5 @@ void PlaylistParser::Save(const SongList& songs,
QFile file(filename);
file.open(QIODevice::WriteOnly);
return parser->Save(songs, &file, info.absolutePath());
return parser->Save(songs, &file, info.absolutePath(), path_type);
}

View File

@ -22,6 +22,7 @@
#include <QObject>
#include "core/song.h"
#include "playlist/playlist.h"
class ParserBase;
class LibraryBackendInterface;
@ -48,7 +49,7 @@ class PlaylistParser : public QObject {
SongList LoadFromDevice(QIODevice* device,
const QString& path_hint = QString(),
const QDir& dir_hint = QDir()) const;
void Save(const SongList& songs, const QString& filename) const;
void Save(const SongList& songs, const QString& filename, Playlist::Path) const;
private:
QString FilterForParser(const ParserBase* parser,

View File

@ -62,7 +62,7 @@ SongList PLSParser::Load(QIODevice* device, const QString& playlist_path,
}
void PLSParser::Save(const SongList& songs, QIODevice* device,
const QDir& dir) const {
const QDir& dir, Playlist::Path path_type) const {
QTextStream s(device);
s << "[playlist]" << endl;
s << "Version=2" << endl;
@ -70,7 +70,7 @@ void PLSParser::Save(const SongList& songs, QIODevice* device,
int n = 1;
for (const Song& song : songs) {
s << "File" << n << "=" << URLOrRelativeFilename(song.url(), dir) << endl;
s << "File" << n << "=" << URLOrFilename(song.url(), dir, path_type) << endl;
s << "Title" << n << "=" << song.title() << endl;
s << "Length" << n << "=" << song.length_nanosec() / kNsecPerSec << endl;
++n;

View File

@ -34,7 +34,8 @@ class PLSParser : public ParserBase {
SongList Load(QIODevice* device, const QString& playlist_path = "",
const QDir& dir = QDir()) const;
void Save(const SongList& songs, QIODevice* device,
const QDir& dir = QDir()) const;
const QDir& dir = QDir(),
Playlist::Path path_type = Playlist::Path_Automatic) const;
};
#endif // PLSPARSER_H

View File

@ -77,7 +77,7 @@ void WplParser::ParseSeq(const QDir& dir, QXmlStreamReader* reader,
}
void WplParser::Save(const SongList& songs, QIODevice* device,
const QDir& dir) const {
const QDir& dir, Playlist::Path path_type) const {
QXmlStreamWriter writer(device);
writer.setAutoFormatting(true);
writer.setAutoFormattingIndent(2);
@ -98,7 +98,7 @@ void WplParser::Save(const SongList& songs, QIODevice* device,
StreamElement seq("seq", &writer);
for (const Song& song : songs) {
writer.writeStartElement("media");
writer.writeAttribute("src", URLOrRelativeFilename(song.url(), dir));
writer.writeAttribute("src", URLOrFilename(song.url(), dir, path_type));
writer.writeEndElement();
}
}

View File

@ -32,7 +32,8 @@ class WplParser : public XMLParser {
SongList Load(QIODevice* device, const QString& playlist_path,
const QDir& dir) const;
void Save(const SongList& songs, QIODevice* device, const QDir& dir) const;
void Save(const SongList& songs, QIODevice* device, const QDir& dir,
Playlist::Path path_type = Playlist::Path_Automatic) const;
private:
void ParseSeq(const QDir& dir, QXmlStreamReader* reader,

View File

@ -103,7 +103,7 @@ return_song:
}
void XSPFParser::Save(const SongList& songs, QIODevice* device,
const QDir& dir) const {
const QDir& dir, Playlist::Path path_type) const {
QFileInfo file;
QXmlStreamWriter writer(device);
writer.setAutoFormatting(true);
@ -120,7 +120,7 @@ void XSPFParser::Save(const SongList& songs, QIODevice* device,
StreamElement tracklist("trackList", &writer);
for (const Song& song : songs) {
QString filename_or_url = URLOrRelativeFilename(song.url(), dir).toUtf8();
QString filename_or_url = URLOrFilename(song.url(), dir, path_type).toUtf8();
StreamElement track("track", &writer);
writer.writeTextElement("location", filename_or_url);
@ -154,7 +154,7 @@ void XSPFParser::Save(const SongList& songs, QIODevice* device,
// playlist.
QUrl url = QUrl(art_filename);
url.setScheme("file"); // Need to explicitly set this.
art_filename = URLOrRelativeFilename(url, dir).toUtf8();
art_filename = URLOrFilename(url, dir, path_type).toUtf8();
} else {
// Just use whatever URL was in the Song.
art_filename = art;

View File

@ -39,7 +39,8 @@ class XSPFParser : public XMLParser {
SongList Load(QIODevice* device, const QString& playlist_path = "",
const QDir& dir = QDir()) const;
void Save(const SongList& songs, QIODevice* device,
const QDir& dir = QDir()) const;
const QDir& dir = QDir(),
Playlist::Path path_type = Playlist::Path_Automatic) const;
private:
Song ParseTrack(QXmlStreamReader* reader, const QDir& dir) const;

View File

@ -146,6 +146,8 @@ void BehaviourSettingsPage::Load() {
case Playlist::Path_Relative:
ui_->b_relative_path->setChecked(true);
break;
case Playlist::Path_Ask_User:
ui_->b_ask_path->setChecked(true);
}
ui_->b_write_metadata->setChecked(
s.value(Playlist::kWriteMetadata, true).toBool());
@ -185,6 +187,8 @@ void BehaviourSettingsPage::Save() {
path = Playlist::Path_Absolute;
} else if (ui_->b_relative_path->isChecked()) {
path = Playlist::Path_Relative;
} else if (ui_->b_ask_path->isChecked()) {
path = Playlist::Path_Ask_User;
}
s.beginGroup(MainWindow::kSettingsGroup);

View File

@ -265,6 +265,13 @@
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="b_ask_path">
<property name="text">
<string>Ask when saving</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_7">
<property name="orientation">