Merge branch 'master' into c++11

This commit is contained in:
John Maguire 2014-01-27 16:30:57 +01:00
commit 767965a2f4
116 changed files with 26791 additions and 20344 deletions

View File

@ -183,31 +183,39 @@ static const unsigned int KeyTbl[] = {
// numeric and function keypad keys
XK_KP_Space, Qt::Key_Space,
XK_KP_Tab, Qt::Key_Tab,
XK_KP_Enter, Qt::Key_Enter,
//XK_KP_F1, Qt::Key_F1,
//XK_KP_F2, Qt::Key_F2,
//XK_KP_F3, Qt::Key_F3,
//XK_KP_F4, Qt::Key_F4,
XK_KP_Home, Qt::Key_Home,
XK_KP_Left, Qt::Key_Left,
XK_KP_Up, Qt::Key_Up,
XK_KP_Right, Qt::Key_Right,
XK_KP_Down, Qt::Key_Down,
XK_KP_Prior, Qt::Key_PageUp,
XK_KP_Next, Qt::Key_PageDown,
XK_KP_End, Qt::Key_End,
XK_KP_Begin, Qt::Key_Clear,
XK_KP_Insert, Qt::Key_Insert,
XK_KP_Delete, Qt::Key_Delete,
XK_KP_Equal, Qt::Key_Equal,
XK_KP_Multiply, Qt::Key_Asterisk,
XK_KP_Add, Qt::Key_Plus,
XK_KP_Separator, Qt::Key_Comma,
XK_KP_Subtract, Qt::Key_Minus,
XK_KP_Decimal, Qt::Key_Period,
XK_KP_Divide, Qt::Key_Slash,
// special and additional keys
XK_Clear, Qt::Key_Clear,
XK_Delete, Qt::Key_Delete,
XK_space, Qt::Key_Space,
XK_exclam, Qt::Key_Exclam,
XK_quotedbl, Qt::Key_QuoteDbl,
XK_numbersign, Qt::Key_NumberSign,
XK_dollar, Qt::Key_Dollar,
XK_percent, Qt::Key_Percent,
XK_ampersand, Qt::Key_Ampersand,
XK_apostrophe, Qt::Key_Apostrophe,
XK_parenleft, Qt::Key_ParenLeft,
XK_parenright, Qt::Key_ParenRight,
XK_asterisk, Qt::Key_Asterisk,
XK_plus, Qt::Key_Plus,
XK_comma, Qt::Key_Comma,
XK_minus, Qt::Key_Minus,
XK_period, Qt::Key_Period,
XK_slash, Qt::Key_Slash,
XK_colon, Qt::Key_Colon,
XK_semicolon, Qt::Key_Semicolon,
XK_less, Qt::Key_Less,
XK_equal, Qt::Key_Equal,
XK_greater, Qt::Key_Greater,
XK_question, Qt::Key_Question,
XK_bracketleft, Qt::Key_BracketLeft,
XK_backslash, Qt::Key_Backslash,
XK_bracketright, Qt::Key_BracketRight,
XK_asciicircum, Qt::Key_AsciiCircum,
XK_underscore, Qt::Key_Underscore,
// International input method support keys

View File

@ -96,11 +96,9 @@ find_path(SPARSEHASH_INCLUDE_DIRS google/sparsetable)
# distros. If the user seems to want Drive support (ie. they have sparsehash
# installed and haven't disabled drive), and has an old taglib, compile our
# internal one and use that instead.
option(USE_BUILTIN_TAGLIB "If the system's version of Taglib is too old for Google Drive support, compile our builtin version instead" ON)
if (USE_BUILTIN_TAGLIB AND
(NOT "${ENABLE_GOOGLE_DRIVE}" STREQUAL "OFF") AND
SPARSEHASH_INCLUDE_DIRS AND
TAGLIB_VERSION VERSION_LESS 1.8)
option(USE_BUILTIN_TAGLIB "If the system's version of Taglib is too old, compile our builtin version instead" ON)
if (USE_BUILTIN_TAGLIB AND TAGLIB_VERSION VERSION_LESS 1.8)
message(STATUS "Using builtin taglib because your system's version is too old")
set(TAGLIB_VERSION 1.9.1)
set(TAGLIB_INCLUDE_DIRS "${CMAKE_BINARY_DIR}/3rdparty/taglib/headers/taglib/;${CMAKE_BINARY_DIR}/3rdparty/taglib/headers/")
set(TAGLIB_LIBRARY_DIRS "")

26
README.md Normal file
View File

@ -0,0 +1,26 @@
Clementine
==========
Clementine is a modern music player and library organizer for Windows, Linux and Mac OS X.
- Website: http://www.clementine-player.org/
- Github: https://github.com/clementine-player/Clementine
- Buildbot: http://buildbot.clementine-player.org/grid
- Latest developer builds: http://builds.clementine-player.org/
Compiling from source
---------------------
Get the code (if you haven't already):
git clone https://github.com/clementine-player/Clementine.git && cd Clementine
Compile and install:
cd bin
cmake ..
make -j8
sudo make install
See the Wiki for more instructions and a list of dependencies:
https://github.com/clementine-player/Clementine/wiki/Compiling-from-Source

View File

@ -1027,9 +1027,13 @@ optional_source(HAVE_AUDIOCD
SOURCES
devices/cddadevice.cpp
devices/cddalister.cpp
ui/ripcd.cpp
HEADERS
devices/cddadevice.h
devices/cddalister.h
ui/ripcd.h
UI
ui/ripcd.ui
)
# mtp device

View File

@ -451,4 +451,12 @@ void EnableFullScreen(const QWidget& main_window) {
[window setCollectionBehavior: kFullScreenPrimary];
}
float GetDevicePixelRatio(QWidget* widget) {
NSView* view = reinterpret_cast<NSView*>(widget->winId());
if ([[view window] respondsToSelector: @selector(backingScaleFactor)]) {
return [[view window] backingScaleFactor];
}
return 1.0f;
}
} // namespace mac

View File

@ -29,5 +29,6 @@ namespace mac {
QKeySequence KeySequenceFromNSEvent(NSEvent* event);
void DumpDictionary(CFDictionaryRef dict);
float GetDevicePixelRatio(QWidget* widget);
}

View File

@ -69,7 +69,7 @@ void Player::Init() {
connect(engine_.get(), SIGNAL(TrackAboutToEnd()), SLOT(TrackAboutToEnd()));
connect(engine_.get(), SIGNAL(TrackEnded()), SLOT(TrackEnded()));
connect(engine_.get(), SIGNAL(MetaData(Engine::SimpleMetaBundle)),
SLOT(EngineMetadataReceived(Engine::SimpleMetaBundle)));
SLOT(EngineMetadataReceived(Engine::SimpleMetaBundle)));
engine_->SetVolume(settings_.value("volume", 50).toInt());

View File

@ -797,7 +797,7 @@ void Song::BindToQuery(QSqlQuery *query) const {
&& Utilities::UrlOnSameDriveAsClementine(d->url_)) {
query->bindValue(":filename", Utilities::GetRelativePathToClementineBin(d->url_));
} else {
query->bindValue(":filename", d->url_);
query->bindValue(":filename", d->url_.toEncoded());
}
query->bindValue(":mtime", notnullintval(d->mtime_));

View File

@ -33,16 +33,17 @@
#include "config.h"
#include "core/concurrentrun.h"
#include "core/logging.h"
#include "core/song.h"
#include "core/player.h"
#include "core/signalchecker.h"
#include "core/song.h"
#include "core/tagreaderclient.h"
#include "core/timeconstants.h"
#include "internet/fixlastfm.h"
#include "internet/internetmodel.h"
#include "library/librarybackend.h"
#include "library/sqlrow.h"
#include "playlistparsers/parserbase.h"
#include "playlistparsers/cueparser.h"
#include "playlistparsers/parserbase.h"
#include "playlistparsers/playlistparser.h"
#include "podcasts/podcastparser.h"
#include "podcasts/podcastservice.h"
@ -52,7 +53,9 @@
QSet<QString> SongLoader::sRawUriSchemes;
const int SongLoader::kDefaultTimeout = 5000;
SongLoader::SongLoader(LibraryBackendInterface* library, QObject *parent)
SongLoader::SongLoader(LibraryBackendInterface* library,
const Player* player,
QObject *parent)
: QObject(parent),
timeout_timer_(new QTimer(this)),
playlist_parser_(new PlaylistParser(library, this)),
@ -63,7 +66,8 @@ SongLoader::SongLoader(LibraryBackendInterface* library, QObject *parent)
success_(false),
parser_(NULL),
is_podcast_(false),
library_(library)
library_(library),
player_(player)
{
if (sRawUriSchemes.isEmpty()) {
sRawUriSchemes << "udp" << "mms" << "mmsh" << "mmst" << "mmsu" << "rtsp"
@ -91,9 +95,10 @@ SongLoader::Result SongLoader::Load(const QUrl& url) {
return LoadLocal(url_.toLocalFile());
}
if (sRawUriSchemes.contains(url_.scheme())) {
// The URI scheme indicates that it can't possibly be a playlist, so add
// it as a raw stream.
if (sRawUriSchemes.contains(url_.scheme()) ||
player_->HandlerForUrl(url) != nullptr) {
// The URI scheme indicates that it can't possibly be a playlist, or we have
// a custom handler for the URL, so add it as a raw stream.
AddAsRawStream();
return Success;
}

View File

@ -33,13 +33,15 @@
class CueParser;
class LibraryBackendInterface;
class ParserBase;
class Player;
class PlaylistParser;
class PodcastParser;
class SongLoader : public QObject {
Q_OBJECT
public:
SongLoader(LibraryBackendInterface* library, QObject* parent = 0);
SongLoader(LibraryBackendInterface* library, const Player* player,
QObject* parent = 0);
~SongLoader();
enum Result {
@ -130,6 +132,7 @@ private:
bool is_podcast_;
QByteArray buffer_;
LibraryBackendInterface* library_;
const Player* player_;
boost::shared_ptr<GstElement> pipeline_;

View File

@ -457,9 +457,9 @@ QByteArray Sha256(const QByteArray& data) {
}
// File must not be open and will be closed afterwards!
QByteArray Md5File(QFile &file) {
QByteArray Sha1File(QFile &file) {
file.open(QIODevice::ReadOnly);
QCryptographicHash hash(QCryptographicHash::Md5);
QCryptographicHash hash(QCryptographicHash::Sha1);
QByteArray data;
while(!file.atEnd()) {

View File

@ -67,7 +67,7 @@ namespace Utilities {
QByteArray HmacSha256(const QByteArray& key, const QByteArray& data);
QByteArray HmacSha1(const QByteArray& key, const QByteArray& data);
QByteArray Sha256(const QByteArray& data);
QByteArray Md5File(QFile& file);
QByteArray Sha1File(QFile& file);
QByteArray Sha1CoverHash(const QString& artist, const QString& album);

View File

@ -13,7 +13,7 @@ using boost::scoped_ptr;
namespace {
static const char* kServiceName = "Skydrive";
static const char* kServiceName = "OneDrive";
static const char* kServiceId = "skydrive";
static const char* kSettingsGroup = "Skydrive";

View File

@ -212,6 +212,10 @@ void LibraryFilterWidget::SetQueryMode(QueryOptions::QueryMode query_mode) {
model_->SetFilterQueryMode(query_mode);
}
void LibraryFilterWidget::ShowInLibrary(const QString& search) {
ui_->filter->setText(search);
}
void LibraryFilterWidget::SetAgeFilterEnabled(bool enabled) {
filter_age_menu_->setEnabled(enabled);
}

View File

@ -56,6 +56,7 @@ class LibraryFilterWidget : public QWidget {
void SetDelayBehaviour(DelayBehaviour behaviour) { delay_behaviour_ = behaviour; }
void SetAgeFilterEnabled(bool enabled);
void SetGroupByEnabled(bool enabled);
void ShowInLibrary(const QString& search);
QMenu* menu() const { return library_menu_; }
void AddMenuAction(QAction* action);

View File

@ -39,19 +39,37 @@ LibraryQuery::LibraryQuery(const QueryOptions& options)
// expected with sqlite's FTS3:
// 1) Append * to all tokens.
// 2) Prefix "fts" to column names.
// 3) Remove colons which don't correspond to column names.
// Split on whitespace
QStringList tokens(options.filter().split(QRegExp("\\s+")));
QStringList tokens(options.filter().split(
QRegExp("\\s+"), QString::SkipEmptyParts));
QString query;
foreach (QString token, tokens) {
token.remove('(');
token.remove(')');
token.remove('"');
token.replace('-', ' ');
if (token.contains(':'))
query += "fts" + token + "* ";
else
if (token.contains(':')) {
// Only prefix fts if the token is a valid column name.
if (Song::kFtsColumns.contains("fts" + token.section(':', 0, 0),
Qt::CaseInsensitive)) {
// Account for multiple colons.
QString columntoken = token.section(
':', 0, 0, QString::SectionIncludeTrailingSep);
QString subtoken = token.section(':', 1, -1);
subtoken.replace(":", " ");
subtoken = subtoken.trimmed();
query += "fts" + columntoken + subtoken + "* ";
} else {
token.replace(":", " ");
token = token.trimmed();
query += token + "* ";
}
} else {
query += token + "* ";
}
}
where_clauses_ << "fts.%fts_table_noprefix MATCH ?";

View File

@ -642,9 +642,9 @@ void OutgoingDataCreator::SendSingleSong(RemoteClient* client, const Song &song,
// Open the file
QFile file(song.url().toLocalFile());
// Get md5 for file
QByteArray md5 = Utilities::Md5File(file).toHex();
qLog(Debug) << "md5 for file" << song.url().toLocalFile() << "=" << md5;
// Get sha1 for file
QByteArray sha1 = Utilities::Sha1File(file).toHex();
qLog(Debug) << "sha1 for file" << song.url().toLocalFile() << "=" << sha1;
file.open(QIODevice::ReadOnly);
@ -670,7 +670,7 @@ void OutgoingDataCreator::SendSingleSong(RemoteClient* client, const Song &song,
chunk->set_file_number(song_no);
chunk->set_size(file.size());
chunk->set_data(data.data(), data.size());
chunk->set_file_hash(md5.data(), md5.size());
chunk->set_file_hash(sha1.data(), sha1.size());
// On the first chunk send the metadata, so the client knows
// what file it receives.
@ -753,9 +753,9 @@ void OutgoingDataCreator::SendLibrary(RemoteClient *client) {
// Open the file
QFile file(temp_file_name);
// Get the md5 hash
QByteArray md5 = Utilities::Md5File(file).toHex();
qLog(Debug) << "Library md5" << md5;
// Get the sha1 hash
QByteArray sha1 = Utilities::Sha1File(file).toHex();
qLog(Debug) << "Library sha1" << sha1;
file.open(QIODevice::ReadOnly);
@ -777,7 +777,7 @@ void OutgoingDataCreator::SendLibrary(RemoteClient *client) {
chunk->set_chunk_number(chunk_number);
chunk->set_size(file.size());
chunk->set_data(data.data(), data.size());
chunk->set_file_hash(md5.data(), md5.size());
chunk->set_file_hash(sha1.data(), sha1.size());
// Send data directly to the client
client->SendData(&msg);

View File

@ -25,6 +25,7 @@
#include "songloaderinserter.h"
#include "songmimedata.h"
#include "songplaylistitem.h"
#include "core/application.h"
#include "core/closure.h"
#include "core/logging.h"
#include "core/modelfuturewatcher.h"
@ -761,7 +762,8 @@ bool Playlist::dropMimeData(const QMimeData* data, Qt::DropAction action, int ro
}
}
} else if (data->hasFormat(kCddaMimeType)) {
SongLoaderInserter* inserter = new SongLoaderInserter(task_manager_, library_);
SongLoaderInserter* inserter = new SongLoaderInserter(
task_manager_, library_, backend_->app()->player());
connect(inserter, SIGNAL(Error(QString)), SIGNAL(LoadTracksError(QString)));
inserter->LoadAudioCD(this, row, play_now, enqueue_now);
} else if (data->hasUrls()) {
@ -773,7 +775,8 @@ bool Playlist::dropMimeData(const QMimeData* data, Qt::DropAction action, int ro
}
void Playlist::InsertUrls(const QList<QUrl> &urls, int pos, bool play_now, bool enqueue) {
SongLoaderInserter* inserter = new SongLoaderInserter(task_manager_, library_);
SongLoaderInserter* inserter = new SongLoaderInserter(
task_manager_, library_, backend_->app()->player());
connect(inserter, SIGNAL(Error(QString)), SIGNAL(LoadTracksError(QString)));
inserter->Load(this, pos, play_now, enqueue, urls);

View File

@ -78,6 +78,8 @@ class PlaylistBackend : public QObject {
void FavoritePlaylist(int id, bool is_favorite);
void RemovePlaylist(int id);
Application* app() const { return app_; }
public slots:
void SavePlaylist(int playlist, const PlaylistItemList& items,
int last_played, smart_playlists::GeneratorPtr dynamic);

View File

@ -16,13 +16,6 @@
*/
#include "playlistdelegates.h"
#include "queue.h"
#include "core/logging.h"
#include "core/player.h"
#include "core/utilities.h"
#include "library/librarybackend.h"
#include "widgets/trackslider.h"
#include "ui/iconloader.h"
#include <QDateTime>
#include <QDir>
@ -39,6 +32,18 @@
#include <QWhatsThis>
#include <QtConcurrentRun>
#include "queue.h"
#include "core/logging.h"
#include "core/player.h"
#include "core/utilities.h"
#include "library/librarybackend.h"
#include "widgets/trackslider.h"
#include "ui/iconloader.h"
#ifdef Q_OS_DARWIN
#include "core/mac_utilities.h"
#endif // Q_OS_DARWIN
const int QueuedItemDelegate::kQueueBoxBorder = 1;
const int QueuedItemDelegate::kQueueBoxCornerRadius = 3;
const int QueuedItemDelegate::kQueueBoxLength = 30;
@ -492,8 +497,14 @@ void SongSourceDelegate::paint(
const QUrl& url = index.data().toUrl();
QPixmap pixmap = LookupPixmap(url, option_copy.decorationSize);
float device_pixel_ratio = 1.0f;
#ifdef Q_OS_DARWIN
QWidget* parent_widget = reinterpret_cast<QWidget*>(parent());
device_pixel_ratio = mac::GetDevicePixelRatio(parent_widget);
#endif
// Draw the pixmap in the middle of the rectangle
QRect draw_rect(QPoint(0, 0), option_copy.decorationSize);
QRect draw_rect(QPoint(0, 0), option_copy.decorationSize / device_pixel_ratio);
draw_rect.moveCenter(option_copy.rect.center());
painter->drawPixmap(draw_rect, pixmap);

View File

@ -156,7 +156,7 @@ void PlaylistManager::New(const QString& name, const SongList& songs,
void PlaylistManager::Load(const QString& filename) {
QUrl url = QUrl::fromLocalFile(filename);
SongLoader* loader = new SongLoader(library_backend_, this);
SongLoader* loader = new SongLoader(library_backend_, app_->player(), this);
connect(loader, SIGNAL(LoadFinished(bool)), SLOT(LoadFinished(bool)));
SongLoader::Result result = loader->Load(url);
QFileInfo info(filename);

View File

@ -1254,3 +1254,24 @@ void PlaylistView::FadePreviousBackgroundImage(qreal value) {
void PlaylistView::PlayerStopped() {
CurrentSongChanged(Song(), QString(), QImage());
}
void PlaylistView::focusInEvent(QFocusEvent* event) {
QTreeView::focusInEvent(event);
if (event->reason() == Qt::TabFocusReason ||
event->reason() == Qt::BacktabFocusReason) {
// If there's a current item but no selection it probably means the list was
// filtered, and the selected item does not match the filter. If there's
// only 1 item in the view it is now impossible to select that item without
// using the mouse.
const QModelIndex& current = selectionModel()->currentIndex();
if (current.isValid() &&
selectionModel()->selectedIndexes().isEmpty()) {
QItemSelection new_selection(
current.sibling(current.row(), 0),
current.sibling(current.row(),
current.model()->columnCount(current.parent()) - 1));
selectionModel()->select(new_selection, QItemSelectionModel::Select);
}
}
}

View File

@ -134,6 +134,7 @@ class PlaylistView : public QTreeView {
void dropEvent(QDropEvent *event);
void resizeEvent(QResizeEvent* event);
bool eventFilter(QObject* object, QEvent* event);
void focusInEvent(QFocusEvent* event);
// QAbstractScrollArea
void scrollContentsBy(int dx, int dy);

View File

@ -23,8 +23,9 @@
#include "core/songloader.h"
#include "core/taskmanager.h"
SongLoaderInserter::SongLoaderInserter(
TaskManager* task_manager, LibraryBackendInterface* library)
SongLoaderInserter::SongLoaderInserter(TaskManager* task_manager,
LibraryBackendInterface* library,
const Player* player)
: task_manager_(task_manager),
destination_(NULL),
row_(-1),
@ -32,7 +33,8 @@ SongLoaderInserter::SongLoaderInserter(
enqueue_(false),
async_load_id_(0),
async_progress_(0),
library_(library) {
library_(library),
player_(player) {
}
SongLoaderInserter::~SongLoaderInserter() {
@ -53,7 +55,7 @@ void SongLoaderInserter::Load(Playlist *destination,
destination, SLOT(UpdateItems(const SongList&)));
foreach (const QUrl& url, urls) {
SongLoader* loader = new SongLoader(library_, this);
SongLoader* loader = new SongLoader(library_, player_, this);
// we're connecting this before we're even sure if this is an async load
// to avoid race conditions (signal emission before we're listening to it)
@ -92,7 +94,7 @@ void SongLoaderInserter::LoadAudioCD(Playlist *destination,
play_now_ = play_now;
enqueue_ = enqueue;
SongLoader *loader = new SongLoader(library_, this);
SongLoader* loader = new SongLoader(library_, player_, this);
connect(loader, SIGNAL(LoadFinished(bool)), SLOT(AudioCDTagsLoaded(bool)));
qLog(Info) << "Loading audio CD...";
SongLoader::Result ret = loader->LoadAudioCD();

View File

@ -25,6 +25,7 @@
#include "core/song.h"
class LibraryBackendInterface;
class Player;
class Playlist;
class SongLoader;
class TaskManager;
@ -34,7 +35,9 @@ class QModelIndex;
class SongLoaderInserter : public QObject {
Q_OBJECT
public:
SongLoaderInserter(TaskManager* task_manager, LibraryBackendInterface* library);
SongLoaderInserter(TaskManager* task_manager,
LibraryBackendInterface* library,
const Player* player);
~SongLoaderInserter();
void Load(Playlist* destination, int row, bool play_now, bool enqueue,
@ -70,6 +73,7 @@ private:
int async_load_id_;
int async_progress_;
LibraryBackendInterface* library_;
const Player* player_;
};
#endif // SONGLOADERINSERTER_H

View File

@ -1,16 +1,16 @@
/* This file is part of Clementine.
Copyright 2012, 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
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
@ -27,6 +27,12 @@
#include <QRegExp>
#include <QSet>
#ifdef Q_OS_WIN
#include <time.h>
#else
#include <sys/time.h>
#endif
class Application;
class PodcastBackend;

View File

@ -138,7 +138,7 @@ void UltimateLyricsProvider::LyricsFetched() {
// Apply exclude rules
foreach (const Rule& rule, exclude_rules_) {
ApplyExcludeRule(rule, &lyrics);
ApplyExcludeRule(rule, &content);
}
if (!content.isEmpty() and HTMLHasAlphaNumeric(content)) {

View File

@ -20,6 +20,7 @@
#include "transcoderoptionsdialog.h"
#include "ui_transcodedialog.h"
#include "ui_transcodelogdialog.h"
#include "ui/iconloader.h"
#include "ui/mainwindow.h"
#include "widgets/fileview.h"
@ -35,6 +36,7 @@
const char* TranscodeDialog::kSettingsGroup = "Transcoder";
const int TranscodeDialog::kProgressInterval = 500;
const int TranscodeDialog::kMaxDestinationItems = 10;
static bool ComparePresetsByName(const TranscoderPreset& left,
const TranscoderPreset& right) {
@ -103,6 +105,8 @@ TranscodeDialog::TranscodeDialog(QWidget *parent)
connect(close_button_, SIGNAL(clicked()), SLOT(hide()));
connect(ui_->details, SIGNAL(clicked()), log_dialog_, SLOT(show()));
connect(ui_->options, SIGNAL(clicked()), SLOT(Options()));
connect(ui_->select, SIGNAL(clicked()), SLOT(AddDestination()));
connect(transcoder_, SIGNAL(JobComplete(QString,bool)), SLOT(JobComplete(QString,bool)));
connect(transcoder_, SIGNAL(LogLine(QString)), SLOT(LogLine(QString)));
@ -138,7 +142,8 @@ void TranscodeDialog::Start() {
// Add jobs to the transcoder
for (int i=0 ; i<file_model->rowCount() ; ++i) {
QString filename = file_model->index(i, 0).data(Qt::UserRole).toString();
transcoder_->AddJob(filename, preset);
QString outfilename = GetOutputFileName(filename, preset);
transcoder_->AddJob(filename, preset, outfilename);
}
// Set up the progressbar
@ -265,3 +270,50 @@ void TranscodeDialog::Options() {
dialog.exec();
}
}
// Adds a folder to the destination box.
void TranscodeDialog::AddDestination() {
int index = ui_->destination->currentIndex();
QString initial_dir = (!ui_->destination->itemData(index).isNull() ?
ui_->destination->itemData(index).toString() :
QDir::homePath());
QString dir = QFileDialog::getExistingDirectory(
this, tr("Add folder"), initial_dir);
if (!dir.isEmpty()) {
// Keep only a finite number of items in the box.
while (ui_->destination->count() >= kMaxDestinationItems) {
ui_->destination->removeItem(1); // The oldest folder item.
}
QIcon icon = IconLoader::Load("folder");
QVariant data = QVariant::fromValue(dir);
// Do not insert duplicates.
int duplicate_index = ui_->destination->findData(data);
if (duplicate_index == -1) {
ui_->destination->addItem(icon, dir, data);
ui_->destination->setCurrentIndex(ui_->destination->count() - 1);
} else {
ui_->destination->setCurrentIndex(duplicate_index);
}
}
}
// Returns the rightmost non-empty part of 'path'.
QString TranscodeDialog::TrimPath(const QString& path) const {
return path.section('/', -1, -1, QString::SectionSkipEmpty);
}
QString TranscodeDialog::GetOutputFileName(const QString& input,
const TranscoderPreset &preset) const {
QString path = ui_->destination->itemData(
ui_->destination->currentIndex()).toString();
if (path.isEmpty()) {
// Keep the original path.
return input.section('.', 0, -2) + '.' + preset.extension_;
} else {
QString file_name = TrimPath(input);
file_name = file_name.section('.', 0, -2);
return path + '/' + file_name + '.' + preset.extension_;
}
}

View File

@ -25,6 +25,8 @@ class Transcoder;
class Ui_TranscodeDialog;
class Ui_TranscodeLogDialog;
struct TranscoderPreset;
class TranscodeDialog : public QDialog {
Q_OBJECT
@ -34,6 +36,7 @@ class TranscodeDialog : public QDialog {
static const char* kSettingsGroup;
static const int kProgressInterval;
static const int kMaxDestinationItems;
void SetFilenames(const QStringList& filenames);
@ -49,11 +52,15 @@ class TranscodeDialog : public QDialog {
void LogLine(const QString& message);
void AllJobsComplete();
void Options();
void AddDestination();
private:
void SetWorking(bool working);
void UpdateStatusText();
void UpdateProgress();
QString TrimPath(const QString& path) const;
QString GetOutputFileName(const QString& input,
const TranscoderPreset& preset) const;
private:
Ui_TranscodeDialog* ui_;

View File

@ -98,7 +98,7 @@
<property name="title">
<string>Output options</string>
</property>
<layout class="QFormLayout" name="formLayout">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
@ -107,25 +107,21 @@
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QComboBox" name="format">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="options">
<property name="text">
<string>Options...</string>
</property>
</widget>
</item>
</layout>
<widget class="QComboBox" name="format">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="options">
<property name="text">
<string>Options...</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
@ -136,6 +132,15 @@
</item>
<item row="1" column="1">
<widget class="QComboBox" name="destination">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string>Alongside the originals</string>
@ -143,6 +148,13 @@
</item>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="select">
<property name="text">
<string>Select...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@ -196,7 +208,6 @@
<tabstop>add</tabstop>
<tabstop>remove</tabstop>
<tabstop>format</tabstop>
<tabstop>destination</tabstop>
<tabstop>button_box</tabstop>
</tabstops>
<resources/>

View File

@ -297,7 +297,7 @@ void Transcoder::AddJob(const QString& input,
// Never overwrite existing files
if (QFile::exists(job.output)) {
for (int i=0 ; ; ++i) {
QString new_filename = QString("%1.%2").arg(job.output).arg(i);
QString new_filename = QString("%1.%2.%3").arg(job.output.section('.',0,-2)).arg(i).arg(preset.extension_);
if (!QFile::exists(new_filename)) {
job.output = new_filename;
break;
@ -331,8 +331,10 @@ Transcoder::StartJobStatus Transcoder::MaybeStartNextJob() {
}
Job job = queued_jobs_.takeFirst();
if (StartJob(job))
if (StartJob(job)) {
emit(JobOutputName(job.output));
return StartedSuccessfully;
}
emit JobComplete(job.input, false);
return FailedToStart;

View File

@ -75,6 +75,7 @@ class Transcoder : public QObject {
void JobComplete(const QString& filename, bool success);
void LogLine(const QString& message);
void AllJobsComplete();
void JobOutputName(const QString& filename);
protected:
bool event(QEvent* e);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -27,10 +27,11 @@ class AlbumCoverFetcher;
class AlbumCoverSearcher;
class Application;
class CoverFromURLDialog;
class CoverSearchStatistics;
class QFileDialog;
class Song;
struct CoverSearchStatistics;
// Controller for the common album cover related menu options.
class AlbumCoverChoiceController : public QWidget {
Q_OBJECT

Some files were not shown because too many files have changed in this diff Show More