2022-05-27 02:48:54 +05:30
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright ( C ) 2022 Sefa Eyeoglu < contact @ scrumplex . net >
2013-12-31 05:54:28 +05:30
*
2022-05-27 02:48:54 +05:30
* This program 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 , version 3.
2013-12-31 05:54:28 +05:30
*
2022-05-27 02:48:54 +05:30
* This program 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 .
2013-12-31 05:54:28 +05:30
*
2022-05-27 02:48:54 +05:30
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < https : //www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice :
*
* Copyright 2013 - 2021 MultiMC Contributors
*
* Licensed under the Apache License , Version 2.0 ( the " License " ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an " AS IS " BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
2013-12-31 05:54:28 +05:30
*/
# include "IconList.h"
2015-10-05 05:17:27 +05:30
# include <FileSystem.h>
2013-12-31 05:54:28 +05:30
# include <QMap>
# include <QEventLoop>
# include <QMimeData>
# include <QUrl>
# include <QFileSystemWatcher>
2015-02-01 16:14:47 +05:30
# include <QSet>
2015-02-02 06:44:14 +05:30
# include <QDebug>
2013-12-31 05:54:28 +05:30
# define MAX_SIZE 1024
2016-11-10 07:24:53 +05:30
IconList : : IconList ( const QStringList & builtinPaths , QString path , QObject * parent ) : QAbstractListModel ( parent )
2013-12-31 05:54:28 +05:30
{
2018-07-15 18:21:05 +05:30
QSet < QString > builtinNames ;
// add builtin icons
for ( auto & builtinPath : builtinPaths )
{
QDir instance_icons ( builtinPath ) ;
auto file_info_list = instance_icons . entryInfoList ( QDir : : Files , QDir : : Name ) ;
for ( auto file_info : file_info_list )
{
2022-05-14 01:51:35 +05:30
builtinNames . insert ( file_info . completeBaseName ( ) ) ;
2018-07-15 18:21:05 +05:30
}
}
for ( auto & builtinName : builtinNames )
{
addThemeIcon ( builtinName ) ;
}
m_watcher . reset ( new QFileSystemWatcher ( ) ) ;
is_watching = false ;
connect ( m_watcher . get ( ) , SIGNAL ( directoryChanged ( QString ) ) ,
SLOT ( directoryChanged ( QString ) ) ) ;
connect ( m_watcher . get ( ) , SIGNAL ( fileChanged ( QString ) ) , SLOT ( fileChanged ( QString ) ) ) ;
directoryChanged ( path ) ;
2022-05-14 01:51:35 +05:30
// Forces the UI to update, so that lengthy icon names are shown properly from the start
emit iconUpdated ( { } ) ;
2013-12-31 05:54:28 +05:30
}
2022-06-06 22:42:50 +05:30
void IconList : : sortIconList ( )
{
qDebug ( ) < < " Sorting icon list... " ;
2022-06-07 02:48:19 +05:30
std : : sort ( icons . begin ( ) , icons . end ( ) , [ ] ( const MMCIcon & a , const MMCIcon & b ) {
2022-06-07 19:57:57 +05:30
return a . m_key . localeAwareCompare ( b . m_key ) < 0 ;
2022-06-07 02:48:19 +05:30
} ) ;
2022-06-06 22:42:50 +05:30
reindex ( ) ;
}
2013-12-31 05:54:28 +05:30
void IconList : : directoryChanged ( const QString & path )
{
2018-07-15 18:21:05 +05:30
QDir new_dir ( path ) ;
if ( m_dir . absolutePath ( ) ! = new_dir . absolutePath ( ) )
{
m_dir . setPath ( path ) ;
m_dir . refresh ( ) ;
if ( is_watching )
stopWatching ( ) ;
startWatching ( ) ;
}
if ( ! m_dir . exists ( ) )
if ( ! FS : : ensureFolderPathExists ( m_dir . absolutePath ( ) ) )
return ;
m_dir . refresh ( ) ;
auto new_list = m_dir . entryList ( QDir : : Files , QDir : : Name ) ;
for ( auto it = new_list . begin ( ) ; it ! = new_list . end ( ) ; it + + )
{
QString & foo = ( * it ) ;
foo = m_dir . filePath ( foo ) ;
}
2022-05-02 22:40:45 +05:30
# if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
QSet < QString > new_set ( new_list . begin ( ) , new_list . end ( ) ) ;
# else
2018-07-15 18:21:05 +05:30
auto new_set = new_list . toSet ( ) ;
2022-05-02 22:40:45 +05:30
# endif
2018-07-15 18:21:05 +05:30
QList < QString > current_list ;
for ( auto & it : icons )
{
if ( ! it . has ( IconType : : FileBased ) )
continue ;
current_list . push_back ( it . m_images [ IconType : : FileBased ] . filename ) ;
}
2022-05-02 22:40:45 +05:30
# if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
QSet < QString > current_set ( current_list . begin ( ) , current_list . end ( ) ) ;
# else
2018-07-15 18:21:05 +05:30
QSet < QString > current_set = current_list . toSet ( ) ;
2022-05-02 22:40:45 +05:30
# endif
2018-07-15 18:21:05 +05:30
QSet < QString > to_remove = current_set ;
to_remove - = new_set ;
QSet < QString > to_add = new_set ;
to_add - = current_set ;
for ( auto remove : to_remove )
{
qDebug ( ) < < " Removing " < < remove ;
QFileInfo rmfile ( remove ) ;
2022-05-14 01:51:35 +05:30
QString key = rmfile . completeBaseName ( ) ;
QString suffix = rmfile . suffix ( ) ;
// The icon doesnt have a suffix, but it can have other .s in the name, so we account for those as well
if ( suffix ! = " jpeg " & & suffix ! = " png " & & suffix ! = " jpg " & & suffix ! = " ico " & & suffix ! = " svg " & & suffix ! = " gif " )
key = rmfile . fileName ( ) ;
2018-07-15 18:21:05 +05:30
int idx = getIconIndex ( key ) ;
if ( idx = = - 1 )
continue ;
icons [ idx ] . remove ( IconType : : FileBased ) ;
if ( icons [ idx ] . type ( ) = = IconType : : ToBeDeleted )
{
beginRemoveRows ( QModelIndex ( ) , idx , idx ) ;
icons . remove ( idx ) ;
reindex ( ) ;
endRemoveRows ( ) ;
}
else
{
dataChanged ( index ( idx ) , index ( idx ) ) ;
}
m_watcher - > removePath ( remove ) ;
emit iconUpdated ( key ) ;
}
for ( auto add : to_add )
{
qDebug ( ) < < " Adding " < < add ;
2022-05-14 01:51:35 +05:30
2018-07-15 18:21:05 +05:30
QFileInfo addfile ( add ) ;
2022-05-14 01:51:35 +05:30
QString key = addfile . completeBaseName ( ) ;
QString suffix = addfile . suffix ( ) ;
// The icon doesnt have a suffix, but it can have other .s in the name, so we account for those as well
if ( suffix ! = " jpeg " & & suffix ! = " png " & & suffix ! = " jpg " & & suffix ! = " ico " & & suffix ! = " svg " & & suffix ! = " gif " )
key = addfile . fileName ( ) ;
2018-07-15 18:21:05 +05:30
if ( addIcon ( key , QString ( ) , addfile . filePath ( ) , IconType : : FileBased ) )
{
m_watcher - > addPath ( add ) ;
emit iconUpdated ( key ) ;
}
}
2022-06-06 22:42:50 +05:30
sortIconList ( ) ;
2013-12-31 05:54:28 +05:30
}
void IconList : : fileChanged ( const QString & path )
{
2018-07-15 18:21:05 +05:30
qDebug ( ) < < " Checking " < < path ;
QFileInfo checkfile ( path ) ;
if ( ! checkfile . exists ( ) )
return ;
2022-05-14 01:51:35 +05:30
QString key = checkfile . completeBaseName ( ) ;
2018-07-15 18:21:05 +05:30
int idx = getIconIndex ( key ) ;
if ( idx = = - 1 )
return ;
QIcon icon ( path ) ;
if ( ! icon . availableSizes ( ) . size ( ) )
return ;
icons [ idx ] . m_images [ IconType : : FileBased ] . icon = icon ;
dataChanged ( index ( idx ) , index ( idx ) ) ;
emit iconUpdated ( key ) ;
2013-12-31 05:54:28 +05:30
}
2014-07-01 05:18:09 +05:30
void IconList : : SettingChanged ( const Setting & setting , QVariant value )
2013-12-31 05:54:28 +05:30
{
2018-07-15 18:21:05 +05:30
if ( setting . id ( ) ! = " IconsDir " )
return ;
2013-12-31 05:54:28 +05:30
2018-07-15 18:21:05 +05:30
directoryChanged ( value . toString ( ) ) ;
2013-12-31 05:54:28 +05:30
}
void IconList : : startWatching ( )
{
2018-07-15 18:21:05 +05:30
auto abs_path = m_dir . absolutePath ( ) ;
FS : : ensureFolderPathExists ( abs_path ) ;
is_watching = m_watcher - > addPath ( abs_path ) ;
if ( is_watching )
{
qDebug ( ) < < " Started watching " < < abs_path ;
}
else
{
qDebug ( ) < < " Failed to start watching " < < abs_path ;
}
2013-12-31 05:54:28 +05:30
}
void IconList : : stopWatching ( )
{
2018-07-15 18:21:05 +05:30
m_watcher - > removePaths ( m_watcher - > files ( ) ) ;
m_watcher - > removePaths ( m_watcher - > directories ( ) ) ;
is_watching = false ;
2013-12-31 05:54:28 +05:30
}
QStringList IconList : : mimeTypes ( ) const
{
2018-07-15 18:21:05 +05:30
QStringList types ;
types < < " text/uri-list " ;
return types ;
2013-12-31 05:54:28 +05:30
}
Qt : : DropActions IconList : : supportedDropActions ( ) const
{
2018-07-15 18:21:05 +05:30
return Qt : : CopyAction ;
2013-12-31 05:54:28 +05:30
}
2022-11-13 20:03:25 +05:30
bool IconList : : dropMimeData ( const QMimeData * data , Qt : : DropAction action , [[maybe_unused]] int row, [[maybe_unused]] int column, [[maybe_unused]] const QModelIndex & parent )
2013-12-31 05:54:28 +05:30
{
2018-07-15 18:21:05 +05:30
if ( action = = Qt : : IgnoreAction )
return true ;
// check if the action is supported
if ( ! data | | ! ( action & supportedDropActions ( ) ) )
return false ;
// files dropped from outside?
if ( data - > hasUrls ( ) )
{
auto urls = data - > urls ( ) ;
QStringList iconFiles ;
for ( auto url : urls )
{
// only local files may be dropped...
if ( ! url . isLocalFile ( ) )
continue ;
iconFiles + = url . toLocalFile ( ) ;
}
installIcons ( iconFiles ) ;
return true ;
}
return false ;
2013-12-31 05:54:28 +05:30
}
Qt : : ItemFlags IconList : : flags ( const QModelIndex & index ) const
{
2018-07-15 18:21:05 +05:30
Qt : : ItemFlags defaultFlags = QAbstractListModel : : flags ( index ) ;
if ( index . isValid ( ) )
return Qt : : ItemIsDropEnabled | defaultFlags ;
else
return Qt : : ItemIsDropEnabled | defaultFlags ;
2013-12-31 05:54:28 +05:30
}
QVariant IconList : : data ( const QModelIndex & index , int role ) const
{
2018-07-15 18:21:05 +05:30
if ( ! index . isValid ( ) )
return QVariant ( ) ;
int row = index . row ( ) ;
if ( row < 0 | | row > = icons . size ( ) )
return QVariant ( ) ;
switch ( role )
{
case Qt : : DecorationRole :
return icons [ row ] . icon ( ) ;
case Qt : : DisplayRole :
return icons [ row ] . name ( ) ;
case Qt : : UserRole :
return icons [ row ] . m_key ;
default :
return QVariant ( ) ;
}
2013-12-31 05:54:28 +05:30
}
int IconList : : rowCount ( const QModelIndex & parent ) const
{
2018-07-15 18:21:05 +05:30
return icons . size ( ) ;
2013-12-31 05:54:28 +05:30
}
2016-10-03 04:25:54 +05:30
void IconList : : installIcons ( const QStringList & iconFiles )
2013-12-31 05:54:28 +05:30
{
2018-07-15 18:21:05 +05:30
for ( QString file : iconFiles )
{
QFileInfo fileinfo ( file ) ;
if ( ! fileinfo . isReadable ( ) | | ! fileinfo . isFile ( ) )
continue ;
2022-05-27 17:45:32 +05:30
QString target = FS : : PathCombine ( getDirectory ( ) , fileinfo . fileName ( ) ) ;
2018-07-15 18:21:05 +05:30
QString suffix = fileinfo . suffix ( ) ;
2018-11-01 02:24:22 +05:30
if ( suffix ! = " jpeg " & & suffix ! = " png " & & suffix ! = " jpg " & & suffix ! = " ico " & & suffix ! = " svg " & & suffix ! = " gif " )
2018-07-15 18:21:05 +05:30
continue ;
if ( ! QFile : : copy ( file , target ) )
continue ;
}
2013-12-31 05:54:28 +05:30
}
2018-04-07 19:45:58 +05:30
void IconList : : installIcon ( const QString & file , const QString & name )
{
2018-07-15 18:21:05 +05:30
QFileInfo fileinfo ( file ) ;
if ( ! fileinfo . isReadable ( ) | | ! fileinfo . isFile ( ) )
return ;
2018-04-07 19:45:58 +05:30
2022-05-27 17:45:32 +05:30
QString target = FS : : PathCombine ( getDirectory ( ) , name ) ;
2018-04-07 19:45:58 +05:30
2018-07-15 18:21:05 +05:30
QFile : : copy ( file , target ) ;
2018-04-07 19:45:58 +05:30
}
2016-10-03 04:25:54 +05:30
bool IconList : : iconFileExists ( const QString & key ) const
2015-06-01 04:49:12 +05:30
{
2018-07-15 18:21:05 +05:30
auto iconEntry = icon ( key ) ;
if ( ! iconEntry )
{
return false ;
}
return iconEntry - > has ( IconType : : FileBased ) ;
2015-06-01 04:49:12 +05:30
}
2016-10-03 04:25:54 +05:30
const MMCIcon * IconList : : icon ( const QString & key ) const
2015-06-01 04:49:12 +05:30
{
2018-07-15 18:21:05 +05:30
int iconIdx = getIconIndex ( key ) ;
if ( iconIdx = = - 1 )
return nullptr ;
return & icons [ iconIdx ] ;
2015-06-01 04:49:12 +05:30
}
2016-10-03 04:25:54 +05:30
bool IconList : : deleteIcon ( const QString & key )
2013-12-31 05:54:28 +05:30
{
2018-07-15 18:21:05 +05:30
int iconIdx = getIconIndex ( key ) ;
if ( iconIdx = = - 1 )
return false ;
auto & iconEntry = icons [ iconIdx ] ;
if ( iconEntry . has ( IconType : : FileBased ) )
{
return QFile : : remove ( iconEntry . m_images [ IconType : : FileBased ] . filename ) ;
}
return false ;
2013-12-31 05:54:28 +05:30
}
2016-11-10 07:24:53 +05:30
bool IconList : : addThemeIcon ( const QString & key )
{
2018-07-15 18:21:05 +05:30
auto iter = name_index . find ( key ) ;
if ( iter ! = name_index . end ( ) )
{
auto & oldOne = icons [ * iter ] ;
oldOne . replace ( Builtin , key ) ;
dataChanged ( index ( * iter ) , index ( * iter ) ) ;
return true ;
}
else
{
// add a new icon
beginInsertRows ( QModelIndex ( ) , icons . size ( ) , icons . size ( ) ) ;
{
MMCIcon mmc_icon ;
mmc_icon . m_name = key ;
mmc_icon . m_key = key ;
mmc_icon . replace ( Builtin , key ) ;
icons . push_back ( mmc_icon ) ;
name_index [ key ] = icons . size ( ) - 1 ;
}
endInsertRows ( ) ;
return true ;
}
2016-11-10 07:24:53 +05:30
}
2016-10-20 04:32:28 +05:30
bool IconList : : addIcon ( const QString & key , const QString & name , const QString & path , const IconType type )
2013-12-31 05:54:28 +05:30
{
2018-07-15 18:21:05 +05:30
// replace the icon even? is the input valid?
QIcon icon ( path ) ;
if ( icon . isNull ( ) )
return false ;
auto iter = name_index . find ( key ) ;
if ( iter ! = name_index . end ( ) )
{
auto & oldOne = icons [ * iter ] ;
oldOne . replace ( type , icon , path ) ;
dataChanged ( index ( * iter ) , index ( * iter ) ) ;
return true ;
}
else
{
// add a new icon
beginInsertRows ( QModelIndex ( ) , icons . size ( ) , icons . size ( ) ) ;
{
MMCIcon mmc_icon ;
mmc_icon . m_name = name ;
mmc_icon . m_key = key ;
mmc_icon . replace ( type , icon , path ) ;
icons . push_back ( mmc_icon ) ;
name_index [ key ] = icons . size ( ) - 1 ;
}
endInsertRows ( ) ;
return true ;
}
2013-12-31 05:54:28 +05:30
}
2016-10-20 04:32:28 +05:30
void IconList : : saveIcon ( const QString & key , const QString & path , const char * format ) const
{
2018-07-15 18:21:05 +05:30
auto icon = getIcon ( key ) ;
auto pixmap = icon . pixmap ( 128 , 128 ) ;
pixmap . save ( path , format ) ;
2016-10-20 04:32:28 +05:30
}
2013-12-31 05:54:28 +05:30
void IconList : : reindex ( )
{
2018-07-15 18:21:05 +05:30
name_index . clear ( ) ;
int i = 0 ;
for ( auto & iter : icons )
{
name_index [ iter . m_key ] = i ;
i + + ;
}
2013-12-31 05:54:28 +05:30
}
2016-10-20 04:32:28 +05:30
QIcon IconList : : getIcon ( const QString & key ) const
2013-12-31 05:54:28 +05:30
{
2018-07-15 18:21:05 +05:30
int icon_index = getIconIndex ( key ) ;
2013-12-31 05:54:28 +05:30
2018-07-15 18:21:05 +05:30
if ( icon_index ! = - 1 )
return icons [ icon_index ] . icon ( ) ;
2013-12-31 05:54:28 +05:30
2018-07-15 18:21:05 +05:30
// Fallback for icons that don't exist.
2021-10-16 04:12:01 +05:30
icon_index = getIconIndex ( " grass " ) ;
2013-12-31 05:54:28 +05:30
2018-07-15 18:21:05 +05:30
if ( icon_index ! = - 1 )
return icons [ icon_index ] . icon ( ) ;
return QIcon ( ) ;
2013-12-31 05:54:28 +05:30
}
2016-10-20 04:32:28 +05:30
int IconList : : getIconIndex ( const QString & key ) const
2013-12-31 05:54:28 +05:30
{
2021-10-16 04:12:01 +05:30
auto iter = name_index . find ( key = = " default " ? " grass " : key ) ;
2018-07-15 18:21:05 +05:30
if ( iter ! = name_index . end ( ) )
return * iter ;
2013-12-31 05:54:28 +05:30
2018-07-15 18:21:05 +05:30
return - 1 ;
2013-12-31 05:54:28 +05:30
}
2018-02-05 06:31:12 +05:30
QString IconList : : getDirectory ( ) const
{
2018-07-15 18:21:05 +05:30
return m_dir . absolutePath ( ) ;
2018-02-05 06:31:12 +05:30
}
2014-01-02 23:21:40 +05:30
//#include "IconList.moc"