2018-02-27 18:06:05 +01:00
/*
* Strawberry Music Player
* This file was part of Clementine .
* Copyright 2010 , David Sansome < me @ davidsansome . com >
2021-03-20 21:14:47 +01:00
* Copyright 2018 - 2021 , Jonas Kvinge < jonas @ jkvinge . net >
2018-02-27 18:06:05 +01:00
*
* Strawberry 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 .
*
* Strawberry 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 Strawberry . If not , see < http : //www.gnu.org/licenses/>.
2018-08-09 18:39:44 +02:00
*
2018-02-27 18:06:05 +01:00
*/
# include "config.h"
# include <memory>
2020-06-14 23:54:18 +02:00
# include <cstdlib>
2019-07-14 03:08:19 +02:00
# include <iconv.h>
2018-02-27 18:06:05 +01:00
2018-05-01 00:41:33 +02:00
# include <QtGlobal>
2018-02-27 18:06:05 +01:00
# include <QApplication>
2018-05-01 00:41:33 +02:00
# include <QCoreApplication>
2021-05-12 20:08:06 +02:00
# include <QWindow>
2018-05-01 00:41:33 +02:00
# include <QWidget>
# include <QObject>
# include <QIODevice>
# include <QByteArray>
# include <QMetaObject>
# include <QChar>
# include <QCryptographicHash>
# include <QDate>
2018-02-27 18:06:05 +01:00
# include <QDateTime>
# include <QDesktopServices>
# include <QDir>
# include <QFile>
2018-05-01 00:41:33 +02:00
# include <QFileInfo>
2020-03-15 01:21:30 +01:00
# include <QProcess>
2020-02-08 03:40:30 +01:00
# include <QList>
# include <QMap>
2018-05-01 00:41:33 +02:00
# include <QString>
2018-02-27 18:06:05 +01:00
# include <QStringList>
2020-02-08 03:40:30 +01:00
# include <QUrl>
2020-07-18 04:05:07 +02:00
# include <QRegularExpression>
2020-07-20 00:57:42 +02:00
# include <QRegularExpressionMatch>
2020-02-08 03:40:30 +01:00
# include <QSize>
2020-04-27 00:22:46 +02:00
# include <QColor>
2021-05-12 20:08:06 +02:00
# include <QRegion>
2018-05-01 00:41:33 +02:00
# include <QMetaEnum>
2018-02-27 18:06:05 +01:00
# include <QXmlStreamReader>
2018-05-01 00:41:33 +02:00
# include <QSettings>
# include <QtEvents>
2019-06-20 17:00:10 +02:00
# include <QMessageBox>
2019-09-22 17:04:57 +02:00
# include <QNetworkInterface>
2021-02-26 21:03:51 +01:00
# include <QMimeDatabase>
2018-05-01 00:41:33 +02:00
# include <QtDebug>
2020-05-29 17:37:46 +02:00
# if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
# include <QRandomGenerator>
# endif
2018-02-27 18:06:05 +01:00
2020-06-14 23:54:18 +02:00
# include <cstdio>
2020-02-08 03:40:30 +01:00
2018-02-27 18:06:05 +01:00
# ifdef Q_OS_LINUX
2018-07-01 22:26:46 +02:00
# include <unistd.h>
# include <sys / syscall.h>
2018-02-27 18:06:05 +01:00
# endif
2018-07-01 22:26:46 +02:00
# ifdef Q_OS_MACOS
# include <sys / resource.h>
# include <sys / sysctl.h>
# include <sys / param.h>
2018-02-27 18:06:05 +01:00
# endif
# if defined(Q_OS_UNIX)
2018-07-01 22:26:46 +02:00
# include <sys / statvfs.h>
# elif defined(Q_OS_WIN)
# include <windows.h>
2021-05-12 20:08:06 +02:00
# include <dwmapi.h>
2018-02-27 18:06:05 +01:00
# endif
2018-07-01 22:26:46 +02:00
# ifdef Q_OS_MACOS
# include "CoreServices / CoreServices.h"
# include "IOKit / ps / IOPSKeys.h"
# include "IOKit / ps / IOPowerSources.h"
2018-02-27 18:06:05 +01:00
# endif
# include "core/logging.h"
2019-12-22 12:09:05 +01:00
# include "core/song.h"
2018-05-01 00:41:33 +02:00
# include "utilities.h"
# include "timeconstants.h"
# include "application.h"
2018-02-27 18:06:05 +01:00
2018-07-01 22:26:46 +02:00
# ifdef Q_OS_MACOS
# include "mac_startup.h"
# include "mac_utilities.h"
# include "scoped_cftyperef.h"
2018-02-27 18:06:05 +01:00
# endif
namespace Utilities {
static QString tr ( const char * str ) {
return QCoreApplication : : translate ( " " , str ) ;
}
2021-06-20 19:04:08 +02:00
QString PrettyTimeDelta ( const int seconds ) {
2018-02-27 18:06:05 +01:00
return ( seconds > = 0 ? " + " : " - " ) + PrettyTime ( seconds ) ;
}
QString PrettyTime ( int seconds ) {
2018-04-06 22:13:11 +02:00
// last.fm sometimes gets the track length wrong, so you end up with negative times.
2018-02-27 18:06:05 +01:00
seconds = qAbs ( seconds ) ;
int hours = seconds / ( 60 * 60 ) ;
int minutes = ( seconds / 60 ) % 60 ;
seconds % = 60 ;
QString ret ;
2021-06-22 13:54:58 +02:00
if ( hours > 0 ) ret = QString : : asprintf ( " %d:%02d:%02d " , hours , minutes , seconds ) ;
2019-12-21 21:56:48 +01:00
else ret = QString : : asprintf ( " %d:%02d " , minutes , seconds ) ;
2018-02-27 18:06:05 +01:00
return ret ;
}
2021-06-20 19:04:08 +02:00
QString PrettyTimeNanosec ( const qint64 nanoseconds ) {
2021-03-21 18:53:02 +01:00
return PrettyTime ( static_cast < int > ( nanoseconds / kNsecPerSec ) ) ;
2018-02-27 18:06:05 +01:00
}
2021-06-20 19:04:08 +02:00
QString WordyTime ( const quint64 seconds ) {
2018-02-27 18:06:05 +01:00
quint64 days = seconds / ( 60 * 60 * 24 ) ;
// TODO: Make the plural rules translatable
QStringList parts ;
2021-06-22 13:54:58 +02:00
if ( days > 0 ) parts < < ( days = = 1 ? tr ( " 1 day " ) : tr ( " %1 days " ) . arg ( days ) ) ;
2021-03-21 18:53:02 +01:00
parts < < PrettyTime ( static_cast < int > ( seconds - days * 60 * 60 * 24 ) ) ;
2018-02-27 18:06:05 +01:00
return parts . join ( " " ) ;
}
2021-09-12 21:24:22 +02:00
QString WordyTimeNanosec ( const quint64 nanoseconds ) {
2018-02-27 18:06:05 +01:00
return WordyTime ( nanoseconds / kNsecPerSec ) ;
}
2021-06-20 19:04:08 +02:00
QString Ago ( const qint64 seconds_since_epoch , const QLocale & locale ) {
2018-02-27 18:06:05 +01:00
const QDateTime now = QDateTime : : currentDateTime ( ) ;
2020-07-18 04:05:07 +02:00
const QDateTime then = QDateTime : : fromSecsSinceEpoch ( seconds_since_epoch ) ;
2021-02-10 18:27:40 +01:00
const qint64 days_ago = then . date ( ) . daysTo ( now . date ( ) ) ;
2018-02-27 18:06:05 +01:00
const QString time = then . time ( ) . toString ( locale . timeFormat ( QLocale : : ShortFormat ) ) ;
if ( days_ago = = 0 ) return tr ( " Today " ) + " " + time ;
if ( days_ago = = 1 ) return tr ( " Yesterday " ) + " " + time ;
if ( days_ago < = 7 ) return tr ( " %1 days ago " ) . arg ( days_ago ) ;
return then . date ( ) . toString ( locale . dateFormat ( QLocale : : ShortFormat ) ) ;
}
2021-06-20 19:04:08 +02:00
QString PrettyFutureDate ( const QDate date ) {
2018-02-27 18:06:05 +01:00
const QDate now = QDate : : currentDate ( ) ;
2021-02-10 18:27:40 +01:00
const qint64 delta_days = now . daysTo ( date ) ;
2018-02-27 18:06:05 +01:00
if ( delta_days < 0 ) return QString ( ) ;
if ( delta_days = = 0 ) return tr ( " Today " ) ;
if ( delta_days = = 1 ) return tr ( " Tomorrow " ) ;
if ( delta_days < = 7 ) return tr ( " In %1 days " ) . arg ( delta_days ) ;
if ( delta_days < = 14 ) return tr ( " Next week " ) ;
return tr ( " In %1 weeks " ) . arg ( delta_days / 7 ) ;
}
2021-06-20 19:04:08 +02:00
QString PrettySize ( const quint64 bytes ) {
2018-02-27 18:06:05 +01:00
QString ret ;
if ( bytes > 0 ) {
2021-08-23 21:21:08 +02:00
if ( bytes < = 1000 ) {
2018-02-27 18:06:05 +01:00
ret = QString : : number ( bytes ) + " bytes " ;
2021-08-23 21:21:08 +02:00
}
else if ( bytes < = 1000 * 1000 ) {
2019-12-21 21:56:48 +01:00
ret = QString : : asprintf ( " %.1f KB " , float ( bytes ) / 1000 ) ;
2021-08-23 21:21:08 +02:00
}
else if ( bytes < = 1000 * 1000 * 1000 ) {
2019-12-21 21:56:48 +01:00
ret = QString : : asprintf ( " %.1f MB " , float ( bytes ) / ( 1000 * 1000 ) ) ;
2021-08-23 21:21:08 +02:00
}
else {
2019-12-21 21:56:48 +01:00
ret = QString : : asprintf ( " %.1f GB " , float ( bytes ) / ( 1000 * 1000 * 1000 ) ) ;
2021-08-23 21:21:08 +02:00
}
2018-02-27 18:06:05 +01:00
}
return ret ;
}
quint64 FileSystemCapacity ( const QString & path ) {
# if defined(Q_OS_UNIX)
2021-06-20 23:53:28 +02:00
struct statvfs fs_info { } ;
2018-02-27 18:06:05 +01:00
if ( statvfs ( path . toLocal8Bit ( ) . constData ( ) , & fs_info ) = = 0 )
return quint64 ( fs_info . f_blocks ) * quint64 ( fs_info . f_bsize ) ;
# elif defined(Q_OS_WIN32)
_ULARGE_INTEGER ret ;
2020-12-12 12:50:33 +01:00
ScopedWCharArray wchar ( QDir : : toNativeSeparators ( path ) ) ;
if ( GetDiskFreeSpaceEx ( wchar . get ( ) , nullptr , & ret , nullptr ) ! = 0 )
2018-02-27 18:06:05 +01:00
return ret . QuadPart ;
# endif
return 0 ;
}
quint64 FileSystemFreeSpace ( const QString & path ) {
# if defined(Q_OS_UNIX)
2021-07-11 09:49:38 +02:00
struct statvfs fs_info { } ;
2018-02-27 18:06:05 +01:00
if ( statvfs ( path . toLocal8Bit ( ) . constData ( ) , & fs_info ) = = 0 )
return quint64 ( fs_info . f_bavail ) * quint64 ( fs_info . f_bsize ) ;
# elif defined(Q_OS_WIN32)
_ULARGE_INTEGER ret ;
2020-12-12 12:50:33 +01:00
ScopedWCharArray wchar ( QDir : : toNativeSeparators ( path ) ) ;
if ( GetDiskFreeSpaceEx ( wchar . get ( ) , & ret , nullptr , nullptr ) ! = 0 )
2018-02-27 18:06:05 +01:00
return ret . QuadPart ;
# endif
return 0 ;
}
2020-08-19 22:02:35 +02:00
bool MoveToTrashRecursive ( const QString & path ) {
# if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
QDir dir ( path ) ;
for ( const QString & child : dir . entryList ( QDir : : NoDotAndDotDot | QDir : : Dirs | QDir : : Hidden ) ) {
2021-08-23 21:21:08 +02:00
if ( ! MoveToTrashRecursive ( path + " / " + child ) ) {
2020-08-19 22:02:35 +02:00
return false ;
2021-08-23 21:21:08 +02:00
}
2020-08-19 22:02:35 +02:00
}
for ( const QString & child : dir . entryList ( QDir : : NoDotAndDotDot | QDir : : Files | QDir : : Hidden ) ) {
2021-08-23 21:21:08 +02:00
if ( ! QFile : : moveToTrash ( path + " / " + child ) ) {
2020-08-19 22:02:35 +02:00
return false ;
2021-08-23 21:21:08 +02:00
}
2020-08-19 22:02:35 +02:00
}
return dir . rmdir ( path ) ;
# else
2020-09-20 14:55:52 +02:00
Q_UNUSED ( path )
2020-08-19 22:02:35 +02:00
return false ;
# endif
}
2018-02-27 18:06:05 +01:00
bool RemoveRecursive ( const QString & path ) {
QDir dir ( path ) ;
for ( const QString & child : dir . entryList ( QDir : : NoDotAndDotDot | QDir : : Dirs | QDir : : Hidden ) ) {
2021-08-23 21:21:08 +02:00
if ( ! RemoveRecursive ( path + " / " + child ) ) {
2018-02-27 18:06:05 +01:00
return false ;
2021-08-23 21:21:08 +02:00
}
2018-02-27 18:06:05 +01:00
}
for ( const QString & child : dir . entryList ( QDir : : NoDotAndDotDot | QDir : : Files | QDir : : Hidden ) ) {
2021-08-23 21:21:08 +02:00
if ( ! QFile : : remove ( path + " / " + child ) ) {
2018-02-27 18:06:05 +01:00
return false ;
2021-08-23 21:21:08 +02:00
}
2018-02-27 18:06:05 +01:00
}
2019-04-08 18:46:11 +02:00
return dir . rmdir ( path ) ;
2018-02-27 18:06:05 +01:00
}
bool CopyRecursive ( const QString & source , const QString & destination ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
// Make the destination directory
QString dir_name = source . section ( ' / ' , - 1 , - 1 ) ;
QString dest_path = destination + " / " + dir_name ;
QDir ( ) . mkpath ( dest_path ) ;
QDir dir ( source ) ;
for ( const QString & child : dir . entryList ( QDir : : NoDotAndDotDot | QDir : : Dirs ) ) {
if ( ! CopyRecursive ( source + " / " + child , dest_path ) ) {
qLog ( Warning ) < < " Failed to copy dir " < < source + " / " + child < < " to " < < dest_path ;
return false ;
}
}
for ( const QString & child : dir . entryList ( QDir : : NoDotAndDotDot | QDir : : Files ) ) {
if ( ! QFile : : copy ( source + " / " + child , dest_path + " / " + child ) ) {
qLog ( Warning ) < < " Failed to copy file " < < source + " / " + child < < " to " < < dest_path ;
return false ;
}
}
return true ;
}
bool Copy ( QIODevice * source , QIODevice * destination ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
if ( ! source - > open ( QIODevice : : ReadOnly ) ) return false ;
if ( ! destination - > open ( QIODevice : : WriteOnly ) ) return false ;
const qint64 bytes = source - > size ( ) ;
std : : unique_ptr < char [ ] > data ( new char [ bytes ] ) ;
qint64 pos = 0 ;
2021-03-26 21:30:13 +01:00
qint64 bytes_read = 0 ;
2018-02-27 18:06:05 +01:00
do {
bytes_read = source - > read ( data . get ( ) + pos , bytes - pos ) ;
if ( bytes_read = = - 1 ) return false ;
pos + = bytes_read ;
} while ( bytes_read > 0 & & pos ! = bytes ) ;
pos = 0 ;
2021-03-26 21:30:13 +01:00
qint64 bytes_written = 0 ;
2018-02-27 18:06:05 +01:00
do {
bytes_written = destination - > write ( data . get ( ) + pos , bytes - pos ) ;
if ( bytes_written = = - 1 ) return false ;
pos + = bytes_written ;
} while ( bytes_written > 0 & & pos ! = bytes ) ;
return true ;
}
QString ColorToRgba ( const QColor & c ) {
return QString ( " rgba(%1, %2, %3, %4) " )
. arg ( c . red ( ) )
. arg ( c . green ( ) )
. arg ( c . blue ( ) )
. arg ( c . alpha ( ) ) ;
}
2020-03-15 01:21:30 +01:00
# if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
2021-06-20 02:48:58 +02:00
void OpenInFileManager ( const QString & path , const QUrl & url ) ;
void OpenInFileManager ( const QString & path , const QUrl & url ) {
2020-07-26 15:10:00 +02:00
if ( ! url . isLocalFile ( ) ) return ;
2020-03-15 01:21:30 +01:00
QProcess proc ;
2021-06-20 02:48:58 +02:00
# if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
proc . startCommand ( " xdg-mime query default inode/directory " ) ;
# else
2020-03-15 01:21:30 +01:00
proc . start ( " xdg-mime " , QStringList ( ) < < " query " < < " default " < < " inode/directory " ) ;
2021-06-20 02:48:58 +02:00
# endif
2020-03-15 01:21:30 +01:00
proc . waitForFinished ( ) ;
QString desktop_file = proc . readLine ( ) . simplified ( ) ;
2021-06-20 19:04:08 +02:00
QStringList data_dirs = QString ( qgetenv ( " XDG_DATA_DIRS " ) ) . split ( " : " ) ;
2020-03-15 01:21:30 +01:00
QString command ;
QStringList command_params ;
for ( const QString & data_dir : data_dirs ) {
QString desktop_file_path = QString ( " %1/applications/%2 " ) . arg ( data_dir , desktop_file ) ;
if ( ! QFile : : exists ( desktop_file_path ) ) continue ;
QSettings setting ( desktop_file_path , QSettings : : IniFormat ) ;
setting . beginGroup ( " Desktop Entry " ) ;
if ( setting . contains ( " Exec " ) ) {
QString cmd = setting . value ( " Exec " ) . toString ( ) ;
if ( cmd . isEmpty ( ) ) break ;
2021-08-13 23:33:41 +02:00
cmd = cmd . remove ( QRegularExpression ( " [%][a-zA-Z]*( |$) " , QRegularExpression : : CaseInsensitiveOption ) ) ;
2021-03-29 22:45:01 +02:00
# if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
2021-03-29 22:37:55 +02:00
command_params = cmd . split ( ' ' , Qt : : SkipEmptyParts ) ;
2021-03-29 22:45:01 +02:00
# else
command_params = cmd . split ( ' ' , QString : : SkipEmptyParts ) ;
# endif
2020-03-15 01:21:30 +01:00
command = command_params . first ( ) ;
command_params . removeFirst ( ) ;
}
setting . endGroup ( ) ;
if ( ! command . isEmpty ( ) ) break ;
}
2020-05-14 22:19:26 +02:00
if ( command . startsWith ( " /usr/bin/ " ) ) {
command = command . split ( " / " ) . last ( ) ;
}
2020-03-15 01:21:30 +01:00
if ( command . isEmpty ( ) | | command = = " exo-open " ) {
2020-07-26 15:10:00 +02:00
QDesktopServices : : openUrl ( QUrl : : fromLocalFile ( path ) ) ;
2020-03-15 01:21:30 +01:00
}
else if ( command . startsWith ( " nautilus " ) ) {
2021-06-25 18:19:37 +02:00
proc . startDetached ( command , QStringList ( ) < < command_params < < " --select " < < url . toLocalFile ( ) ) ;
2020-03-15 01:21:30 +01:00
}
else if ( command . startsWith ( " dolphin " ) | | command . startsWith ( " konqueror " ) | | command . startsWith ( " kfmclient " ) ) {
2021-06-25 18:19:37 +02:00
proc . startDetached ( command , QStringList ( ) < < command_params < < " --select " < < " --new-window " < < url . toLocalFile ( ) ) ;
2020-03-15 01:21:30 +01:00
}
else if ( command . startsWith ( " caja " ) ) {
2021-06-25 18:19:37 +02:00
proc . startDetached ( command , QStringList ( ) < < command_params < < " --no-desktop " < < path ) ;
2020-03-15 01:21:30 +01:00
}
2021-04-01 18:31:19 +02:00
else if ( command . startsWith ( " pcmanfm " ) | | command . startsWith ( " thunar " ) ) {
2021-06-25 18:19:37 +02:00
proc . startDetached ( command , QStringList ( ) < < command_params < < path ) ;
2020-07-06 20:27:48 +02:00
}
2020-03-15 01:21:30 +01:00
else {
2021-06-25 18:19:37 +02:00
proc . startDetached ( command , QStringList ( ) < < command_params < < url . toLocalFile ( ) ) ;
2020-03-15 01:21:30 +01:00
}
}
# endif
2018-07-01 22:26:46 +02:00
# ifdef Q_OS_MACOS
2018-02-27 18:06:05 +01:00
// Better than openUrl(dirname(path)) - also highlights file at path
2020-07-26 15:10:00 +02:00
void RevealFileInFinder ( const QString & path ) {
2018-02-27 18:06:05 +01:00
QProcess : : execute ( " /usr/bin/open " , QStringList ( ) < < " -R " < < path ) ;
}
2018-07-01 22:26:46 +02:00
# endif // Q_OS_MACOS
2018-02-27 18:06:05 +01:00
# ifdef Q_OS_WIN
2020-07-26 15:10:00 +02:00
void ShowFileInExplorer ( const QString & path ) ;
void ShowFileInExplorer ( const QString & path ) {
2018-02-27 18:06:05 +01:00
QProcess : : execute ( " explorer.exe " , QStringList ( ) < < " /select, " < < QDir : : toNativeSeparators ( path ) ) ;
}
# endif
void OpenInFileBrowser ( const QList < QUrl > & urls ) {
2020-07-26 15:10:00 +02:00
QMap < QString , QUrl > dirs ;
for ( const QUrl & url : urls ) {
if ( ! url . isLocalFile ( ) ) {
continue ;
}
QString path = url . toLocalFile ( ) ;
if ( ! QFile : : exists ( path ) ) continue ;
const QString directory = QFileInfo ( path ) . dir ( ) . path ( ) ;
if ( dirs . contains ( directory ) ) continue ;
dirs . insert ( directory , url ) ;
}
if ( dirs . count ( ) > 50 ) {
2019-06-20 17:02:29 +02:00
QMessageBox messagebox ( QMessageBox : : Critical , tr ( " Show in file browser " ) , tr ( " Too many songs selected. " ) ) ;
2019-06-20 17:00:10 +02:00
messagebox . exec ( ) ;
return ;
}
2020-07-26 15:10:00 +02:00
if ( dirs . count ( ) > 5 ) {
QMessageBox messagebox ( QMessageBox : : Information , tr ( " Show in file browser " ) , tr ( " %1 songs in %2 different directories selected, are you sure you want to open them all? " ) . arg ( urls . count ( ) ) . arg ( dirs . count ( ) ) , QMessageBox : : Open | QMessageBox : : Cancel ) ;
2019-06-20 17:00:10 +02:00
messagebox . setTextFormat ( Qt : : RichText ) ;
int result = messagebox . exec ( ) ;
switch ( result ) {
2021-07-11 09:49:38 +02:00
case QMessageBox : : Open :
break ;
case QMessageBox : : Cancel :
default :
return ;
2019-06-20 17:00:10 +02:00
}
}
2020-07-26 15:10:00 +02:00
QMap < QString , QUrl > : : iterator i ;
for ( i = dirs . begin ( ) ; i ! = dirs . end ( ) ; + + i ) {
2020-03-15 01:21:30 +01:00
# if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
2020-07-26 15:10:00 +02:00
OpenInFileManager ( i . key ( ) , i . value ( ) ) ;
2020-03-15 02:00:13 +01:00
# elif defined(Q_OS_MACOS)
2018-05-01 00:41:33 +02:00
// Revealing multiple files in the finder only opens one window, so it also makes sense to reveal at most one per directory
2020-07-26 15:10:00 +02:00
RevealFileInFinder ( i . value ( ) . toLocalFile ( ) ) ;
2018-02-27 18:06:05 +01:00
# elif defined(Q_OS_WIN32)
2020-07-26 15:10:00 +02:00
ShowFileInExplorer ( i . value ( ) . toLocalFile ( ) ) ;
2018-02-27 18:06:05 +01:00
# endif
}
}
2021-07-07 23:54:21 +02:00
QByteArray Hmac ( const QByteArray & key , const QByteArray & data , const QCryptographicHash : : Algorithm method ) {
2018-02-27 18:06:05 +01:00
const int kBlockSize = 64 ; // bytes
Q_ASSERT ( key . length ( ) < = kBlockSize ) ;
QByteArray inner_padding ( kBlockSize , char ( 0x36 ) ) ;
QByteArray outer_padding ( kBlockSize , char ( 0x5c ) ) ;
for ( int i = 0 ; i < key . length ( ) ; + + i ) {
inner_padding [ i ] = inner_padding [ i ] ^ key [ i ] ;
outer_padding [ i ] = outer_padding [ i ] ^ key [ i ] ;
}
2021-07-07 23:54:21 +02:00
QByteArray part ;
part . append ( inner_padding ) ;
part . append ( data ) ;
QByteArray total ;
total . append ( outer_padding ) ;
total . append ( QCryptographicHash : : hash ( part , method ) ) ;
return QCryptographicHash : : hash ( total , method ) ;
2018-02-27 18:06:05 +01:00
}
QByteArray HmacSha256 ( const QByteArray & key , const QByteArray & data ) {
2021-07-07 23:54:21 +02:00
return Hmac ( key , data , QCryptographicHash : : Sha256 ) ;
2018-02-27 18:06:05 +01:00
}
QByteArray HmacMd5 ( const QByteArray & key , const QByteArray & data ) {
2021-07-07 23:54:21 +02:00
return Hmac ( key , data , QCryptographicHash : : Md5 ) ;
2018-02-27 18:06:05 +01:00
}
QByteArray HmacSha1 ( const QByteArray & key , const QByteArray & data ) {
2021-07-07 23:54:21 +02:00
return Hmac ( key , data , QCryptographicHash : : Sha1 ) ;
2018-02-27 18:06:05 +01:00
}
QByteArray Sha1CoverHash ( const QString & artist , const QString & album ) {
QCryptographicHash hash ( QCryptographicHash : : Sha1 ) ;
hash . addData ( artist . toLower ( ) . toUtf8 ( ) . constData ( ) ) ;
hash . addData ( album . toLower ( ) . toUtf8 ( ) . constData ( ) ) ;
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
return hash . result ( ) ;
}
2021-06-20 19:04:08 +02:00
QString PrettySize ( const QSize size ) {
2018-02-27 18:06:05 +01:00
return QString : : number ( size . width ( ) ) + " x " + QString : : number ( size . height ( ) ) ;
}
void ConsumeCurrentElement ( QXmlStreamReader * reader ) {
int level = 1 ;
while ( level ! = 0 & & ! reader - > atEnd ( ) ) {
switch ( reader - > readNext ( ) ) {
case QXmlStreamReader : : StartElement : + + level ; break ;
2021-07-11 09:49:38 +02:00
case QXmlStreamReader : : EndElement : - - level ; break ;
2018-02-27 18:06:05 +01:00
default : break ;
}
}
}
bool ParseUntilElement ( QXmlStreamReader * reader , const QString & name ) {
while ( ! reader - > atEnd ( ) ) {
QXmlStreamReader : : TokenType type = reader - > readNext ( ) ;
2021-06-21 15:44:13 +02:00
if ( type = = QXmlStreamReader : : StartElement & & reader - > name ( ) = = name ) {
return true ;
2018-02-27 18:06:05 +01:00
}
}
return false ;
}
2019-11-20 19:34:57 +01:00
bool ParseUntilElementCI ( QXmlStreamReader * reader , const QString & name ) {
while ( ! reader - > atEnd ( ) ) {
QXmlStreamReader : : TokenType type = reader - > readNext ( ) ;
2021-06-21 15:44:13 +02:00
if ( type = = QXmlStreamReader : : StartElement ) {
QString element = reader - > name ( ) . toString ( ) . toLower ( ) ;
if ( element = = name ) {
return true ;
2019-11-20 19:34:57 +01:00
}
}
}
2021-06-21 15:44:13 +02:00
2019-11-20 19:34:57 +01:00
return false ;
}
2018-02-27 18:06:05 +01:00
QDateTime ParseRFC822DateTime ( const QString & text ) {
2020-07-20 00:57:42 +02:00
QRegularExpression regexp ( " ( \\ d{1,2}) ( \ \ w { 3 , 12 } ) ( \ \ d + ) ( \ \ d { 1 , 2 } ) : ( \ \ d { 1 , 2 } ) : ( \ \ d { 1 , 2 } ) " );
QRegularExpressionMatch re_match = regexp . match ( text ) ;
if ( ! re_match . hasMatch ( ) ) {
2018-02-27 18:06:05 +01:00
return QDateTime ( ) ;
}
enum class MatchNames { DAYS = 1 , MONTHS , YEARS , HOURS , MINUTES , SECONDS } ;
QMap < QString , int > monthmap ;
monthmap [ " Jan " ] = 1 ;
monthmap [ " Feb " ] = 2 ;
monthmap [ " Mar " ] = 3 ;
monthmap [ " Apr " ] = 4 ;
monthmap [ " May " ] = 5 ;
monthmap [ " Jun " ] = 6 ;
monthmap [ " Jul " ] = 7 ;
monthmap [ " Aug " ] = 8 ;
monthmap [ " Sep " ] = 9 ;
monthmap [ " Oct " ] = 10 ;
monthmap [ " Nov " ] = 11 ;
monthmap [ " Dec " ] = 12 ;
monthmap [ " January " ] = 1 ;
monthmap [ " February " ] = 2 ;
monthmap [ " March " ] = 3 ;
monthmap [ " April " ] = 4 ;
monthmap [ " May " ] = 5 ;
monthmap [ " June " ] = 6 ;
monthmap [ " July " ] = 7 ;
monthmap [ " August " ] = 8 ;
monthmap [ " September " ] = 9 ;
monthmap [ " October " ] = 10 ;
monthmap [ " November " ] = 11 ;
monthmap [ " December " ] = 12 ;
2020-07-20 00:57:42 +02:00
const QDate date ( re_match . captured ( static_cast < int > ( MatchNames : : YEARS ) ) . toInt ( ) , monthmap [ re_match . captured ( static_cast < int > ( MatchNames : : MONTHS ) ) ] , re_match . captured ( static_cast < int > ( MatchNames : : DAYS ) ) . toInt ( ) ) ;
2018-02-27 18:06:05 +01:00
2020-07-20 00:57:42 +02:00
const QTime time ( re_match . captured ( static_cast < int > ( MatchNames : : HOURS ) ) . toInt ( ) , re_match . captured ( static_cast < int > ( MatchNames : : MINUTES ) ) . toInt ( ) , re_match . captured ( static_cast < int > ( MatchNames : : SECONDS ) ) . toInt ( ) ) ;
2018-02-27 18:06:05 +01:00
return QDateTime ( date , time ) ;
}
2021-06-20 19:04:08 +02:00
const char * EnumToString ( const QMetaObject & meta , const char * name , const int value ) {
2018-02-27 18:06:05 +01:00
int index = meta . indexOfEnumerator ( name ) ;
if ( index = = - 1 ) return " [UnknownEnum] " ;
QMetaEnum metaenum = meta . enumerator ( index ) ;
const char * result = metaenum . valueToKey ( value ) ;
2020-06-14 17:02:47 +02:00
if ( ! result ) return " [UnknownEnumValue] " ;
2018-02-27 18:06:05 +01:00
return result ;
}
QStringList Prepend ( const QString & text , const QStringList & list ) {
QStringList ret ( list ) ;
for ( int i = 0 ; i < ret . count ( ) ; + + i ) ret [ i ] . prepend ( text ) ;
return ret ;
}
QStringList Updateify ( const QStringList & list ) {
QStringList ret ( list ) ;
for ( int i = 0 ; i < ret . count ( ) ; + + i ) ret [ i ] . prepend ( ret [ i ] + " = : " ) ;
return ret ;
}
QString DecodeHtmlEntities ( const QString & text ) {
QString copy ( text ) ;
2021-01-11 16:05:39 +01:00
copy . replace ( " & " , " & " )
. replace ( " & " , " & " )
. replace ( " " " , " \" " )
. replace ( " " " , " \" " )
. replace ( " ' " , " ' " )
. replace ( " ' " , " ' " )
. replace ( " < " , " < " )
. replace ( " < " , " < " )
. replace ( " > " , " > " )
. replace ( " > " , " > " ) ;
2018-02-27 18:06:05 +01:00
return copy ;
}
2021-06-20 19:04:08 +02:00
int SetThreadIOPriority ( const IoPriority priority ) {
2018-02-27 18:06:05 +01:00
# ifdef Q_OS_LINUX
2018-05-01 00:41:33 +02:00
return syscall ( SYS_ioprio_set , IOPRIO_WHO_PROCESS , GetThreadId ( ) , 4 | priority < < IOPRIO_CLASS_SHIFT ) ;
2018-07-01 22:26:46 +02:00
# elif defined(Q_OS_MACOS)
2018-05-01 00:41:33 +02:00
return setpriority ( PRIO_DARWIN_THREAD , 0 , priority = = IOPRIO_CLASS_IDLE ? PRIO_DARWIN_BG : 0 ) ;
2018-02-27 18:06:05 +01:00
# else
2019-09-19 17:44:14 +02:00
Q_UNUSED ( priority ) ;
2018-02-27 18:06:05 +01:00
return 0 ;
# endif
}
int GetThreadId ( ) {
# ifdef Q_OS_LINUX
return syscall ( SYS_gettid ) ;
# else
return 0 ;
# endif
}
2020-10-19 19:56:40 +02:00
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 ;
}
2019-01-25 21:36:28 +01:00
QString GetEnv ( const QString & key ) {
return QString : : fromLocal8Bit ( qgetenv ( key . toLocal8Bit ( ) ) ) ;
}
2018-02-27 18:06:05 +01:00
void SetEnv ( const char * key , const QString & value ) {
# ifdef Q_OS_WIN32
2021-08-24 17:52:08 +02:00
_putenv ( QString ( " %1=%2 " ) . arg ( key , value ) . toLocal8Bit ( ) . constData ( ) ) ;
2018-02-27 18:06:05 +01:00
# else
setenv ( key , value . toLocal8Bit ( ) . constData ( ) , 1 ) ;
# endif
}
void IncreaseFDLimit ( ) {
2018-07-01 22:26:46 +02:00
# ifdef Q_OS_MACOS
2018-04-06 22:13:11 +02:00
// Bump the soft limit for the number of file descriptors from the default of 256 to the maximum (usually 10240).
2018-02-27 18:06:05 +01:00
struct rlimit limit ;
getrlimit ( RLIMIT_NOFILE , & limit ) ;
// getrlimit() lies about the hard limit so we have to check sysctl.
int max_fd = 0 ;
size_t len = sizeof ( max_fd ) ;
sysctlbyname ( " kern.maxfilesperproc " , & max_fd , & len , nullptr , 0 ) ;
limit . rlim_cur = max_fd ;
int ret = setrlimit ( RLIMIT_NOFILE , & limit ) ;
if ( ret = = 0 ) {
qLog ( Debug ) < < " Max fd: " < < max_fd ;
}
# endif
}
2018-09-22 15:37:42 +02:00
QString GetRandomStringWithChars ( const int len ) {
const QString UseCharacters ( " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz " ) ;
return GetRandomString ( len , UseCharacters ) ;
}
QString GetRandomStringWithCharsAndNumbers ( const int len ) {
const QString UseCharacters ( " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 " ) ;
return GetRandomString ( len , UseCharacters ) ;
}
2019-06-10 02:29:57 +02:00
QString CryptographicRandomString ( const int len ) {
const QString UseCharacters ( " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~ " ) ;
return GetRandomString ( len , UseCharacters ) ;
}
2018-09-22 15:37:42 +02:00
QString GetRandomString ( const int len , const QString & UseCharacters ) {
2021-07-11 09:49:38 +02:00
QString randstr ;
2021-08-23 21:21:08 +02:00
for ( int i = 0 ; i < len ; + + i ) {
2020-05-29 17:37:46 +02:00
# if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
2021-07-11 09:49:38 +02:00
const int index = QRandomGenerator : : global ( ) - > bounded ( 0 , UseCharacters . length ( ) ) ;
2020-05-29 17:37:46 +02:00
# else
2021-07-11 09:49:38 +02:00
const int index = qrand ( ) % UseCharacters . length ( ) ;
2020-05-29 17:37:46 +02:00
# endif
2021-07-11 09:49:38 +02:00
QChar nextchar = UseCharacters . at ( index ) ;
randstr . append ( nextchar ) ;
}
return randstr ;
2018-09-22 15:37:42 +02:00
}
2019-01-25 21:36:28 +01:00
QString DesktopEnvironment ( ) {
const QString de = GetEnv ( " XDG_CURRENT_DESKTOP " ) ;
if ( ! de . isEmpty ( ) ) return de ;
if ( ! qEnvironmentVariableIsEmpty ( " KDE_FULL_SESSION " ) ) return " KDE " ;
if ( ! qEnvironmentVariableIsEmpty ( " GNOME_DESKTOP_SESSION_ID " ) ) return " Gnome " ;
QString session = GetEnv ( " DESKTOP_SESSION " ) ;
int slash = session . lastIndexOf ( ' / ' ) ;
if ( slash ! = - 1 ) {
QSettings desktop_file ( QString ( session + " .desktop " ) , QSettings : : IniFormat ) ;
desktop_file . beginGroup ( " Desktop Entry " ) ;
QString name = desktop_file . value ( " DesktopNames " ) . toString ( ) ;
desktop_file . endGroup ( ) ;
if ( ! name . isEmpty ( ) ) return name ;
session = session . mid ( slash + 1 ) ;
}
if ( session = = " kde " ) return " KDE " ;
else if ( session = = " gnome " ) return " Gnome " ;
else if ( session = = " xfce " ) return " XFCE " ;
return " Unknown " ;
}
2021-08-24 17:52:08 +02:00
QString UnicodeToAscii ( QString unicode ) {
# ifdef _MSC_VER
return unicode
. replace ( QChar ( 229 ) , " a " )
. replace ( QChar ( 197 ) , ' A ' )
. replace ( QChar ( 230 ) , " ae " )
. replace ( QChar ( 198 ) , " AE " )
. replace ( QChar ( 248 ) , ' o ' )
. replace ( QChar ( 216 ) , ' O ' ) ;
# else
2019-07-14 03:08:19 +02:00
2019-08-26 01:37:22 +02:00
# ifdef LC_ALL
2019-07-14 03:08:19 +02:00
setlocale ( LC_ALL , " " ) ;
2019-08-26 01:37:22 +02:00
# endif
2019-07-14 03:08:19 +02:00
iconv_t conv = iconv_open ( " ASCII//TRANSLIT " , " UTF-8 " ) ;
2020-06-15 17:59:02 +02:00
if ( conv = = reinterpret_cast < iconv_t > ( - 1 ) ) return unicode ;
2019-07-14 03:08:19 +02:00
QByteArray utf8 = unicode . toUtf8 ( ) ;
2019-09-15 20:27:32 +02:00
2019-07-14 03:08:19 +02:00
size_t input_len = utf8 . length ( ) + 1 ;
2020-02-22 17:39:06 +01:00
char * input_ptr = new char [ input_len ] ;
char * input = input_ptr ;
2019-07-14 03:08:19 +02:00
2020-02-22 17:39:06 +01:00
size_t output_len = input_len * 2 ;
char * output_ptr = new char [ output_len ] ;
char * output = output_ptr ;
2019-07-14 03:08:19 +02:00
2019-09-15 20:27:32 +02:00
snprintf ( input , input_len , " %s " , utf8 . constData ( ) ) ;
2019-07-14 03:08:19 +02:00
2019-09-15 20:27:32 +02:00
iconv ( conv , & input , & input_len , & output , & output_len ) ;
2019-07-14 03:08:19 +02:00
iconv_close ( conv ) ;
2019-09-15 20:27:32 +02:00
QString ret ( output_ptr ) ;
delete [ ] input_ptr ;
delete [ ] output_ptr ;
return ret ;
2019-07-14 03:08:19 +02:00
2021-08-24 17:52:08 +02:00
# endif // _MSC_VER
2019-07-14 03:08:19 +02:00
}
2019-09-22 17:04:57 +02:00
QString MacAddress ( ) {
QString ret ;
2019-09-23 00:34:29 +02:00
for ( QNetworkInterface & netif : QNetworkInterface : : allInterfaces ( ) ) {
2019-09-22 17:04:57 +02:00
if (
2019-09-23 00:34:29 +02:00
( netif . hardwareAddress ( ) = = " 00:00:00:00:00:00 " ) | |
( netif . flags ( ) & QNetworkInterface : : IsLoopBack ) | |
! ( netif . flags ( ) & QNetworkInterface : : IsUp ) | |
! ( netif . flags ( ) & QNetworkInterface : : IsRunning )
2019-09-22 17:04:57 +02:00
) { continue ; }
2019-09-22 22:43:54 +02:00
if ( ret . isEmpty ( )
# if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
2019-09-23 00:34:29 +02:00
| | netif . type ( ) = = QNetworkInterface : : Ethernet | | netif . type ( ) = = QNetworkInterface : : Wifi
2019-09-22 22:43:54 +02:00
# endif
) {
2019-09-23 00:34:29 +02:00
ret = netif . hardwareAddress ( ) ;
2019-09-22 17:04:57 +02:00
}
}
if ( ret . isEmpty ( ) ) ret = " 00:00:00:00:00:00 " ;
return ret ;
}
2020-12-11 23:59:38 +01:00
QString ReplaceMessage ( const QString & message , const Song & song , const QString & newline , const bool html_escaped ) {
2019-12-22 12:09:05 +01:00
2020-07-20 00:57:42 +02:00
QRegularExpression variable_replacer ( " [%][a-z]+[%] " ) ;
2019-12-22 12:09:05 +01:00
QString copy ( message ) ;
// Replace the first line
int pos = 0 ;
2020-07-20 00:57:42 +02:00
QRegularExpressionMatch match ;
2021-08-23 21:21:08 +02:00
for ( match = variable_replacer . match ( message , pos ) ; match . hasMatch ( ) ; match = variable_replacer . match ( message , pos ) ) {
2020-07-20 00:57:42 +02:00
pos = match . capturedStart ( ) ;
QStringList captured = match . capturedTexts ( ) ;
2020-12-11 23:59:38 +01:00
copy . replace ( captured [ 0 ] , ReplaceVariable ( captured [ 0 ] , song , newline , html_escaped ) ) ;
2020-07-20 00:57:42 +02:00
pos + = match . capturedLength ( ) ;
2019-12-22 12:09:05 +01:00
}
2020-07-18 04:05:07 +02:00
int index_of = copy . indexOf ( QRegularExpression ( " - (>|$) " ) ) ;
2020-05-15 22:15:52 +02:00
if ( index_of > = 0 ) copy = copy . remove ( index_of , 3 ) ;
2019-12-22 12:09:05 +01:00
return copy ;
2020-12-11 23:59:38 +01:00
2019-12-22 12:09:05 +01:00
}
2020-12-11 23:59:38 +01:00
QString ReplaceVariable ( const QString & variable , const Song & song , const QString & newline , const bool html_escaped ) {
QString value = variable ;
2019-12-22 12:09:05 +01:00
2020-12-11 23:59:38 +01:00
if ( variable = = " %title% " ) {
value = song . PrettyTitle ( ) ;
2019-12-22 12:09:05 +01:00
}
else if ( variable = = " %album% " ) {
2020-12-11 23:59:38 +01:00
value = song . album ( ) ;
2019-12-22 12:09:05 +01:00
}
2020-12-11 23:59:38 +01:00
else if ( variable = = " %artist% " ) {
value = song . artist ( ) ;
2019-12-22 12:09:05 +01:00
}
else if ( variable = = " %albumartist% " ) {
2020-12-11 23:59:38 +01:00
value = song . effective_albumartist ( ) ;
}
else if ( variable = = " %track% " ) {
value . setNum ( song . track ( ) ) ;
}
else if ( variable = = " %disc% " ) {
value . setNum ( song . disc ( ) ) ;
2019-12-22 12:09:05 +01:00
}
else if ( variable = = " %year% " ) {
2020-12-11 23:59:38 +01:00
value = song . PrettyYear ( ) ;
}
else if ( variable = = " %originalyear% " ) {
value = song . PrettyOriginalYear ( ) ;
}
else if ( variable = = " %genre% " ) {
value = song . genre ( ) ;
2019-12-22 12:09:05 +01:00
}
else if ( variable = = " %composer% " ) {
2020-12-11 23:59:38 +01:00
value = song . composer ( ) ;
2019-12-22 12:09:05 +01:00
}
else if ( variable = = " %performer% " ) {
2020-12-11 23:59:38 +01:00
value = song . performer ( ) ;
2019-12-22 12:09:05 +01:00
}
else if ( variable = = " %grouping% " ) {
2020-12-11 23:59:38 +01:00
value = song . grouping ( ) ;
2019-12-22 12:09:05 +01:00
}
else if ( variable = = " %length% " ) {
2020-12-11 23:59:38 +01:00
value = song . PrettyLength ( ) ;
2019-12-22 12:09:05 +01:00
}
2020-12-11 23:59:38 +01:00
else if ( variable = = " %filename% " ) {
value = song . basefilename ( ) ;
2019-12-22 12:09:05 +01:00
}
2020-12-11 23:59:38 +01:00
else if ( variable = = " %url% " ) {
value = song . url ( ) . toString ( ) ;
2019-12-22 12:09:05 +01:00
}
else if ( variable = = " %playcount% " ) {
2020-12-11 23:59:38 +01:00
value . setNum ( song . playcount ( ) ) ;
2019-12-22 12:09:05 +01:00
}
else if ( variable = = " %skipcount% " ) {
2020-12-11 23:59:38 +01:00
value . setNum ( song . skipcount ( ) ) ;
2019-12-22 12:09:05 +01:00
}
2020-12-11 23:59:38 +01:00
else if ( variable = = " %rating% " ) {
value = song . PrettyRating ( ) ;
2019-12-22 12:09:05 +01:00
}
else if ( variable = = " %newline% " ) {
2021-07-11 09:49:38 +02:00
return QString ( newline ) ; // No HTML escaping, return immediately.
2020-12-11 23:59:38 +01:00
}
if ( html_escaped ) {
value = value . toHtmlEscaped ( ) ;
2019-12-22 12:09:05 +01:00
}
2020-12-11 23:59:38 +01:00
return value ;
2019-12-22 12:09:05 +01:00
}
2020-04-27 00:22:46 +02:00
bool IsColorDark ( const QColor & color ) {
return ( ( 30 * color . red ( ) + 59 * color . green ( ) + 11 * color . blue ( ) ) / 100 ) < = 130 ;
}
2021-02-26 21:03:51 +01:00
QByteArray ReadDataFromFile ( const QString & filename ) {
2020-10-11 01:08:42 +02:00
2021-02-26 21:03:51 +01:00
QFile file ( filename ) ;
QByteArray data ;
if ( file . open ( QIODevice : : ReadOnly ) ) {
data = file . readAll ( ) ;
file . close ( ) ;
2020-10-11 01:08:42 +02:00
}
2021-08-09 23:32:26 +02:00
else {
qLog ( Error ) < < " Failed to open file " < < filename < < " for reading: " < < file . errorString ( ) ;
}
2021-02-26 21:03:51 +01:00
return data ;
2020-10-11 01:08:42 +02:00
}
2021-02-26 21:03:51 +01:00
QString MimeTypeFromData ( const QByteArray & data ) {
2020-10-11 01:08:42 +02:00
2021-02-26 21:03:51 +01:00
if ( data . isEmpty ( ) ) return QString ( ) ;
2020-10-11 01:08:42 +02:00
2021-02-26 21:03:51 +01:00
return QMimeDatabase ( ) . mimeTypeForData ( data ) . name ( ) ;
2020-05-12 19:48:37 +02:00
}
2021-05-12 20:08:06 +02:00
# ifdef Q_OS_WIN
HRGN qt_RectToHRGN ( const QRect & rc ) ;
HRGN qt_RectToHRGN ( const QRect & rc ) {
return CreateRectRgn ( rc . left ( ) , rc . top ( ) , rc . right ( ) + 1 , rc . bottom ( ) + 1 ) ;
}
HRGN toHRGN ( const QRegion & region ) ;
HRGN toHRGN ( const QRegion & region ) {
# if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
return region . toHRGN ( ) ;
# else
const int rect_count = region . rectCount ( ) ;
if ( rect_count = = 0 ) {
return nullptr ;
}
HRGN resultRgn = nullptr ;
QRegion : : const_iterator rects = region . begin ( ) ;
resultRgn = qt_RectToHRGN ( rects [ 0 ] ) ;
2021-08-23 21:21:08 +02:00
for ( int i = 1 ; i < rect_count ; + + i ) {
2021-05-12 20:08:06 +02:00
HRGN tmpRgn = qt_RectToHRGN ( rects [ i ] ) ;
const int res = CombineRgn ( resultRgn , resultRgn , tmpRgn , RGN_OR ) ;
if ( res = = ERROR ) qWarning ( " Error combining HRGNs. " ) ;
DeleteObject ( tmpRgn ) ;
}
return resultRgn ;
# endif // Qt 6
}
void enableBlurBehindWindow ( QWindow * window , const QRegion & region ) {
DWM_BLURBEHIND dwmbb = { 0 , 0 , nullptr , 0 } ;
dwmbb . dwFlags = DWM_BB_ENABLE ;
dwmbb . fEnable = TRUE ;
HRGN rgn = nullptr ;
if ( ! region . isNull ( ) ) {
rgn = toHRGN ( region ) ;
if ( rgn ) {
dwmbb . hRgnBlur = rgn ;
dwmbb . dwFlags | = DWM_BB_BLURREGION ;
}
}
DwmEnableBlurBehindWindow ( reinterpret_cast < HWND > ( window - > winId ( ) ) , & dwmbb ) ;
if ( rgn ) {
DeleteObject ( rgn ) ;
}
}
# endif // Q_OS_WIN
2018-02-27 18:06:05 +01:00
} // namespace Utilities
ScopedWCharArray : : ScopedWCharArray ( const QString & str )
: chars_ ( str . length ( ) ) , data_ ( new wchar_t [ chars_ + 1 ] ) {
str . toWCharArray ( data_ . get ( ) ) ;
data_ [ chars_ ] = ' \0 ' ;
}