This commit is contained in:
asiviero 2014-02-03 21:48:55 -02:00
commit fc728f9bfc
130 changed files with 21411 additions and 17471 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

@ -1,3 +1,3 @@
cmake_minimum_required(VERSION 2.6)
add_library(sha2 STATIC sha2.c)
add_library(sha2 STATIC sha2.cpp)

View File

@ -92,6 +92,8 @@
#error Define BYTE_ORDER to be equal to either LITTLE_ENDIAN or BIG_ENDIAN
#endif
namespace clementine_sha2 {
/*
* Define the followingsha2_* types to types of the correct length on
* the native archtecture. Most BSD systems and Linux define u_intXX_t
@ -1066,3 +1068,4 @@ char* SHA384_Data(const sha2_byte* data, size_t len, char digest[SHA384_DIGEST_S
return SHA384_End(&context, digest);
}
} // namespace clementine_sha2

121
3rdparty/sha2/sha2.h vendored
View File

@ -32,13 +32,8 @@
* $Id: sha2.h,v 1.1 2001/11/08 00:02:01 adg Exp adg $
*/
#ifndef __SHA2_H__
#define __SHA2_H__
#ifdef __cplusplus
extern "C" {
#endif
#ifndef __CLEMENTINE_SHA2_H__
#define __CLEMENTINE_SHA2_H__
/*
* Import u_intXX_t size_t type definitions from system headers. You
@ -47,23 +42,18 @@ extern "C" {
*/
#include <sys/types.h>
#ifdef SHA2_USE_INTTYPES_H
#include <inttypes.h>
#endif /* SHA2_USE_INTTYPES_H */
namespace clementine_sha2 {
/*** SHA-256/384/512 Various Length Definitions ***********************/
#define SHA256_BLOCK_LENGTH 64
#define SHA256_DIGEST_LENGTH 32
#define SHA256_DIGEST_STRING_LENGTH (SHA256_DIGEST_LENGTH * 2 + 1)
#define SHA384_BLOCK_LENGTH 128
#define SHA384_DIGEST_LENGTH 48
#define SHA384_DIGEST_STRING_LENGTH (SHA384_DIGEST_LENGTH * 2 + 1)
#define SHA512_BLOCK_LENGTH 128
#define SHA512_DIGEST_LENGTH 64
#define SHA512_DIGEST_STRING_LENGTH (SHA512_DIGEST_LENGTH * 2 + 1)
static const int SHA256_BLOCK_LENGTH = 64;
static const int SHA256_DIGEST_LENGTH = 32;
static const int SHA256_DIGEST_STRING_LENGTH = (SHA256_DIGEST_LENGTH * 2 + 1);
static const int SHA384_BLOCK_LENGTH = 128;
static const int SHA384_DIGEST_LENGTH = 48;
static const int SHA384_DIGEST_STRING_LENGTH = (SHA384_DIGEST_LENGTH * 2 + 1);
static const int SHA512_BLOCK_LENGTH = 128;
static const int SHA512_DIGEST_LENGTH = 64;
static const int SHA512_DIGEST_STRING_LENGTH = (SHA512_DIGEST_LENGTH * 2 + 1);
/*** SHA-256/384/512 Context Structures *******************************/
@ -76,36 +66,6 @@ typedef unsigned char u_int8_t; /* 1-byte (8-bits) */
typedef unsigned int u_int32_t; /* 4-bytes (32-bits) */
typedef unsigned long long u_int64_t; /* 8-bytes (64-bits) */
#endif
/*
* Most BSD systems already define u_intXX_t types, as does Linux.
* Some systems, however, like Compaq's Tru64 Unix instead can use
* uintXX_t types defined by very recent ANSI C standards and included
* in the file:
*
* #include <inttypes.h>
*
* If you choose to use <inttypes.h> then please define:
*
* #define SHA2_USE_INTTYPES_H
*
* Or on the command line during compile:
*
* cc -DSHA2_USE_INTTYPES_H ...
*/
#ifdef SHA2_USE_INTTYPES_H
typedef struct _SHA256_CTX {
uint32_t state[8];
uint64_t bitcount;
uint8_t buffer[SHA256_BLOCK_LENGTH];
} SHA256_CTX;
typedef struct _SHA512_CTX {
uint64_t state[8];
uint64_t bitcount[2];
uint8_t buffer[SHA512_BLOCK_LENGTH];
} SHA512_CTX;
#else /* SHA2_USE_INTTYPES_H */
typedef struct _SHA256_CTX {
u_int32_t state[8];
@ -118,35 +78,9 @@ typedef struct _SHA512_CTX {
u_int8_t buffer[SHA512_BLOCK_LENGTH];
} SHA512_CTX;
#endif /* SHA2_USE_INTTYPES_H */
typedef SHA512_CTX SHA384_CTX;
/*** SHA-256/384/512 Function Prototypes ******************************/
#ifndef NOPROTO
#ifdef SHA2_USE_INTTYPES_H
void SHA256_Init(SHA256_CTX *);
void SHA256_Update(SHA256_CTX*, const uint8_t*, size_t);
void SHA256_Final(uint8_t[SHA256_DIGEST_LENGTH], SHA256_CTX*);
char* SHA256_End(SHA256_CTX*, char[SHA256_DIGEST_STRING_LENGTH]);
char* SHA256_Data(const uint8_t*, size_t, char[SHA256_DIGEST_STRING_LENGTH]);
void SHA384_Init(SHA384_CTX*);
void SHA384_Update(SHA384_CTX*, const uint8_t*, size_t);
void SHA384_Final(uint8_t[SHA384_DIGEST_LENGTH], SHA384_CTX*);
char* SHA384_End(SHA384_CTX*, char[SHA384_DIGEST_STRING_LENGTH]);
char* SHA384_Data(const uint8_t*, size_t, char[SHA384_DIGEST_STRING_LENGTH]);
void SHA512_Init(SHA512_CTX*);
void SHA512_Update(SHA512_CTX*, const uint8_t*, size_t);
void SHA512_Final(uint8_t[SHA512_DIGEST_LENGTH], SHA512_CTX*);
char* SHA512_End(SHA512_CTX*, char[SHA512_DIGEST_STRING_LENGTH]);
char* SHA512_Data(const uint8_t*, size_t, char[SHA512_DIGEST_STRING_LENGTH]);
#else /* SHA2_USE_INTTYPES_H */
void SHA256_Init(SHA256_CTX *);
void SHA256_Update(SHA256_CTX*, const u_int8_t*, size_t);
void SHA256_Final(u_int8_t[SHA256_DIGEST_LENGTH], SHA256_CTX*);
@ -165,33 +99,6 @@ void SHA512_Final(u_int8_t[SHA512_DIGEST_LENGTH], SHA512_CTX*);
char* SHA512_End(SHA512_CTX*, char[SHA512_DIGEST_STRING_LENGTH]);
char* SHA512_Data(const u_int8_t*, size_t, char[SHA512_DIGEST_STRING_LENGTH]);
#endif /* SHA2_USE_INTTYPES_H */
#else /* NOPROTO */
void SHA256_Init();
void SHA256_Update();
void SHA256_Final();
char* SHA256_End();
char* SHA256_Data();
void SHA384_Init();
void SHA384_Update();
void SHA384_Final();
char* SHA384_End();
char* SHA384_Data();
void SHA512_Init();
void SHA512_Update();
void SHA512_Final();
char* SHA512_End();
char* SHA512_Data();
#endif /* NOPROTO */
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* __SHA2_H__ */
} // namespace clementine_sha2
#endif /* __CLEMENTINE_SHA2_H__ */

View File

@ -16,6 +16,10 @@ if (CMAKE_CXX_COMPILER MATCHES ".*clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-uninitialized")
endif ()
if (APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --stdlib=libc++")
endif ()
set(CMAKE_REQUIRED_FLAGS "-std=c++0x")
check_cxx_source_compiles(
"#include <unordered_map>
@ -96,11 +100,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 "")

View File

@ -25,6 +25,8 @@ endmacro(summary_show_part)
macro(summary_show)
list(SORT summary_willbuild)
list(SORT summary_willnotbuild)
message("")
message("Building Clementine version: ${CLEMENTINE_VERSION_DISPLAY}")
summary_show_part(summary_willbuild "The following components will be built:")
summary_show_part(summary_willnotbuild "The following components WILL NOT be built:")
message("")

View File

@ -2,17 +2,29 @@
Version=1.0
Type=Application
Name=Clementine
Name[sr]=Клементина
Name[sr@ijekavian]=Клементина
Name[sr@ijekavianlatin]=Klementina
Name[sr@latin]=Klementina
GenericName=Clementine Music Player
GenericName[pl]=Odtwarzacz muzyki Clementine
GenericName[pt]=Reprodutor de músicas Clementine
GenericName[sr]=Клементина музички плејер
GenericName[sr@ijekavian]=Клементина музички плејер
GenericName[sr@ijekavianlatin]=Klementina muzički plejer
GenericName[sr@latin]=Klementina muzički plejer
Comment=Plays music and last.fm streams
Comment[pl]=Odtwarzanie muzyki i strumieni last.fm
Comment[pt]=Reprodução de músicas e emissões last.fm
Comment[sr]=Репродукује музику и last.fm токове
Comment[sr@ijekavian]=Репродукује музику и last.fm токове
Comment[sr@ijekavianlatin]=Reprodukuje muziku i last.fm tokove
Comment[sr@latin]=Reprodukuje muziku i last.fm tokove
Exec=clementine %U
TryExec=clementine
Icon=application-x-clementine
Terminal=false
Categories=AudioVideo;Player;Qt;
Categories=AudioVideo;Player;Qt;Audio;
StartupNotify=false
MimeType=application/ogg;application/x-ogg;application/x-ogm-audio;audio/aac;audio/mp4;audio/mpeg;audio/mpegurl;audio/ogg;audio/vnd.rn-realaudio;audio/vorbis;audio/x-flac;audio/x-mp3;audio/x-mpeg;audio/x-mpegurl;audio/x-ms-wma;audio/x-musepack;audio/x-oggflac;audio/x-pn-realaudio;audio/x-scpls;audio/x-speex;audio/x-vorbis;audio/x-vorbis+ogg;audio/x-wav;video/x-ms-asf;x-content/audio-player;x-scheme-handler/zune;x-scheme-handler/itpc;x-scheme-handler/itms;x-scheme-handler/feed;
X-Ayatana-Desktop-Shortcuts=Play;Pause;Stop;Previous;Next;
@ -59,6 +71,9 @@ Name[ru]=Воспроизвести
Name[sk]=Hrať
Name[sl]=Predvajaj
Name[sr]=Пусти
Name[sr@ijekavian]=Пусти
Name[sr@ijekavianlatin]=Pusti
Name[sr@latin]=Pusti
Name[sv]=Spela upp
Name[tr]=Çal
Name[uk]=Відтворити
@ -103,6 +118,9 @@ Name[ru]=Приостановить
Name[sk]=Pozastaviť
Name[sl]=Začasno ustavi
Name[sr]=Паузирај
Name[sr@ijekavian]=Паузирај
Name[sr@ijekavianlatin]=Pauziraj
Name[sr@latin]=Pauziraj
Name[sv]=Gör paus
Name[tr]=Duraklat
Name[uk]=Призупинити
@ -150,6 +168,9 @@ Name[ru]=Остановить
Name[sk]=Zastaviť
Name[sl]=Zaustavi
Name[sr]=Заустави
Name[sr@ijekavian]=Заустави
Name[sr@ijekavianlatin]=Zaustavi
Name[sr@latin]=Zaustavi
Name[sv]=Stoppa
Name[tr]=Durdur
Name[uk]=Зупинити
@ -194,7 +215,10 @@ Name[ro]=Precedenta
Name[ru]=Предыдущий
Name[sk]=Predchádzajúca
Name[sl]=Predhodni
Name[sr]=Prethodno
Name[sr]=Претходна
Name[sr@ijekavian]=Претходна
Name[sr@ijekavianlatin]=Prethodna
Name[sr@latin]=Prethodna
Name[sv]=Föregående
Name[tr]=Önceki
Name[uk]=Попередня
@ -243,7 +267,10 @@ Name[ro]=Următoarea
Name[ru]=Дальше
Name[sk]=Ďalšia
Name[sl]=Naslednji
Name[sr]=Sledeće
Name[sr]=Следећа
Name[sr@ijekavian]=Сљедећа
Name[sr@ijekavianlatin]=Sljedeća
Name[sr@latin]=Sledeća
Name[sv]=Nästa
Name[tr]=İleri
Name[uk]=Наступна

View File

@ -351,7 +351,6 @@ set(SOURCES
ui/organiseerrordialog.cpp
ui/playbacksettingspage.cpp
ui/qtsystemtrayicon.cpp
ui/ripcd.cpp
ui/screensaver.cpp
ui/settingsdialog.cpp
ui/settingspage.cpp
@ -630,7 +629,6 @@ set(HEADERS
ui/organiseerrordialog.h
ui/playbacksettingspage.h
ui/qtsystemtrayicon.h
ui/ripcd.h
ui/settingsdialog.h
ui/settingspage.h
ui/standarditemiconloader.h
@ -753,7 +751,6 @@ set(UI
ui/organisedialog.ui
ui/organiseerrordialog.ui
ui/playbacksettingspage.ui
ui/ripcd.ui
ui/settingsdialog.ui
ui/trackselectiondialog.ui
@ -1030,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

@ -56,16 +56,14 @@ struct sqlite3_tokenizer_module {
int (*xCreate)(
int argc, /* Size of argv array */
const char *const*argv, /* Tokenizer argument strings */
sqlite3_tokenizer **ppTokenizer /* OUT: Created tokenizer */
);
sqlite3_tokenizer **ppTokenizer); /* OUT: Created tokenizer */
int (*xDestroy)(sqlite3_tokenizer *pTokenizer);
int (*xOpen)(
sqlite3_tokenizer *pTokenizer, /* Tokenizer object */
const char *pInput, int nBytes, /* Input buffer */
sqlite3_tokenizer_cursor **ppCursor /* OUT: Created tokenizer cursor */
);
sqlite3_tokenizer_cursor **ppCursor);/* OUT: Created tokenizer cursor */
int (*xClose)(sqlite3_tokenizer_cursor *pCursor);
@ -74,8 +72,7 @@ struct sqlite3_tokenizer_module {
const char **ppToken, int *pnBytes, /* OUT: Normalized text for token */
int *piStartOffset, /* OUT: Byte offset of token in input buffer */
int *piEndOffset, /* OUT: Byte offset of end of token in input buffer */
int *piPosition /* OUT: Number of tokens returned before this one */
);
int *piPosition); /* OUT: Number of tokens returned before this one */
};
struct sqlite3_tokenizer {
@ -221,7 +218,7 @@ Database::Database(Application* app, QObject* parent, const QString& database_na
{
{
QMutexLocker l(&sNextConnectionIdMutex);
connection_id_ = sNextConnectionId ++;
connection_id_ = sNextConnectionId++;
}
directory_ = QDir::toNativeSeparators(
@ -288,7 +285,7 @@ QSqlDatabase Database::Connect() {
}
// Attach external databases
foreach (const QString& key, attached_databases_.keys()) {
for (const QString& key : attached_databases_.keys()) {
QString filename = attached_databases_[key].filename_;
if (!injected_database_name_.isNull())
@ -303,13 +300,13 @@ QSqlDatabase Database::Connect() {
}
}
if(startup_schema_version_ == -1) {
if (startup_schema_version_ == -1) {
UpdateMainSchema(&db);
}
// We might have to initialise the schema in some attached databases now, if
// they were deleted and don't match up with the main schema version.
foreach (const QString& key, attached_databases_.keys()) {
for (const QString& key : attached_databases_.keys()) {
if (attached_databases_[key].is_temporary_ &&
attached_databases_[key].schema_.isEmpty())
continue;
@ -344,7 +341,7 @@ void Database::UpdateMainSchema(QSqlDatabase* db) {
}
if (schema_version < kSchemaVersion) {
// Update the schema
for (int v=schema_version+1 ; v<= kSchemaVersion ; ++v) {
for (int v = schema_version+1; v <= kSchemaVersion; ++v) {
UpdateDatabaseSchema(v, *db);
}
}
@ -377,7 +374,7 @@ void Database::RecreateAttachedDb(const QString& database_name) {
// We can't just re-attach the database now because it needs to be done for
// each thread. Close all the database connections, so each thread will
// re-attach it when they next connect.
foreach (const QString& name, QSqlDatabase::connectionNames()) {
for (const QString& name : QSqlDatabase::connectionNames()) {
QSqlDatabase::removeDatabase(name);
}
}
@ -432,7 +429,7 @@ void Database::UpdateDatabaseSchema(int version, QSqlDatabase &db) {
UrlEncodeFilenameColumn("songs", db);
UrlEncodeFilenameColumn("playlist_items", db);
foreach (const QString& table, db.tables()) {
for (const QString& table : db.tables()) {
if (table.startsWith("device_") && table.endsWith("_songs")) {
UrlEncodeFilenameColumn(table, db);
}
@ -511,12 +508,12 @@ void Database::ExecSchemaCommands(QSqlDatabase& db,
void Database::ExecSongTablesCommands(QSqlDatabase& db,
const QStringList& song_tables,
const QStringList& commands) {
foreach (const QString& command, commands) {
for (const QString& command : commands) {
// There are now lots of "songs" tables that need to have the same schema:
// songs, magnatune_songs, and device_*_songs. We allow a magic value
// in the schema files to update all songs tables at once.
if (command.contains(kMagicAllSongsTables)) {
foreach (const QString& table, song_tables) {
for (const QString& table : song_tables) {
// Another horrible hack: device songs tables don't have matching _fts
// tables, so if this command tries to touch one, ignore it.
if (table.startsWith("device_") &&
@ -543,17 +540,17 @@ QStringList Database::SongsTables(QSqlDatabase& db, int schema_version) const {
QStringList ret;
// look for the tables in the main db
foreach (const QString& table, db.tables()) {
for (const QString& table : db.tables()) {
if (table == "songs" || table.endsWith("_songs"))
ret << table;
}
// look for the tables in attached dbs
foreach (const QString& key, attached_databases_.keys()) {
for (const QString& key : attached_databases_.keys()) {
QSqlQuery q(QString("SELECT NAME FROM %1.sqlite_master"
" WHERE type='table' AND name='songs' OR name LIKE '%songs'").arg(key), db);
if (q.exec()) {
while(q.next()) {
while (q.next()) {
QString tab_name = key + "." + q.value(0).toString();
ret << tab_name;
}
@ -575,7 +572,6 @@ bool Database::CheckErrors(const QSqlQuery& query) {
qLog(Error) << "faulty query: " << query.lastQuery();
qLog(Error) << "bound values: " << query.boundValues();
app_->AddError("LibraryBackend: " + last_error.text());
return true;
}

View File

@ -62,16 +62,17 @@
#include <QtDebug>
QDebug operator <<(QDebug dbg, NSObject* object) {
QString ns_format = [[NSString stringWithFormat: @"%@", object] UTF8String];
QDebug operator<<(QDebug dbg, NSObject* object) {
QString ns_format = [[NSString stringWithFormat:@"%@", object] UTF8String];
dbg.nospace() << ns_format;
return dbg.space();
}
// Capture global media keys on Mac (Cocoa only!)
// See: http://www.rogueamoeba.com/utm/2007/09/29/apple-keyboard-media-key-event-handling/
// See:
// http://www.rogueamoeba.com/utm/2007/09/29/apple-keyboard-media-key-event-handling/
@interface MacApplication :NSApplication {
@interface MacApplication : NSApplication {
PlatformInterface* application_handler_;
AppDelegate* delegate_;
// shortcut_handler_ only used to temporarily save it
@ -79,18 +80,16 @@ QDebug operator <<(QDebug dbg, NSObject* object) {
MacGlobalShortcutBackend* shortcut_handler_;
}
- (MacGlobalShortcutBackend*) shortcut_handler;
- (void) SetShortcutHandler: (MacGlobalShortcutBackend*)handler;
- (MacGlobalShortcutBackend*)shortcut_handler;
- (void)SetShortcutHandler:(MacGlobalShortcutBackend*)handler;
- (PlatformInterface*) application_handler;
- (void) SetApplicationHandler: (PlatformInterface*)handler;
- (PlatformInterface*)application_handler;
- (void)SetApplicationHandler:(PlatformInterface*)handler;
@end
#ifdef HAVE_BREAKPAD
static bool BreakpadCallback(int, int, mach_port_t, void*) {
return true;
}
static bool BreakpadCallback(int, int, mach_port_t, void*) { return true; }
static BreakpadRef InitBreakpad() {
ScopedNSAutoreleasePool pool;
@ -103,11 +102,11 @@ static BreakpadRef InitBreakpad() {
[pool release];
return breakpad;
}
#endif // HAVE_BREAKPAD
#endif // HAVE_BREAKPAD
@implementation AppDelegate
- (id) init {
- (id)init {
if ((self = [super init])) {
application_handler_ = nil;
shortcut_handler_ = nil;
@ -116,7 +115,7 @@ static BreakpadRef InitBreakpad() {
return self;
}
- (id) initWithHandler: (PlatformInterface*)handler {
- (id)initWithHandler:(PlatformInterface*)handler {
application_handler_ = handler;
#ifdef HAVE_BREAKPAD
@ -124,45 +123,48 @@ static BreakpadRef InitBreakpad() {
#endif
// Register defaults for the whitelist of apps that want to use media keys
[[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:
[SPMediaKeyTap defaultMediaKeyUserBundleIdentifiers], kMediaKeyUsingBundleIdentifiersDefaultsKey,
nil]];
[[NSUserDefaults standardUserDefaults]
registerDefaults:
[NSDictionary
dictionaryWithObjectsAndKeys:
[SPMediaKeyTap defaultMediaKeyUserBundleIdentifiers],
kMediaKeyUsingBundleIdentifiersDefaultsKey, nil]];
return self;
}
- (BOOL) applicationShouldHandleReopen: (NSApplication*)app hasVisibleWindows:(BOOL)flag {
- (BOOL)applicationShouldHandleReopen:(NSApplication*)app
hasVisibleWindows:(BOOL)flag {
if (application_handler_) {
application_handler_->Activate();
}
return YES;
}
- (void) setDockMenu: (NSMenu*)menu {
- (void)setDockMenu:(NSMenu*)menu {
dock_menu_ = menu;
}
- (NSMenu*) applicationDockMenu: (NSApplication*)sender {
- (NSMenu*)applicationDockMenu:(NSApplication*)sender {
return dock_menu_;
}
- (void) setShortcutHandler: (MacGlobalShortcutBackend*)backend {
- (void)setShortcutHandler:(MacGlobalShortcutBackend*)backend {
shortcut_handler_ = backend;
}
- (MacGlobalShortcutBackend*) shortcut_handler {
- (MacGlobalShortcutBackend*)shortcut_handler {
return shortcut_handler_;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
- (void)applicationDidFinishLaunching:(NSNotification*)aNotification {
key_tap_ = [[SPMediaKeyTap alloc] initWithDelegate:self];
if([SPMediaKeyTap usesGlobalMediaKeyTap])
if ([SPMediaKeyTap usesGlobalMediaKeyTap])
[key_tap_ startWatchingMediaKeys];
else
qLog(Warning)<<"Media key monitoring disabled";
qLog(Warning) << "Media key monitoring disabled";
}
- (BOOL) application: (NSApplication*)app openFile:(NSString*)filename {
- (BOOL)application:(NSApplication*)app openFile:(NSString*)filename {
qLog(Debug) << "Wants to open:" << [filename UTF8String];
if (application_handler_->LoadUrl(QString::fromUtf8([filename UTF8String]))) {
@ -172,15 +174,19 @@ static BreakpadRef InitBreakpad() {
return NO;
}
- (void) application: (NSApplication*)app openFiles:(NSArray*)filenames {
- (void)application:(NSApplication*)app openFiles:(NSArray*)filenames {
qLog(Debug) << "Wants to open:" << filenames;
[filenames enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL* stop) {
[self application:app openFile:(NSString*)object];
}];
[filenames
enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL* stop) {
[self application:app openFile:(NSString*)object];
}];
}
- (void) mediaKeyTap: (SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event {
NSAssert([event type] == NSSystemDefined && [event subtype] == SPSystemDefinedEventMediaKeys, @"Unexpected NSEvent in mediaKeyTap:receivedMediaKeyEvent:");
- (void)mediaKeyTap:(SPMediaKeyTap*)keyTap
receivedMediaKeyEvent:(NSEvent*)event {
NSAssert([event type] == NSSystemDefined &&
[event subtype] == SPSystemDefinedEventMediaKeys,
@"Unexpected NSEvent in mediaKeyTap:receivedMediaKeyEvent:");
int key_code = (([event data1] & 0xFFFF0000) >> 16);
int key_flags = ([event data1] & 0x0000FFFF);
@ -196,14 +202,16 @@ static BreakpadRef InitBreakpad() {
}
}
- (NSApplicationTerminateReply) applicationShouldTerminate:(NSApplication*) sender {
- (NSApplicationTerminateReply)applicationShouldTerminate:
(NSApplication*)sender {
#ifdef HAVE_BREAKPAD
BreakpadRelease(breakpad_);
#endif
return NSTerminateNow;
}
- (BOOL) userNotificationCenter: (id)center shouldPresentNotification: (id)notification {
- (BOOL)userNotificationCenter:(id)center
shouldPresentNotification:(id)notification {
// Always show notifications, even if Clementine is in the foreground.
return YES;
}
@ -212,51 +220,54 @@ static BreakpadRef InitBreakpad() {
@implementation MacApplication
- (id) init {
- (id)init {
if ((self = [super init])) {
[self SetShortcutHandler:nil];
}
return self;
}
- (MacGlobalShortcutBackend*) shortcut_handler {
- (MacGlobalShortcutBackend*)shortcut_handler {
// should be the same as delegate_'s shortcut handler
return shortcut_handler_;
}
- (void) SetShortcutHandler: (MacGlobalShortcutBackend*)handler {
- (void)SetShortcutHandler:(MacGlobalShortcutBackend*)handler {
shortcut_handler_ = handler;
if(delegate_)
[delegate_ setShortcutHandler:handler];
if (delegate_) [delegate_ setShortcutHandler:handler];
}
- (PlatformInterface*) application_handler {
- (PlatformInterface*)application_handler {
return application_handler_;
}
- (void) SetApplicationHandler: (PlatformInterface*)handler {
- (void)SetApplicationHandler:(PlatformInterface*)handler {
delegate_ = [[AppDelegate alloc] initWithHandler:handler];
// App-shortcut-handler set before delegate is set.
// this makes sure the delegate's shortcut_handler is set
[delegate_ setShortcutHandler:shortcut_handler_];
[self setDelegate:delegate_];
Class notification_center_class = NSClassFromString(@"NSUserNotificationCenter");
Class notification_center_class =
NSClassFromString(@"NSUserNotificationCenter");
if (notification_center_class) {
id notification_center = [notification_center_class defaultUserNotificationCenter];
[notification_center setDelegate: delegate_];
id notification_center =
[notification_center_class defaultUserNotificationCenter];
[notification_center setDelegate:delegate_];
}
}
-(void) sendEvent: (NSEvent*)event {
- (void)sendEvent:(NSEvent*)event {
// If event tap is not installed, handle events that reach the app instead
BOOL shouldHandleMediaKeyEventLocally = ![SPMediaKeyTap usesGlobalMediaKeyTap];
BOOL shouldHandleMediaKeyEventLocally =
![SPMediaKeyTap usesGlobalMediaKeyTap];
if(shouldHandleMediaKeyEventLocally && [event type] == NSSystemDefined && [event subtype] == SPSystemDefinedEventMediaKeys) {
[(id)[self delegate] mediaKeyTap: nil receivedMediaKeyEvent: event];
if (shouldHandleMediaKeyEventLocally && [event type] == NSSystemDefined &&
[event subtype] == SPSystemDefinedEventMediaKeys) {
[(id)[self delegate] mediaKeyTap:nil receivedMediaKeyEvent:event];
}
[super sendEvent: event];
[super sendEvent:event];
}
@end
@ -267,30 +278,33 @@ void MacMain() {
ScopedNSAutoreleasePool pool;
// Creates and sets the magic global variable so QApplication will find it.
[MacApplication sharedApplication];
#ifdef HAVE_SPARKLE
// Creates and sets the magic global variable for Sparkle.
[[SUUpdater sharedUpdater] setDelegate: NSApp];
#endif
#ifdef HAVE_SPARKLE
// Creates and sets the magic global variable for Sparkle.
[[SUUpdater sharedUpdater] setDelegate:NSApp];
#endif
}
void SetShortcutHandler(MacGlobalShortcutBackend* handler) {
[NSApp SetShortcutHandler: handler];
[NSApp SetShortcutHandler:handler];
}
void SetApplicationHandler(PlatformInterface* handler) {
[NSApp SetApplicationHandler: handler];
[NSApp SetApplicationHandler:handler];
}
void CheckForUpdates() {
#ifdef HAVE_SPARKLE
[[SUUpdater sharedUpdater] checkForUpdates: NSApp];
#endif
#ifdef HAVE_SPARKLE
[[SUUpdater sharedUpdater] checkForUpdates:NSApp];
#endif
}
QString GetBundlePath() {
ScopedCFTypeRef<CFURLRef> app_url(CFBundleCopyBundleURL(CFBundleGetMainBundle()));
ScopedCFTypeRef<CFStringRef> mac_path(CFURLCopyFileSystemPath(app_url.get(), kCFURLPOSIXPathStyle));
const char* path = CFStringGetCStringPtr(mac_path.get(), CFStringGetSystemEncoding());
ScopedCFTypeRef<CFURLRef> app_url(
CFBundleCopyBundleURL(CFBundleGetMainBundle()));
ScopedCFTypeRef<CFStringRef> mac_path(
CFURLCopyFileSystemPath(app_url.get(), kCFURLPOSIXPathStyle));
const char* path =
CFStringGetCStringPtr(mac_path.get(), CFStringGetSystemEncoding());
QString bundle_path = QString::fromUtf8(path);
return bundle_path;
}
@ -303,9 +317,7 @@ QString GetResourcesPath() {
QString GetApplicationSupportPath() {
ScopedNSAutoreleasePool pool;
NSArray* paths = NSSearchPathForDirectoriesInDomains(
NSApplicationSupportDirectory,
NSUserDomainMask,
YES);
NSApplicationSupportDirectory, NSUserDomainMask, YES);
QString ret;
if ([paths count] > 0) {
NSString* user_path = [paths objectAtIndex:0];
@ -318,10 +330,8 @@ QString GetApplicationSupportPath() {
QString GetMusicDirectory() {
ScopedNSAutoreleasePool pool;
NSArray* paths = NSSearchPathForDirectoriesInDomains(
NSMusicDirectory,
NSUserDomainMask,
YES);
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSMusicDirectory,
NSUserDomainMask, YES);
QString ret;
if ([paths count] > 0) {
NSString* user_path = [paths objectAtIndex:0];
@ -335,57 +345,108 @@ QString GetMusicDirectory() {
static int MapFunctionKey(int keycode) {
switch (keycode) {
// Function keys
case NSInsertFunctionKey: return Qt::Key_Insert;
case NSDeleteFunctionKey: return Qt::Key_Delete;
case NSPauseFunctionKey: return Qt::Key_Pause;
case NSPrintFunctionKey: return Qt::Key_Print;
case NSSysReqFunctionKey: return Qt::Key_SysReq;
case NSHomeFunctionKey: return Qt::Key_Home;
case NSEndFunctionKey: return Qt::Key_End;
case NSLeftArrowFunctionKey: return Qt::Key_Left;
case NSUpArrowFunctionKey: return Qt::Key_Up;
case NSRightArrowFunctionKey: return Qt::Key_Right;
case NSDownArrowFunctionKey: return Qt::Key_Down;
case NSPageUpFunctionKey: return Qt::Key_PageUp;
case NSPageDownFunctionKey: return Qt::Key_PageDown;
case NSScrollLockFunctionKey: return Qt::Key_ScrollLock;
case NSF1FunctionKey: return Qt::Key_F1;
case NSF2FunctionKey: return Qt::Key_F2;
case NSF3FunctionKey: return Qt::Key_F3;
case NSF4FunctionKey: return Qt::Key_F4;
case NSF5FunctionKey: return Qt::Key_F5;
case NSF6FunctionKey: return Qt::Key_F6;
case NSF7FunctionKey: return Qt::Key_F7;
case NSF8FunctionKey: return Qt::Key_F8;
case NSF9FunctionKey: return Qt::Key_F9;
case NSF10FunctionKey: return Qt::Key_F10;
case NSF11FunctionKey: return Qt::Key_F11;
case NSF12FunctionKey: return Qt::Key_F12;
case NSF13FunctionKey: return Qt::Key_F13;
case NSF14FunctionKey: return Qt::Key_F14;
case NSF15FunctionKey: return Qt::Key_F15;
case NSF16FunctionKey: return Qt::Key_F16;
case NSF17FunctionKey: return Qt::Key_F17;
case NSF18FunctionKey: return Qt::Key_F18;
case NSF19FunctionKey: return Qt::Key_F19;
case NSF20FunctionKey: return Qt::Key_F20;
case NSF21FunctionKey: return Qt::Key_F21;
case NSF22FunctionKey: return Qt::Key_F22;
case NSF23FunctionKey: return Qt::Key_F23;
case NSF24FunctionKey: return Qt::Key_F24;
case NSF25FunctionKey: return Qt::Key_F25;
case NSF26FunctionKey: return Qt::Key_F26;
case NSF27FunctionKey: return Qt::Key_F27;
case NSF28FunctionKey: return Qt::Key_F28;
case NSF29FunctionKey: return Qt::Key_F29;
case NSF30FunctionKey: return Qt::Key_F30;
case NSF31FunctionKey: return Qt::Key_F31;
case NSF32FunctionKey: return Qt::Key_F32;
case NSF33FunctionKey: return Qt::Key_F33;
case NSF34FunctionKey: return Qt::Key_F34;
case NSF35FunctionKey: return Qt::Key_F35;
case NSMenuFunctionKey: return Qt::Key_Menu;
case NSHelpFunctionKey: return Qt::Key_Help;
case NSInsertFunctionKey:
return Qt::Key_Insert;
case NSDeleteFunctionKey:
return Qt::Key_Delete;
case NSPauseFunctionKey:
return Qt::Key_Pause;
case NSPrintFunctionKey:
return Qt::Key_Print;
case NSSysReqFunctionKey:
return Qt::Key_SysReq;
case NSHomeFunctionKey:
return Qt::Key_Home;
case NSEndFunctionKey:
return Qt::Key_End;
case NSLeftArrowFunctionKey:
return Qt::Key_Left;
case NSUpArrowFunctionKey:
return Qt::Key_Up;
case NSRightArrowFunctionKey:
return Qt::Key_Right;
case NSDownArrowFunctionKey:
return Qt::Key_Down;
case NSPageUpFunctionKey:
return Qt::Key_PageUp;
case NSPageDownFunctionKey:
return Qt::Key_PageDown;
case NSScrollLockFunctionKey:
return Qt::Key_ScrollLock;
case NSF1FunctionKey:
return Qt::Key_F1;
case NSF2FunctionKey:
return Qt::Key_F2;
case NSF3FunctionKey:
return Qt::Key_F3;
case NSF4FunctionKey:
return Qt::Key_F4;
case NSF5FunctionKey:
return Qt::Key_F5;
case NSF6FunctionKey:
return Qt::Key_F6;
case NSF7FunctionKey:
return Qt::Key_F7;
case NSF8FunctionKey:
return Qt::Key_F8;
case NSF9FunctionKey:
return Qt::Key_F9;
case NSF10FunctionKey:
return Qt::Key_F10;
case NSF11FunctionKey:
return Qt::Key_F11;
case NSF12FunctionKey:
return Qt::Key_F12;
case NSF13FunctionKey:
return Qt::Key_F13;
case NSF14FunctionKey:
return Qt::Key_F14;
case NSF15FunctionKey:
return Qt::Key_F15;
case NSF16FunctionKey:
return Qt::Key_F16;
case NSF17FunctionKey:
return Qt::Key_F17;
case NSF18FunctionKey:
return Qt::Key_F18;
case NSF19FunctionKey:
return Qt::Key_F19;
case NSF20FunctionKey:
return Qt::Key_F20;
case NSF21FunctionKey:
return Qt::Key_F21;
case NSF22FunctionKey:
return Qt::Key_F22;
case NSF23FunctionKey:
return Qt::Key_F23;
case NSF24FunctionKey:
return Qt::Key_F24;
case NSF25FunctionKey:
return Qt::Key_F25;
case NSF26FunctionKey:
return Qt::Key_F26;
case NSF27FunctionKey:
return Qt::Key_F27;
case NSF28FunctionKey:
return Qt::Key_F28;
case NSF29FunctionKey:
return Qt::Key_F29;
case NSF30FunctionKey:
return Qt::Key_F30;
case NSF31FunctionKey:
return Qt::Key_F31;
case NSF32FunctionKey:
return Qt::Key_F32;
case NSF33FunctionKey:
return Qt::Key_F33;
case NSF34FunctionKey:
return Qt::Key_F34;
case NSF35FunctionKey:
return Qt::Key_F35;
case NSMenuFunctionKey:
return Qt::Key_Menu;
case NSHelpFunctionKey:
return Qt::Key_Help;
}
return 0;
@ -399,11 +460,21 @@ QKeySequence KeySequenceFromNSEvent(NSEvent* event) {
int key = 0;
unsigned char c = chars[0];
switch (c) {
case 0x1b: key = Qt::Key_Escape; break;
case 0x09: key = Qt::Key_Tab; break;
case 0x0d: key = Qt::Key_Return; break;
case 0x08: key = Qt::Key_Backspace; break;
case 0x03: key = Qt::Key_Enter; break;
case 0x1b:
key = Qt::Key_Escape;
break;
case 0x09:
key = Qt::Key_Tab;
break;
case 0x0d:
key = Qt::Key_Return;
break;
case 0x08:
key = Qt::Key_Backspace;
break;
case 0x03:
key = Qt::Key_Enter;
break;
}
if (key == 0) {
@ -448,12 +519,12 @@ void EnableFullScreen(const QWidget& main_window) {
NSView* view = reinterpret_cast<NSView*>(main_window.winId());
NSWindow* window = [view window];
[window setCollectionBehavior: kFullScreenPrimary];
[window setCollectionBehavior:kFullScreenPrimary];
}
float GetDevicePixelRatio(QWidget* widget) {
NSView* view = reinterpret_cast<NSView*>(widget->winId());
if ([[view window] respondsToSelector: @selector(backingScaleFactor)]) {
if ([[view window] respondsToSelector:@selector(backingScaleFactor)]) {
return [[view window] backingScaleFactor];
}
return 1.0f;

View File

@ -25,24 +25,17 @@
#include "core/scoped_nsobject.h"
MacFSListener::MacFSListener(QObject* parent)
: FileSystemWatcherInterface(parent),
run_loop_(NULL),
stream_(NULL) {
: FileSystemWatcherInterface(parent), run_loop_(NULL), stream_(NULL) {
update_timer_.setSingleShot(true);
update_timer_.setInterval(2000);
connect(&update_timer_, SIGNAL(timeout()), SLOT(UpdateStream()));
}
void MacFSListener::Init() {
run_loop_ = CFRunLoopGetCurrent();
}
void MacFSListener::Init() { run_loop_ = CFRunLoopGetCurrent(); }
void MacFSListener::EventStreamCallback(
ConstFSEventStreamRef stream,
void* user_data,
size_t num_events,
void* event_paths,
const FSEventStreamEventFlags event_flags[],
ConstFSEventStreamRef stream, void* user_data, size_t num_events,
void* event_paths, const FSEventStreamEventFlags event_flags[],
const FSEventStreamEventId event_ids[]) {
MacFSListener* me = reinterpret_cast<MacFSListener*>(user_data);
char** paths = reinterpret_cast<char**>(event_paths);
@ -73,9 +66,7 @@ void MacFSListener::Clear() {
UpdateStreamAsync();
}
void MacFSListener::UpdateStreamAsync() {
update_timer_.start();
}
void MacFSListener::UpdateStreamAsync() { update_timer_.start(); }
void MacFSListener::UpdateStream() {
if (stream_) {
@ -91,10 +82,10 @@ void MacFSListener::UpdateStream() {
scoped_nsobject<NSMutableArray> array([[NSMutableArray alloc] init]);
foreach (const QString& path, paths_) {
for (const QString& path : paths_) {
scoped_nsobject<NSString> string(
[[NSString alloc] initWithUTF8String: path.toUtf8().constData()]);
[array addObject: string.get()];
[[NSString alloc] initWithUTF8String:path.toUtf8().constData()]);
[array addObject:string.get()];
}
FSEventStreamContext context;
@ -102,14 +93,10 @@ void MacFSListener::UpdateStream() {
context.info = this;
CFAbsoluteTime latency = 1.0;
stream_ = FSEventStreamCreate(
NULL,
&EventStreamCallback,
&context, // Copied
reinterpret_cast<CFArrayRef>(array.get()),
kFSEventStreamEventIdSinceNow,
latency,
kFSEventStreamCreateFlagNone);
stream_ = FSEventStreamCreate(NULL, &EventStreamCallback, &context, // Copied
reinterpret_cast<CFArrayRef>(array.get()),
kFSEventStreamEventIdSinceNow, latency,
kFSEventStreamCreateFlagNone);
FSEventStreamScheduleWithRunLoop(stream_, run_loop_, kCFRunLoopDefaultMode);
FSEventStreamStart(stream_);

View File

@ -38,21 +38,23 @@
class MacGlobalShortcutBackendPrivate : boost::noncopyable {
public:
explicit MacGlobalShortcutBackendPrivate(MacGlobalShortcutBackend* backend)
: global_monitor_(nil),
local_monitor_(nil),
backend_(backend) {
}
: global_monitor_(nil), local_monitor_(nil), backend_(backend) {}
bool Register() {
global_monitor_ = [NSEvent addGlobalMonitorForEventsMatchingMask:NSKeyDownMask
handler:^(NSEvent* event) {
HandleKeyEvent(event);
}];
local_monitor_ = [NSEvent addLocalMonitorForEventsMatchingMask:NSKeyDownMask
handler:^(NSEvent* event) {
// Filter event if we handle it as a global shortcut.
return HandleKeyEvent(event) ? nil : event;
}];
global_monitor_ =
[NSEvent addGlobalMonitorForEventsMatchingMask:NSKeyDownMask
handler:^(NSEvent* event) {
HandleKeyEvent(event);
}];
local_monitor_ =
[NSEvent addLocalMonitorForEventsMatchingMask:NSKeyDownMask
handler:^(NSEvent* event) {
// Filter event if we handle
// it as a global shortcut.
return HandleKeyEvent(event)
? nil
: event;
}];
return true;
}
@ -67,26 +69,24 @@ class MacGlobalShortcutBackendPrivate : boost::noncopyable {
return backend_->KeyPressed(sequence);
}
id global_monitor_;
id local_monitor_;
MacGlobalShortcutBackend* backend_;
};
MacGlobalShortcutBackend::MacGlobalShortcutBackend(GlobalShortcuts* parent)
: GlobalShortcutBackend(parent),
p_(new MacGlobalShortcutBackendPrivate(this)) {
}
: GlobalShortcutBackend(parent),
p_(new MacGlobalShortcutBackendPrivate(this)) {}
MacGlobalShortcutBackend::~MacGlobalShortcutBackend() {
}
MacGlobalShortcutBackend::~MacGlobalShortcutBackend() {}
bool MacGlobalShortcutBackend::DoRegister() {
// Always enable media keys.
mac::SetShortcutHandler(this);
if (AXAPIEnabled()) {
foreach (const GlobalShortcuts::Shortcut& shortcut, manager_->shortcuts().values()) {
for (const GlobalShortcuts::Shortcut& shortcut :
manager_->shortcuts().values()) {
shortcuts_[shortcut.action->shortcut()] = shortcut.action;
}
return p_->Register();
@ -134,8 +134,10 @@ void MacGlobalShortcutBackend::ShowAccessibilityDialog() {
NSArray* paths = NSSearchPathForDirectoriesInDomains(
NSPreferencePanesDirectory, NSSystemDomainMask, YES);
if ([paths count] == 1) {
NSURL* prefpane_url = [NSURL fileURLWithPath:
[[paths objectAtIndex:0] stringByAppendingPathComponent:@"UniversalAccessPref.prefPane"]];
NSURL* prefpane_url =
[NSURL fileURLWithPath:[[paths objectAtIndex:0]
stringByAppendingPathComponent:
@"UniversalAccessPref.prefPane"]];
[[NSWorkspace sharedWorkspace] openURL:prefpane_url];
}
}

View File

@ -20,6 +20,7 @@
#include "taskmanager.h"
#include "core/logging.h"
#include "core/tagreaderclient.h"
#include "core/utilities.h"
#include <QDir>
#include <QFileInfo>
@ -35,7 +36,7 @@ const int Organise::kTranscodeProgressInterval = 500;
Organise::Organise(TaskManager* task_manager,
boost::shared_ptr<MusicStorage> destination,
const OrganiseFormat &format, bool copy, bool overwrite,
const QStringList& files, bool eject_after)
const NewSongInfoList& songs_info, bool eject_after)
: thread_(NULL),
task_manager_(task_manager),
transcoder_(new Transcoder(this)),
@ -44,7 +45,7 @@ Organise::Organise(TaskManager* task_manager,
copy_(copy),
overwrite_(overwrite),
eject_after_(eject_after),
task_count_(files.count()),
task_count_(songs_info.count()),
transcode_suffix_(1),
tasks_complete_(0),
started_(false),
@ -53,8 +54,8 @@ Organise::Organise(TaskManager* task_manager,
{
original_thread_ = thread();
foreach (const QString& filename, files) {
tasks_pending_ << Task(filename);
for (const NewSongInfo& song_info : songs_info) {
tasks_pending_ << Task(song_info);
}
}
@ -67,7 +68,7 @@ void Organise::Start() {
thread_ = new QThread;
connect(thread_, SIGNAL(started()), SLOT(ProcessSomeFiles()));
connect(transcoder_, SIGNAL(JobComplete(QString,bool)), SLOT(FileTranscoded(QString,bool)));
connect(transcoder_, SIGNAL(JobComplete(QString, bool)), SLOT(FileTranscoded(QString, bool)));
moveToThread(thread_);
thread_->start();
@ -79,8 +80,8 @@ void Organise::ProcessSomeFiles() {
if (!destination_->StartCopy(&supported_filetypes_)) {
// Failed to start - mark everything as failed :(
foreach (const Task& task, tasks_pending_)
files_with_errors_ << task.filename_;
for (const Task& task : tasks_pending_)
files_with_errors_ << task.song_info_.song_.url().toLocalFile();
tasks_pending_.clear();
}
started_ = true;
@ -116,29 +117,17 @@ void Organise::ProcessSomeFiles() {
}
// We process files in batches so we can be cancelled part-way through.
for (int i=0 ; i<kBatchSize ; ++i) {
for (int i = 0; i < kBatchSize; ++i) {
SetSongProgress(0);
if (tasks_pending_.isEmpty())
break;
Task task = tasks_pending_.takeFirst();
qLog(Info) << "Processing" << task.filename_;
qLog(Info) << "Processing" << task.song_info_.song_.url().toLocalFile();
// Is it a directory?
if (QFileInfo(task.filename_).isDir()) {
QDir dir(task.filename_);
foreach (const QString& entry, dir.entryList(
QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot | QDir::Readable)) {
tasks_pending_ << Task(task.filename_ + "/" + entry);
task_count_ ++;
}
continue;
}
// Read metadata from the file
Song song;
TagReaderClient::Instance()->ReadFileBlocking(task.filename_, &song);
// Use a Song instead of a tag reader
Song song = task.song_info_.song_;
if (!song.is_valid())
continue;
@ -150,8 +139,8 @@ void Organise::ProcessSomeFiles() {
song.set_filetype(task.new_filetype_);
// Fiddle the filename extension as well to match the new type
song.set_url(QUrl::fromLocalFile(FiddleFileExtension(song.url().toLocalFile(), task.new_extension_)));
song.set_basefilename(FiddleFileExtension(song.basefilename(), task.new_extension_));
song.set_url(QUrl::fromLocalFile(Utilities::FiddleFileExtension(song.basefilename(), task.new_extension_)));
song.set_basefilename(Utilities::FiddleFileExtension(song.basefilename(), task.new_extension_));
// Have to set this to the size of the new file or else funny stuff happens
song.set_filesize(QFileInfo(task.transcoded_filename_).size());
@ -168,14 +157,14 @@ void Organise::ProcessSomeFiles() {
QString::number(transcode_suffix_++);
task.new_extension_ = preset.extension_;
task.new_filetype_ = dest_type;
tasks_transcoding_[task.filename_] = task;
tasks_transcoding_[task.song_info_.song_.url().toLocalFile()] = task;
qLog(Debug) << "Transcoding to" << task.transcoded_filename_;
// Start the transcoding - this will happen in the background and
// FileTranscoded() will get called when it's done. At that point the
// task will get re-added to the pending queue with the new filename.
transcoder_->AddJob(task.filename_, preset, task.transcoded_filename_);
transcoder_->AddJob(task.song_info_.song_.url().toLocalFile(), preset, task.transcoded_filename_);
transcoder_->Start();
continue;
}
@ -183,8 +172,8 @@ void Organise::ProcessSomeFiles() {
MusicStorage::CopyJob job;
job.source_ = task.transcoded_filename_.isEmpty() ?
task.filename_ : task.transcoded_filename_;
job.destination_ = format_.GetFilenameForSong(song);
task.song_info_.song_.url().toLocalFile() : task.transcoded_filename_;
job.destination_ = task.song_info_.new_filename_;
job.metadata_ = song;
job.overwrite_ = overwrite_;
job.remove_original_ = !copy_;
@ -192,7 +181,7 @@ void Organise::ProcessSomeFiles() {
this, _1, !task.transcoded_filename_.isEmpty());
if (!destination_->CopyToStorage(job)) {
files_with_errors_ << task.filename_;
files_with_errors_ << task.song_info_.song_.basefilename();
}
// Clean up the temporary transcoded file
@ -240,7 +229,7 @@ Song::FileType Organise::CheckTranscode(Song::FileType original_type) const {
void Organise::SetSongProgress(float progress, bool transcoded) {
const int max = transcoded ? 50 : 100;
current_copy_progress_ = (transcoded ? 50 : 0) +
qBound(0, int(progress * max), max-1);
qBound(0, static_cast<int>(progress * max), max-1);
UpdateProgress();
}
@ -249,7 +238,7 @@ void Organise::UpdateProgress() {
// Update transcoding progress
QMap<QString, float> transcode_progress = transcoder_->GetProgress();
foreach (const QString& filename, transcode_progress.keys()) {
for (const QString& filename : transcode_progress.keys()) {
if (!tasks_transcoding_.contains(filename))
continue;
tasks_transcoding_[filename].transcode_progress_ = transcode_progress[filename];
@ -260,11 +249,11 @@ void Organise::UpdateProgress() {
// only need to be copied total 100.
int progress = tasks_complete_ * 100;
foreach (const Task& task, tasks_pending_) {
progress += qBound(0, int(task.transcode_progress_ * 50), 50);
for (const Task& task : tasks_pending_) {
progress += qBound(0, static_cast<int>(task.transcode_progress_ * 50), 50);
}
foreach (const Task& task, tasks_transcoding_.values()) {
progress += qBound(0, int(task.transcode_progress_ * 50), 50);
for (const Task& task : tasks_transcoding_.values()) {
progress += qBound(0, static_cast<int>(task.transcode_progress_ * 50), 50);
}
// Add the progress of the track that's currently copying
@ -286,12 +275,6 @@ void Organise::FileTranscoded(const QString& filename, bool success) {
QTimer::singleShot(0, this, SLOT(ProcessSomeFiles()));
}
QString Organise::FiddleFileExtension(const QString& filename, const QString& new_extension) {
if (filename.section('/', -1, -1).contains('.'))
return filename.section('.', 0, -2) + "." + new_extension;
return filename + "." + new_extension;
}
void Organise::timerEvent(QTimerEvent* e) {
QObject::timerEvent(e);

View File

@ -34,10 +34,19 @@ class Organise : public QObject {
Q_OBJECT
public:
struct NewSongInfo {
NewSongInfo(const Song& song = Song(), const QString& new_filename = QString())
: song_(song), new_filename_(new_filename) {}
Song song_;
QString new_filename_;
};
typedef QList<NewSongInfo> NewSongInfoList;
Organise(TaskManager* task_manager,
boost::shared_ptr<MusicStorage> destination,
const OrganiseFormat& format, bool copy, bool overwrite,
const QStringList& files, bool eject_after);
const NewSongInfoList& songs, bool eject_after);
static const int kBatchSize;
static const int kTranscodeProgressInterval;
@ -59,14 +68,12 @@ private:
void UpdateProgress();
Song::FileType CheckTranscode(Song::FileType original_type) const;
static QString FiddleFileExtension(const QString& filename, const QString& new_extension);
private:
struct Task {
explicit Task(const QString& filename = QString())
: filename_(filename), transcode_progress_(0.0) {}
explicit Task(const NewSongInfo& song_info = NewSongInfo())
: song_info_(song_info), transcode_progress_(0.0) {}
QString filename_;
NewSongInfo song_info_;
float transcode_progress_;
QString transcoded_filename_;

View File

@ -18,10 +18,12 @@
#include "core/organiseformat.h"
#include <QApplication>
#include <QFileInfo>
#include <QPalette>
#include <QUrl>
#include "core/timeconstants.h"
#include "core/utilities.h"
const char* OrganiseFormat::kTagPattern = "\\%([a-zA-Z]*)";
const char* OrganiseFormat::kBlockPattern = "\\{([^{}]+)\\}";
@ -73,6 +75,14 @@ bool OrganiseFormat::IsValid() const {
QString OrganiseFormat::GetFilenameForSong(const Song &song) const {
QString filename = ParseBlock(format_, song);
if (QFileInfo(filename).completeBaseName().isEmpty()) {
// Avoid having empty filenames, or filenames with extension only: in this
// case, keep the original filename.
// We remove the extension from "filename" if it exists, as song.basefilename()
// also contains the extension.
filename = Utilities::PathWithoutFilenameExtension(filename) + song.basefilename();
}
if (replace_spaces_)
filename.replace(QRegExp("\\s"), "_");
@ -151,8 +161,7 @@ QString OrganiseFormat::TagValue(const QString &tag, const Song &song) const {
QString::number(song.length_nanosec() / kNsecPerSec);
else if (tag == "bitrate") value = QString::number(song.bitrate());
else if (tag == "samplerate") value = QString::number(song.samplerate());
else if (tag == "extension") value =
song.url().toLocalFile().section('.', -1, -1);
else if (tag == "extension") value = QFileInfo(song.url().toLocalFile()).suffix();
else if (tag == "artistinitial") {
value = song.effective_albumartist().trimmed();
if (replace_the_ && !value.isEmpty())

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

@ -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

@ -445,13 +445,15 @@ QByteArray HmacSha1(const QByteArray& key, const QByteArray& data) {
}
QByteArray Sha256(const QByteArray& data) {
SHA256_CTX context;
SHA256_Init(&context);
SHA256_Update(&context, reinterpret_cast<const u_int8_t*>(data.constData()),
data.length());
clementine_sha2::SHA256_CTX context;
clementine_sha2::SHA256_Init(&context);
clementine_sha2::SHA256_Update(
&context, reinterpret_cast<const u_int8_t*>(data.constData()),
data.length());
QByteArray ret(SHA256_DIGEST_LENGTH, '\0');
SHA256_Final(reinterpret_cast<u_int8_t*>(ret.data()), &context);
QByteArray ret(clementine_sha2::SHA256_DIGEST_LENGTH, '\0');
clementine_sha2::SHA256_Final(
reinterpret_cast<u_int8_t*>(ret.data()), &context);
return ret;
}
@ -667,6 +669,16 @@ QUrl GetRelativePathToClementineBin(const QUrl& url) {
return QUrl::fromLocalFile(appPath.relativeFilePath(url.toLocalFile()));
}
QString PathWithoutFilenameExtension(const QString& filename) {
if (filename.section('/', -1, -1).contains('.'))
return filename.section('.', 0, -2);
return filename;
}
QString FiddleFileExtension(const QString& filename, const QString& new_extension) {
return PathWithoutFilenameExtension(filename) + "." + new_extension;
}
} // namespace Utilities

View File

@ -113,6 +113,9 @@ namespace Utilities {
// Get relative path to clementine binary
QUrl GetRelativePathToClementineBin(const QUrl& url);
// Get the path without the filename extension
QString PathWithoutFilenameExtension(const QString& filename);
QString FiddleFileExtension(const QString& filename, const QString& new_extension);
enum ConfigPath {
Path_Root,

View File

@ -8,7 +8,7 @@
#include "core/network.h"
const char* KittenLoader::kFlickrKittenUrl =
"http://api.flickr.com/services/rest/"
"https://api.flickr.com/services/rest/"
"?method=flickr.photos.search"
"&api_key=808b52887b3cc7fe098abd62f6ed1745"
"&tags=kitten"
@ -17,7 +17,7 @@ const char* KittenLoader::kFlickrKittenUrl =
"&content_type=1";
const char* KittenLoader::kFlickrPhotoUrl =
"http://farm%1.static.flickr.com/%2/%3_%4_m.jpg";
"https://farm%1.static.flickr.com/%2/%3_%4_m.jpg";
KittenLoader::KittenLoader(QObject* parent)
: AlbumCoverLoader(parent) {

View File

@ -30,9 +30,9 @@ using std::mem_fun;
namespace {
static const char* kReleaseSearchUrl =
"http://musicbrainz.org/ws/2/release/";
"https://musicbrainz.org/ws/2/release/";
static const char* kAlbumCoverUrl =
"http://coverartarchive.org/release/%1/front";
"https://coverartarchive.org/release/%1/front";
} // namespace

View File

@ -60,22 +60,18 @@
#define kUSBProductString "USB Product Name"
#endif
// io_object_t, io_service_t, io_iterator_t etc. are all typedef'd to unsigned int,
// io_object_t, io_service_t, io_iterator_t etc. are all typedef'd to unsigned
// int,
// hence the lack of templating here.
class ScopedIOObject {
public:
explicit ScopedIOObject(io_object_t object = 0)
: object_(object) {
}
explicit ScopedIOObject(io_object_t object = 0) : object_(object) {}
~ScopedIOObject() {
if (object_)
IOObjectRelease(object_);
if (object_) IOObjectRelease(object_);
}
io_object_t get() const {
return object_;
}
io_object_t get() const { return object_; }
private:
io_object_t object_;
@ -105,12 +101,9 @@ uint qHash(const MacDeviceLister::MTPDevice& d) {
return qHash(d.vendor_id) ^ qHash(d.product_id);
}
MacDeviceLister::MacDeviceLister() {
}
MacDeviceLister::MacDeviceLister() {}
MacDeviceLister::~MacDeviceLister() {
CFRelease(loop_session_);
}
MacDeviceLister::~MacDeviceLister() { CFRelease(loop_session_); }
void MacDeviceLister::Init() {
ScopedNSAutoreleasePool pool;
@ -149,24 +142,23 @@ void MacDeviceLister::Init() {
// Register for disk mounts/unmounts.
loop_session_ = DASessionCreate(kCFAllocatorDefault);
DARegisterDiskAppearedCallback(
loop_session_, kDADiskDescriptionMatchVolumeMountable, &DiskAddedCallback, reinterpret_cast<void*>(this));
DARegisterDiskDisappearedCallback(
loop_session_, NULL, &DiskRemovedCallback, reinterpret_cast<void*>(this));
loop_session_, kDADiskDescriptionMatchVolumeMountable, &DiskAddedCallback,
reinterpret_cast<void*>(this));
DARegisterDiskDisappearedCallback(loop_session_, NULL, &DiskRemovedCallback,
reinterpret_cast<void*>(this));
DASessionScheduleWithRunLoop(loop_session_, run_loop_, kCFRunLoopDefaultMode);
// Register for USB device connection/disconnection.
IONotificationPortRef notification_port = IONotificationPortCreate(kIOMasterPortDefault);
CFMutableDictionaryRef matching_dict = IOServiceMatching(kIOUSBDeviceClassName);
IONotificationPortRef notification_port =
IONotificationPortCreate(kIOMasterPortDefault);
CFMutableDictionaryRef matching_dict =
IOServiceMatching(kIOUSBDeviceClassName);
// IOServiceAddMatchingNotification decreases reference count.
CFRetain(matching_dict);
io_iterator_t it;
kern_return_t err = IOServiceAddMatchingNotification(
notification_port,
kIOFirstMatchNotification,
matching_dict,
&USBDeviceAddedCallback,
reinterpret_cast<void*>(this),
&it);
notification_port, kIOFirstMatchNotification, matching_dict,
&USBDeviceAddedCallback, reinterpret_cast<void*>(this), &it);
if (err == KERN_SUCCESS) {
USBDeviceAddedCallback(this, it);
} else {
@ -174,28 +166,22 @@ void MacDeviceLister::Init() {
}
err = IOServiceAddMatchingNotification(
notification_port,
kIOTerminatedNotification,
matching_dict,
&USBDeviceRemovedCallback,
reinterpret_cast<void*>(this),
&it);
notification_port, kIOTerminatedNotification, matching_dict,
&USBDeviceRemovedCallback, reinterpret_cast<void*>(this), &it);
if (err == KERN_SUCCESS) {
USBDeviceRemovedCallback(this, it);
} else {
qLog(Warning) << "Could not add notification USB device removal";
}
CFRunLoopSourceRef io_source = IONotificationPortGetRunLoopSource(notification_port);
CFRunLoopSourceRef io_source =
IONotificationPortGetRunLoopSource(notification_port);
CFRunLoopAddSource(run_loop_, io_source, kCFRunLoopDefaultMode);
CFRunLoopRun();
}
void MacDeviceLister::ShutDown() {
CFRunLoopStop(run_loop_);
}
void MacDeviceLister::ShutDown() { CFRunLoopStop(run_loop_); }
// IOKit helpers.
namespace {
@ -203,7 +189,8 @@ namespace {
// Caller is responsible for calling CFRelease().
CFTypeRef GetUSBRegistryEntry(io_object_t device, CFStringRef key) {
io_iterator_t it;
if (IORegistryEntryGetParentIterator(device, kIOServicePlane, &it) == KERN_SUCCESS) {
if (IORegistryEntryGetParentIterator(device, kIOServicePlane, &it) ==
KERN_SUCCESS) {
io_object_t next;
while ((next = IOIteratorNext(it))) {
CFTypeRef registry_entry = (CFStringRef)IORegistryEntryCreateCFProperty(
@ -230,7 +217,8 @@ CFTypeRef GetUSBRegistryEntry(io_object_t device, CFStringRef key) {
}
QString GetUSBRegistryEntryString(io_object_t device, CFStringRef key) {
ScopedCFTypeRef<CFStringRef> registry_string((CFStringRef)GetUSBRegistryEntry(device, key));
ScopedCFTypeRef<CFStringRef> registry_string(
(CFStringRef)GetUSBRegistryEntry(device, key));
if (registry_string) {
return QString::fromUtf8([(NSString*)registry_string.get() UTF8String]);
}
@ -240,14 +228,15 @@ QString GetUSBRegistryEntryString(io_object_t device, CFStringRef key) {
NSObject* GetPropertyForDevice(io_object_t device, CFStringRef key) {
CFMutableDictionaryRef properties;
kern_return_t ret = IORegistryEntryCreateCFProperties(
device, &properties, kCFAllocatorDefault, 0);
kern_return_t ret = IORegistryEntryCreateCFProperties(device, &properties,
kCFAllocatorDefault, 0);
if (ret != KERN_SUCCESS) {
return nil;
}
scoped_nsobject<NSDictionary> dict((NSDictionary*)properties); // Takes ownership.
scoped_nsobject<NSDictionary> dict(
(NSDictionary*)properties); // Takes ownership.
NSObject* prop = [dict objectForKey:(NSString*)key];
if (prop) {
// The dictionary goes out of scope so we should retain this object.
@ -266,10 +255,7 @@ NSObject* GetPropertyForDevice(io_object_t device, CFStringRef key) {
int GetUSBDeviceClass(io_object_t device) {
ScopedCFTypeRef<CFTypeRef> interface_class(IORegistryEntrySearchCFProperty(
device,
kIOServicePlane,
CFSTR(kUSBInterfaceClass),
kCFAllocatorDefault,
device, kIOServicePlane, CFSTR(kUSBInterfaceClass), kCFAllocatorDefault,
kIORegistryIterateRecursively));
NSNumber* number = (NSNumber*)interface_class.get();
if (number) {
@ -280,13 +266,17 @@ int GetUSBDeviceClass(io_object_t device) {
}
QString GetIconForDevice(io_object_t device) {
scoped_nsobject<NSDictionary> media_icon((NSDictionary*)GetPropertyForDevice(device, CFSTR("IOMediaIcon")));
scoped_nsobject<NSDictionary> media_icon(
(NSDictionary*)GetPropertyForDevice(device, CFSTR("IOMediaIcon")));
if (media_icon) {
NSString* bundle = (NSString*)[media_icon objectForKey:@"CFBundleIdentifier"];
NSString* file = (NSString*)[media_icon objectForKey:@"IOBundleResourceFile"];
NSString* bundle =
(NSString*)[media_icon objectForKey:@"CFBundleIdentifier"];
NSString* file =
(NSString*)[media_icon objectForKey:@"IOBundleResourceFile"];
scoped_nsobject<NSURL> bundle_url((NSURL*)KextManagerCreateURLForBundleIdentifier(
kCFAllocatorDefault, (CFStringRef)bundle));
scoped_nsobject<NSURL> bundle_url(
(NSURL*)KextManagerCreateURLForBundleIdentifier(kCFAllocatorDefault,
(CFStringRef)bundle));
QString path = QString::fromUtf8([[bundle_url path] UTF8String]);
path += "/Contents/Resources/";
@ -298,7 +288,8 @@ QString GetIconForDevice(io_object_t device) {
}
QString GetSerialForDevice(io_object_t device) {
QString serial = GetUSBRegistryEntryString(device, CFSTR(kUSBSerialNumberString));
QString serial =
GetUSBRegistryEntryString(device, CFSTR(kUSBSerialNumberString));
if (!serial.isEmpty()) {
return "USB/" + serial;
}
@ -306,8 +297,8 @@ QString GetSerialForDevice(io_object_t device) {
}
QString GetSerialForMTPDevice(io_object_t device) {
scoped_nsobject<NSString> serial((NSString*)
GetPropertyForDevice(device, CFSTR(kUSBSerialNumberString)));
scoped_nsobject<NSString> serial(
(NSString*)GetPropertyForDevice(device, CFSTR(kUSBSerialNumberString)));
return QString(QString("MTP/") + QString::fromUtf8([serial UTF8String]));
}
@ -320,14 +311,14 @@ QString FindDeviceProperty(const QString& bsd_name, CFStringRef property) {
QString ret = GetUSBRegistryEntryString(device.get(), property);
return ret;
}
}
quint64 MacDeviceLister::GetFreeSpace(const QUrl& url) {
QMutexLocker l(&libmtp_mutex_);
MtpConnection connection(url);
if (!connection.is_valid()) {
qLog(Warning) << "Error connecting to MTP device, couldn't get device free space";
qLog(Warning)
<< "Error connecting to MTP device, couldn't get device free space";
return -1;
}
LIBMTP_devicestorage_t* storage = connection.device()->storage;
@ -343,7 +334,8 @@ quint64 MacDeviceLister::GetCapacity(const QUrl& url) {
QMutexLocker l(&libmtp_mutex_);
MtpConnection connection(url);
if (!connection.is_valid()) {
qLog(Warning) << "Error connecting to MTP device, couldn't get device capacity";
qLog(Warning)
<< "Error connecting to MTP device, couldn't get device capacity";
return -1;
}
LIBMTP_devicestorage_t* storage = connection.device()->storage;
@ -358,9 +350,11 @@ quint64 MacDeviceLister::GetCapacity(const QUrl& url) {
void MacDeviceLister::DiskAddedCallback(DADiskRef disk, void* context) {
MacDeviceLister* me = reinterpret_cast<MacDeviceLister*>(context);
scoped_nsobject<NSDictionary> properties((NSDictionary*)DADiskCopyDescription(disk));
scoped_nsobject<NSDictionary> properties(
(NSDictionary*)DADiskCopyDescription(disk));
NSString* kind = [properties objectForKey:(NSString*)kDADiskDescriptionMediaKindKey];
NSString* kind =
[properties objectForKey:(NSString*)kDADiskDescriptionMediaKindKey];
#ifdef HAVE_AUDIOCD
if (kind && strcmp([kind UTF8String], kIOCDMediaClass) == 0) {
// CD inserted.
@ -371,22 +365,26 @@ void MacDeviceLister::DiskAddedCallback(DADiskRef disk, void* context) {
}
#endif
NSURL* volume_path =
[[properties objectForKey:(NSString*)kDADiskDescriptionVolumePathKey] copy];
NSURL* volume_path = [[properties
objectForKey:(NSString*)kDADiskDescriptionVolumePathKey] copy];
if (volume_path) {
ScopedIOObject device(DADiskCopyIOMedia(disk));
ScopedCFTypeRef<CFStringRef> class_name(IOObjectCopyClass(device.get()));
if (class_name && CFStringCompare(class_name.get(), CFSTR(kIOMediaClass), 0) == kCFCompareEqualTo) {
QString vendor = GetUSBRegistryEntryString(device.get(), CFSTR(kUSBVendorString));
QString product = GetUSBRegistryEntryString(device.get(), CFSTR(kUSBProductString));
if (class_name && CFStringCompare(class_name.get(), CFSTR(kIOMediaClass),
0) == kCFCompareEqualTo) {
QString vendor =
GetUSBRegistryEntryString(device.get(), CFSTR(kUSBVendorString));
QString product =
GetUSBRegistryEntryString(device.get(), CFSTR(kUSBProductString));
CFMutableDictionaryRef cf_properties;
kern_return_t ret = IORegistryEntryCreateCFProperties(
device.get(), &cf_properties, kCFAllocatorDefault, 0);
if (ret == KERN_SUCCESS) {
scoped_nsobject<NSDictionary> dict((NSDictionary*)cf_properties); // Takes ownership.
scoped_nsobject<NSDictionary> dict(
(NSDictionary*)cf_properties); // Takes ownership.
if ([[dict objectForKey:@"Removable"] intValue] == 1) {
QString serial = GetSerialForDevice(device.get());
if (!serial.isEmpty()) {
@ -420,15 +418,9 @@ void MacDeviceLister::DiskRemovedCallback(DADiskRef disk, void* context) {
}
}
bool DeviceRequest(IOUSBDeviceInterface** dev,
quint8 direction,
quint8 type,
quint8 recipient,
quint8 request_code,
quint16 value,
quint16 index,
quint16 length,
QByteArray* data) {
bool DeviceRequest(IOUSBDeviceInterface** dev, quint8 direction, quint8 type,
quint8 recipient, quint8 request_code, quint16 value,
quint16 index, quint16 length, QByteArray* data) {
IOUSBDevRequest req;
req.bmRequestType = USBmakebmRequestType(direction, type, recipient);
req.bRequest = request_code;
@ -468,15 +460,19 @@ void MacDeviceLister::USBDeviceAddedCallback(void* refcon, io_iterator_t it) {
io_object_t object;
while ((object = IOIteratorNext(it))) {
ScopedCFTypeRef<CFStringRef> class_name(IOObjectCopyClass(object));
BOOST_SCOPE_EXIT((object)) {
IOObjectRelease(object);
} BOOST_SCOPE_EXIT_END
BOOST_SCOPE_EXIT((object)) { IOObjectRelease(object); }
BOOST_SCOPE_EXIT_END
if (CFStringCompare(class_name.get(), CFSTR(kIOUSBDeviceClassName), 0) == kCFCompareEqualTo) {
NSString* vendor = (NSString*)GetPropertyForDevice(object, CFSTR(kUSBVendorString));
NSString* product = (NSString*)GetPropertyForDevice(object, CFSTR(kUSBProductString));
NSNumber* vendor_id = (NSNumber*)GetPropertyForDevice(object, CFSTR(kUSBVendorID));
NSNumber* product_id = (NSNumber*)GetPropertyForDevice(object, CFSTR(kUSBProductID));
if (CFStringCompare(class_name.get(), CFSTR(kIOUSBDeviceClassName), 0) ==
kCFCompareEqualTo) {
NSString* vendor =
(NSString*)GetPropertyForDevice(object, CFSTR(kUSBVendorString));
NSString* product =
(NSString*)GetPropertyForDevice(object, CFSTR(kUSBProductString));
NSNumber* vendor_id =
(NSNumber*)GetPropertyForDevice(object, CFSTR(kUSBVendorID));
NSNumber* product_id =
(NSNumber*)GetPropertyForDevice(object, CFSTR(kUSBProductID));
int interface_class = GetUSBDeviceClass(object);
qLog(Debug) << "Interface class:" << interface_class;
@ -492,7 +488,8 @@ void MacDeviceLister::USBDeviceAddedCallback(void* refcon, io_iterator_t it) {
device.bus = -1;
device.address = -1;
if (device.vendor_id == kAppleVendorID || // I think we can safely skip Apple products.
if (device.vendor_id ==
kAppleVendorID || // I think we can safely skip Apple products.
// Blacklist ilok2 as this probe may be breaking it.
(device.vendor_id == 0x088e && device.product_id == 0x5036) ||
// Blacklist eLicenser
@ -504,7 +501,8 @@ void MacDeviceLister::USBDeviceAddedCallback(void* refcon, io_iterator_t it) {
continue;
}
NSNumber* addr = (NSNumber*)GetPropertyForDevice(object, CFSTR("USB Address"));
NSNumber* addr =
(NSNumber*)GetPropertyForDevice(object, CFSTR("USB Address"));
int bus = GetBusNumber(object);
if (!addr || bus == -1) {
// Failed to get bus or address number.
@ -525,19 +523,15 @@ void MacDeviceLister::USBDeviceAddedCallback(void* refcon, io_iterator_t it) {
IOCFPlugInInterface** plugin_interface = NULL;
SInt32 score;
kern_return_t err = IOCreatePlugInInterfaceForService(
object,
kIOUSBDeviceUserClientTypeID,
kIOCFPlugInInterfaceID,
&plugin_interface,
&score);
object, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID,
&plugin_interface, &score);
if (err != KERN_SUCCESS) {
continue;
}
IOUSBDeviceInterface** dev = NULL;
HRESULT result = (*plugin_interface)->QueryInterface(
plugin_interface,
CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID),
plugin_interface, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID),
(LPVOID*)&dev);
(*plugin_interface)->Release(plugin_interface);
@ -555,44 +549,43 @@ void MacDeviceLister::USBDeviceAddedCallback(void* refcon, io_iterator_t it) {
BOOST_SCOPE_EXIT((dev)) {
(*dev)->USBDeviceClose(dev);
(*dev)->Release(dev);
} BOOST_SCOPE_EXIT_END
}
BOOST_SCOPE_EXIT_END
// Request the string descriptor at 0xee.
// This is a magic string that indicates whether this device supports MTP.
QByteArray data;
bool ret = DeviceRequest(
dev, kUSBIn, kUSBStandard, kUSBDevice, kUSBRqGetDescriptor,
(kUSBStringDesc << 8) | 0xee, 0x0409, 2, &data);
if (!ret)
continue;
bool ret = DeviceRequest(dev, kUSBIn, kUSBStandard, kUSBDevice,
kUSBRqGetDescriptor,
(kUSBStringDesc << 8) | 0xee, 0x0409, 2, &data);
if (!ret) continue;
UInt8 string_len = data[0];
ret = DeviceRequest(
dev, kUSBIn, kUSBStandard, kUSBDevice, kUSBRqGetDescriptor,
(kUSBStringDesc << 8) | 0xee, 0x0409, string_len, &data);
if (!ret)
continue;
ret = DeviceRequest(dev, kUSBIn, kUSBStandard, kUSBDevice,
kUSBRqGetDescriptor, (kUSBStringDesc << 8) | 0xee,
0x0409, string_len, &data);
if (!ret) continue;
// The device actually returned something. That's a good sign.
// Because this was designed by MS, the characters are in UTF-16 (LE?).
QString str = QString::fromUtf16(reinterpret_cast<ushort*>(data.data() + 2), (data.size() / 2) - 2);
QString str = QString::fromUtf16(
reinterpret_cast<ushort*>(data.data() + 2), (data.size() / 2) - 2);
if (str.startsWith("MSFT100")) {
// We got the OS descriptor!
char vendor_code = data[16];
ret = DeviceRequest(
dev, kUSBIn, kUSBVendor, kUSBDevice, vendor_code, 0, 4, 256, &data);
if (!ret || data.at(0) != 0x28)
continue;
ret = DeviceRequest(dev, kUSBIn, kUSBVendor, kUSBDevice, vendor_code, 0,
4, 256, &data);
if (!ret || data.at(0) != 0x28) continue;
if (QString::fromAscii(data.data() + 0x12, 3) != "MTP") {
// Not quite.
continue;
}
ret = DeviceRequest(
dev, kUSBIn, kUSBVendor, kUSBDevice, vendor_code, 0, 5, 256, &data);
ret = DeviceRequest(dev, kUSBIn, kUSBVendor, kUSBDevice, vendor_code, 0,
5, 256, &data);
if (!ret || data.at(0) != 0x28) {
continue;
}
@ -613,15 +606,19 @@ void MacDeviceLister::USBDeviceRemovedCallback(void* refcon, io_iterator_t it) {
io_object_t object;
while ((object = IOIteratorNext(it))) {
ScopedCFTypeRef<CFStringRef> class_name(IOObjectCopyClass(object));
BOOST_SCOPE_EXIT((object)) {
IOObjectRelease(object);
} BOOST_SCOPE_EXIT_END
BOOST_SCOPE_EXIT((object)) { IOObjectRelease(object); }
BOOST_SCOPE_EXIT_END
if (CFStringCompare(class_name.get(), CFSTR(kIOUSBDeviceClassName), 0) == kCFCompareEqualTo) {
NSString* vendor = (NSString*)GetPropertyForDevice(object, CFSTR(kUSBVendorString));
NSString* product = (NSString*)GetPropertyForDevice(object, CFSTR(kUSBProductString));
NSNumber* vendor_id = (NSNumber*)GetPropertyForDevice(object, CFSTR(kUSBVendorID));
NSNumber* product_id = (NSNumber*)GetPropertyForDevice(object, CFSTR(kUSBProductID));
if (CFStringCompare(class_name.get(), CFSTR(kIOUSBDeviceClassName), 0) ==
kCFCompareEqualTo) {
NSString* vendor =
(NSString*)GetPropertyForDevice(object, CFSTR(kUSBVendorString));
NSString* product =
(NSString*)GetPropertyForDevice(object, CFSTR(kUSBProductString));
NSNumber* vendor_id =
(NSNumber*)GetPropertyForDevice(object, CFSTR(kUSBVendorID));
NSNumber* product_id =
(NSNumber*)GetPropertyForDevice(object, CFSTR(kUSBProductID));
QString serial = GetSerialForMTPDevice(object);
MTPDevice device;
@ -643,7 +640,8 @@ void MacDeviceLister::RemovedMTPDevice(const QString& serial) {
}
}
void MacDeviceLister::FoundMTPDevice(const MTPDevice& device, const QString& serial) {
void MacDeviceLister::FoundMTPDevice(const MTPDevice& device,
const QString& serial) {
qLog(Debug) << "New MTP device detected!" << device.bus << device.address;
mtp_devices_[serial] = device;
QList<QUrl> urls = MakeDeviceUrls(serial);
@ -653,9 +651,7 @@ void MacDeviceLister::FoundMTPDevice(const MTPDevice& device, const QString& ser
emit DeviceAdded(serial);
}
bool IsMTPSerial(const QString& serial) {
return serial.startsWith("MTP");
}
bool IsMTPSerial(const QString& serial) { return serial.startsWith("MTP"); }
bool MacDeviceLister::IsCDDevice(const QString& serial) const {
return cd_devices_.contains(serial);
@ -671,23 +667,27 @@ QString MacDeviceLister::MakeFriendlyName(const QString& serial) {
}
}
QString bsd_name = IsCDDevice(serial) ? *cd_devices_.find(serial) : current_devices_[serial];
QString bsd_name =
IsCDDevice(serial) ? *cd_devices_.find(serial) : current_devices_[serial];
ScopedCFTypeRef<DASessionRef> session(DASessionCreate(kCFAllocatorDefault));
ScopedCFTypeRef<DADiskRef> disk(DADiskCreateFromBSDName(
kCFAllocatorDefault, session.get(), bsd_name.toAscii().constData()));
if (IsCDDevice(serial)) {
scoped_nsobject<NSDictionary> properties((NSDictionary*)DADiskCopyDescription(disk.get()));
NSString* device_name = (NSString*)[properties.get() objectForKey:
(NSString*)kDADiskDescriptionMediaNameKey];
scoped_nsobject<NSDictionary> properties(
(NSDictionary*)DADiskCopyDescription(disk.get()));
NSString* device_name = (NSString*)[properties.get()
objectForKey:(NSString*)kDADiskDescriptionMediaNameKey];
return QString::fromUtf8([device_name UTF8String]);
}
ScopedIOObject device(DADiskCopyIOMedia(disk));
QString vendor = GetUSBRegistryEntryString(device.get(), CFSTR(kUSBVendorString));
QString product = GetUSBRegistryEntryString(device.get(), CFSTR(kUSBProductString));
QString vendor =
GetUSBRegistryEntryString(device.get(), CFSTR(kUSBVendorString));
QString product =
GetUSBRegistryEntryString(device.get(), CFSTR(kUSBProductString));
if (vendor.isEmpty()) {
return product;
@ -718,9 +718,10 @@ QList<QUrl> MacDeviceLister::MakeDeviceUrls(const QString& serial) {
ScopedCFTypeRef<DADiskRef> disk(DADiskCreateFromBSDName(
kCFAllocatorDefault, session.get(), bsd_name.toAscii().constData()));
scoped_nsobject<NSDictionary> properties((NSDictionary*)DADiskCopyDescription(disk.get()));
scoped_nsobject<NSURL> volume_path(
[[properties objectForKey:(NSString*)kDADiskDescriptionVolumePathKey] copy]);
scoped_nsobject<NSDictionary> properties(
(NSDictionary*)DADiskCopyDescription(disk.get()));
scoped_nsobject<NSURL> volume_path([[properties
objectForKey:(NSString*)kDADiskDescriptionVolumePathKey] copy]);
QString path = QString::fromUtf8([[volume_path path] UTF8String]);
QUrl ret = MakeUrlFromLocalPath(path);
@ -749,9 +750,10 @@ QVariantList MacDeviceLister::DeviceIcons(const QString& serial) {
ScopedIOObject device(DADiskCopyIOMedia(disk.get()));
QString icon = GetIconForDevice(device.get());
scoped_nsobject<NSDictionary> properties((NSDictionary*)DADiskCopyDescription(disk));
scoped_nsobject<NSURL> volume_path(
[[properties objectForKey:(NSString*)kDADiskDescriptionVolumePathKey] copy]);
scoped_nsobject<NSDictionary> properties(
(NSDictionary*)DADiskCopyDescription(disk));
scoped_nsobject<NSURL> volume_path([[properties
objectForKey:(NSString*)kDADiskDescriptionVolumePathKey] copy]);
QString path = QString::fromUtf8([[volume_path path] UTF8String]);
@ -764,21 +766,21 @@ QVariantList MacDeviceLister::DeviceIcons(const QString& serial) {
return ret;
}
QString MacDeviceLister::DeviceManufacturer(const QString& serial){
QString MacDeviceLister::DeviceManufacturer(const QString& serial) {
if (IsMTPSerial(serial)) {
return mtp_devices_[serial].vendor;
}
return FindDeviceProperty(current_devices_[serial], CFSTR(kUSBVendorString));
}
QString MacDeviceLister::DeviceModel(const QString& serial){
QString MacDeviceLister::DeviceModel(const QString& serial) {
if (IsMTPSerial(serial)) {
return mtp_devices_[serial].product;
}
return FindDeviceProperty(current_devices_[serial], CFSTR(kUSBProductString));
}
quint64 MacDeviceLister::DeviceCapacity(const QString& serial){
quint64 MacDeviceLister::DeviceCapacity(const QString& serial) {
if (IsMTPSerial(serial)) {
QList<QUrl> urls = MakeDeviceUrls(serial);
return mtp_devices_[serial].capacity;
@ -799,7 +801,7 @@ quint64 MacDeviceLister::DeviceCapacity(const QString& serial){
return ret;
}
quint64 MacDeviceLister::DeviceFreeSpace(const QString& serial){
quint64 MacDeviceLister::DeviceFreeSpace(const QString& serial) {
if (IsMTPSerial(serial)) {
QList<QUrl> urls = MakeDeviceUrls(serial);
return mtp_devices_[serial].free_space;
@ -809,20 +811,25 @@ quint64 MacDeviceLister::DeviceFreeSpace(const QString& serial){
ScopedCFTypeRef<DADiskRef> disk(DADiskCreateFromBSDName(
kCFAllocatorDefault, session.get(), bsd_name.toAscii().constData()));
scoped_nsobject<NSDictionary> properties((NSDictionary*)DADiskCopyDescription(disk));
scoped_nsobject<NSURL> volume_path(
[[properties objectForKey:(NSString*)kDADiskDescriptionVolumePathKey] copy]);
scoped_nsobject<NSDictionary> properties(
(NSDictionary*)DADiskCopyDescription(disk));
scoped_nsobject<NSURL> volume_path([[properties
objectForKey:(NSString*)kDADiskDescriptionVolumePathKey] copy]);
NSNumber* value = nil;
NSError* error = nil;
if ([volume_path getResourceValue: &value
forKey: NSURLVolumeAvailableCapacityKey error: &error] && value) {
if ([volume_path getResourceValue:&value
forKey:NSURLVolumeAvailableCapacityKey
error:&error] &&
value) {
return [value unsignedLongLongValue];
}
return 0;
}
QVariantMap MacDeviceLister::DeviceHardwareInfo(const QString& serial){return QVariantMap();}
QVariantMap MacDeviceLister::DeviceHardwareInfo(const QString& serial) {
return QVariantMap();
}
bool MacDeviceLister::AskForScan(const QString& serial) const {
return !IsCDDevice(serial);
@ -838,8 +845,9 @@ void MacDeviceLister::UnmountDevice(const QString& serial) {
DADiskUnmount(disk, kDADiskUnmountOptionDefault, &DiskUnmountCallback, this);
}
void MacDeviceLister::DiskUnmountCallback(
DADiskRef disk, DADissenterRef dissenter, void* context) {
void MacDeviceLister::DiskUnmountCallback(DADiskRef disk,
DADissenterRef dissenter,
void* context) {
if (dissenter) {
qLog(Warning) << "Another app blocked the unmount";
} else {

View File

@ -44,7 +44,7 @@ SearchProviderStatusWidget::SearchProviderStatusWidget(
if (enabled && logged_in) {
ui_->disabled_group->hide();
} else {
const QString disabled_text = tr("Disabled");
const QString disabled_text = tr("Disabled", "Refers to search provider's status.");
const QString not_logged_in_text = tr("Not logged in");
const int disabled_width = fontMetrics().width(" ") + qMax(
fontMetrics().width(disabled_text),

View File

@ -3,6 +3,8 @@
#include "internetservice.h"
#include <memory>
#include <QMenu>
#include "core/tagreaderclient.h"
@ -61,8 +63,8 @@ class CloudFileService : public InternetService {
LibraryModel* library_model_;
QSortFilterProxyModel* library_sort_model_;
boost::scoped_ptr<QMenu> context_menu_;
boost::scoped_ptr<AlbumCoverManager> cover_manager_;
std::unique_ptr<QMenu> context_menu_;
std::unique_ptr<AlbumCoverManager> cover_manager_;
PlaylistManager* playlist_manager_;
TaskManager* task_manager_;

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

@ -27,8 +27,8 @@
#include "core/network.h"
#include "core/utilities.h"
const char* MusicBrainzClient::kTrackUrl = "http://musicbrainz.org/ws/2/recording/";
const char* MusicBrainzClient::kDiscUrl = "http://musicbrainz.org/ws/2/discid/";
const char* MusicBrainzClient::kTrackUrl = "https://musicbrainz.org/ws/2/recording/";
const char* MusicBrainzClient::kDiscUrl = "https://musicbrainz.org/ws/2/discid/";
const char* MusicBrainzClient::kDateRegex = "^[12]\\d{3}";
const int MusicBrainzClient::kDefaultTimeout = 5000; // msec

View File

@ -11,23 +11,24 @@
- (void)netServiceWillPublish:(NSNetService*)netService;
- (void)netService:(NSNetService*)netService
didNotPublish:(NSDictionary*)errorDict;
didNotPublish:(NSDictionary*)errorDict;
- (void)netServiceDidStop:(NSNetService*)netService;
@end
@implementation NetServicePublicationDelegate
- (void)netServiceWillPublish: (NSNetService*)netService {
- (void)netServiceWillPublish:(NSNetService*)netService {
qLog(Debug) << "Publishing:" << [[netService name] UTF8String];
}
- (void)netService: (NSNetService*)netServie didNotPublish: (NSDictionary*)errorDict {
- (void)netService:(NSNetService*)netServie
didNotPublish:(NSDictionary*)errorDict {
qLog(Debug) << "Failed to publish remote service with Bonjour";
NSLog(@"%@", errorDict);
}
- (void)netServiceDidStop: (NSNetService*)netService {
- (void)netServiceDidStop:(NSNetService*)netService {
qLog(Debug) << "Unpublished:" << [[netService name] UTF8String];
}
@ -36,32 +37,24 @@
namespace {
NSString* NSStringFromQString(const QString& s) {
return [[NSString alloc] initWithUTF8String: s.toUtf8().constData()];
return [[NSString alloc] initWithUTF8String:s.toUtf8().constData()];
}
}
}
Bonjour::Bonjour() : delegate_([[NetServicePublicationDelegate alloc] init]) {}
Bonjour::Bonjour()
: delegate_([[NetServicePublicationDelegate alloc] init]) {
}
Bonjour::~Bonjour() { [delegate_ release]; }
Bonjour::~Bonjour() {
[delegate_ release];
}
void Bonjour::PublishInternal(
const QString& domain,
const QString& type,
const QByteArray& name,
quint16 port) {
void Bonjour::PublishInternal(const QString& domain, const QString& type,
const QByteArray& name, quint16 port) {
ScopedNSAutoreleasePool pool;
NSNetService* service = [[NSNetService alloc]
initWithDomain: NSStringFromQString(domain)
type: NSStringFromQString(type)
name: [NSString stringWithUTF8String: name.constData()]
port: port];
initWithDomain:NSStringFromQString(domain)
type:NSStringFromQString(type)
name:[NSString stringWithUTF8String:name.constData()]
port:port];
if (service) {
[service setDelegate: delegate_];
[service setDelegate:delegate_];
[service publish];
}
}

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

@ -71,7 +71,7 @@ PlaylistListContainer::PlaylistListContainer(QWidget* parent)
action_new_folder_->setText(tr("New folder"));
action_remove_->setText(tr("Delete"));
action_save_playlist_->setText(tr("Save playlist"));
action_save_playlist_->setText(tr("Save playlist", "Save playlist menu action."));
ui_->new_folder->setDefaultAction(action_new_folder_);
ui_->remove->setDefaultAction(action_remove_);

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);
@ -237,7 +237,7 @@ void PlaylistManager::SaveWithUI(int id, const QString& suggested_filename) {
QString default_filter = parser()->default_filter();
filename = QFileDialog::getSaveFileName(
NULL, tr("Save playlist"), filename,
NULL, tr("Save playlist", "Title of the playlist save dialog."), filename,
parser()->filters(), &default_filter);
if (filename.isNull())

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

@ -62,7 +62,7 @@ void PodcastBackend::Subscribe(Podcast* podcast) {
// Update the IDs of any episodes.
PodcastEpisodeList* episodes = podcast->mutable_episodes();
for (PodcastEpisodeList::iterator it = episodes->begin() ; it != episodes->end() ; ++it) {
for (auto it = episodes->begin() ; it != episodes->end() ; ++it) {
it->set_podcast_database_id(database_id);
}
@ -107,7 +107,7 @@ void PodcastBackend::AddEpisodes(PodcastEpisodeList* episodes, QSqlDatabase* db)
QSqlQuery q("INSERT INTO podcast_episodes (" + PodcastEpisode::kColumnSpec + ")"
" VALUES (" + PodcastEpisode::kBindSpec + ")", *db);
for (PodcastEpisodeList::iterator it = episodes->begin() ; it != episodes->end() ; ++it) {
for (auto it = episodes->begin() ; it != episodes->end() ; ++it) {
it->BindToQuery(&q);
q.exec();
if (db_->CheckErrors(q))
@ -141,7 +141,7 @@ void PodcastBackend::UpdateEpisodes(const PodcastEpisodeList& episodes) {
" local_url = :local_url"
" WHERE ROWID = :id", db);
foreach (const PodcastEpisode& episode, episodes) {
for (const PodcastEpisode& episode : episodes) {
q.bindValue(":listened", episode.listened());
q.bindValue(":listened_date", episode.listened_date().toTime_t());
q.bindValue(":downloaded", episode.downloaded());
@ -313,3 +313,26 @@ PodcastEpisodeList PodcastBackend::GetOldDownloadedEpisodes(const QDateTime& max
return ret;
}
PodcastEpisodeList PodcastBackend::GetNewDownloadedEpisodes() {
PodcastEpisodeList ret;
QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect());
QSqlQuery q("SELECT ROWID, " + PodcastEpisode::kColumnSpec +
" FROM podcast_episodes"
" WHERE downloaded = 'true'"
" AND listened = 'false'", db);
q.exec();
if (db_->CheckErrors(q))
return ret;
while (q.next()) {
PodcastEpisode episode;
episode.InitFromQuery(q);
ret << episode;
}
return ret;
}

View File

@ -59,6 +59,7 @@ public:
// last listened to before the given QDateTime. This query is NOT indexed so
// it involves a full search of the table.
PodcastEpisodeList GetOldDownloadedEpisodes(const QDateTime& max_listened_date);
PodcastEpisodeList GetNewDownloadedEpisodes();
// Adds episodes to the database. Every episode must have a valid
// podcast_database_id set already.
@ -73,10 +74,10 @@ signals:
void SubscriptionRemoved(const Podcast& podcast);
// Emitted when episodes are added to a subscription that *already exists*.
void EpisodesAdded(const QList<PodcastEpisode>& episodes);
void EpisodesAdded(const PodcastEpisodeList& episodes);
// Emitted when existing episodes are updated.
void EpisodesUpdated(const QList<PodcastEpisode>& episodes);
void EpisodesUpdated(const PodcastEpisodeList& episodes);
private:
// Adds each episode to the database, setting their IDs after inserting each

View File

@ -20,6 +20,7 @@
#include "core/application.h"
#include "core/logging.h"
#include "core/network.h"
#include "core/tagreaderclient.h"
#include "core/timeconstants.h"
#include "core/utilities.h"
#include "library/librarydirectorymodel.h"
@ -55,8 +56,8 @@ PodcastDownloader::PodcastDownloader(Application* app, QObject* parent)
last_progress_signal_(0),
auto_delete_timer_(new QTimer(this))
{
connect(backend_, SIGNAL(EpisodesAdded(QList<PodcastEpisode>)),
SLOT(EpisodesAdded(QList<PodcastEpisode>)));
connect(backend_, SIGNAL(EpisodesAdded(PodcastEpisodeList)),
SLOT(EpisodesAdded(PodcastEpisodeList)));
connect(backend_, SIGNAL(SubscriptionAdded(Podcast)),
SLOT(SubscriptionAdded(Podcast)));
connect(app_, SIGNAL(SettingsChanged()), SLOT(ReloadSettings()));
@ -125,9 +126,15 @@ void PodcastDownloader::DeleteEpisode(const PodcastEpisode& episode) {
}
void PodcastDownloader::FinishAndDelete(Task* task) {
Podcast podcast =
backend_->GetSubscriptionById(task->episode.podcast_database_id());
Song song = task->episode.ToSong(podcast);
downloading_episode_ids_.remove(task->episode.database_id());
emit ProgressChanged(task->episode, Finished, 0);
// I didn't ecountered even a single podcast with a corect metadata
TagReaderClient::Instance()->SaveFileBlocking(task->file->fileName(), song);
delete task;
NextTask();
@ -137,17 +144,17 @@ QString PodcastDownloader::FilenameForEpisode(const QString& directory,
const PodcastEpisode& episode) const {
const QString file_extension = QFileInfo(episode.url().path()).suffix();
int count = 0;
// The file name contains the publication date and episode title
QString base_filename =
episode.publication_date().date().toString(Qt::ISODate) + "-" +
SanitiseFilenameComponent(episode.title());
// Add numbers on to the end of the filename until we find one that doesn't
// exist.
forever {
QString filename;
if (count == 0) {
filename = QString("%1/%2.%3").arg(
directory, base_filename, file_extension);
@ -155,12 +162,12 @@ QString PodcastDownloader::FilenameForEpisode(const QString& directory,
filename = QString("%1/%2 (%3).%4").arg(
directory, base_filename, QString::number(count), file_extension);
}
if (!QFile::exists(filename)) {
return filename;
}
count ++;
count++;
}
}
@ -197,8 +204,8 @@ void PodcastDownloader::StartDownloading(Task* task) {
RedirectFollower* reply = new RedirectFollower(network_->get(req));
connect(reply, SIGNAL(readyRead()), SLOT(ReplyReadyRead()));
connect(reply, SIGNAL(finished()), SLOT(ReplyFinished()));
connect(reply, SIGNAL(downloadProgress(qint64,qint64)),
SLOT(ReplyDownloadProgress(qint64,qint64)));
connect(reply, SIGNAL(downloadProgress(qint64, qint64)),
SLOT(ReplyDownloadProgress(qint64, qint64)));
emit ProgressChanged(task->episode, Downloading, 0);
}
@ -235,7 +242,7 @@ void PodcastDownloader::ReplyDownloadProgress(qint64 received, qint64 total) {
last_progress_signal_ = current_time;
emit ProgressChanged(current_task_->episode, Downloading,
float(received) / total * 100);
static_cast<float>(received) / total * 100);
}
void PodcastDownloader::ReplyFinished() {
@ -275,9 +282,9 @@ void PodcastDownloader::SubscriptionAdded(const Podcast& podcast) {
EpisodesAdded(podcast.episodes());
}
void PodcastDownloader::EpisodesAdded(const QList<PodcastEpisode>& episodes) {
void PodcastDownloader::EpisodesAdded(const PodcastEpisodeList& episodes) {
if (auto_download_) {
foreach (const PodcastEpisode& episode, episodes) {
for (const PodcastEpisode& episode : episodes) {
DownloadEpisode(episode);
}
}
@ -300,7 +307,7 @@ void PodcastDownloader::AutoDelete() {
<< (delete_after_secs_ / kSecsPerDay)
<< "days ago";
foreach (const PodcastEpisode& episode, old_episodes) {
for (const PodcastEpisode& episode : old_episodes) {
DeleteEpisode(episode);
}
}

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

@ -174,7 +174,9 @@ Song PodcastEpisode::ToSong(const Podcast& podcast) const {
if (podcast.is_valid()) {
ret.set_album(podcast.title().simplified());
ret.set_art_automatic(podcast.ImageUrlLarge().toString());
}
if (author().isEmpty())
ret.set_artist(podcast.title().simplified());
}
return ret;
}

View File

@ -25,9 +25,14 @@
#include "core/application.h"
#include "core/logging.h"
#include "core/mergedproxymodel.h"
#include "devices/devicemanager.h"
#include "devices/devicestatefiltermodel.h"
#include "devices/deviceview.h"
#include "internet/internetmodel.h"
#include "library/libraryview.h"
#include "ui/iconloader.h"
#include "ui/organisedialog.h"
#include "ui/organiseerrordialog.h"
#include "ui/standarditemiconloader.h"
#include <QMenu>
@ -54,7 +59,8 @@ PodcastService::PodcastService(Application* app, InternetModel* parent)
model_(new PodcastServiceModel(this)),
proxy_(new PodcastSortProxyModel(this)),
context_menu_(NULL),
root_(NULL)
root_(NULL),
organise_dialog_(new OrganiseDialog(app_->task_manager()))
{
icon_loader_->SetModel(model_);
proxy_->setSourceModel(model_);
@ -63,8 +69,8 @@ PodcastService::PodcastService(Application* app, InternetModel* parent)
connect(backend_, SIGNAL(SubscriptionAdded(Podcast)), SLOT(SubscriptionAdded(Podcast)));
connect(backend_, SIGNAL(SubscriptionRemoved(Podcast)), SLOT(SubscriptionRemoved(Podcast)));
connect(backend_, SIGNAL(EpisodesAdded(QList<PodcastEpisode>)), SLOT(EpisodesAdded(QList<PodcastEpisode>)));
connect(backend_, SIGNAL(EpisodesUpdated(QList<PodcastEpisode>)), SLOT(EpisodesUpdated(QList<PodcastEpisode>)));
connect(backend_, SIGNAL(EpisodesAdded(PodcastEpisodeList)), SLOT(EpisodesAdded(PodcastEpisodeList)));
connect(backend_, SIGNAL(EpisodesUpdated(PodcastEpisodeList)), SLOT(EpisodesUpdated(PodcastEpisodeList)));
connect(app_->playlist_manager(), SIGNAL(CurrentSongChanged(Song)), SLOT(CurrentSongChanged(Song)));
}
@ -112,6 +118,59 @@ QStandardItem* PodcastService::CreateRootItem() {
return root_;
}
void PodcastService::CopyToDevice() {
if (selected_episodes_.isEmpty() && explicitly_selected_podcasts_.isEmpty()) {
CopyToDevice(backend_->GetNewDownloadedEpisodes());
} else {
CopyToDevice(selected_episodes_, explicitly_selected_podcasts_);
}
}
void PodcastService::CopyToDevice(const PodcastEpisodeList& episodes_list) {
SongList songs;
Podcast podcast;
for (const PodcastEpisode& episode : episodes_list) {
podcast = backend_->GetSubscriptionById(episode.podcast_database_id());
songs.append(episode.ToSong(podcast));
}
organise_dialog_->SetDestinationModel(app_->device_manager()->connected_devices_model(), true);
organise_dialog_->SetCopy(true);
if (organise_dialog_->SetSongs(songs))
organise_dialog_->show();
}
void PodcastService::CopyToDevice(const QModelIndexList& episode_indexes,
const QModelIndexList& podcast_indexes) {
PodcastEpisode episode_tmp;
SongList songs;
PodcastEpisodeList episodes;
Podcast podcast;
for (const QModelIndex& index : episode_indexes) {
episode_tmp = index.data(Role_Episode).value<PodcastEpisode>();
if (episode_tmp.downloaded())
episodes << episode_tmp;
}
for (const QModelIndex& podcast : podcast_indexes) {
for (int i = 0; i < podcast.model()->rowCount(podcast); ++i) {
const QModelIndex& index = podcast.child(i, 0);
episode_tmp = index.data(Role_Episode).value<PodcastEpisode>();
if (episode_tmp.downloaded() && !episode_tmp.listened())
episodes << episode_tmp;
}
}
for (const PodcastEpisode& episode : episodes) {
podcast = backend_->GetSubscriptionById(episode.podcast_database_id());
songs.append(episode.ToSong(podcast));
}
organise_dialog_->SetDestinationModel(app_->device_manager()->connected_devices_model(), true);
organise_dialog_->SetCopy(true);
if (organise_dialog_->SetSongs(songs))
organise_dialog_->show();
}
void PodcastService::LazyPopulate(QStandardItem* parent) {
switch (parent->data(InternetModel::Role_Type).toInt()) {
case InternetModel::Type_Service:
@ -124,14 +183,14 @@ void PodcastService::LazyPopulate(QStandardItem* parent) {
void PodcastService::PopulatePodcastList(QStandardItem* parent) {
// Do this here since the downloader won't be created yet in the ctor.
connect(app_->podcast_downloader(),
SIGNAL(ProgressChanged(PodcastEpisode,PodcastDownloader::State,int)),
SLOT(DownloadProgressChanged(PodcastEpisode,PodcastDownloader::State,int)));
SIGNAL(ProgressChanged(PodcastEpisode, PodcastDownloader::State, int)),
SLOT(DownloadProgressChanged(PodcastEpisode, PodcastDownloader::State, int)));
if (default_icon_.isNull()) {
default_icon_ = QIcon(":providers/podcast16.png");
}
foreach (const Podcast& podcast, backend_->GetAllSubscriptions()) {
for (const Podcast& podcast : backend_->GetAllSubscriptions()) {
parent->appendRow(CreatePodcastItem(podcast));
}
}
@ -211,9 +270,9 @@ QStandardItem* PodcastService::CreatePodcastItem(const Podcast& podcast) {
// Add the episodes in this podcast and gather aggregate stats.
int unlistened_count = 0;
foreach (const PodcastEpisode& episode, backend_->GetEpisodes(podcast.database_id())) {
for (const PodcastEpisode& episode : backend_->GetEpisodes(podcast.database_id())) {
if (!episode.listened()) {
unlistened_count ++;
unlistened_count++;
}
item->appendRow(CreatePodcastEpisodeItem(episode));
@ -259,7 +318,7 @@ void PodcastService::ShowContextMenu(const QPoint& global_pos) {
context_menu_->addAction(
IconLoader::Load("view-refresh"), tr("Update all podcasts"),
app_->podcast_updater(), SLOT(UpdateAllPodcastsNow()));
context_menu_->addSeparator();
context_menu_->addActions(GetPlaylistActions());
@ -273,6 +332,9 @@ void PodcastService::ShowContextMenu(const QPoint& global_pos) {
delete_downloaded_action_ = context_menu_->addAction(
IconLoader::Load("edit-delete"), tr("Delete downloaded data"),
this, SLOT(DeleteDownloadedData()));
copy_to_device_ = context_menu_->addAction(
IconLoader::Load("multimedia-player-ipod-mini-blue"), tr("Copy to device..."),
this, SLOT(CopyToDevice()));
remove_selected_action_ = context_menu_->addAction(
IconLoader::Load("list-remove"), tr("Unsubscribe"),
this, SLOT(RemoveSelectedPodcast()));
@ -287,6 +349,10 @@ void PodcastService::ShowContextMenu(const QPoint& global_pos) {
context_menu_->addAction(
IconLoader::Load("configure"), tr("Configure podcasts..."),
this, SLOT(ShowConfig()));
copy_to_device_->setDisabled(app_->device_manager()->connected_devices_model()->rowCount() == 0);
connect(app_->device_manager()->connected_devices_model(), SIGNAL(IsEmptyChanged(bool)),
copy_to_device_, SLOT(setDisabled(bool)));
}
selected_episodes_.clear();
@ -294,7 +360,7 @@ void PodcastService::ShowContextMenu(const QPoint& global_pos) {
explicitly_selected_podcasts_.clear();
QSet<int> podcast_ids;
foreach (const QModelIndex& index, model()->selected_indexes()) {
for (const QModelIndex& index : model()->selected_indexes()) {
switch (index.data(InternetModel::Role_Type).toInt()) {
case Type_Podcast: {
const int id = index.data(Role_Podcast).value<Podcast>().database_id();
@ -346,12 +412,17 @@ void PodcastService::ShowContextMenu(const QPoint& global_pos) {
delete_downloaded_action_->setEnabled(episodes);
}
if (explicitly_selected_podcasts_.isEmpty() && selected_episodes_.isEmpty()) {
PodcastEpisodeList epis = backend_->GetNewDownloadedEpisodes();
set_listened_action_->setEnabled(!epis.isEmpty());
}
if (selected_episodes_.count() > 1) {
download_selected_action_->setText(tr("Download %n episodes", "", selected_episodes_.count()));
} else {
download_selected_action_->setText(tr("Download this episode"));
}
GetAppendToPlaylistAction()->setEnabled(episodes || podcasts);
GetReplacePlaylistAction()->setEnabled(episodes || podcasts);
GetOpenInNewPlaylistAction()->setEnabled(episodes || podcasts);
@ -360,14 +431,14 @@ void PodcastService::ShowContextMenu(const QPoint& global_pos) {
}
void PodcastService::UpdateSelectedPodcast() {
foreach (const QModelIndex& index, selected_podcasts_) {
for (const QModelIndex& index : selected_podcasts_) {
app_->podcast_updater()->UpdatePodcastNow(
index.data(Role_Podcast).value<Podcast>());
}
}
void PodcastService::RemoveSelectedPodcast() {
foreach (const QModelIndex& index, selected_podcasts_) {
for (const QModelIndex& index : selected_podcasts_) {
backend_->Unsubscribe(index.data(Role_Podcast).value<Podcast>());
}
}
@ -394,7 +465,7 @@ void PodcastService::AddPodcast() {
void PodcastService::SubscriptionAdded(const Podcast& podcast) {
// Ensure the root item is lazy loaded already
LazyLoadRoot();
// The podcast might already be in the list - maybe the LazyLoadRoot() above
// added it.
QStandardItem* item = podcasts_by_database_id_[podcast.database_id()];
@ -410,7 +481,7 @@ void PodcastService::SubscriptionRemoved(const Podcast& podcast) {
QStandardItem* item = podcasts_by_database_id_.take(podcast.database_id());
if (item) {
// Remove any episode ID -> item mappings for the episodes in this podcast.
for (int i=0 ; i<item->rowCount() ; ++i) {
for (int i = 0; i < item->rowCount(); ++i) {
QStandardItem* episode_item = item->child(i);
const int episode_id =
episode_item->data(Role_Episode).value<PodcastEpisode>().database_id();
@ -423,10 +494,10 @@ void PodcastService::SubscriptionRemoved(const Podcast& podcast) {
}
}
void PodcastService::EpisodesAdded(const QList<PodcastEpisode>& episodes) {
void PodcastService::EpisodesAdded(const PodcastEpisodeList& episodes) {
QSet<int> seen_podcast_ids;
foreach (const PodcastEpisode& episode, episodes) {
for (const PodcastEpisode& episode : episodes) {
const int database_id = episode.podcast_database_id();
QStandardItem* parent = podcasts_by_database_id_[database_id];
if (!parent)
@ -437,9 +508,9 @@ void PodcastService::EpisodesAdded(const QList<PodcastEpisode>& episodes) {
if (!seen_podcast_ids.contains(database_id)) {
// Update the unlistened count text once for each podcast
int unlistened_count = 0;
foreach (const PodcastEpisode& episode, backend_->GetEpisodes(database_id)) {
for (const PodcastEpisode& episode : backend_->GetEpisodes(database_id)) {
if (!episode.listened()) {
unlistened_count ++;
unlistened_count++;
}
}
@ -449,10 +520,10 @@ void PodcastService::EpisodesAdded(const QList<PodcastEpisode>& episodes) {
}
}
void PodcastService::EpisodesUpdated(const QList<PodcastEpisode>& episodes) {
void PodcastService::EpisodesUpdated(const PodcastEpisodeList& episodes) {
QSet<int> seen_podcast_ids;
foreach (const PodcastEpisode& episode, episodes) {
for (const PodcastEpisode& episode : episodes) {
const int podcast_database_id = episode.podcast_database_id();
QStandardItem* item = episodes_by_database_id_[episode.database_id()];
QStandardItem* parent = podcasts_by_database_id_[podcast_database_id];
@ -467,9 +538,9 @@ void PodcastService::EpisodesUpdated(const QList<PodcastEpisode>& episodes) {
if (!seen_podcast_ids.contains(podcast_database_id)) {
// Update the unlistened count text once for each podcast
int unlistened_count = 0;
foreach (const PodcastEpisode& episode, backend_->GetEpisodes(podcast_database_id)) {
for (const PodcastEpisode& episode : backend_->GetEpisodes(podcast_database_id)) {
if (!episode.listened()) {
unlistened_count ++;
unlistened_count++;
}
}
@ -480,14 +551,14 @@ void PodcastService::EpisodesUpdated(const QList<PodcastEpisode>& episodes) {
}
void PodcastService::DownloadSelectedEpisode() {
foreach (const QModelIndex& index, selected_episodes_) {
for (const QModelIndex& index : selected_episodes_) {
app_->podcast_downloader()->DownloadEpisode(
index.data(Role_Episode).value<PodcastEpisode>());
}
}
void PodcastService::DeleteDownloadedData() {
foreach (const QModelIndex& index, selected_episodes_) {
for (const QModelIndex& index : selected_episodes_) {
app_->podcast_downloader()->DeleteEpisode(
index.data(Role_Episode).value<PodcastEpisode>());
}
@ -526,7 +597,25 @@ void PodcastService::SetNew() {
}
void PodcastService::SetListened() {
SetListened(selected_episodes_, explicitly_selected_podcasts_, true);
if (selected_episodes_.isEmpty() && explicitly_selected_podcasts_.isEmpty())
SetListened(backend_->GetNewDownloadedEpisodes(), true);
else
SetListened(selected_episodes_, explicitly_selected_podcasts_, true);
}
void PodcastService::SetListened(const PodcastEpisodeList& episodes_list,
bool listened) {
PodcastEpisodeList episodes;
QDateTime current_date_time = QDateTime::currentDateTime();
for (PodcastEpisode episode : episodes_list) {
episode.set_listened(listened);
if (listened) {
episode.set_listened_date(current_date_time);
}
episodes << episode;
}
backend_->UpdateEpisodes(episodes);
}
void PodcastService::SetListened(const QModelIndexList& episode_indexes,
@ -535,12 +624,12 @@ void PodcastService::SetListened(const QModelIndexList& episode_indexes,
PodcastEpisodeList episodes;
// Get all the episodes from the indexes.
foreach (const QModelIndex& index, episode_indexes) {
for (const QModelIndex& index : episode_indexes) {
episodes << index.data(Role_Episode).value<PodcastEpisode>();
}
foreach (const QModelIndex& podcast, podcast_indexes) {
for (int i=0 ; i<podcast.model()->rowCount(podcast) ; ++i) {
for (const QModelIndex& podcast : podcast_indexes) {
for (int i = 0; i < podcast.model()->rowCount(podcast); ++i) {
const QModelIndex& index = podcast.child(i, 0);
episodes << index.data(Role_Episode).value<PodcastEpisode>();
}
@ -548,7 +637,7 @@ void PodcastService::SetListened(const QModelIndexList& episode_indexes,
// Update each one with the new state and maybe the listened time.
QDateTime current_date_time = QDateTime::currentDateTime();
for (int i=0 ; i<episodes.count() ; ++i) {
for (int i = 0; i < episodes.count(); ++i) {
PodcastEpisode* episode = &episodes[i];
episode->set_listened(listened);
if (listened) {

View File

@ -22,9 +22,11 @@
#include "internet/internetmodel.h"
#include "internet/internetservice.h"
#include <memory>
#include <QScopedPointer>
class AddPodcastDialog;
class OrganiseDialog;
class Podcast;
class PodcastBackend;
class PodcastEpisode;
@ -78,8 +80,8 @@ private slots:
void SubscriptionAdded(const Podcast& podcast);
void SubscriptionRemoved(const Podcast& podcast);
void EpisodesAdded(const QList<PodcastEpisode>& episodes);
void EpisodesUpdated(const QList<PodcastEpisode>& episodes);
void EpisodesAdded(const PodcastEpisodeList& episodes);
void EpisodesUpdated(const PodcastEpisodeList& episodes);
void DownloadProgressChanged(const PodcastEpisode& episode,
PodcastDownloader::State state,
@ -87,6 +89,11 @@ private slots:
void CurrentSongChanged(const Song& metadata);
void CopyToDevice();
void CopyToDevice(const PodcastEpisodeList& episodes_list);
void CopyToDevice(const QModelIndexList& episode_indexes,
const QModelIndexList& podcast_indexes);
private:
void EnsureAddPodcastDialogCreated();
@ -104,7 +111,9 @@ private:
void SetListened(const QModelIndexList& episode_indexes,
const QModelIndexList& podcast_indexes,
bool listened);
void SetListened(const PodcastEpisodeList& episodes_list,
bool listened);
void LazyLoadRoot();
private:
@ -130,7 +139,9 @@ private:
QAction* delete_downloaded_action_;
QAction* set_new_action_;
QAction* set_listened_action_;
QAction* copy_to_device_;
QStandardItem* root_;
std::unique_ptr<OrganiseDialog> organise_dialog_;
QModelIndexList explicitly_selected_podcasts_;
QModelIndexList selected_podcasts_;

View File

@ -51,10 +51,10 @@ QUrl PodcastUrlLoader::FixPodcastUrl(const QString& url_text) {
// Thanks gpodder!
QuickPrefixList quick_prefixes = QuickPrefixList()
<< QuickPrefix("fb:", "http://feeds.feedburner.com/%1")
<< QuickPrefix("yt:", "http://www.youtube.com/rss/user/%1/videos.rss")
<< QuickPrefix("sc:", "http://soundcloud.com/%1")
<< QuickPrefix("yt:", "https://www.youtube.com/rss/user/%1/videos.rss")
<< QuickPrefix("sc:", "https://soundcloud.com/%1")
<< QuickPrefix("fm4od:", "http://onapp1.orf.at/webcam/fm4/fod/%1.xspf")
<< QuickPrefix("ytpl:", "http://gdata.youtube.com/feeds/api/playlists/%1");
<< QuickPrefix("ytpl:", "https://gdata.youtube.com/feeds/api/playlists/%1");
// Check if it matches one of the quick prefixes.
for (QuickPrefixList::const_iterator it = quick_prefixes.constBegin() ;

View File

@ -31,7 +31,7 @@
const char* SongkickConcerts::kSongkickArtistBucket = "id:songkick";
const char* SongkickConcerts::kSongkickArtistCalendarUrl =
"http://api.songkick.com/api/3.0/artists/%1/calendar.json?"
"https://api.songkick.com/api/3.0/artists/%1/calendar.json?"
"per_page=5&"
"apikey=8rgKfy1WU6IlJFfN";

View File

@ -90,7 +90,7 @@ void SongKickConcertWidget::Init(const QString& title, const QString& url,
void SongKickConcertWidget::SetMap(const QString& lat, const QString& lng,
const QString& venue_name) {
static const char* kStaticMapUrl =
"http://maps.googleapis.com/maps/api/staticmap"
"https://maps.googleapis.com/maps/api/staticmap"
"?key=AIzaSyDDJqmLOeE1mY_EBONhnQmdXbKtasgCtqg"
"&sensor=false"
"&size=%1x%2"

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

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