2022-03-20 19:01:08 +00:00
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright ( c ) 2022 Jamie Mansfield < jmansfield @ cadixdev . org >
2022-06-12 11:50:58 +00:00
* Copyright ( C ) 2022 Sefa Eyeoglu < contact @ scrumplex . net >
2015-08-30 19:33:53 +00:00
*
2022-03-20 19:01:08 +00:00
* 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.
2015-08-30 19:33:53 +00:00
*
2022-03-20 19:01:08 +00:00
* 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 .
2015-08-30 19:33:53 +00:00
*
2022-03-20 19:01:08 +00:00
* 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 .
2015-08-30 19:33:53 +00:00
*/
2015-08-18 23:10:17 +00:00
# include "WorldListPage.h"
# include "ui_WorldListPage.h"
# include "minecraft/WorldList.h"
2021-11-22 02:55:16 +00:00
2015-08-18 23:10:17 +00:00
# include <QEvent>
2019-07-17 00:01:29 +00:00
# include <QMenu>
2015-08-18 23:10:17 +00:00
# include <QKeyEvent>
2015-09-06 21:35:58 +00:00
# include <QClipboard>
2015-08-31 19:34:15 +00:00
# include <QMessageBox>
2015-09-06 21:35:58 +00:00
# include <QTreeView>
2015-09-14 21:49:32 +00:00
# include <QInputDialog>
2021-11-22 02:55:16 +00:00
# include <QProcess>
2022-04-19 13:07:14 +00:00
# include <Qt>
2022-02-20 02:55:58 +00:00
# include <QSortFilterProxyModel>
2021-11-22 02:55:16 +00:00
# include "tools/MCEditTool.h"
# include "FileSystem.h"
# include "ui/GuiUtil.h"
# include "DesktopServices.h"
2015-09-06 21:35:58 +00:00
2021-11-20 15:22:22 +00:00
# include "Application.h"
2021-11-22 02:55:16 +00:00
2015-08-18 23:10:17 +00:00
2020-08-21 23:34:55 +00:00
class WorldListProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
public :
WorldListProxyModel ( QObject * parent ) : QSortFilterProxyModel ( parent ) { }
virtual QVariant data ( const QModelIndex & index , int role = Qt : : DisplayRole ) const
{
QModelIndex sourceIndex = mapToSource ( index ) ;
if ( index . column ( ) = = 0 & & role = = Qt : : DecorationRole )
{
WorldList * worlds = qobject_cast < WorldList * > ( sourceModel ( ) ) ;
auto iconFile = worlds - > data ( sourceIndex , WorldList : : IconFileRole ) . toString ( ) ;
if ( iconFile . isNull ( ) ) {
// NOTE: Minecraft uses the same placeholder for servers AND worlds
2021-11-20 15:22:22 +00:00
return APPLICATION - > getThemedIcon ( " unknown_server " ) ;
2020-08-21 23:34:55 +00:00
}
return QIcon ( iconFile ) ;
}
return sourceIndex . data ( role ) ;
}
} ;
2019-07-17 00:01:29 +00:00
WorldListPage : : WorldListPage ( BaseInstance * inst , std : : shared_ptr < WorldList > worlds , QWidget * parent )
: QMainWindow ( parent ) , m_inst ( inst ) , ui ( new Ui : : WorldListPage ) , m_worlds ( worlds )
2015-08-18 23:10:17 +00:00
{
2018-07-15 12:51:05 +00:00
ui - > setupUi ( this ) ;
2019-07-17 00:01:29 +00:00
2019-07-22 22:48:14 +00:00
ui - > toolBar - > insertSpacer ( ui - > actionRefresh ) ;
2019-07-17 00:01:29 +00:00
2020-08-21 23:34:55 +00:00
WorldListProxyModel * proxy = new WorldListProxyModel ( this ) ;
2018-07-15 12:51:05 +00:00
proxy - > setSortCaseSensitivity ( Qt : : CaseInsensitive ) ;
proxy - > setSourceModel ( m_worlds . get ( ) ) ;
2022-04-19 13:07:14 +00:00
proxy - > setSortRole ( Qt : : UserRole ) ;
2018-07-15 12:51:05 +00:00
ui - > worldTreeView - > setSortingEnabled ( true ) ;
ui - > worldTreeView - > setModel ( proxy ) ;
ui - > worldTreeView - > installEventFilter ( this ) ;
2019-07-24 23:02:30 +00:00
ui - > worldTreeView - > setContextMenuPolicy ( Qt : : CustomContextMenu ) ;
2020-08-21 23:34:55 +00:00
ui - > worldTreeView - > setIconSize ( QSize ( 64 , 64 ) ) ;
2019-07-24 23:02:30 +00:00
connect ( ui - > worldTreeView , & QTreeView : : customContextMenuRequested , this , & WorldListPage : : ShowContextMenu ) ;
2018-07-15 12:51:05 +00:00
auto head = ui - > worldTreeView - > header ( ) ;
head - > setSectionResizeMode ( 0 , QHeaderView : : Stretch ) ;
head - > setSectionResizeMode ( 1 , QHeaderView : : ResizeToContents ) ;
2019-07-17 00:01:29 +00:00
connect ( ui - > worldTreeView - > selectionModel ( ) , & QItemSelectionModel : : currentChanged , this , & WorldListPage : : worldChanged ) ;
2018-07-15 12:51:05 +00:00
worldChanged ( QModelIndex ( ) , QModelIndex ( ) ) ;
2015-08-18 23:10:17 +00:00
}
2018-03-19 01:36:12 +00:00
void WorldListPage : : openedImpl ( )
2015-08-18 23:10:17 +00:00
{
2018-07-15 12:51:05 +00:00
m_worlds - > startWatching ( ) ;
2015-08-18 23:10:17 +00:00
}
2018-03-19 01:36:12 +00:00
void WorldListPage : : closedImpl ( )
2015-08-18 23:10:17 +00:00
{
2018-07-15 12:51:05 +00:00
m_worlds - > stopWatching ( ) ;
2015-08-18 23:10:17 +00:00
}
WorldListPage : : ~ WorldListPage ( )
{
2018-07-15 12:51:05 +00:00
m_worlds - > stopWatching ( ) ;
delete ui ;
2015-08-18 23:10:17 +00:00
}
2019-07-24 23:02:30 +00:00
void WorldListPage : : ShowContextMenu ( const QPoint & pos )
{
auto menu = ui - > toolBar - > createContextMenu ( this , tr ( " Context menu " ) ) ;
menu - > exec ( ui - > worldTreeView - > mapToGlobal ( pos ) ) ;
delete menu ;
}
2019-07-17 00:01:29 +00:00
QMenu * WorldListPage : : createPopupMenu ( )
{
QMenu * filteredMenu = QMainWindow : : createPopupMenu ( ) ;
filteredMenu - > removeAction ( ui - > toolBar - > toggleViewAction ( ) ) ;
return filteredMenu ;
}
2015-08-18 23:10:17 +00:00
bool WorldListPage : : shouldDisplay ( ) const
{
2018-07-15 12:51:05 +00:00
return true ;
2015-08-18 23:10:17 +00:00
}
2022-02-22 18:23:53 +00:00
void WorldListPage : : retranslate ( )
{
ui - > retranslateUi ( this ) ;
}
2015-08-18 23:10:17 +00:00
bool WorldListPage : : worldListFilter ( QKeyEvent * keyEvent )
{
2018-07-15 12:51:05 +00:00
switch ( keyEvent - > key ( ) )
{
case Qt : : Key_Delete :
2019-07-17 00:01:29 +00:00
on_actionRemove_triggered ( ) ;
2018-07-15 12:51:05 +00:00
return true ;
default :
break ;
}
return QWidget : : eventFilter ( ui - > worldTreeView , keyEvent ) ;
2015-08-18 23:10:17 +00:00
}
bool WorldListPage : : eventFilter ( QObject * obj , QEvent * ev )
{
2018-07-15 12:51:05 +00:00
if ( ev - > type ( ) ! = QEvent : : KeyPress )
{
return QWidget : : eventFilter ( obj , ev ) ;
}
QKeyEvent * keyEvent = static_cast < QKeyEvent * > ( ev ) ;
if ( obj = = ui - > worldTreeView )
return worldListFilter ( keyEvent ) ;
return QWidget : : eventFilter ( obj , ev ) ;
2015-08-18 23:10:17 +00:00
}
2015-09-06 21:35:58 +00:00
2019-07-17 00:01:29 +00:00
void WorldListPage : : on_actionRemove_triggered ( )
2015-08-18 23:10:17 +00:00
{
2018-07-15 12:51:05 +00:00
auto proxiedIndex = getSelectedWorld ( ) ;
if ( ! proxiedIndex . isValid ( ) )
return ;
auto result = QMessageBox : : question ( this ,
tr ( " Are you sure? " ) ,
tr ( " This will remove the selected world permenantly. \n "
" The world will be gone forever (A LONG TIME). \n "
" \n "
" Do you want to continue? " ) ) ;
if ( result ! = QMessageBox : : Yes )
{
return ;
}
m_worlds - > stopWatching ( ) ;
m_worlds - > deleteWorld ( proxiedIndex . row ( ) ) ;
m_worlds - > startWatching ( ) ;
2015-08-18 23:10:17 +00:00
}
2019-07-17 00:01:29 +00:00
void WorldListPage : : on_actionView_Folder_triggered ( )
2015-08-18 23:10:17 +00:00
{
2018-07-15 12:51:05 +00:00
DesktopServices : : openDirectory ( m_worlds - > dir ( ) . absolutePath ( ) , true ) ;
2015-08-18 23:10:17 +00:00
}
2015-09-06 21:35:58 +00:00
2020-11-24 21:31:19 +00:00
void WorldListPage : : on_actionDatapacks_triggered ( )
{
QModelIndex index = getSelectedWorld ( ) ;
if ( ! index . isValid ( ) )
{
return ;
}
if ( ! worldSafetyNagQuestion ( ) )
return ;
auto fullPath = m_worlds - > data ( index , WorldList : : FolderRole ) . toString ( ) ;
DesktopServices : : openDirectory ( FS : : PathCombine ( fullPath , " datapacks " ) , true ) ;
}
2020-08-21 23:34:55 +00:00
void WorldListPage : : on_actionReset_Icon_triggered ( )
{
auto proxiedIndex = getSelectedWorld ( ) ;
if ( ! proxiedIndex . isValid ( ) )
return ;
if ( m_worlds - > resetIcon ( proxiedIndex . row ( ) ) ) {
ui - > actionReset_Icon - > setEnabled ( false ) ;
}
}
2015-09-06 21:35:58 +00:00
QModelIndex WorldListPage : : getSelectedWorld ( )
{
2018-07-15 12:51:05 +00:00
auto index = ui - > worldTreeView - > selectionModel ( ) - > currentIndex ( ) ;
2015-09-06 21:35:58 +00:00
2018-07-15 12:51:05 +00:00
auto proxy = ( QSortFilterProxyModel * ) ui - > worldTreeView - > model ( ) ;
return proxy - > mapToSource ( index ) ;
2015-09-06 21:35:58 +00:00
}
2019-07-17 00:01:29 +00:00
void WorldListPage : : on_actionCopy_Seed_triggered ( )
2015-09-06 21:35:58 +00:00
{
2018-07-15 12:51:05 +00:00
QModelIndex index = getSelectedWorld ( ) ;
if ( ! index . isValid ( ) )
{
return ;
}
int64_t seed = m_worlds - > data ( index , WorldList : : SeedRole ) . toLongLong ( ) ;
2021-11-20 15:22:22 +00:00
APPLICATION - > clipboard ( ) - > setText ( QString : : number ( seed ) ) ;
2015-09-06 21:35:58 +00:00
}
2019-07-17 00:01:29 +00:00
void WorldListPage : : on_actionMCEdit_triggered ( )
2015-09-06 21:35:58 +00:00
{
2018-07-15 12:51:05 +00:00
if ( m_mceditStarting )
return ;
2016-11-03 00:10:16 +00:00
2021-11-20 15:22:22 +00:00
auto mcedit = APPLICATION - > mcedit ( ) ;
2016-11-03 00:10:16 +00:00
2018-07-15 12:51:05 +00:00
const QString mceditPath = mcedit - > path ( ) ;
2015-09-06 21:35:58 +00:00
2018-07-15 12:51:05 +00:00
QModelIndex index = getSelectedWorld ( ) ;
2015-09-06 21:35:58 +00:00
2018-07-15 12:51:05 +00:00
if ( ! index . isValid ( ) )
{
return ;
}
2015-09-06 21:35:58 +00:00
2018-07-15 12:51:05 +00:00
if ( ! worldSafetyNagQuestion ( ) )
return ;
2015-09-15 20:51:10 +00:00
2018-07-15 12:51:05 +00:00
auto fullPath = m_worlds - > data ( index , WorldList : : FolderRole ) . toString ( ) ;
2015-09-06 21:35:58 +00:00
2018-07-15 12:51:05 +00:00
auto program = mcedit - > getProgramPath ( ) ;
if ( program . size ( ) )
{
2016-11-03 01:41:01 +00:00
# ifdef Q_OS_WIN32
2018-07-15 12:51:05 +00:00
if ( ! QProcess : : startDetached ( program , { fullPath } , mceditPath ) )
{
mceditError ( ) ;
}
2016-11-03 01:41:01 +00:00
# else
2018-07-15 12:51:05 +00:00
m_mceditProcess . reset ( new LoggedProcess ( ) ) ;
m_mceditProcess - > setDetachable ( true ) ;
connect ( m_mceditProcess . get ( ) , & LoggedProcess : : stateChanged , this , & WorldListPage : : mceditState ) ;
m_mceditProcess - > start ( program , { fullPath } ) ;
m_mceditProcess - > setWorkingDirectory ( mceditPath ) ;
m_mceditStarting = true ;
2016-11-03 01:41:01 +00:00
# endif
2018-07-15 12:51:05 +00:00
}
else
{
QMessageBox : : warning (
this - > parentWidget ( ) ,
tr ( " No MCEdit found or set up! " ) ,
tr ( " You do not have MCEdit set up or it was moved. \n You can set it up in the global settings. " )
) ;
}
2015-09-06 21:35:58 +00:00
}
2016-11-03 01:41:01 +00:00
void WorldListPage : : mceditError ( )
{
2018-07-15 12:51:05 +00:00
QMessageBox : : warning (
this - > parentWidget ( ) ,
tr ( " MCEdit failed to start! " ) ,
tr ( " MCEdit failed to start. \n It may be necessary to reinstall it. " )
) ;
2016-11-03 01:41:01 +00:00
}
2016-11-03 00:10:16 +00:00
void WorldListPage : : mceditState ( LoggedProcess : : State state )
{
2018-07-15 12:51:05 +00:00
bool failed = false ;
switch ( state )
{
case LoggedProcess : : NotRunning :
case LoggedProcess : : Starting :
return ;
case LoggedProcess : : FailedToStart :
case LoggedProcess : : Crashed :
case LoggedProcess : : Aborted :
{
failed = true ;
}
case LoggedProcess : : Running :
case LoggedProcess : : Finished :
{
m_mceditStarting = false ;
break ;
}
}
if ( failed )
{
mceditError ( ) ;
}
2016-11-03 00:10:16 +00:00
}
2015-09-06 21:35:58 +00:00
void WorldListPage : : worldChanged ( const QModelIndex & current , const QModelIndex & previous )
{
2018-07-15 12:51:05 +00:00
QModelIndex index = getSelectedWorld ( ) ;
bool enable = index . isValid ( ) ;
2019-07-17 00:01:29 +00:00
ui - > actionCopy_Seed - > setEnabled ( enable ) ;
ui - > actionMCEdit - > setEnabled ( enable ) ;
ui - > actionRemove - > setEnabled ( enable ) ;
ui - > actionCopy - > setEnabled ( enable ) ;
ui - > actionRename - > setEnabled ( enable ) ;
2021-03-23 20:54:48 +00:00
ui - > actionDatapacks - > setEnabled ( enable ) ;
2020-08-21 23:34:55 +00:00
bool hasIcon = ! index . data ( WorldList : : IconFileRole ) . isNull ( ) ;
ui - > actionReset_Icon - > setEnabled ( enable & & hasIcon ) ;
2015-09-06 21:35:58 +00:00
}
2015-09-09 21:53:33 +00:00
2019-07-17 00:01:29 +00:00
void WorldListPage : : on_actionAdd_triggered ( )
2015-09-09 21:53:33 +00:00
{
2018-07-15 12:51:05 +00:00
auto list = GuiUtil : : BrowseForFiles (
2019-07-17 00:01:29 +00:00
displayName ( ) ,
2018-07-15 12:51:05 +00:00
tr ( " Select a Minecraft world zip " ) ,
tr ( " Minecraft World Zip File (*.zip) " ) , QString ( ) , this - > parentWidget ( ) ) ;
if ( ! list . empty ( ) )
{
m_worlds - > stopWatching ( ) ;
for ( auto filename : list )
{
m_worlds - > installWorld ( QFileInfo ( filename ) ) ;
}
m_worlds - > startWatching ( ) ;
}
2015-09-14 21:49:32 +00:00
}
2015-09-15 20:51:10 +00:00
bool WorldListPage : : isWorldSafe ( QModelIndex )
{
2018-07-15 12:51:05 +00:00
return ! m_inst - > isRunning ( ) ;
2015-09-15 20:51:10 +00:00
}
bool WorldListPage : : worldSafetyNagQuestion ( )
{
2018-07-15 12:51:05 +00:00
if ( ! isWorldSafe ( getSelectedWorld ( ) ) )
{
auto result = QMessageBox : : question ( this , tr ( " Copy World " ) , tr ( " Changing a world while Minecraft is running is potentially unsafe. \n Do you wish to proceed? " ) ) ;
if ( result = = QMessageBox : : No )
{
return false ;
}
}
return true ;
2015-09-15 20:51:10 +00:00
}
2019-07-17 00:01:29 +00:00
void WorldListPage : : on_actionCopy_triggered ( )
2015-09-14 21:49:32 +00:00
{
2018-07-15 12:51:05 +00:00
QModelIndex index = getSelectedWorld ( ) ;
if ( ! index . isValid ( ) )
{
return ;
}
if ( ! worldSafetyNagQuestion ( ) )
return ;
auto worldVariant = m_worlds - > data ( index , WorldList : : ObjectRole ) ;
auto world = ( World * ) worldVariant . value < void * > ( ) ;
bool ok = false ;
QString name = QInputDialog : : getText ( this , tr ( " World name " ) , tr ( " Enter a new name for the copy. " ) , QLineEdit : : Normal , world - > name ( ) , & ok ) ;
if ( ok & & name . length ( ) > 0 )
{
world - > install ( m_worlds - > dir ( ) . absolutePath ( ) , name ) ;
}
2015-09-14 21:49:32 +00:00
}
2019-07-17 00:01:29 +00:00
void WorldListPage : : on_actionRename_triggered ( )
2015-09-14 21:49:32 +00:00
{
2018-07-15 12:51:05 +00:00
QModelIndex index = getSelectedWorld ( ) ;
if ( ! index . isValid ( ) )
{
return ;
}
2015-09-15 20:51:10 +00:00
2018-07-15 12:51:05 +00:00
if ( ! worldSafetyNagQuestion ( ) )
return ;
2015-09-15 20:51:10 +00:00
2018-07-15 12:51:05 +00:00
auto worldVariant = m_worlds - > data ( index , WorldList : : ObjectRole ) ;
auto world = ( World * ) worldVariant . value < void * > ( ) ;
2015-09-14 21:49:32 +00:00
2018-07-15 12:51:05 +00:00
bool ok = false ;
QString name = QInputDialog : : getText ( this , tr ( " World name " ) , tr ( " Enter a new world name. " ) , QLineEdit : : Normal , world - > name ( ) , & ok ) ;
2015-09-14 21:49:32 +00:00
2018-07-15 12:51:05 +00:00
if ( ok & & name . length ( ) > 0 )
{
world - > rename ( name ) ;
}
2015-09-14 21:49:32 +00:00
}
2015-09-15 20:51:10 +00:00
2019-07-17 00:01:29 +00:00
void WorldListPage : : on_actionRefresh_triggered ( )
2015-09-15 20:51:10 +00:00
{
2018-07-15 12:51:05 +00:00
m_worlds - > update ( ) ;
2015-09-15 20:51:10 +00:00
}
2020-08-21 23:34:55 +00:00
# include "WorldListPage.moc"